walters / rpms / nfs-utils

Forked from rpms/nfs-utils 6 years ago
Clone
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;
 	}
 
-------------------------------------------------------------------------------
libexport-a-remove-dead-code
-------------------------------------------------------------------------------
libexport.a: Remove dead code

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

Clean up:  I can't find any call sites for client_find() or
client_match().

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

 support/export/client.c    |   22 ----------------------
 support/include/exportfs.h |    2 --
 2 files changed, 0 insertions(+), 24 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 5fcf355..2af07da 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -211,28 +211,6 @@ client_freeall(void)
 	}
 }
 
-nfs_client *
-client_find(struct hostent *hp)
-{
-	nfs_client	*clp;
-	int		i;
-
-	for (i = 0; i < MCL_MAXTYPES; i++) {
-		for (clp = clientlist[i]; clp; clp = clp->m_next) {
-			if (!client_check(clp, hp))
-				continue;
-#ifdef notdef
-			if (clp->m_type == MCL_FQDN)
-				return clp;
-			return client_dup(clp, hp);
-#else
-			return clp;
-#endif
-		}
-	}
-	return NULL;
-}
-
 struct hostent *
 client_resolve(struct in_addr addr)
 {
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 1ad41e2..ef96a02 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -80,12 +80,10 @@ extern exp_hash_table exportlist[MCL_MAXTYPES];
 extern nfs_client *		clientlist[MCL_MAXTYPES];
 
 nfs_client *			client_lookup(char *hname, int canonical);
-nfs_client *			client_find(struct hostent *);
 void				client_add(nfs_client *);
 nfs_client *			client_dup(nfs_client *, struct hostent *);
 int				client_gettype(char *hname);
 int				client_check(nfs_client *, struct hostent *);
-int				client_match(nfs_client *, char *hname);
 void				client_release(nfs_client *);
 void				client_freeall(void);
 char *				client_compose(struct hostent *he);
-------------------------------------------------------------------------------
libexport-a-eliminate-nfs_clie
-------------------------------------------------------------------------------
libexport.a: Eliminate nfs_client.m_count field

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

Since nfs_client records are not garbage collected, there doesn't seem
to be any real need to maintain a reference count for each record.

In addition to removing nominally dead code, this commit gets rid of a
site where a mountd or exportfs process can exit suddenly due to a
software bug.

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

 support/export/client.c    |    9 ---------
 support/export/export.c    |    3 ---
 support/include/exportfs.h |    2 --
 3 files changed, 0 insertions(+), 14 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 2af07da..b08dda2 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -135,7 +135,6 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 		clp->m_hostname = xstrdup(hname);
 
 	clp->m_exported = 0;
-	clp->m_count = 0;
 
 	if (clp->m_type == MCL_SUBNETWORK) {
 		char	*cp = strchr(clp->m_hostname, '/');
@@ -188,14 +187,6 @@ client_add(nfs_client *clp)
 }
 
 void
-client_release(nfs_client *clp)
-{
-	if (clp->m_count <= 0)
-		xlog(L_FATAL, "client_free: m_count <= 0!");
-	clp->m_count--;
-}
-
-void
 client_freeall(void)
 {
 	nfs_client	*clp, **head;
diff --git a/support/export/export.c b/support/export/export.c
index e5e6cb0..f5846d0 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -95,7 +95,6 @@ export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep)
 	exp->m_changed = 0;
 	exp->m_warned = 0;
 	exp->m_client = clp;
-	clp->m_count++;
 }
 
 /*
@@ -115,7 +114,6 @@ export_dup(nfs_export *exp, struct hostent *hp)
 	if (exp->m_export.e_hostname)
 		new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname);
 	clp = client_dup(exp->m_client, hp);
-	clp->m_count++;
 	new->m_client = clp;
 	new->m_mayexport = exp->m_mayexport;
 	new->m_exported = 0;
@@ -266,7 +264,6 @@ export_freeall(void)
 	for (i = 0; i < MCL_MAXTYPES; i++) {
 		for (exp = exportlist[i].p_head; exp; exp = nxt) {
 			nxt = exp->m_next;
-			client_release(exp->m_client);
 			if (exp->m_export.e_squids)
 				xfree(exp->m_export.e_squids);
 			if (exp->m_export.e_sqgids)
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index ef96a02..6dec3d6 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -48,7 +48,6 @@ typedef struct mclient {
 	int			m_naddr;
 	struct in_addr		m_addrlist[NFSCLNT_ADDRMAX];
 	int			m_exported;	/* exported to nfsd */
-	int			m_count;
 } nfs_client;
 
 typedef struct mexport {
@@ -84,7 +83,6 @@ void				client_add(nfs_client *);
 nfs_client *			client_dup(nfs_client *, struct hostent *);
 int				client_gettype(char *hname);
 int				client_check(nfs_client *, struct hostent *);
-void				client_release(nfs_client *);
 void				client_freeall(void);
 char *				client_compose(struct hostent *he);
 struct hostent *		client_resolve(struct in_addr addr);
-------------------------------------------------------------------------------
libexport-a-clean-up-client_co
-------------------------------------------------------------------------------
libexport.a: Clean up client_compose() and client_member()

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

Clean up:  Replace outdated comments, and fix some function
declarations.

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

 support/export/client.c |   32 +++++++++++++++++++++-----------
 1 files changed, 21 insertions(+), 11 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index b08dda2..179471f 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -28,6 +28,8 @@
 #if !defined(__GLIBC__) || __GLIBC__ < 2
 extern int	innetgr(char *netgr, char *host, char *, char *);
 #endif
+
+static char	*add_name(char *old, char *add);
 static void	client_init(nfs_client *clp, const char *hname,
 					struct hostent *hp);
 static int	client_checkaddr(nfs_client *clp, struct in_addr addr);
@@ -215,14 +217,18 @@ client_resolve(struct in_addr addr)
 	return he;
 }
 
-/*
- * Find client name given an IP address
- * This is found by gathering all known names that match that IP address,
- * sorting them and joining them with '+'
+/**
+ * client_compose - Make a list of cached hostnames that match an IP address
+ * @he: pointer to hostent containing IP address information to match
+ *
+ * Gather all known client hostnames that match the IP address, and sort
+ * the result into a comma-separated list.
  *
+ * Returns a '\0'-terminated ASCII string containing a comma-separated
+ * sorted list of client hostnames, or NULL if no client records matched
+ * the IP address or memory could not be allocated.  Caller must free the
+ * returned string with free(3).
  */
-static char *add_name(char *old, char *add);
-
 char *
 client_compose(struct hostent *he)
 {
@@ -240,12 +246,17 @@ client_compose(struct hostent *he)
 	return name;
 }
 
+/**
+ * client_member - check if @name is contained in the list @client
+ * @client: '\0'-terminated ASCII string containing
+ *		comma-separated list of hostnames
+ * @name: '\0'-terminated ASCII string containing hostname to look for
+ *
+ * Returns 1 if @name was found in @client, otherwise zero is returned.
+ */
 int
 client_member(char *client, char *name)
 {
-	/* check if "client" (a ',' separated list of names)
-	 * contains 'name' as a member
-	 */
 	int l = strlen(name);
 	while (*client) {
 		if (strncmp(client, name, l) == 0 &&
@@ -259,8 +270,7 @@ client_member(char *client, char *name)
 	return 0;
 }
 
-
-int
+static int
 name_cmp(char *a, char *b)
 {
 	/* compare strings a and b, but only upto ',' in a */
-------------------------------------------------------------------------------
libexport-a-factor-fqdn-checki
-------------------------------------------------------------------------------
libexport.a: Factor FQDN checking out of check_client()

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

Clean up:  Factor the MCL_FQDN case out of check_client() and
client_checkaddr().  This will make it easier to add IPv6 support
eventually.

The logic in the new helper function will get a little more tangled
once IPv6 support is introduced.  Each slot in the clp address list
eventually may contain an address from either address family.

Note that the switch statement in client_checkaddr() is redundant,
since clp->cl_mtype is loop invariant.  This change makes FQDN client
checking more efficient, at the cost of a few extra lines of code.

This commit should not change code behavior in any way.

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

 support/export/client.c |   29 +++++++++++++++++++++++------
 1 files changed, 23 insertions(+), 6 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 179471f..292066d 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -319,6 +319,28 @@ add_name(char *old, char *add)
 }
 
 /*
+ * Check each address listed in @hp against each address
+ * stored in @clp.  Return 1 if a match is found, otherwise
+ * zero.
+ */
+static int
+check_fqdn(const nfs_client *clp, const struct hostent *hp)
+{
+	struct in_addr addr;
+	char **ap;
+	int i;
+
+	for (ap = hp->h_addr_list; *ap; ap++) {
+		addr = *(struct in_addr *)*ap;
+
+		for (i = 0; i < clp->m_naddr; i++)
+			if (clp->m_addrlist[i].s_addr == addr.s_addr)
+				return 1;
+	}
+	return 0;
+}
+
+/*
  * Match a host (given its hostent record) to a client record. This
  * is usually called from mountd.
  */
@@ -331,6 +353,7 @@ client_check(nfs_client *clp, struct hostent *hp)
 
 	switch (clp->m_type) {
 	case MCL_FQDN:
+		return check_fqdn(clp, hp);
 	case MCL_SUBNETWORK:
 		for (ap = hp->h_addr_list; *ap; ap++) {
 			if (client_checkaddr(clp, *(struct in_addr *) *ap))
@@ -404,12 +427,6 @@ client_checkaddr(nfs_client *clp, struct in_addr addr)
 	int	i;
 
 	switch (clp->m_type) {
-	case MCL_FQDN:
-		for (i = 0; i < clp->m_naddr; i++) {
-			if (clp->m_addrlist[i].s_addr == addr.s_addr)
-				return 1;
-		}
-		return 0;
 	case MCL_SUBNETWORK:
 		return !((clp->m_addrlist[0].s_addr ^ addr.s_addr)
 			& clp->m_addrlist[1].s_addr);
-------------------------------------------------------------------------------
libexport-a-factor-subnetwork-
-------------------------------------------------------------------------------
libexport.a: Factor SUBNETWORK checking out of check_client()

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

Clean up:  Factor the MCL_SUBNETWORK case out of check_client() and
client_checkaddr().  This will make it easier to add IPv6 support
eventually.

The logic in the new helper function will get a little more tangled
once IPv6 support is introduced.  Each slot in the clp address list
eventually may contain an address from either address family.

Note that the switch statement in client_checkaddr() is redundant,
since clp->cl_mtype is loop invariant.  This change makes SUBNETWORK
client checking more efficient, at the cost of a few extra lines of
code.

This commit should not change code behavior in any way.

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

 support/export/client.c |   32 ++++++++++++++++++++++----------
 1 files changed, 22 insertions(+), 10 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 292066d..1e9e870 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -341,6 +341,27 @@ check_fqdn(const nfs_client *clp, const struct hostent *hp)
 }
 
 /*
+ * Check each address listed in @hp against the subnetwork or
+ * host address stored in @clp.  Return 1 if an address in @hp
+ * matches the host address stored in @clp, otherwise zero.
+ */
+static int
+check_subnetwork(const nfs_client *clp, const struct hostent *hp)
+{
+	struct in_addr addr;
+	char **ap;
+
+	for (ap = hp->h_addr_list; *ap; ap++) {
+		addr = *(struct in_addr *)*ap;
+
+		if (!((clp->m_addrlist[0].s_addr ^ addr.s_addr) &
+		      clp->m_addrlist[1].s_addr))
+			return 1;
+	}
+	return 0;
+}
+
+/*
  * Match a host (given its hostent record) to a client record. This
  * is usually called from mountd.
  */
@@ -355,11 +376,7 @@ client_check(nfs_client *clp, struct hostent *hp)
 	case MCL_FQDN:
 		return check_fqdn(clp, hp);
 	case MCL_SUBNETWORK:
-		for (ap = hp->h_addr_list; *ap; ap++) {
-			if (client_checkaddr(clp, *(struct in_addr *) *ap))
-				return 1;
-		}
-		return 0;
+		return check_subnetwork(clp, hp);
 	case MCL_WILDCARD:
 		if (wildmat(hname, cname))
 			return 1;
@@ -426,11 +443,6 @@ client_checkaddr(nfs_client *clp, struct in_addr addr)
 {
 	int	i;
 
-	switch (clp->m_type) {
-	case MCL_SUBNETWORK:
-		return !((clp->m_addrlist[0].s_addr ^ addr.s_addr)
-			& clp->m_addrlist[1].s_addr);
-	}
 	return 0;
 }
 
-------------------------------------------------------------------------------
libexport-a-remove-unused-func
-------------------------------------------------------------------------------
libexport.a: Remove unused function client_checkaddr()

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

Clean up.

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

 support/export/client.c |    9 ---------
 1 files changed, 0 insertions(+), 9 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 1e9e870..11441ed 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -32,7 +32,6 @@ extern int	innetgr(char *netgr, char *host, char *, char *);
 static char	*add_name(char *old, char *add);
 static void	client_init(nfs_client *clp, const char *hname,
 					struct hostent *hp);
-static int	client_checkaddr(nfs_client *clp, struct in_addr addr);
 
 nfs_client	*clientlist[MCL_MAXTYPES] = { NULL, };
 
@@ -438,14 +437,6 @@ client_check(nfs_client *clp, struct hostent *hp)
 	return 0;
 }
 
-static int
-client_checkaddr(nfs_client *clp, struct in_addr addr)
-{
-	int	i;
-
-	return 0;
-}
-
 int
 client_gettype(char *ident)
 {
-------------------------------------------------------------------------------
libexport-a-refactor-netgroup-
-------------------------------------------------------------------------------
libexport.a: Refactor netgroup checking in client_check()

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

Clean up: refactor netgroup logic out of client_check() to make it
easier to introduce IPv6 support.

 +  Use preferred style of keeping #ifdef out of the middle of
    function definitions.  Squelch compiler warnings for "#ifndef
    HAVE_INNETGR" by using __attribute__((unused)).

 +  Use preferred style of not using curly braces around switch cases.

 +  Match style used for check_{fqdn,subnetwork}.

 +  Clarify comment documenting use of h_aliases

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

 support/export/client.c |   95 +++++++++++++++++++++++++++--------------------
 1 files changed, 55 insertions(+), 40 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 11441ed..a573767 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -361,6 +361,60 @@ check_subnetwork(const nfs_client *clp, const struct hostent *hp)
 }
 
 /*
+ * Check if @hp's hostname or aliases fall in a given netgroup.
+ * Return 1 if @hp represents a host in the netgroup, otherwise zero.
+ */
+#ifdef HAVE_INNETGR
+static int
+check_netgroup(const char *cname, const struct hostent *hp)
+{
+	const char *netgroup = cname + 1;
+	char *hname = hp->h_name;
+	struct hostent *nhp = NULL;
+	struct sockaddr_in addr;
+	int match, i;
+	char *dot;
+
+	/* First, try to match the hostname without
+	 * splitting off the domain */
+	if (innetgr(netgroup, hname, NULL, NULL))
+		return 1;
+
+	/* Try matching aliases of this host listed in /etc/hosts or nis[+] */
+	for (i = 0; hp->h_aliases[i]; i++) {
+		if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL))
+			return 1;
+	}
+
+	/* If hname is ip address convert to FQDN */
+	if (inet_aton(hname, &addr.sin_addr) &&
+	   (nhp = gethostbyaddr((const char *)&(addr.sin_addr),
+	    sizeof(addr.sin_addr), AF_INET))) {
+		hname = nhp->h_name;
+		if (innetgr(netgroup, hname, NULL, NULL))
+			return 1;
+	}
+
+	/* Okay, strip off the domain (if we have one) */
+	if ((dot = strchr(hname, '.')) == NULL)
+		return 0;
+
+	*dot = '\0';
+	match = innetgr(netgroup, hname, NULL, NULL);
+	*dot = '.';
+
+	return match;
+}
+#else	/* !HAVE_INNETGR */
+static int
+check_netgroup(__attribute__((unused)) const char *cname,
+		__attribute__((unused)) const struct hostent *hp)
+{
+	return 0;
+}
+#endif	/* !HAVE_INNETGR */
+
+/*
  * Match a host (given its hostent record) to a client record. This
  * is usually called from mountd.
  */
@@ -386,46 +440,7 @@ client_check(nfs_client *clp, struct hostent *hp)
 		}
 		return 0;
 	case MCL_NETGROUP:
-#ifdef HAVE_INNETGR
-		{
-			char	*dot;
-			int	match, i;
-			struct hostent *nhp = NULL;
-			struct sockaddr_in addr;
-
-			/* First, try to match the hostname without
-			 * splitting off the domain */
-			if (innetgr(cname+1, hname, NULL, NULL))
-				return 1;
-
-			/* try the aliases as well */
-			for (i = 0; hp->h_aliases[i]; i++) {
-				if (innetgr(cname+1, hp->h_aliases[i], NULL, NULL))
-					return 1;
-			}
-
-			/* If hname is ip address convert to FQDN */
-			if (inet_aton(hname, &addr.sin_addr) &&
-			   (nhp = gethostbyaddr((const char *)&(addr.sin_addr),
-			    sizeof(addr.sin_addr), AF_INET))) {
-				hname = (char *)nhp->h_name;
-				if (innetgr(cname+1, hname, NULL, NULL))
-					return 1;
-			}
-
-			/* Okay, strip off the domain (if we have one) */
-			if ((dot = strchr(hname, '.')) == NULL)
-				return 0;
-
-			*dot = '\0';
-			match = innetgr(cname+1, hname, NULL, NULL);
-			*dot = '.';
-
-			return match;
-		}
-#else
-		return 0;
-#endif
+		return check_netgroup(clp->m_hostname, hp);
 	case MCL_ANONYMOUS:
 		return 1;
 	case MCL_GSS:
-------------------------------------------------------------------------------
libexport-a-refactor-wildcard-
-------------------------------------------------------------------------------
libexport.a: Refactor wildcard checking in client_check()

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

Clean up: refactor wildcard logic out of client_check() to make it
easier to introduce IPv6 support.

Match the style used for client_check_{fqdn,subnetwork,netgroup}.

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

 support/export/client.c |   35 +++++++++++++++++++++++------------
 1 files changed, 23 insertions(+), 12 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index a573767..2ebab82 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -361,6 +361,28 @@ check_subnetwork(const nfs_client *clp, const struct hostent *hp)
 }
 
 /*
+ * Check if a wildcard nfs_client record matches the canonical name
+ * or the aliases of a host.  Return 1 if a match is found, otherwise
+ * zero.
+ */
+static int
+check_wildcard(char *cname, const struct hostent *hp)
+{
+	char *hname = hp->h_name;
+	char **ap;
+
+	if (wildmat(hname, cname))
+		return 1;
+
+	/* Try matching aliases of this host listed in /etc/hosts or nis[+] */
+	for (ap = hp->h_aliases; *ap; ap++)
+		if (wildmat(*ap, cname))
+			return 1;
+
+	return 0;
+}
+
+/*
  * Check if @hp's hostname or aliases fall in a given netgroup.
  * Return 1 if @hp represents a host in the netgroup, otherwise zero.
  */
@@ -421,24 +443,13 @@ check_netgroup(__attribute__((unused)) const char *cname,
 int
 client_check(nfs_client *clp, struct hostent *hp)
 {
-	char	*hname = (char *) hp->h_name;
-	char	*cname = clp->m_hostname;
-	char	**ap;
-
 	switch (clp->m_type) {
 	case MCL_FQDN:
 		return check_fqdn(clp, hp);
 	case MCL_SUBNETWORK:
 		return check_subnetwork(clp, hp);
 	case MCL_WILDCARD:
-		if (wildmat(hname, cname))
-			return 1;
-		else {
-			for (ap = hp->h_aliases; *ap; ap++)
-				if (wildmat(*ap, cname))
-					return 1;
-		}
-		return 0;
+		return check_wildcard(clp->m_hostname, hp);
 	case MCL_NETGROUP:
 		return check_netgroup(clp->m_hostname, hp);
 	case MCL_ANONYMOUS:
-------------------------------------------------------------------------------
libexport-a-replace-xlog-l_fat
-------------------------------------------------------------------------------
libexport.a: replace xlog(L_FATAL) in client_check()

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

Clean up: Prevent the process from exiting in client_check().  Report
bad m_type values, but return 0.

This removes another site where a mountd or exportfs process can exit
sideways.

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

 support/export/client.c |   13 +++++++++----
 1 files changed, 9 insertions(+), 4 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 2ebab82..adc502e 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -436,9 +436,13 @@ check_netgroup(__attribute__((unused)) const char *cname,
 }
 #endif	/* !HAVE_INNETGR */
 
-/*
- * Match a host (given its hostent record) to a client record. This
- * is usually called from mountd.
+/**
+ * client_check - check if IP address information matches a cached nfs_client
+ * @clp: pointer to a cached nfs_client record
+ * @hp: pointer to hostent containing host IP information
+ *
+ * Returns 1 if the address information matches the cached nfs_client,
+ * otherwise zero.
  */
 int
 client_check(nfs_client *clp, struct hostent *hp)
@@ -457,7 +461,8 @@ client_check(nfs_client *clp, struct hostent *hp)
 	case MCL_GSS:
 		return 0;
 	default:
-		xlog(L_FATAL, "internal: bad client type %d", clp->m_type);
+		xlog(D_GENERAL, "%s: unrecognized client type: %d",
+				__func__, clp->m_type);
 	}
 
 	return 0;
-------------------------------------------------------------------------------
libexport-a-add-client_resolve
-------------------------------------------------------------------------------
libexport.a:  Add client_resolve_addr()

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

Clean up:  Introduce a new function in libexport.a that combines
client_resolve() and converting a presentation address to a sockaddr.

This combination appears in several places in libexport.a, each of
which will need to be expanded to support IPv6.

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

 support/export/client.c    |   27 +++++++++++++++++++++++++++
 support/include/exportfs.h |    2 ++
 utils/mountd/cache.c       |   21 ++++++---------------
 3 files changed, 35 insertions(+), 15 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index adc502e..cc64b3d 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -203,6 +203,13 @@ client_freeall(void)
 	}
 }
 
+/**
+ * client_resolve - look up an IPv4 address
+ * @addr: IPv4 address to resolve
+ *
+ * Returns a dynamically allocated hostent structure, or NULL if
+ * some problem occurred.  Caller must free the result with free(3).
+ */
 struct hostent *
 client_resolve(struct in_addr addr)
 {
@@ -217,6 +224,26 @@ client_resolve(struct in_addr addr)
 }
 
 /**
+ * client_resolve_paddr - resolve a presentation address
+ * @address: '\0'-terminated ASCII string containing an IP presentation address
+ * @result: OUT: pointer to result
+ *
+ * Returns 1 and sets @result if @paddr is a valid IP presentation address;
+ * otherwise 0 is returned.  Caller must free @result with free(3).
+ */
+int
+client_resolve_paddr(const char *address, struct hostent **result)
+{
+	struct in_addr addr;
+
+	if (!inet_aton(address, &addr))
+		return 0;
+
+	*result = client_resolve(addr);
+	return 1;
+}
+
+/**
  * client_compose - Make a list of cached hostnames that match an IP address
  * @he: pointer to hostent containing IP address information to match
  *
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 6dec3d6..fa6b47b 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -86,6 +86,8 @@ int				client_check(nfs_client *, struct hostent *);
 void				client_freeall(void);
 char *				client_compose(struct hostent *he);
 struct hostent *		client_resolve(struct in_addr addr);
+int				client_resolve_paddr(const char *address,
+						struct hostent **result);
 int 				client_member(char *client, char *name);
 
 int				export_read(char *fname);
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 0af1daa..92cc87f 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -66,7 +66,6 @@ void auth_unix_ip(FILE *f)
 	char class[20];
 	char ipaddr[20];
 	char *client = NULL;
-	struct in_addr addr;
 	struct hostent *he = NULL;
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
 		return;
@@ -82,14 +81,12 @@ void auth_unix_ip(FILE *f)
 	if (qword_get(&cp, ipaddr, 20) <= 0)
 		return;
 
-	if (inet_aton(ipaddr, &addr)==0)
-		return;
-
 	auth_reload();
 
 	/* addr is a valid, interesting address, find the domain name... */
 	if (!use_ipaddr) {
-		he = client_resolve(addr);
+		if (!client_resolve_paddr(ipaddr, &he))
+			return;
 		client = client_compose(he);
 	}
 	
@@ -468,11 +465,8 @@ void nfsd_fh(FILE *f)
 				break;
 			}
 			if (use_ipaddr) {
-				if (he == NULL) {
-					if (!inet_aton(dom, &addr))
-						goto out;
-					he = client_resolve(addr);
-				}
+				if (!he && !client_resolve_paddr(dom, &he))
+					goto out;
 				if (!client_check(exp->m_client, he))
 					continue;
 			}
@@ -690,11 +684,8 @@ void nfsd_export(FILE *f)
 				continue;
 			}
 			if (use_ipaddr) {
-				if (he == NULL) {
-					if (!inet_aton(dom, &addr))
-						goto out;
-					he = client_resolve(addr);
-				}
+				if (!he && !client_resolve_paddr(dom, &he))
+					goto out;
 				if (!client_check(exp->m_client, he))
 					continue;
 			}
-------------------------------------------------------------------------------
libexport-a-reduce-code-duplic
-------------------------------------------------------------------------------
libexport.a: Reduce code duplication in client_init()

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

Clean up:  Most cases in client_init() set clp->m_naddr to zero.  Move
it to the common part of the function, and simplify the logic.  This
will make IPv6 support here more straightforward.

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

 support/export/client.c |    9 +++++----
 1 files changed, 5 insertions(+), 4 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index cc64b3d..594f459 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -136,6 +136,7 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 		clp->m_hostname = xstrdup(hname);
 
 	clp->m_exported = 0;
+	clp->m_naddr = 0;
 
 	if (clp->m_type == MCL_SUBNETWORK) {
 		char	*cp = strchr(clp->m_hostname, '/');
@@ -159,10 +160,10 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 			}
 		}
 		*cp = '/';
-		clp->m_naddr = 0;
-	} else if (!hp) {
-		clp->m_naddr = 0;
-	} else {
+		return;
+	}
+	
+	if (hp) {
 		char	**ap = hp->h_addr_list;
 		int	i;
 
-------------------------------------------------------------------------------
libexport-a-add-helper-for-pop
-------------------------------------------------------------------------------
libexport.a: Add helper for populating m_addrlist[]

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

Clean up: Move common code that populates an nfs_client record's
m_addrlist to a helper function.  This eliminates a little code
duplication, and makes it simpler to add IPv6 support later.

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

 support/export/client.c |   35 ++++++++++++++++++-----------------
 1 files changed, 18 insertions(+), 17 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 594f459..6b76e83 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -36,6 +36,21 @@ static void	client_init(nfs_client *clp, const char *hname,
 nfs_client	*clientlist[MCL_MAXTYPES] = { NULL, };
 
 
+static void
+init_addrlist(nfs_client *clp, const struct hostent *hp)
+{
+	char **ap;
+	int i;
+
+	if (!hp)
+		return;
+
+	ap = hp->h_addr_list;
+	for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
+		clp->m_addrlist[i] = *(struct in_addr *)*ap;
+	clp->m_naddr = i;
+}
+
 /* if canonical is set, then we *know* this is already a canonical name
  * so hostname lookup is avoided.
  * This is used when reading /proc/fs/nfs/exports
@@ -96,14 +111,8 @@ client_lookup(char *hname, int canonical)
 		client_add(clp);
 	}
 
-	if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) {
-		char	**ap = hp->h_addr_list;
-		int	i;
-
-		for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
-			clp->m_addrlist[i] = *(struct in_addr *)*ap;
-		clp->m_naddr = i;
-	}
+	if (htype == MCL_FQDN && clp->m_naddr == 0)
+		init_addrlist(clp, hp);
 
 	if (hp)
 		free (hp);
@@ -163,15 +172,7 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 		return;
 	}
 	
-	if (hp) {
-		char	**ap = hp->h_addr_list;
-		int	i;
-
-		for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
-			clp->m_addrlist[i] = *(struct in_addr *)*ap;
-		}
-		clp->m_naddr = i;
-	}
+	init_addrlist(clp, hp);
 }
 
 void
-------------------------------------------------------------------------------
libexport-a-fix-whitespace-dam
-------------------------------------------------------------------------------
libexport.a: Fix whitespace damage in support/export/export.c

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

Clean up whitespace damage introduced by commit 4cacc965.

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

 support/export/export.c |   11 ++++++-----
 1 files changed, 6 insertions(+), 5 deletions(-)


diff --git a/support/export/export.c b/support/export/export.c
index f5846d0..f03d7d1 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -124,6 +124,7 @@ export_dup(nfs_export *exp, struct hostent *hp)
 
 	return new;
 }
+
 /*
  * Add export entry to hash table
  */
@@ -275,11 +276,11 @@ export_freeall(void)
 			xfree(exp->m_export.e_hostname);
 			xfree(exp);
 		}
-      for(j = 0; j < HASH_TABLE_SIZE; j++) {
-        exportlist[i].entries[j].p_first = NULL;
-        exportlist[i].entries[j].p_last = NULL;
-      }
-      exportlist[i].p_head = NULL;
+		for (j = 0; j < HASH_TABLE_SIZE; j++) {
+			exportlist[i].entries[j].p_first = NULL;
+			exportlist[i].entries[j].p_last = NULL;
+		}
+		exportlist[i].p_head = NULL;
 	}
 	client_freeall();
 }
-------------------------------------------------------------------------------
libexport-a-e_fslocdata-should
-------------------------------------------------------------------------------
libexport.a: e_fslocdata should be freed with free(3)

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

Clean up: Since e_fslocdata is allocated with strdup(3), and not
xstrdup(), it should be freed with free(3), and not xfree().

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

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


diff --git a/support/export/export.c b/support/export/export.c
index f03d7d1..46045ae 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -272,7 +272,7 @@ export_freeall(void)
 			if (exp->m_export.e_mountpoint)
 				free(exp->m_export.e_mountpoint);
 			if (exp->m_export.e_fslocdata)
-				xfree(exp->m_export.e_fslocdata);
+				free(exp->m_export.e_fslocdata);
 			xfree(exp->m_export.e_hostname);
 			xfree(exp);
 		}
-------------------------------------------------------------------------------
libexport-a-add-export_free
-------------------------------------------------------------------------------
libexport.a: Add export_free()

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

Clean up: Introduce a helper to free nfs_export records.

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

 support/export/export.c |   14 ++++++++++++--
 1 files changed, 12 insertions(+), 2 deletions(-)


diff --git a/support/export/export.c b/support/export/export.c
index 46045ae..aae1995 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -28,6 +28,13 @@ static int	export_check(nfs_export *, struct hostent *, char *);
 static nfs_export *
 		export_allowed_internal(struct hostent *hp, char *path);
 
+static void
+export_free(nfs_export *exp)
+{
+	xfree(exp->m_export.e_hostname);
+	xfree(exp);
+}
+
 int
 export_read(char *fname)
 {
@@ -256,6 +263,10 @@ export_check(nfs_export *exp, struct hostent *hp, char *path)
 	return client_check(exp->m_client, hp);
 }
 
+/**
+ * export_freeall - deallocate all nfs_export records
+ *
+ */
 void
 export_freeall(void)
 {
@@ -273,8 +284,7 @@ export_freeall(void)
 				free(exp->m_export.e_mountpoint);
 			if (exp->m_export.e_fslocdata)
 				free(exp->m_export.e_fslocdata);
-			xfree(exp->m_export.e_hostname);
-			xfree(exp);
+			export_free(exp);
 		}
 		for (j = 0; j < HASH_TABLE_SIZE; j++) {
 			exportlist[i].entries[j].p_first = NULL;
-------------------------------------------------------------------------------
libexport-a-export_find-should
-------------------------------------------------------------------------------
libexport.a: export_find() should handle address parsing errors

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

Either a failed memory allocation or an address mask parsing error can
cause client_init(), and therefore client_dup(), to make our process
exit suddenly.

Since export_find() can return NULL in some cases, it can handle NULL
returns from its subroutines if an address parsing error occurs, or if
memory is exhausted.  Allow for client_dup() to return NULL instead of
exiting sideways.

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

 support/export/export.c |   19 +++++++++++++++----
 1 files changed, 15 insertions(+), 4 deletions(-)


diff --git a/support/export/export.c b/support/export/export.c
index aae1995..b64933c 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -104,10 +104,17 @@ export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep)
 	exp->m_client = clp;
 }
 
-/*
- * Duplicate exports data. The in-core export struct retains the
- * original hostname from /etc/exports, while the in-core client struct
- * gets the newly found FQDN.
+/**
+ * export_dup - Duplicate an export record
+ * @exp: pointer to export record to duplicate
+ * @hp: pointer to host info to plant in the new client record
+ *
+ * The in-core export struct retains the original hostname from
+ * /etc/exports, while the in-core client struct gets the newly
+ * found FQDN.
+ *
+ * Returns a freshly instantiated export record, or NULL if
+ * a problem occurred.
  */
 nfs_export *
 export_dup(nfs_export *exp, struct hostent *hp)
@@ -121,6 +128,10 @@ export_dup(nfs_export *exp, struct hostent *hp)
 	if (exp->m_export.e_hostname)
 		new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname);
 	clp = client_dup(exp->m_client, hp);
+	if (!clp) {
+		export_free(new);
+		return NULL;
+	}
 	new->m_client = clp;
 	new->m_mayexport = exp->m_mayexport;
 	new->m_exported = 0;
-------------------------------------------------------------------------------
libexport-a-add-client_free
-------------------------------------------------------------------------------
libexport.a: Add client_free()

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

Clean up: Introduce a helper to free nfs_client records.

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

 support/export/client.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 6b76e83..bfe5c9e 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -51,6 +51,13 @@ init_addrlist(nfs_client *clp, const struct hostent *hp)
 	clp->m_naddr = i;
 }
 
+static void
+client_free(nfs_client *clp)
+{
+	xfree(clp->m_hostname);
+	xfree(clp);
+}
+
 /* if canonical is set, then we *know* this is already a canonical name
  * so hostname lookup is avoided.
  * This is used when reading /proc/fs/nfs/exports
@@ -199,8 +206,7 @@ client_freeall(void)
 		head = clientlist + i;
 		while (*head) {
 			*head = (clp = *head)->m_next;
-			xfree(clp->m_hostname);
-			xfree(clp);
+			client_free(clp);
 		}
 	}
 }
-------------------------------------------------------------------------------
libexport-a-allow-client_init-
-------------------------------------------------------------------------------
libexport.a: Allow client_init() to fail instead of exit

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

client_init()'s current callers can now deal correctly with a failure.
Get rid of code that can cause our process to exit in client_init(),
if address mask parsing or memory allocation fails.

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

 support/export/client.c |   24 +++++++++++++++++-------
 1 files changed, 17 insertions(+), 7 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index bfe5c9e..36561b8 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -30,7 +30,7 @@ extern int	innetgr(char *netgr, char *host, char *, char *);
 #endif
 
 static char	*add_name(char *old, char *add);
-static void	client_init(nfs_client *clp, const char *hname,
+static int	client_init(nfs_client *clp, const char *hname,
 					struct hostent *hp);
 
 nfs_client	*clientlist[MCL_MAXTYPES] = { NULL, };
@@ -114,13 +114,18 @@ client_lookup(char *hname, int canonical)
 		clp = (nfs_client *) xmalloc(sizeof(*clp));
 		memset(clp, 0, sizeof(*clp));
 		clp->m_type = htype;
-		client_init(clp, hname, NULL);
+		if (!client_init(clp, hname, NULL)) {
+			client_free(clp);
+			clp = NULL;
+			goto out;
+		}
 		client_add(clp);
 	}
 
 	if (htype == MCL_FQDN && clp->m_naddr == 0)
 		init_addrlist(clp, hp);
 
+out:
 	if (hp)
 		free (hp);
 
@@ -137,12 +142,15 @@ client_dup(nfs_client *clp, struct hostent *hp)
 	new->m_type = MCL_FQDN;
 	new->m_hostname = NULL;
 
-	client_init(new, (char *) hp->h_name, hp);
+	if (!client_init(new, hp->h_name, hp)) {
+		client_free(new);
+		return NULL;
+	}
 	client_add(new);
 	return new;
 }
 
-static void
+static int
 client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 {
 	xfree(clp->m_hostname);
@@ -171,15 +179,17 @@ client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 					htonl ((uint32_t) ~0 << (32 - netmask));
 			}
 			else {
-				xlog(L_FATAL, "invalid netmask `%s' for %s",
-				     cp + 1, clp->m_hostname);
+				xlog(L_ERROR, "invalid netmask `%s' for %s",
+					     cp + 1, clp->m_hostname);
+				return 0;
 			}
 		}
 		*cp = '/';
-		return;
+		return 1;
 	}
 	
 	init_addrlist(clp, hp);
+	return 1;
 }
 
 void
-------------------------------------------------------------------------------
libexport-a-allow-m_hostname-a
-------------------------------------------------------------------------------
libexport.a: Allow m_hostname allocation to fail instead of exit

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

Replace xstrdup() with strdup(3) in client_init, to prevent the
process from exiting if the memory allocation fails.

Since I am replacing xstrdup(), I also took the opportunity to
simplify the m_hostname allocation logic.  Every caller passes a NULL
m_hostname field, so there is no need for the extra free(3) at the top
of the function.  In addition, every caller passes an appropriate
hostname string in @hname, so there's no need to decide based on the
value of @hp which string to copy.

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

 support/export/client.c |   11 ++++-------
 1 files changed, 4 insertions(+), 7 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 36561b8..56854f2 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -54,7 +54,7 @@ init_addrlist(nfs_client *clp, const struct hostent *hp)
 static void
 client_free(nfs_client *clp)
 {
-	xfree(clp->m_hostname);
+	free(clp->m_hostname);
 	xfree(clp);
 }
 
@@ -140,7 +140,6 @@ client_dup(nfs_client *clp, struct hostent *hp)
 	new = (nfs_client *) xmalloc(sizeof(*new));
 	memcpy(new, clp, sizeof(*new));
 	new->m_type = MCL_FQDN;
-	new->m_hostname = NULL;
 
 	if (!client_init(new, hp->h_name, hp)) {
 		client_free(new);
@@ -153,11 +152,9 @@ client_dup(nfs_client *clp, struct hostent *hp)
 static int
 client_init(nfs_client *clp, const char *hname, struct hostent *hp)
 {
-	xfree(clp->m_hostname);
-	if (hp)
-		clp->m_hostname = xstrdup(hp->h_name);
-	else
-		clp->m_hostname = xstrdup(hname);
+	clp->m_hostname = strdup(hname);
+	if (!clp->m_hostname)
+		return 0;
 
 	clp->m_exported = 0;
 	clp->m_naddr = 0;
-------------------------------------------------------------------------------
libexport-a-allow-malloc-3-fai
-------------------------------------------------------------------------------
libexport.a: Allow malloc(3) failures in client_lookup() and friends

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

Use malloc(3) instead of xmalloc() in client_lookup() and
client_dup(), ensuring that a failed memory allocation here doesn't
cause our process to exit suddenly.

Allocation of nfs_client records and the m_hostname string are now
consistently handled with malloc(3), calloc(3), strdup(3), and
free(3).

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

 support/export/client.c |   13 ++++++++-----
 1 files changed, 8 insertions(+), 5 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 56854f2..fb0deac 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -17,7 +17,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <netdb.h>
-#include "xmalloc.h"
+
 #include "misc.h"
 #include "nfslib.h"
 #include "exportfs.h"
@@ -55,7 +55,7 @@ static void
 client_free(nfs_client *clp)
 {
 	free(clp->m_hostname);
-	xfree(clp);
+	free(clp);
 }
 
 /* if canonical is set, then we *know* this is already a canonical name
@@ -111,8 +111,9 @@ client_lookup(char *hname, int canonical)
 	}
 
 	if (!clp) {
-		clp = (nfs_client *) xmalloc(sizeof(*clp));
-		memset(clp, 0, sizeof(*clp));
+		clp = calloc(1, sizeof(*clp));
+		if (!clp)
+			goto out;
 		clp->m_type = htype;
 		if (!client_init(clp, hname, NULL)) {
 			client_free(clp);
@@ -137,7 +138,9 @@ client_dup(nfs_client *clp, struct hostent *hp)
 {
 	nfs_client		*new;
 
-	new = (nfs_client *) xmalloc(sizeof(*new));
+	new = (nfs_client *)malloc(sizeof(*new));
+	if (!new)
+		return NULL;
 	memcpy(new, clp, sizeof(*new));
 	new->m_type = MCL_FQDN;
 
-------------------------------------------------------------------------------
libexport-a-refactor-client_in
-------------------------------------------------------------------------------
libexport.a: Refactor client_init()

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

Clean up:  Move subnet hostname parsing logic out of client_init() to
make it simpler to introduce IPv6 support.

Make client_init() a helper, since it's already static.

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

 support/export/client.c |   97 +++++++++++++++++++++++++++--------------------
 1 files changed, 55 insertions(+), 42 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index fb0deac..725730e 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -30,8 +30,6 @@ extern int	innetgr(char *netgr, char *host, char *, char *);
 #endif
 
 static char	*add_name(char *old, char *add);
-static int	client_init(nfs_client *clp, const char *hname,
-					struct hostent *hp);
 
 nfs_client	*clientlist[MCL_MAXTYPES] = { NULL, };
 
@@ -58,6 +56,61 @@ client_free(nfs_client *clp)
 	free(clp);
 }
 
+static int
+init_netmask(nfs_client *clp, const char *slash)
+{
+	if (strchr(slash + 1, '.')) {
+		clp->m_addrlist[1].s_addr = inet_addr(slash + 1);
+	} else {
+		int prefixlen = atoi(slash + 1);
+		if (0 < prefixlen && prefixlen <= 32)
+			clp->m_addrlist[1].s_addr =
+					htonl((uint32_t)~0 << (32 - prefixlen));
+		else
+			goto out_badprefix;
+	}
+
+	return 1;
+
+out_badprefix:
+	xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname);
+	return 0;
+}
+
+static int
+init_subnetwork(nfs_client *clp)
+{
+	static char slash32[] = "/32";
+	char *cp;
+
+	cp = strchr(clp->m_hostname, '/');
+	if (!cp)
+		cp = slash32;
+
+	*cp = '\0';
+	clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
+	*cp = '/';
+
+	return init_netmask(clp, cp);
+}
+
+static int
+client_init(nfs_client *clp, const char *hname, const struct hostent *hp)
+{
+	clp->m_hostname = strdup(hname);
+	if (!clp->m_hostname)
+		return 0;
+
+	clp->m_exported = 0;
+	clp->m_naddr = 0;
+
+	if (clp->m_type == MCL_SUBNETWORK)
+		return init_subnetwork(clp);
+
+	init_addrlist(clp, hp);
+	return 1;
+}
+
 /* if canonical is set, then we *know* this is already a canonical name
  * so hostname lookup is avoided.
  * This is used when reading /proc/fs/nfs/exports
@@ -152,46 +205,6 @@ client_dup(nfs_client *clp, struct hostent *hp)
 	return new;
 }
 
-static int
-client_init(nfs_client *clp, const char *hname, struct hostent *hp)
-{
-	clp->m_hostname = strdup(hname);
-	if (!clp->m_hostname)
-		return 0;
-
-	clp->m_exported = 0;
-	clp->m_naddr = 0;
-
-	if (clp->m_type == MCL_SUBNETWORK) {
-		char	*cp = strchr(clp->m_hostname, '/');
-		static char slash32[] = "/32";
-
-		if(!cp) cp = slash32;
-		*cp = '\0';
-		clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
-		if (strchr(cp + 1, '.')) {
-			clp->m_addrlist[1].s_addr = inet_addr(cp+1);
-		}
-		else {
-			int netmask = atoi(cp + 1);
-			if (0 < netmask && netmask <= 32) {
-				clp->m_addrlist[1].s_addr =
-					htonl ((uint32_t) ~0 << (32 - netmask));
-			}
-			else {
-				xlog(L_ERROR, "invalid netmask `%s' for %s",
-					     cp + 1, clp->m_hostname);
-				return 0;
-			}
-		}
-		*cp = '/';
-		return 1;
-	}
-	
-	init_addrlist(clp, hp);
-	return 1;
-}
-
 void
 client_add(nfs_client *clp)
 {
-------------------------------------------------------------------------------
libexport-a-clean-up-client_ad
-------------------------------------------------------------------------------
libexport.a: Clean up client_add()

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

Clean up: client_add()'s current callers never set unknown m_type
values, so the m_type check is unnecessary.  Remove another site where
client_lookup() or client_dup() could exit sideways.

All of client_add()'s callers are in the same source file where it is
defined, so make it a static helper function.

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

 support/export/client.c    |   26 ++++++++++++--------------
 support/include/exportfs.h |    1 -
 2 files changed, 12 insertions(+), 15 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 725730e..3c80b5b 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -111,6 +111,18 @@ client_init(nfs_client *clp, const char *hname, const struct hostent *hp)
 	return 1;
 }
 
+static void
+client_add(nfs_client *clp)
+{
+	nfs_client **cpp;
+
+	cpp = &clientlist[clp->m_type];
+	while (*cpp)
+		cpp = &((*cpp)->m_next);
+	clp->m_next = NULL;
+	*cpp = clp;
+}
+
 /* if canonical is set, then we *know* this is already a canonical name
  * so hostname lookup is avoided.
  * This is used when reading /proc/fs/nfs/exports
@@ -206,20 +218,6 @@ client_dup(nfs_client *clp, struct hostent *hp)
 }
 
 void
-client_add(nfs_client *clp)
-{
-	nfs_client	**cpp;
-
-	if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES)
-		xlog(L_FATAL, "unknown client type in client_add");
-	cpp = clientlist + clp->m_type;
-	while (*cpp)
-		cpp = &((*cpp)->m_next);
-	clp->m_next = NULL;
-	*cpp = clp;
-}
-
-void
 client_freeall(void)
 {
 	nfs_client	*clp, **head;
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index fa6b47b..36b7935 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -79,7 +79,6 @@ extern exp_hash_table exportlist[MCL_MAXTYPES];
 extern nfs_client *		clientlist[MCL_MAXTYPES];
 
 nfs_client *			client_lookup(char *hname, int canonical);
-void				client_add(nfs_client *);
 nfs_client *			client_dup(nfs_client *, struct hostent *);
 int				client_gettype(char *hname);
 int				client_check(nfs_client *, struct hostent *);
-------------------------------------------------------------------------------
mountd-exportfs-make-m_addrlis
-------------------------------------------------------------------------------
mountd/exportfs: Make m_addrlist field a sockaddr_storage

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

To store non-AF_INET addresses in the nfs_client structure, we need to
use sockaddr_storage (or at least sockaddr_in6) instead of in_addr for
the m_addrlist field.  Add a few helper functions to handle type
casting and array indexing for us.

We could treat the nfs_client address list as if all the addresses
in the list were the same family.  This might work for MCL_SUBNETWORK
type nfs_clients.  However, during the transition to IPv6, most hosts
will have at least one IPv4 and one IPv6 address.  For MCL_FQDN, I
think we need to have the ability to store addresses from both
families in one nfs_client.

Additionally, IPv6 scope IDs are not part of struct sin6_addr.  To
support link-local IPv6 addresses, the scope ID must be stored.

Thus, each slot in the address list needs to be a full socket address.

A "struct sockaddr_storage" is 130 bytes.  This increases the size of
the nfs_client data structure significantly.  We could use
sockaddr_in6 instead, if size matters.  My girlfriend tells me it
doesn't.

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

 support/export/client.c    |   38 +++++++++++++++++++++++++++++---------
 support/export/nfsctl.c    |   17 +++++++++++++----
 support/include/exportfs.h |   40 +++++++++++++++++++++++++++++++++++++++-
 utils/mountd/auth.c        |    2 +-
 utils/mountd/cache.c       |    3 ++-
 5 files changed, 84 insertions(+), 16 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 3c80b5b..f47f972 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -37,6 +37,9 @@ nfs_client	*clientlist[MCL_MAXTYPES] = { NULL, };
 static void
 init_addrlist(nfs_client *clp, const struct hostent *hp)
 {
+	struct sockaddr_in sin = {
+		.sin_family	= AF_INET,
+	};
 	char **ap;
 	int i;
 
@@ -44,8 +47,10 @@ init_addrlist(nfs_client *clp, const struct hostent *hp)
 		return;
 
 	ap = hp->h_addr_list;
-	for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
-		clp->m_addrlist[i] = *(struct in_addr *)*ap;
+	for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
+		sin.sin_addr = *(struct in_addr *)*ap;
+		set_addrlist_in(clp, i, &sin);
+	}
 	clp->m_naddr = i;
 }
 
@@ -59,17 +64,22 @@ client_free(nfs_client *clp)
 static int
 init_netmask(nfs_client *clp, const char *slash)
 {
+	struct sockaddr_in sin = {
+		.sin_family	= AF_INET,
+	};
+
 	if (strchr(slash + 1, '.')) {
-		clp->m_addrlist[1].s_addr = inet_addr(slash + 1);
+		sin.sin_addr.s_addr = inet_addr(slash + 1);
 	} else {
 		int prefixlen = atoi(slash + 1);
 		if (0 < prefixlen && prefixlen <= 32)
-			clp->m_addrlist[1].s_addr =
+			sin.sin_addr.s_addr =
 					htonl((uint32_t)~0 << (32 - prefixlen));
 		else
 			goto out_badprefix;
 	}
 
+	set_addrlist_in(clp, 1, &sin);
 	return 1;
 
 out_badprefix:
@@ -80,6 +90,9 @@ out_badprefix:
 static int
 init_subnetwork(nfs_client *clp)
 {
+	struct sockaddr_in sin = {
+		.sin_family	= AF_INET,
+	};
 	static char slash32[] = "/32";
 	char *cp;
 
@@ -88,7 +101,8 @@ init_subnetwork(nfs_client *clp)
 		cp = slash32;
 
 	*cp = '\0';
-	clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
+	sin.sin_addr.s_addr = inet_addr(clp->m_hostname);
+	set_addrlist_in(clp, 0, &sin);
 	*cp = '/';
 
 	return init_netmask(clp, cp);
@@ -381,6 +395,7 @@ add_name(char *old, char *add)
 static int
 check_fqdn(const nfs_client *clp, const struct hostent *hp)
 {
+	struct sockaddr_in *sin;
 	struct in_addr addr;
 	char **ap;
 	int i;
@@ -388,9 +403,11 @@ check_fqdn(const nfs_client *clp, const struct hostent *hp)
 	for (ap = hp->h_addr_list; *ap; ap++) {
 		addr = *(struct in_addr *)*ap;
 
-		for (i = 0; i < clp->m_naddr; i++)
-			if (clp->m_addrlist[i].s_addr == addr.s_addr)
+		for (i = 0; i < clp->m_naddr; i++) {
+			sin = get_addrlist_in(clp, i);
+			if (sin->sin_addr.s_addr == addr.s_addr)
 				return 1;
+		}
 	}
 	return 0;
 }
@@ -403,14 +420,17 @@ check_fqdn(const nfs_client *clp, const struct hostent *hp)
 static int
 check_subnetwork(const nfs_client *clp, const struct hostent *hp)
 {
+	struct sockaddr_in *address, *mask;
 	struct in_addr addr;
 	char **ap;
 
 	for (ap = hp->h_addr_list; *ap; ap++) {
+		address = get_addrlist_in(clp, 0);
+		mask = get_addrlist_in(clp, 1);
 		addr = *(struct in_addr *)*ap;
 
-		if (!((clp->m_addrlist[0].s_addr ^ addr.s_addr) &
-		      clp->m_addrlist[1].s_addr))
+		if (!((address->sin_addr.s_addr ^ addr.s_addr) &
+		      mask->sin_addr.s_addr))
 			return 1;
 	}
 	return 0;
diff --git a/support/export/nfsctl.c b/support/export/nfsctl.c
index e2877b9..ae357c7 100644
--- a/support/export/nfsctl.c
+++ b/support/export/nfsctl.c
@@ -66,7 +66,7 @@ str_tolower(char *s)
 static int
 cltsetup(struct nfsctl_client *cltarg, nfs_client *clp)
 {
-	int	i;
+	int i, j;
 
 	if (clp->m_type != MCL_FQDN) {
 		xlog(L_ERROR, "internal: can't export non-FQDN host");
@@ -76,10 +76,19 @@ cltsetup(struct nfsctl_client *cltarg, nfs_client *clp)
 	strncpy(cltarg->cl_ident, clp->m_hostname,
 		sizeof (cltarg->cl_ident) - 1);
 	str_tolower(cltarg->cl_ident);
-	cltarg->cl_naddr = clp->m_naddr;
-	for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++)
-		cltarg->cl_addrlist[i] = clp->m_addrlist[i];
 
+	j = 0;
+	for (i = 0; i < cltarg->cl_naddr && i < NFSCLNT_ADDRMAX; i++) {
+		struct sockaddr_in *sin = get_addrlist_in(clp, i);
+		if (sin->sin_family == AF_INET)
+			cltarg->cl_addrlist[j++] = sin->sin_addr;
+	}
+	if (j == 0) {
+		xlog(L_ERROR, "internal: no supported addresses in nfs_client");
+		return 0;
+	}
+
+	cltarg->cl_naddr = j;
 	return 1;
 }
 
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 36b7935..1b3c942 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -46,10 +46,48 @@ typedef struct mclient {
 	char *			m_hostname;
 	int			m_type;
 	int			m_naddr;
-	struct in_addr		m_addrlist[NFSCLNT_ADDRMAX];
+	struct sockaddr_storage	m_addrlist[NFSCLNT_ADDRMAX];
 	int			m_exported;	/* exported to nfsd */
 } nfs_client;
 
+static inline struct sockaddr *
+get_addrlist(const nfs_client *clp, const int i)
+{
+	return (struct sockaddr *)&clp->m_addrlist[i];
+}
+
+static inline struct sockaddr_in *
+get_addrlist_in(const nfs_client *clp, const int i)
+{
+	return (struct sockaddr_in *)get_addrlist(clp, i);
+}
+
+static inline struct sockaddr_in6 *
+get_addrlist_in6(const nfs_client *clp, const int i)
+{
+	return (struct sockaddr_in6 *)get_addrlist(clp, i);
+}
+
+static inline void
+set_addrlist(nfs_client *clp, const int i,
+		const struct sockaddr *sap, const socklen_t salen)
+{
+	if (salen <= sizeof(clp->m_addrlist[i]))
+		memcpy(&clp->m_addrlist[i], sap, salen);
+}
+
+static inline void
+set_addrlist_in(nfs_client *clp, const int i, const struct sockaddr_in *sin)
+{
+	set_addrlist(clp, i, (const struct sockaddr *)sin, sizeof(*sin));
+}
+
+static inline void
+set_addrlist_in6(nfs_client *clp, const int i, const struct sockaddr_in6 *sin6)
+{
+	set_addrlist(clp, i, (const struct sockaddr *)sin6, sizeof(*sin6));
+}
+
 typedef struct mexport {
 	struct mexport *	m_next;
 	struct mclient *	m_client;
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
index 388dbb3..db7f109 100644
--- a/utils/mountd/auth.c
+++ b/utils/mountd/auth.c
@@ -141,7 +141,7 @@ auth_authenticate_internal(char *what, struct sockaddr_in *caller,
 		if (my_client.m_hostname == NULL)
 			return NULL;
 		my_client.m_naddr = 1;
-		my_client.m_addrlist[0] = caller->sin_addr;
+		set_addrlist_in(&my_client, 0, caller);
 		my_exp.m_client = &my_client;
 
 		exp = NULL;
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 92cc87f..e54c4c6 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -864,6 +864,7 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path)
 
 int cache_export(nfs_export *exp, char *path)
 {
+	struct sockaddr_in *sin = get_addrlist_in(exp->m_client, 0);
 	int err;
 	FILE *f;
 
@@ -872,7 +873,7 @@ int cache_export(nfs_export *exp, char *path)
 		return -1;
 
 	qword_print(f, "nfsd");
-	qword_print(f, inet_ntoa(exp->m_client->m_addrlist[0]));
+	qword_print(f, inet_ntoa(sin->sin_addr));
 	qword_printint(f, time(0)+30*60);
 	qword_print(f, exp->m_client->m_hostname);
 	err = qword_eol(f);
-------------------------------------------------------------------------------
libexport-a-add-helpers-to-man
-------------------------------------------------------------------------------
libexport.a: Add helpers to manage DNS lookups

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

Introduce DNS query helpers based on getaddrinfo(3) and
getnameinfo(3).  These will be used to replace the existing
hostent-based helpers.

Put some of these new helpers to immediate use, where convenient.

I've added forward declarations for these new functions in exportfs.h
since they are part of libexport.a, rather than misc.h, where the
hostent-based forward declarations are currently.

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

 support/export/hostname.c  |  284 ++++++++++++++++++++++++++++++++++++++++++++
 support/include/exportfs.h |    7 +
 utils/exportfs/exportfs.c  |   11 +-
 utils/mountd/cache.c       |    3 
 utils/mountd/rmtab.c       |   19 +--
 5 files changed, 306 insertions(+), 18 deletions(-)


diff --git a/support/export/hostname.c b/support/export/hostname.c
index 8a23a89..3cd1131 100644
--- a/support/export/hostname.c
+++ b/support/export/hostname.c
@@ -26,6 +26,13 @@
 #include "misc.h"
 #endif
 
+#include "exportfs.h"
+#include "compaddr.h"
+
+#ifndef HAVE_DECL_AI_ADDRCONFIG
+#define AI_ADDRCONFIG	0
+#endif
+
 #define ALIGNMENT	sizeof (char *)
 
 static int
@@ -158,6 +165,283 @@ hostent_dup (struct hostent *hp)
   return cp;
 }
 
+#ifdef HAVE_GETNAMEINFO
+static socklen_t
+sockaddr_size(const struct sockaddr *sap)
+{
+	if (sap->sa_family != AF_INET)
+		return 0;
+	return sizeof(struct sockaddr_in);
+}
+#endif
+
+/**
+ * host_ntop - generate presentation address given a sockaddr
+ * @sap: pointer to socket address
+ *
+ * Returns a pointer to a static character buffer containing
+ * a presentation address.
+ */
+#ifdef HAVE_GETNAMEINFO
+char *
+host_ntop(const struct sockaddr *sap)
+{
+	static char buf[NI_MAXHOST];
+	socklen_t salen = sockaddr_size(sap);
+
+	if (salen == 0)
+		return strncpy(buf, "unknown family", sizeof(buf));
+
+	if (!getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0,
+				NI_NUMERICHOST))
+		return buf;
+
+	return strncpy(buf, "bad address", sizeof(buf));
+}
+#else	/* !HAVE_GETNAMEINFO */
+char *
+host_ntop(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	static char buf[INET_ADDRSTRLEN];
+
+	if (sap->sa_family != AF_INET)
+		return strncpy(buf, "unknown family", sizeof(buf));
+
+	if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, sizeof(buf)))
+		return buf;
+
+	return strncpy(buf, "bad address", sizeof(buf));
+}
+#endif	/* !HAVE_GETNAMEINFO */
+
+/**
+ * host_pton - return addrinfo for a given presentation address
+ * @paddr: pointer to a '\0'-terminated ASCII string containing an
+ *		IP presentation address
+ *
+ * Returns address info structure, or NULL if an error occurs.  Caller
+ * must free the returned structure with freeaddrinfo(3).
+ */
+struct addrinfo *
+host_pton(const char *paddr)
+{
+	struct addrinfo *ai, hints = {
+		.ai_protocol	= IPPROTO_UDP,	/* don't return duplicates */
+		.ai_flags	= AI_NUMERICHOST,
+	};
+	struct sockaddr_in sin;
+	int error;
+
+	/*
+	 * getaddrinfo(3) recognizes incomplete addresses like "10.4"
+	 * as valid AF_INET addresses.  It also accepts presentation
+	 * addresses that end with a blank.
+	 *
+	 * inet_pton(3) is much stricter.  Use it to be certain we
+	 * have a real AF_INET presentation address.
+	 */
+	if (!inet_pton(AF_INET, paddr, &sin.sin_addr))
+		return NULL;
+
+	error = getaddrinfo(paddr, NULL, &hints, &ai);
+	if (error) {
+		xlog(D_GENERAL, "%s: failed to convert %s: %s",
+				__func__, paddr, gai_strerror(error));
+		return NULL;
+	}
+
+	return ai;
+}
+
+/**
+ * host_addrinfo - return addrinfo for a given hostname
+ * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname
+ *
+ * Returns address info structure with ai_canonname filled in, or NULL
+ * if no information is available for @hostname.  Caller must free the
+ * returned structure with freeaddrinfo(3).
+ */
+struct addrinfo *
+host_addrinfo(const char *hostname)
+{
+	struct addrinfo hints = {
+		.ai_family	= AF_INET,
+		.ai_protocol	= IPPROTO_UDP,	/* don't return duplicates */
+		.ai_flags	= AI_ADDRCONFIG | AI_CANONNAME,
+	};
+	struct addrinfo *ai;
+	int error;
+
+	error = getaddrinfo(hostname, NULL, &hints, &ai);
+	if (error) {
+		xlog(D_GENERAL, "%s: failed to resolve %s: %s",
+				__func__, hostname, gai_strerror(error));
+		return NULL;
+	}
+
+	return ai;
+}
+
+/**
+ * host_canonname - return canonical hostname bound to an address
+ * @sap: pointer to socket address to look up
+ *
+ * Discover the canonical hostname associated with the given socket
+ * address.  The host's reverse mapping is verified in the process.
+ *
+ * Returns a '\0'-terminated ASCII string containing a hostname, or
+ * NULL if no hostname can be found for @sap.  Caller must free
+ * the string.
+ */
+#ifdef HAVE_GETNAMEINFO
+char *
+host_canonname(const struct sockaddr *sap)
+{
+	char buf[NI_MAXHOST];
+	socklen_t salen = sockaddr_size(sap);
+	int error;
+
+	if (salen == 0) {
+		xlog(D_GENERAL, "%s: unsupported address family %d",
+				__func__, sap->sa_family);
+		return NULL;
+	}
+
+	error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0, NI_NAMEREQD);
+	if (error) {
+		getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0,
+				NI_NUMERICHOST);
+		xlog(D_GENERAL, "%s: failed to resolve %s: %s",
+				__func__, buf, gai_strerror(error));
+		return NULL;
+	}
+
+	return strdup(buf);
+}
+#else	/* !HAVE_GETNAMEINFO */
+char *
+host_canonname(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	const struct in_addr *addr = &sin->sin_addr;
+	struct hostent *hp;
+
+	if (sap->sa_family != AF_INET)
+		return NULL;
+
+	hp = gethostbyaddr(addr, sizeof(addr), AF_INET);
+	if (!hp)
+		return NULL;
+
+	return strdup(hp->h_name);
+}
+#endif	/* !HAVE_GETNAMEINFO */
+
+/**
+ * host_reliable_addrinfo - return addrinfo for a given address
+ * @sap: pointer to socket address to look up
+ *
+ * Reverse and forward lookups are performed to ensure the address has
+ * proper forward and reverse mappings.
+ *
+ * Returns address info structure with ai_canonname filled in, or NULL
+ * if no information is available for @sap.  Caller must free the returned
+ * structure with freeaddrinfo(3).
+ */
+struct addrinfo *
+host_reliable_addrinfo(const struct sockaddr *sap)
+{
+	struct addrinfo *ai;
+	char *hostname;
+
+	hostname = host_canonname(sap);
+	if (!hostname)
+		return NULL;
+
+	ai = host_addrinfo(hostname);
+
+	free(hostname);
+	return ai;
+}
+
+/**
+ * host_numeric_addrinfo - return addrinfo without doing DNS queries
+ * @sap: pointer to socket address
+ *
+ * Returns address info structure, or NULL if an error occurred.
+ * Caller must free the returned structure with freeaddrinfo(3).
+ */
+#ifdef HAVE_GETNAMEINFO
+struct addrinfo *
+host_numeric_addrinfo(const struct sockaddr *sap)
+{
+	char buf[NI_MAXHOST];
+	struct addrinfo *ai;
+	socklen_t salen = sockaddr_size(sap);
+	int error;
+
+	if (salen == 0) {
+		xlog(D_GENERAL, "%s: unsupported address family %d",
+				__func__, sap->sa_family);
+		return NULL;
+	}
+
+	error = getnameinfo(sap, salen, buf, sizeof(buf), NULL, 0,
+				NI_NUMERICHOST);
+	if (error) {
+		xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s",
+				__func__, gai_strerror(error));
+		return NULL;
+	}
+
+	ai = host_pton(buf);
+
+	/*
+	 * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
+	 */
+	if (ai) {
+		ai->ai_canonname = strdup(buf);
+		if (!ai->ai_canonname) {
+			freeaddrinfo(ai);
+			ai = NULL;
+		}
+	}
+
+	return ai;
+}
+#else	/* !HAVE_GETNAMEINFO */
+struct addrinfo *
+host_numeric_addrinfo(const struct sockaddr *sap)
+{
+	const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+	const struct in_addr *addr = &sin->sin_addr;
+	char buf[INET_ADDRSTRLEN];
+	struct addrinfo *ai;
+
+	if (sap->sa_family != AF_INET)
+		return NULL;
+
+	if (!inet_ntop(AF_INET, (char *)addr, buf, sizeof(buf)))
+		return NULL;
+
+	ai = host_pton(buf);
+
+	/*
+	 * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
+	 */
+	if (ai) {
+		ai->ai_canonname = strdup(buf);
+		if (!ai->ai_canonname) {
+			freeaddrinfo(ai);
+			ai = NULL;
+		}
+	}
+
+	return ai;
+}
+#endif	/* !HAVE_GETNAMEINFO */
+
 static int
 is_hostname(const char *sp)
 {
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 1b3c942..9a51398 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -145,6 +145,13 @@ int				xtab_mount_write(void);
 int				xtab_export_write(void);
 void				xtab_append(nfs_export *);
 
+char *				host_ntop(const struct sockaddr *sap);
+struct addrinfo *		host_pton(const char *paddr);
+struct addrinfo *		host_addrinfo(const char *hostname);
+char *				host_canonname(const struct sockaddr *sap);
+struct addrinfo *		host_reliable_addrinfo(const struct sockaddr *sap);
+struct addrinfo *		host_numeric_addrinfo(const struct sockaddr *sap);
+
 int				rmtab_read(void);
 
 struct nfskey *			key_lookup(char *hname);
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 593a8eb..f1d1c01 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -286,7 +286,7 @@ static void
 unexportfs(char *arg, int verbose)
 {
 	nfs_export	*exp;
-	struct hostent	*hp = NULL;
+	struct addrinfo *ai = NULL;
 	char		*path;
 	char		*hname = arg;
 	int		htype;
@@ -301,10 +301,9 @@ unexportfs(char *arg, int verbose)
 	}
 
 	if ((htype = client_gettype(hname)) == MCL_FQDN) {
-		if ((hp = gethostbyname(hname)) != 0) {
-			hp = hostent_dup (hp);
-			hname = (char *) hp->h_name;
-		}
+		ai = host_addrinfo(hname);
+		if (ai)
+			hname = ai->ai_canonname;
 	}
 
 	for (exp = exportlist[htype].p_head; exp; exp = exp->m_next) {
@@ -340,7 +339,7 @@ unexportfs(char *arg, int verbose)
 		exp->m_mayexport = 0;
 	}
 
-	if (hp) free (hp);
+	freeaddrinfo(ai);
 }
 
 static int can_test(void)
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index e54c4c6..a1191d2 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -864,7 +864,6 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path)
 
 int cache_export(nfs_export *exp, char *path)
 {
-	struct sockaddr_in *sin = get_addrlist_in(exp->m_client, 0);
 	int err;
 	FILE *f;
 
@@ -873,7 +872,7 @@ int cache_export(nfs_export *exp, char *path)
 		return -1;
 
 	qword_print(f, "nfsd");
-	qword_print(f, inet_ntoa(sin->sin_addr));
+	qword_print(f, host_ntop(get_addrlist(exp->m_client, 0)));
 	qword_printint(f, time(0)+30*60);
 	qword_print(f, exp->m_client->m_hostname);
 	err = qword_eol(f);
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index c371f8d..3b45d57 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -132,8 +132,7 @@ mountlist_del(char *hname, const char *path)
 void
 mountlist_del_all(struct sockaddr_in *sin)
 {
-	struct in_addr	addr = sin->sin_addr;
-	struct hostent	*hp;
+	char		*hostname;
 	struct rmtabent	*rep;
 	nfs_export	*exp;
 	FILE		*fp;
@@ -141,27 +140,27 @@ mountlist_del_all(struct sockaddr_in *sin)
 
 	if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0)
 		return;
-	if (!(hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) {
-		xlog(L_ERROR, "can't get hostname of %s", inet_ntoa(addr));
+	hostname = host_canonname((struct sockaddr *)sin);
+	if (!hostname) {
+		xlog(L_ERROR, "can't get hostname of %s",
+				host_ntop((struct sockaddr *)sin));
 		xfunlock(lockid);
 		return;
 	}
-	else
-		hp = hostent_dup (hp);
 
 	if (!setrmtabent("r")) {
 		xfunlock(lockid);
-		free (hp);
+		free(hostname);
 		return;
 	}
 	if (!(fp = fsetrmtabent(_PATH_RMTABTMP, "w"))) {
 		endrmtabent();
 		xfunlock(lockid);
-		free (hp);
+		free(hostname);
 		return;
 	}
 	while ((rep = getrmtabent(1, NULL)) != NULL) {
-		if (strcmp(rep->r_client, hp->h_name) == 0 &&
+		if (strcmp(rep->r_client, hostname) == 0 &&
 		    (exp = auth_authenticate("umountall", sin, rep->r_path)))
 			continue;
 		fputrmtabent(fp, rep, NULL);
@@ -173,7 +172,7 @@ mountlist_del_all(struct sockaddr_in *sin)
 	endrmtabent();	/* close & unlink */
 	fendrmtabent(fp);
 	xfunlock(lockid);
-	free (hp);
+	free(hostname);
 }
 
 mountlist
-------------------------------------------------------------------------------
mountd-replace-struct-hostent-
-------------------------------------------------------------------------------
mountd: Replace "struct hostent" with "struct addrinfo"

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

struct hostent can store either IPv4 or IPv6 addresses, but it can't
store both for the same host.  Neither can hostent deal with parts of
socket addresses that are outside of the sin{,6}_addr field.

Replace the use of "struct hostent" everywhere in libexport.a, mountd,
and exportfs with "struct addrinfo".  This is a large change, but
there are so many strong dependencies on struct hostent that this
can't be broken into smaller pieces.

This also means we can get rid of hostent_dup(), since the results of
getaddrinfo(3) are already dynamically allocated.

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

 support/export/client.c    |  256 ++++++++++++++++++++++++--------------------
 support/export/export.c    |   46 ++++++--
 support/export/rmtab.c     |   46 ++++----
 support/include/exportfs.h |   19 ++-
 utils/exportfs/exportfs.c  |   39 +++----
 utils/mountd/auth.c        |   35 +++---
 utils/mountd/cache.c       |   31 ++---
 utils/mountd/mountd.c      |   24 ++--
 8 files changed, 265 insertions(+), 231 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index f47f972..c299299 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -35,21 +35,19 @@ nfs_client	*clientlist[MCL_MAXTYPES] = { NULL, };
 
 
 static void
-init_addrlist(nfs_client *clp, const struct hostent *hp)
+init_addrlist(nfs_client *clp, const struct addrinfo *ai)
 {
-	struct sockaddr_in sin = {
-		.sin_family	= AF_INET,
-	};
-	char **ap;
 	int i;
 
-	if (!hp)
+	if (!ai)
 		return;
 
-	ap = hp->h_addr_list;
-	for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
-		sin.sin_addr = *(struct in_addr *)*ap;
-		set_addrlist_in(clp, i, &sin);
+	for (i = 0; i < NFSCLNT_ADDRMAX; i++) {
+		set_addrlist(clp, i, ai->ai_addr, ai->ai_addrlen);
+
+		ai = ai->ai_next;
+		if (ai == NULL)
+			break;
 	}
 	clp->m_naddr = i;
 }
@@ -109,7 +107,7 @@ init_subnetwork(nfs_client *clp)
 }
 
 static int
-client_init(nfs_client *clp, const char *hname, const struct hostent *hp)
+client_init(nfs_client *clp, const char *hname, const struct addrinfo *ai)
 {
 	clp->m_hostname = strdup(hname);
 	if (!clp->m_hostname)
@@ -121,7 +119,7 @@ client_init(nfs_client *clp, const char *hname, const struct hostent *hp)
 	if (clp->m_type == MCL_SUBNETWORK)
 		return init_subnetwork(clp);
 
-	init_addrlist(clp, hp);
+	init_addrlist(clp, ai);
 	return 1;
 }
 
@@ -146,42 +144,21 @@ client_lookup(char *hname, int canonical)
 {
 	nfs_client	*clp = NULL;
 	int		htype;
-	struct hostent	*hp = NULL;
+	struct addrinfo	*ai = NULL;
 
 	htype = client_gettype(hname);
 
 	if (htype == MCL_FQDN && !canonical) {
-		struct hostent *hp2;
-		hp = gethostbyname(hname);
-		if (hp == NULL || hp->h_addrtype != AF_INET) {
-			xlog(L_ERROR, "%s has non-inet addr", hname);
-			return NULL;
+		ai = host_addrinfo(hname);
+		if (!ai) {
+			xlog(L_ERROR, "Failed to resolve %s", hname);
+			goto out;
 		}
-		/* make sure we have canonical name */
-		hp2 = hostent_dup(hp);
-		hp = gethostbyaddr(hp2->h_addr, hp2->h_length,
-				   hp2->h_addrtype);
-		if (hp) {
-			hp = hostent_dup(hp);
-			/* but now we might not have all addresses... */
-			if (hp2->h_addr_list[1]) {
-				struct hostent *hp3 =
-					gethostbyname(hp->h_name);
-				if (hp3) {
-					free(hp);
-					hp = hostent_dup(hp3);
-				}
-			}
-			free(hp2);
-		} else
-			hp = hp2;
-
-		hname = (char *) hp->h_name;
+		hname = ai->ai_canonname;
 
-		for (clp = clientlist[htype]; clp; clp = clp->m_next) {
-			if (client_check(clp, hp))
+		for (clp = clientlist[htype]; clp; clp = clp->m_next)
+			if (client_check(clp, ai))
 				break;
-		}
 	} else {
 		for (clp = clientlist[htype]; clp; clp = clp->m_next) {
 			if (strcasecmp(hname, clp->m_hostname)==0)
@@ -203,17 +180,24 @@ client_lookup(char *hname, int canonical)
 	}
 
 	if (htype == MCL_FQDN && clp->m_naddr == 0)
-		init_addrlist(clp, hp);
+		init_addrlist(clp, ai);
 
 out:
-	if (hp)
-		free (hp);
-
+	freeaddrinfo(ai);
 	return clp;
 }
 
+/**
+ * client_dup - create a copy of an nfs_client
+ * @clp: pointer to nfs_client to copy
+ * @ai: pointer to addrinfo used to initialize the new client's addrlist
+ *
+ * Returns a dynamically allocated nfs_client if successful, or
+ * NULL if some problem occurs.  Caller must free the returned
+ * nfs_client with free(3).
+ */
 nfs_client *
-client_dup(nfs_client *clp, struct hostent *hp)
+client_dup(const nfs_client *clp, const struct addrinfo *ai)
 {
 	nfs_client		*new;
 
@@ -223,7 +207,7 @@ client_dup(nfs_client *clp, struct hostent *hp)
 	memcpy(new, clp, sizeof(*new));
 	new->m_type = MCL_FQDN;
 
-	if (!client_init(new, hp->h_name, hp)) {
+	if (!client_init(new, ai->ai_canonname, ai)) {
 		client_free(new);
 		return NULL;
 	}
@@ -247,23 +231,23 @@ client_freeall(void)
 }
 
 /**
- * client_resolve - look up an IPv4 address
- * @addr: IPv4 address to resolve
+ * client_resolve - look up an IP address
+ * @sap: pointer to socket address to resolve
  *
- * Returns a dynamically allocated hostent structure, or NULL if
- * some problem occurred.  Caller must free the result with free(3).
+ * Returns an addrinfo structure, or NULL if some problem occurred.
+ * Caller must free the result with freeaddrinfo(3).
  */
-struct hostent *
-client_resolve(struct in_addr addr)
+struct addrinfo *
+client_resolve(const struct sockaddr *sap)
 {
-	struct hostent *he = NULL;
+	struct addrinfo *ai = NULL;
 
 	if (clientlist[MCL_WILDCARD] || clientlist[MCL_NETGROUP])
-		he = get_reliable_hostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
-	if (he == NULL)
-		he = get_hostent((const char*)&addr, sizeof(addr), AF_INET);
+		ai = host_reliable_addrinfo(sap);
+	if (!ai)
+		ai = host_numeric_addrinfo(sap);
 
-	return he;
+	return ai;
 }
 
 /**
@@ -275,20 +259,23 @@ client_resolve(struct in_addr addr)
  * otherwise 0 is returned.  Caller must free @result with free(3).
  */
 int
-client_resolve_paddr(const char *address, struct hostent **result)
+client_resolve_paddr(const char *address, struct addrinfo **result)
 {
-	struct in_addr addr;
+	struct addrinfo *ai;
 
-	if (!inet_aton(address, &addr))
+	ai = host_pton(address);
+	if (!ai)
 		return 0;
 
-	*result = client_resolve(addr);
+	*result = client_resolve(ai->ai_addr);
+
+	freeaddrinfo(ai);
 	return 1;
 }
 
 /**
  * client_compose - Make a list of cached hostnames that match an IP address
- * @he: pointer to hostent containing IP address information to match
+ * @ai: pointer to addrinfo containing IP address information to match
  *
  * Gather all known client hostnames that match the IP address, and sort
  * the result into a comma-separated list.
@@ -299,7 +286,7 @@ client_resolve_paddr(const char *address, struct hostent **result)
  * returned string with free(3).
  */
 char *
-client_compose(struct hostent *he)
+client_compose(const struct addrinfo *ai)
 {
 	char *name = NULL;
 	int i;
@@ -307,7 +294,7 @@ client_compose(struct hostent *he)
 	for (i = 0 ; i < MCL_MAXTYPES; i++) {
 		nfs_client	*clp;
 		for (clp = clientlist[i]; clp ; clp = clp->m_next) {
-			if (!client_check(clp, he))
+			if (!client_check(clp, ai))
 				continue;
 			name = add_name(name, clp->m_hostname);
 		}
@@ -327,6 +314,7 @@ int
 client_member(char *client, char *name)
 {
 	int l = strlen(name);
+
 	while (*client) {
 		if (strncmp(client, name, l) == 0 &&
 		    (client[l] == ',' || client[l] == '\0'))
@@ -387,52 +375,83 @@ add_name(char *old, char *add)
 	return new;
 }
 
+static int
+addrs_match4(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	const struct sockaddr_in *si1 = (const struct sockaddr_in *)sa1;
+	const struct sockaddr_in *si2 = (const struct sockaddr_in *)sa2;
+
+	return si1->sin_addr.s_addr == si2->sin_addr.s_addr;
+}
+
+static int
+addrs_match(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	if (sa1->sa_family == sa2->sa_family)
+		switch (sa1->sa_family) {
+		case AF_INET:
+			return addrs_match4(sa1, sa2);
+		}
+
+	return 0;
+}
+
 /*
- * Check each address listed in @hp against each address
+ * Check each address listed in @ai against each address
  * stored in @clp.  Return 1 if a match is found, otherwise
  * zero.
  */
 static int
-check_fqdn(const nfs_client *clp, const struct hostent *hp)
+check_fqdn(const nfs_client *clp, const struct addrinfo *ai)
 {
-	struct sockaddr_in *sin;
-	struct in_addr addr;
-	char **ap;
 	int i;
 
-	for (ap = hp->h_addr_list; *ap; ap++) {
-		addr = *(struct in_addr *)*ap;
-
-		for (i = 0; i < clp->m_naddr; i++) {
-			sin = get_addrlist_in(clp, i);
-			if (sin->sin_addr.s_addr == addr.s_addr)
+	for (; ai; ai = ai->ai_next)
+		for (i = 0; i < clp->m_naddr; i++)
+			if (addrs_match(ai->ai_addr, get_addrlist(clp, i)))
 				return 1;
-		}
+
+	return 0;
+}
+
+static int
+mask_match(const uint32_t a, const uint32_t b, const uint32_t m)
+{
+	return !((a ^ b) & m);
+}
+
+static int
+check_subnet_v4(const struct sockaddr_in *address,
+		const struct sockaddr_in *mask, const struct addrinfo *ai)
+{
+	for (; ai; ai = ai->ai_next) {
+		struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+
+		if (sin->sin_family != AF_INET)
+			continue;
+
+		if (mask_match(address->sin_addr.s_addr,
+				sin->sin_addr.s_addr,
+				mask->sin_addr.s_addr))
+			return 1;
 	}
 	return 0;
 }
 
 /*
- * Check each address listed in @hp against the subnetwork or
+ * Check each address listed in @ai against the subnetwork or
  * host address stored in @clp.  Return 1 if an address in @hp
  * matches the host address stored in @clp, otherwise zero.
  */
 static int
-check_subnetwork(const nfs_client *clp, const struct hostent *hp)
+check_subnetwork(const nfs_client *clp, const struct addrinfo *ai)
 {
-	struct sockaddr_in *address, *mask;
-	struct in_addr addr;
-	char **ap;
-
-	for (ap = hp->h_addr_list; *ap; ap++) {
-		address = get_addrlist_in(clp, 0);
-		mask = get_addrlist_in(clp, 1);
-		addr = *(struct in_addr *)*ap;
-
-		if (!((address->sin_addr.s_addr ^ addr.s_addr) &
-		      mask->sin_addr.s_addr))
-			return 1;
+	switch (get_addrlist(clp, 0)->sa_family) {
+	case AF_INET:
+		return check_subnet_v4(get_addrlist_in(clp, 0),
+				get_addrlist_in(clp, 1), ai);
 	}
+
 	return 0;
 }
 
@@ -442,34 +461,35 @@ check_subnetwork(const nfs_client *clp, const struct hostent *hp)
  * zero.
  */
 static int
-check_wildcard(char *cname, const struct hostent *hp)
+check_wildcard(char *cname, char *hname)
 {
-	char *hname = hp->h_name;
+	struct hostent *hp;
 	char **ap;
 
 	if (wildmat(hname, cname))
 		return 1;
 
 	/* Try matching aliases of this host listed in /etc/hosts or nis[+] */
-	for (ap = hp->h_aliases; *ap; ap++)
-		if (wildmat(*ap, cname))
-			return 1;
+	hp = gethostbyname(hname);
+	if (hp)
+		for (ap = hp->h_aliases; *ap; ap++)
+			if (wildmat(*ap, cname))
+				return 1;
 
 	return 0;
 }
 
 /*
- * Check if @hp's hostname or aliases fall in a given netgroup.
- * Return 1 if @hp represents a host in the netgroup, otherwise zero.
+ * Check if @ai's hostname or aliases fall in a given netgroup.
+ * Return 1 if @ai represents a host in the netgroup, otherwise zero.
  */
 #ifdef HAVE_INNETGR
 static int
-check_netgroup(const char *cname, const struct hostent *hp)
+check_netgroup(const char *cname, char *hname)
 {
 	const char *netgroup = cname + 1;
-	char *hname = hp->h_name;
-	struct hostent *nhp = NULL;
-	struct sockaddr_in addr;
+	struct addrinfo *ai = NULL;
+	struct hostent *hp;
 	int match, i;
 	char *dot;
 
@@ -479,17 +499,17 @@ check_netgroup(const char *cname, const struct hostent *hp)
 		return 1;
 
 	/* Try matching aliases of this host listed in /etc/hosts or nis[+] */
-	for (i = 0; hp->h_aliases[i]; i++) {
-		if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL))
-			return 1;
-	}
+	hp = gethostbyname(hname);
+	if (hp)
+		for (i = 0; hp->h_aliases[i]; i++)
+			if (innetgr(netgroup, hp->h_aliases[i], NULL, NULL))
+				return 1;
 
 	/* If hname is ip address convert to FQDN */
-	if (inet_aton(hname, &addr.sin_addr) &&
-	   (nhp = gethostbyaddr((const char *)&(addr.sin_addr),
-	    sizeof(addr.sin_addr), AF_INET))) {
-		hname = nhp->h_name;
-		if (innetgr(netgroup, hname, NULL, NULL))
+	ai = host_pton(hname);
+	if (ai) {
+		freeaddrinfo(ai);
+		if (innetgr(cname + 1, hname, NULL, NULL))
 			return 1;
 	}
 
@@ -506,7 +526,7 @@ check_netgroup(const char *cname, const struct hostent *hp)
 #else	/* !HAVE_INNETGR */
 static int
 check_netgroup(__attribute__((unused)) const char *cname,
-		__attribute__((unused)) const struct hostent *hp)
+		__attribute__((unused)) const char *hname)
 {
 	return 0;
 }
@@ -515,23 +535,23 @@ check_netgroup(__attribute__((unused)) const char *cname,
 /**
  * client_check - check if IP address information matches a cached nfs_client
  * @clp: pointer to a cached nfs_client record
- * @hp: pointer to hostent containing host IP information
+ * @ai: pointer to addrinfo to compare it with
  *
  * Returns 1 if the address information matches the cached nfs_client,
  * otherwise zero.
  */
 int
-client_check(nfs_client *clp, struct hostent *hp)
+client_check(const nfs_client *clp, const struct addrinfo *ai)
 {
 	switch (clp->m_type) {
 	case MCL_FQDN:
-		return check_fqdn(clp, hp);
+		return check_fqdn(clp, ai);
 	case MCL_SUBNETWORK:
-		return check_subnetwork(clp, hp);
+		return check_subnetwork(clp, ai);
 	case MCL_WILDCARD:
-		return check_wildcard(clp->m_hostname, hp);
+		return check_wildcard(clp->m_hostname, ai->ai_canonname);
 	case MCL_NETGROUP:
-		return check_netgroup(clp->m_hostname, hp);
+		return check_netgroup(clp->m_hostname, ai->ai_canonname);
 	case MCL_ANONYMOUS:
 		return 1;
 	case MCL_GSS:
diff --git a/support/export/export.c b/support/export/export.c
index b64933c..fe99f4f 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -24,9 +24,11 @@ static int export_hash(char *);
 
 static void	export_init(nfs_export *exp, nfs_client *clp,
 					struct exportent *nep);
-static int	export_check(nfs_export *, struct hostent *, char *);
+static int	export_check(const nfs_export *exp, const struct addrinfo *ai,
+				const char *path);
 static nfs_export *
-		export_allowed_internal(struct hostent *hp, char *path);
+		export_allowed_internal(const struct addrinfo *ai,
+				const char *path);
 
 static void
 export_free(nfs_export *exp)
@@ -116,8 +118,8 @@ export_init(nfs_export *exp, nfs_client *clp, struct exportent *nep)
  * Returns a freshly instantiated export record, or NULL if
  * a problem occurred.
  */
-nfs_export *
-export_dup(nfs_export *exp, struct hostent *hp)
+static nfs_export *
+export_dup(nfs_export *exp, const struct addrinfo *ai)
 {
 	nfs_export		*new;
 	nfs_client		*clp;
@@ -127,7 +129,7 @@ export_dup(nfs_export *exp, struct hostent *hp)
 	dupexportent(&new->m_export, &exp->m_export);
 	if (exp->m_export.e_hostname)
 		new->m_export.e_hostname = xstrdup(exp->m_export.e_hostname);
-	clp = client_dup(exp->m_client, hp);
+	clp = client_dup(exp->m_client, ai);
 	if (!clp) {
 		export_free(new);
 		return NULL;
@@ -174,19 +176,27 @@ export_add(nfs_export *exp)
 	}
 }
 
+/**
+ * export_find - find or create a suitable nfs_export for @ai and @path
+ * @ai: pointer to addrinfo for client
+ * @path: '\0'-terminated ASCII string containing export path
+ *
+ * Returns a pointer to nfs_export data matching @ai and @path,
+ * or NULL if an error occurs.
+ */
 nfs_export *
-export_find(struct hostent *hp, char *path)
+export_find(const struct addrinfo *ai, const char *path)
 {
 	nfs_export	*exp;
 	int		i;
 
 	for (i = 0; i < MCL_MAXTYPES; i++) {
 		for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
-			if (!export_check(exp, hp, path))
+			if (!export_check(exp, ai, path))
 				continue;
 			if (exp->m_client->m_type == MCL_FQDN)
 				return exp;
-			return export_dup(exp, hp);
+			return export_dup(exp, ai);
 		}
 	}
 
@@ -194,7 +204,7 @@ export_find(struct hostent *hp, char *path)
 }
 
 static nfs_export *
-export_allowed_internal (struct hostent *hp, char *path)
+export_allowed_internal(const struct addrinfo *ai, const char *path)
 {
 	nfs_export	*exp;
 	int		i;
@@ -202,7 +212,7 @@ export_allowed_internal (struct hostent *hp, char *path)
 	for (i = 0; i < MCL_MAXTYPES; i++) {
 		for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
 			if (!exp->m_mayexport ||
-			    !export_check(exp, hp, path))
+			    !export_check(exp, ai, path))
 				continue;
 			return exp;
 		}
@@ -211,8 +221,16 @@ export_allowed_internal (struct hostent *hp, char *path)
 	return NULL;
 }
 
+/**
+ * export_allowed - determine if this export is allowed
+ * @ai: pointer to addrinfo for client
+ * @path: '\0'-terminated ASCII string containing export path
+ *
+ * Returns a pointer to nfs_export data matching @ai and @path,
+ * or NULL if the export is not allowed.
+ */
 nfs_export *
-export_allowed(struct hostent *hp, char *path)
+export_allowed(const struct addrinfo *ai, const char *path)
 {
 	nfs_export		*exp;
 	char			epath[MAXPATHLEN+1];
@@ -225,7 +243,7 @@ export_allowed(struct hostent *hp, char *path)
 
 	/* Try the longest matching exported pathname. */
 	while (1) {
-		exp = export_allowed_internal (hp, epath);
+		exp = export_allowed_internal(ai, epath);
 		if (exp)
 			return exp;
 		/* We have to treat the root, "/", specially. */
@@ -266,12 +284,12 @@ export_lookup(char *hname, char *path, int canonical)
 }
 
 static int
-export_check(nfs_export *exp, struct hostent *hp, char *path)
+export_check(const nfs_export *exp, const struct addrinfo *ai, const char *path)
 {
 	if (strcmp(path, exp->m_export.e_path))
 		return 0;
 
-	return client_check(exp->m_client, hp);
+	return client_check(exp->m_client, ai);
 }
 
 /**
diff --git a/support/export/rmtab.c b/support/export/rmtab.c
index b49e1aa..ff348bd 100644
--- a/support/export/rmtab.c
+++ b/support/export/rmtab.c
@@ -27,31 +27,37 @@ rmtab_read(void)
 
 	setrmtabent("r");
 	while ((rep = getrmtabent(1, NULL)) != NULL) {
-		struct hostent		*hp = NULL;
-		int			htype;
+		int htype;
 		
 		htype = client_gettype(rep->r_client);
-		if ((htype == MCL_FQDN || htype == MCL_SUBNETWORK)
-		    && (hp = gethostbyname (rep->r_client))
-		    && (hp = hostent_dup (hp),
-			exp = export_allowed (hp, rep->r_path))) {
-			/* see if the entry already exists, otherwise this was an instantiated
-			 * wild card, and we must add it
-			 */
-			nfs_export *exp2 = export_lookup(rep->r_client,
+		if (htype == MCL_FQDN || htype == MCL_SUBNETWORK) {
+			struct addrinfo *ai;
+
+			ai = host_addrinfo(rep->r_client);
+			if (ai) {
+				/*
+				 * See if the entry already exists; otherwise,
+				 * this was an instantiated wild card, and we
+				 * must add it.
+				 */
+				exp = export_allowed(ai, rep->r_path);
+				if (exp) {
+					nfs_export *exp2 = export_lookup(rep->r_client,
 							exp->m_export.e_path, 0);
-			if (!exp2) {
-				struct exportent ee;
-				dupexportent(&ee, &exp->m_export);
-				ee.e_hostname = rep->r_client;
-				exp2 = export_create(&ee, 0);
-				exp2->m_changed = exp->m_changed;
+					if (!exp2) {
+						struct exportent ee;
+						dupexportent(&ee, &exp->m_export);
+						ee.e_hostname = rep->r_client;
+						exp2 = export_create(&ee, 0);
+						exp2->m_changed = exp->m_changed;
+					}
+					exp2->m_mayexport = 1;
+				}
+				freeaddrinfo(ai);
 			}
-			free (hp);
-			exp2->m_mayexport = 1;
-		} else if (hp) /* export_allowed failed */
-			free(hp);
+		}
 	}
+
 	if (errno == EINVAL) {
 		/* Something goes wrong. We need to fix the rmtab
 		   file. */
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 9a51398..60185dd 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -117,24 +117,27 @@ extern exp_hash_table exportlist[MCL_MAXTYPES];
 extern nfs_client *		clientlist[MCL_MAXTYPES];
 
 nfs_client *			client_lookup(char *hname, int canonical);
-nfs_client *			client_dup(nfs_client *, struct hostent *);
+nfs_client *			client_dup(const nfs_client *clp,
+						const struct addrinfo *ai);
 int				client_gettype(char *hname);
-int				client_check(nfs_client *, struct hostent *);
+int				client_check(const nfs_client *clp,
+						const struct addrinfo *ai);
 void				client_freeall(void);
-char *				client_compose(struct hostent *he);
-struct hostent *		client_resolve(struct in_addr addr);
+char *				client_compose(const struct addrinfo *ai);
+struct addrinfo *		client_resolve(const struct sockaddr *sap);
 int				client_resolve_paddr(const char *address,
-						struct hostent **result);
+						struct addrinfo **result);
 int 				client_member(char *client, char *name);
 
 int				export_read(char *fname);
 void			export_add(nfs_export *);
 void				export_reset(nfs_export *);
 nfs_export *			export_lookup(char *hname, char *path, int caconical);
-nfs_export *			export_find(struct hostent *, char *path);
-nfs_export *			export_allowed(struct hostent *, char *path);
+nfs_export *			export_find(const struct addrinfo *ai,
+						const char *path);
+nfs_export *			export_allowed(const struct addrinfo *ai,
+						const char *path);
 nfs_export *			export_create(struct exportent *, int canonical);
-nfs_export *			export_dup(nfs_export *, struct hostent *);
 void				export_freeall(void);
 int				export_export(nfs_export *);
 int				export_unexport(nfs_export *);
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index f1d1c01..1d3e403 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -231,7 +231,7 @@ exportfs(char *arg, char *options, int verbose)
 {
 	struct exportent *eep;
 	nfs_export	*exp;
-	struct hostent	*hp = NULL;
+	struct addrinfo	*ai = NULL;
 	char		*path;
 	char		*hname = arg;
 	int		htype;
@@ -244,32 +244,21 @@ exportfs(char *arg, char *options, int verbose)
 		return;
 	}
 
-	if ((htype = client_gettype(hname)) == MCL_FQDN &&
-	    (hp = gethostbyname(hname)) != NULL) {
-		struct hostent *hp2 = hostent_dup (hp);
-		hp = gethostbyaddr(hp2->h_addr, hp2->h_length,
-				   hp2->h_addrtype);
-		if (hp) {
-			free(hp2);
-			hp = hostent_dup(hp);
-		} else
-			hp = hp2;
-		exp = export_find(hp, path);
-		hname = hp->h_name;
-	} else {
+	if ((htype = client_gettype(hname)) == MCL_FQDN) {
+		ai = host_addrinfo(hname);
+		if (ai) {
+			exp = export_find(ai, path);
+			hname = ai->ai_canonname;
+		}
+	} else
 		exp = export_lookup(hname, path, 0);
-	}
 
 	if (!exp) {
 		if (!(eep = mkexportent(hname, path, options)) ||
-		    !(exp = export_create(eep, 0))) {
-			if (hp) free (hp);
-			return;
-		}
-	} else if (!updateexportent(&exp->m_export, options)) {
-		if (hp) free (hp);
-		return;
-	}
+		    !(exp = export_create(eep, 0)))
+			goto out;
+	} else if (!updateexportent(&exp->m_export, options))
+		goto out;
 
 	if (verbose)
 		printf("exporting %s:%s\n", exp->m_client->m_hostname, 
@@ -279,7 +268,9 @@ exportfs(char *arg, char *options, int verbose)
 	exp->m_changed = 1;
 	exp->m_warned = 0;
 	validate_export(exp);
-	if (hp) free (hp);
+
+out:
+	freeaddrinfo(ai);
 }
 
 static void
diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
index db7f109..1eb78a3 100644
--- a/utils/mountd/auth.c
+++ b/utils/mountd/auth.c
@@ -112,8 +112,8 @@ auth_reload()
 }
 
 static nfs_export *
-auth_authenticate_internal(char *what, struct sockaddr_in *caller,
-			   char *path, struct hostent *hp,
+auth_authenticate_internal(struct sockaddr_in *caller,
+			   char *path, struct addrinfo *ai,
 			   enum auth_error *error)
 {
 	nfs_export		*exp;
@@ -127,7 +127,7 @@ auth_authenticate_internal(char *what, struct sockaddr_in *caller,
 			my_client.m_hostname =
 				strdup(inet_ntoa(caller->sin_addr));
 		} else {
-			n = client_compose(hp);
+			n = client_compose(ai);
 			*error = unknown_host;
 			if (!n)
 				my_client.m_hostname = NULL;
@@ -151,7 +151,7 @@ auth_authenticate_internal(char *what, struct sockaddr_in *caller,
 					continue;
 				if (!use_ipaddr && !client_member(my_client.m_hostname, exp->m_client->m_hostname))
 					continue;
-				if (use_ipaddr && !client_check(exp->m_client, hp))
+				if (use_ipaddr && !client_check(exp->m_client, ai))
 					continue;
 				break;
 			}
@@ -163,7 +163,7 @@ auth_authenticate_internal(char *what, struct sockaddr_in *caller,
 		exp = &my_exp;
 
 	} else {
-		if (!(exp = export_find(hp, path))) {
+		if (!(exp = export_find(ai, path))) {
 			*error = no_entry;
 			return NULL;
 		}
@@ -188,7 +188,7 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
 	nfs_export	*exp = NULL;
 	char		epath[MAXPATHLEN+1];
 	char		*p = NULL;
-	struct hostent	*hp = NULL;
+	struct addrinfo *ai = NULL;
 	struct in_addr	addr = caller->sin_addr;
 	enum auth_error	error = bad_path;
 
@@ -202,14 +202,14 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
 	epath[sizeof (epath) - 1] = '\0';
 	auth_fixpath(epath); /* strip duplicate '/' etc */
 
-	hp = client_resolve(caller->sin_addr);
-	if (!hp)
+	ai = client_resolve((struct sockaddr *)caller);
+	if (!ai)
 		return exp;
 
 	/* Try the longest matching exported pathname. */
 	while (1) {
-		exp = auth_authenticate_internal(what, caller, epath,
-						 hp, &error);
+		exp = auth_authenticate_internal(caller, epath,
+						 ai, &error);
 		if (exp || (error != not_exported && error != no_entry))
 			break;
 		/* We have to treat the root, "/", specially. */
@@ -232,31 +232,30 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
 
 	case no_entry:
 		xlog(L_WARNING, "refused %s request from %s for %s (%s): no export entry",
-		     what, hp->h_name, path, epath);
+		     what, ai->ai_canonname, path, epath);
 		break;
 
 	case not_exported:
 		xlog(L_WARNING, "refused %s request from %s for %s (%s): not exported",
-		     what, hp->h_name, path, epath);
+		     what, ai->ai_canonname, path, epath);
 		break;
 
 	case illegal_port:
 		xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
-		     what, hp->h_name, path, epath, ntohs(caller->sin_port));
+		     what, ai->ai_canonname, path, epath, ntohs(caller->sin_port));
 		break;
 
 	case success:
 		xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
-		     what, hp->h_name, ntohs(caller->sin_port), path, epath);
+		     what, ai->ai_canonname, ntohs(caller->sin_port), path, epath);
 		break;
 	default:
 		xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
-		     what, hp->h_name, ntohs(caller->sin_port), path, epath, error);
+		     what, ai->ai_canonname, ntohs(caller->sin_port),
+			path, epath, error);
 	}
 
-	if (hp)
-		free (hp);
-
+	freeaddrinfo(ai);
 	return exp;
 }
 
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index a1191d2..cbe477d 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -66,7 +66,7 @@ void auth_unix_ip(FILE *f)
 	char class[20];
 	char ipaddr[20];
 	char *client = NULL;
-	struct hostent *he = NULL;
+	struct addrinfo *ai = NULL;
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
 		return;
 
@@ -85,11 +85,12 @@ void auth_unix_ip(FILE *f)
 
 	/* addr is a valid, interesting address, find the domain name... */
 	if (!use_ipaddr) {
-		if (!client_resolve_paddr(ipaddr, &he))
+		if (!client_resolve_paddr(ipaddr, &ai))
 			return;
-		client = client_compose(he);
+		client = client_compose(ai);
 	}
-	
+	freeaddrinfo(ai);
+
 	qword_print(f, "nfsd");
 	qword_print(f, ipaddr);
 	qword_printint(f, time(0)+30*60);
@@ -100,8 +101,7 @@ void auth_unix_ip(FILE *f)
 	qword_eol(f);
 	xlog(D_CALL, "auth_unix_ip: client %p '%s'", client, client?client: "DEFAULT");
 
-	if (client) free(client);
-	free(he);
+	free(client);
 }
 
 void auth_unix_gid(FILE *f)
@@ -282,8 +282,7 @@ void nfsd_fh(FILE *f)
 	unsigned int fsidnum=0;
 	char fsid[32];
 	struct exportent *found = NULL;
-	struct hostent *he = NULL;
-	struct in_addr addr;
+	struct addrinfo *ai = NULL;
 	char *found_path = NULL;
 	nfs_export *exp;
 	int i;
@@ -465,9 +464,9 @@ void nfsd_fh(FILE *f)
 				break;
 			}
 			if (use_ipaddr) {
-				if (!he && !client_resolve_paddr(dom, &he))
+				if (!ai && !client_resolve_paddr(dom, &ai))
 					goto out;
-				if (!client_check(exp->m_client, he))
+				if (!client_check(exp->m_client, ai))
 					continue;
 			}
 			/* It's a match !! */
@@ -541,8 +540,7 @@ found:
  out:
 	if (found_path)
 		free(found_path);
-	if (he)
-		free(he);
+	freeaddrinfo(ai);
 	free(dom);
 	xlog(D_CALL, "nfsd_fh: found %p path %s", found, found ? found->e_path : NULL);
 	return;		
@@ -634,8 +632,7 @@ void nfsd_export(FILE *f)
 	char *dom, *path;
 	nfs_export *exp, *found = NULL;
 	int found_type = 0;
-	struct in_addr addr;
-	struct hostent *he = NULL;
+	struct addrinfo *ai = NULL;
 	struct exportent *v4root = NULL;
 
 
@@ -684,9 +681,9 @@ void nfsd_export(FILE *f)
 				continue;
 			}
 			if (use_ipaddr) {
-				if (!he && !client_resolve_paddr(dom, &he))
+				if (!ai && !client_resolve_paddr(dom, &ai))
 					goto out;
-				if (!client_check(exp->m_client, he))
+				if (!client_check(exp->m_client, ai))
 					continue;
 			}
 			if (!found) {
@@ -742,7 +739,7 @@ void nfsd_export(FILE *f)
 
 	if (dom) free(dom);
 	if (path) free(path);
-	if (he) free(he);
+	freeaddrinfo(ai);
 }
 
 
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 888fd8c..4c0c503 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -565,21 +565,21 @@ get_exportlist(void)
 			}
 
 			if (i != MCL_FQDN && e->ex_groups) {
-			  struct hostent 	*hp;
-
 			  cp = &e->ex_groups;
 			  while ((c = *cp) != NULL) {
-			    if (client_gettype (c->gr_name) == MCL_FQDN
-			        && (hp = gethostbyname(c->gr_name))) {
-			      hp = hostent_dup (hp);
-			      if (client_check(exp->m_client, hp)) {
-				*cp = c->gr_next;
-				xfree(c->gr_name);
-				xfree(c);
-				xfree (hp);
-				continue;
+			    if (client_gettype (c->gr_name) == MCL_FQDN) {
+			      struct addrinfo *ai;
+			      ai = host_addrinfo(c->gr_name);
+			      if (ai) {
+				if (client_check(exp->m_client, ai)) {
+				  *cp = c->gr_next;
+				  xfree(c->gr_name);
+				  xfree(c);
+				  freeaddrinfo(ai);
+				  continue;
+				}
+				freeaddrinfo(ai);
 			      }
-			      xfree (hp);
 			    }
 			    cp = &(c->gr_next);
 			  }
-------------------------------------------------------------------------------
libexport-a-make-export_read-r
-------------------------------------------------------------------------------
libexport.a: Make export_read() return void

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

Clean up:  export_read()'s return value is always zero, and its only
caller never checks it.

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

 support/export/export.c    |    9 ++++++---
 support/include/exportfs.h |    2 +-
 2 files changed, 7 insertions(+), 4 deletions(-)


diff --git a/support/export/export.c b/support/export/export.c
index fe99f4f..346af92 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -37,7 +37,12 @@ export_free(nfs_export *exp)
 	xfree(exp);
 }
 
-int
+/**
+ * export_read - read entries from /etc/exports
+ * @fname: name of file to read from
+ *
+ */
+void
 export_read(char *fname)
 {
 	struct exportent	*eep;
@@ -65,8 +70,6 @@ export_read(char *fname)
 	  }
 	}
 	endexportent();
-
-	return 0;
 }
 
 /*
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 60185dd..7f1ea28 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -129,7 +129,7 @@ int				client_resolve_paddr(const char *address,
 						struct addrinfo **result);
 int 				client_member(char *client, char *name);
 
-int				export_read(char *fname);
+void				export_read(char *fname);
 void			export_add(nfs_export *);
 void				export_reset(nfs_export *);
 nfs_export *			export_lookup(char *hname, char *path, int caconical);
-------------------------------------------------------------------------------
libexport-a-make-export_add-st
-------------------------------------------------------------------------------
libexport.a: Make export_add() static

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

Clean up: export_add() is not called from outside of export.c, so make
it a static helper.

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

 support/export/export.c    |   59 +++++++++++++++++++++-----------------------
 support/include/exportfs.h |    1 -
 2 files changed, 28 insertions(+), 32 deletions(-)


diff --git a/support/export/export.c b/support/export/export.c
index 346af92..1f972ff 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -37,6 +37,34 @@ export_free(nfs_export *exp)
 	xfree(exp);
 }
 
+static void 
+export_add(nfs_export *exp)
+{
+	exp_hash_table *p_tbl;
+	exp_hash_entry *p_hen;
+	nfs_export *p_next;
+
+	int type = exp->m_client->m_type;
+	int pos;
+
+	pos = export_hash(exp->m_export.e_path);
+	p_tbl = &(exportlist[type]); /* pointer to hash table */
+	p_hen = &(p_tbl->entries[pos]); /* pointer to hash table entry */
+
+	if (!(p_hen->p_first)) { /* hash table entry is empty */ 
+ 		p_hen->p_first = exp;
+ 		p_hen->p_last  = exp;
+
+ 		exp->m_next = p_tbl->p_head;
+ 		p_tbl->p_head = exp;
+	} else { /* hash table entry is NOT empty */
+		p_next = p_hen->p_last->m_next;
+		p_hen->p_last->m_next = exp;
+		exp->m_next = p_next;
+		p_hen->p_last = exp;
+	}
+}
+
 /**
  * export_read - read entries from /etc/exports
  * @fname: name of file to read from
@@ -148,37 +176,6 @@ export_dup(nfs_export *exp, const struct addrinfo *ai)
 	return new;
 }
 
-/*
- * Add export entry to hash table
- */
-void 
-export_add(nfs_export *exp)
-{
-	exp_hash_table *p_tbl;
-	exp_hash_entry *p_hen;
-	nfs_export *p_next;
-
-	int type = exp->m_client->m_type;
-	int pos;
-
-	pos = export_hash(exp->m_export.e_path);
-	p_tbl = &(exportlist[type]); /* pointer to hash table */
-	p_hen = &(p_tbl->entries[pos]); /* pointer to hash table entry */
-
-	if (!(p_hen->p_first)) { /* hash table entry is empty */ 
- 		p_hen->p_first = exp;
- 		p_hen->p_last  = exp;
-
- 		exp->m_next = p_tbl->p_head;
- 		p_tbl->p_head = exp;
-	} else { /* hash table entry is NOT empty */
-		p_next = p_hen->p_last->m_next;
-		p_hen->p_last->m_next = exp;
-		exp->m_next = p_next;
-		p_hen->p_last = exp;
-	}
-}
-
 /**
  * export_find - find or create a suitable nfs_export for @ai and @path
  * @ai: pointer to addrinfo for client
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 7f1ea28..49ad866 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -130,7 +130,6 @@ int				client_resolve_paddr(const char *address,
 int 				client_member(char *client, char *name);
 
 void				export_read(char *fname);
-void			export_add(nfs_export *);
 void				export_reset(nfs_export *);
 nfs_export *			export_lookup(char *hname, char *path, int caconical);
 nfs_export *			export_find(const struct addrinfo *ai,
-------------------------------------------------------------------------------
libexport-a-add-a-few-addition
-------------------------------------------------------------------------------
libexport.a: Add a few additional documenting comments

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

Clean up.

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

 support/export/client.c |   21 ++++++++++++++++++---
 support/export/export.c |   19 +++++++++++++++----
 2 files changed, 33 insertions(+), 7 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index c299299..712b252 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -135,9 +135,13 @@ client_add(nfs_client *clp)
 	*cpp = clp;
 }
 
-/* if canonical is set, then we *know* this is already a canonical name
- * so hostname lookup is avoided.
- * This is used when reading /proc/fs/nfs/exports
+/**
+ * client_lookup - look for @hname in our list of cached nfs_clients
+ * @hname: '\0'-terminated ASCII string containing hostname to look for
+ * @canonical: if set, @hname is known to be canonical DNS name
+ *
+ * Returns pointer to a matching or freshly created nfs_client.  NULL
+ * is returned if some problem occurs.
  */
 nfs_client *
 client_lookup(char *hname, int canonical)
@@ -215,6 +219,10 @@ client_dup(const nfs_client *clp, const struct addrinfo *ai)
 	return new;
 }
 
+/**
+ * client_freeall - deallocate all nfs_client records
+ *
+ */
 void
 client_freeall(void)
 {
@@ -564,6 +572,13 @@ client_check(const nfs_client *clp, const struct addrinfo *ai)
 	return 0;
 }
 
+/**
+ * client_gettype - determine type of nfs_client given an identifier
+ * @ident: '\0'-terminated ASCII string containing a client identifier
+ *
+ * Returns the type of nfs_client record that would be used for
+ * this client.
+ */
 int
 client_gettype(char *ident)
 {
diff --git a/support/export/export.c b/support/export/export.c
index 1f972ff..7993997 100644
--- a/support/export/export.c
+++ b/support/export/export.c
@@ -100,8 +100,13 @@ export_read(char *fname)
 	endexportent();
 }
 
-/*
- * Create an in-core export struct from an export entry.
+/**
+ * export_create - create an in-core nfs_export record from an export entry
+ * @xep: export entry to lookup
+ * @canonical: if set, e_hostname is known to be canonical DNS name
+ *
+ * Returns a freshly instantiated export record, or NULL if
+ * a problem occurred.
  */
 nfs_export *
 export_create(struct exportent *xep, int canonical)
@@ -256,8 +261,14 @@ export_allowed(const struct addrinfo *ai, const char *path)
 	return NULL;
 }
 
-/*
- * Search hash table for export entry. 
+/**
+ * export_lookup - search hash table for export entry
+ * @hname: '\0'-terminated ASCII string containing client hostname to look for
+ * @path: '\0'-terminated ASCII string containing export path to look for
+ * @canonical: if set, @hname is known to be canonical DNS name
+ *
+ * Returns a pointer to nfs_export record matching @hname and @path,
+ * or NULL if the export was not found.
  */  
 nfs_export *
 export_lookup(char *hname, char *path, int canonical) 
-------------------------------------------------------------------------------
libexport-a-replace-matchhostn
-------------------------------------------------------------------------------
libexport.a: Replace matchhostname()

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

So exportfs can eventually support IPv6 addresses, copy statd's
getaddrinfo(3)-based matchhostname to exportfs, with adjustments for
dealing with export wildcards and netgroups.  For now, we want to
ensure that IPv6 addresses continue to remain blocked in the address
comparison code used by exportfs.

Since it contains special logic for handling wildcard and netgroups,
this function is specialized for exportfs, and does not belong in
generic code.

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

 support/include/misc.h    |    2 -
 utils/exportfs/exportfs.c |   78 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 2 deletions(-)


diff --git a/support/include/misc.h b/support/include/misc.h
index 9a1b25d..e817bc5 100644
--- a/support/include/misc.h
+++ b/support/include/misc.h
@@ -15,8 +15,6 @@
 int	randomkey(unsigned char *keyout, int len);
 int	weakrandomkey(unsigned char *keyout, int len);
 
-int	matchhostname(const char *h1, const char *h2); 
-
 struct hostent;
 struct hostent	*hostent_dup(struct hostent *hp);
 struct hostent	*get_hostent (const char *addr, int len, int type);
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 1d3e403..fae8310 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -35,6 +35,7 @@ static void	dump(int verbose);
 static void	error(nfs_export *exp, int err);
 static void	usage(void);
 static void	validate_export(nfs_export *exp);
+static int	matchhostname(const char *hostname1, const char *hostname2);
 
 int
 main(int argc, char **argv)
@@ -420,6 +421,83 @@ validate_export(nfs_export *exp)
 	}
 }
 
+static int
+is_hostname(const char *sp)
+{
+	if (*sp == '\0' || *sp == '@')
+		return 0;
+
+	for (; *sp; sp++) {
+		if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
+			return 0;
+		if (*sp == '\\' && sp[1])
+			sp++;
+	}
+
+	return 1;
+}
+
+static int
+compare_sockaddrs4(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 int
+compare_sockaddrs(const struct sockaddr *sa1, const struct sockaddr *sa2)
+{
+	if (sa1->sa_family == sa2->sa_family)
+		switch (sa1->sa_family) {
+		case AF_INET:
+			return compare_sockaddrs4(sa1, sa2);
+		}
+
+	return 0;
+}
+
+static int
+matchhostname(const char *hostname1, const char *hostname2)
+{
+	struct addrinfo *gai_results1 = NULL, *gai_results2 = NULL;
+	struct addrinfo *ai1, *ai2;
+	int result = 0;
+
+	if (strcasecmp(hostname1, hostname2) == 0)
+		return 1;
+
+	/*
+	 * Don't pass wildcards or netgroup names to DNS
+	 */
+	if (!is_hostname(hostname1) || !is_hostname(hostname2))
+		return 0;
+
+	gai_results1 = host_addrinfo(hostname1);
+	if (gai_results1 == NULL)
+		goto out;
+	gai_results2 = host_addrinfo(hostname2);
+	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_sockaddrs(ai1->ai_addr, ai2->ai_addr)) {
+				result = 1;
+				goto out;
+			}
+
+out:
+	freeaddrinfo(gai_results2);
+	freeaddrinfo(gai_results1);
+	return result;
+}
 
 static char
 dumpopt(char c, char *fmt, ...)
-------------------------------------------------------------------------------
libexport-a-remove-unused-host
-------------------------------------------------------------------------------
libexport.a: Remove unused hostent-based DNS helper functions

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

Clean up:  Get rid of hostent-based DNS helper functions in
libexport.a that have been replaced by addrinfo-based DNS helpers.

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

 support/export/hostname.c |  320 ++-------------------------------------------
 support/include/misc.h    |    5 -
 2 files changed, 16 insertions(+), 309 deletions(-)


diff --git a/support/export/hostname.c b/support/export/hostname.c
index 3cd1131..8d3a2bc 100644
--- a/support/export/hostname.c
+++ b/support/export/hostname.c
@@ -1,30 +1,30 @@
 /*
- * support/export/hostname.c
+ * Copyright 2009 Oracle.  All rights reserved.
  *
- * Functions for hostname.
+ * This file is part of nfs-utils.
  *
+ * nfs-utils is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * nfs-utils is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
-/*
-#define TEST
-*/
-
 #include <string.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
 #include <stdlib.h>
-#include <xlog.h>
-#ifdef TEST
-#define xmalloc malloc
-#else
-#include "xmalloc.h"
-#include "misc.h"
-#endif
+#include <arpa/inet.h>
+#include <netdb.h>
 
 #include "exportfs.h"
 #include "compaddr.h"
@@ -33,138 +33,6 @@
 #define AI_ADDRCONFIG	0
 #endif
 
-#define ALIGNMENT	sizeof (char *)
-
-static int
-align (int len, int al)
-{
-  int i;
-  i = len % al;
-  if (i)
-    len += al - i;
-  return len;
-}
-
-struct hostent *
-get_hostent (const char *addr, int len, int type)
-{
-  struct hostent *cp;
-  int len_ent;
-  const char *name;
-  int len_name;
-  int num_aliases = 1;
-  int len_aliases = sizeof (char *);
-  int num_addr_list = 1;
-  int len_addr_list = sizeof (char *);
-  int pos;
-  struct in_addr *ipv4;
-
-  switch (type)
-    {
-    case AF_INET:
-      ipv4 = (struct in_addr *) addr;
-      name = inet_ntoa (*ipv4);
-      break;
-
-    default:
-      return NULL;
-    }
-
-  len_ent = align (sizeof (*cp), ALIGNMENT);
-  len_name = align (strlen (name) + 1, ALIGNMENT);
-
-  num_addr_list++;
-  len_addr_list += align (len, ALIGNMENT) + sizeof (char *);
-
-  cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
-				   + len_addr_list);
-
-  cp->h_addrtype = type;
-  cp->h_length = len;
-  pos = len_ent;
-  cp->h_name = (char *) &(((char *) cp) [pos]);
-  strcpy (cp->h_name, name);
-
-  pos += len_name;
-  cp->h_aliases = (char **) &(((char *) cp) [pos]);
-  pos += num_aliases * sizeof (char *);
-  cp->h_aliases [0] = NULL;
-
-  pos = len_ent + len_name + len_aliases;
-  cp->h_addr_list = (char **) &(((char *) cp) [pos]);
-  pos += num_addr_list * sizeof (char *);
-  cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]);
-  memcpy (cp->h_addr_list [0], addr, cp->h_length);
-  pos += align (cp->h_length, ALIGNMENT);
-  cp->h_addr_list [1] = NULL;
-
-  return cp;
-}
-
-struct hostent *
-hostent_dup (struct hostent *hp)
-{
-  int len_ent = align (sizeof (*hp), ALIGNMENT);
-  int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT);
-  int num_aliases = 1;
-  int len_aliases = sizeof (char *);
-  int num_addr_list = 1;
-  int len_addr_list = sizeof (char *);
-  int pos, i;
-  char **sp;
-  struct hostent *cp;
-
-  for (sp = hp->h_aliases; sp && *sp; sp++)
-    {
-      num_aliases++;
-      len_aliases += align (strlen (*sp) + 1, ALIGNMENT)
-		     + sizeof (char *);
-    }
-
-  for (sp = hp->h_addr_list; *sp; sp++)
-    {
-      num_addr_list++;
-      len_addr_list += align (hp->h_length, ALIGNMENT)
-		       + sizeof (char *);
-    }
-
-  cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
-				   + len_addr_list);
-
-  *cp = *hp;
-  pos = len_ent;
-  cp->h_name = (char *) &(((char *) cp) [pos]);
-  strcpy (cp->h_name, hp->h_name);
-
-  pos += len_name;
-  cp->h_aliases = (char **) &(((char *) cp) [pos]);
-  pos += num_aliases * sizeof (char *);
-  for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++)
-    if (sp && *sp)
-      {
-	cp->h_aliases [i] = (char *) &(((char *) cp) [pos]);
-	strcpy (cp->h_aliases [i], *sp);
-	pos += align (strlen (*sp) + 1, ALIGNMENT);
-      }
-    else
-      cp->h_aliases [i] = NULL;
-
-  pos = len_ent + len_name + len_aliases;
-  cp->h_addr_list = (char **) &(((char *) cp) [pos]);
-  pos += num_addr_list * sizeof (char *);
-  for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++)
-    if (*sp)
-      {
-	cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]);
-	memcpy (cp->h_addr_list [i], *sp, hp->h_length);
-	pos += align (hp->h_length, ALIGNMENT);
-      }
-    else
-      cp->h_addr_list [i] = *sp;
-
-  return cp;
-}
-
 #ifdef HAVE_GETNAMEINFO
 static socklen_t
 sockaddr_size(const struct sockaddr *sap)
@@ -441,159 +309,3 @@ host_numeric_addrinfo(const struct sockaddr *sap)
 	return ai;
 }
 #endif	/* !HAVE_GETNAMEINFO */
-
-static int
-is_hostname(const char *sp)
-{
-  if (*sp == '\0' || *sp == '@')
-    return 0;
-
-  for (; *sp; sp++)
-    {
-      if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
-	return 0;
-      if (*sp == '\\' && sp[1])
-	sp++;
-    }
-
-  return 1;
-}
-
-int
-matchhostname (const char *h1, const char *h2)
-{
-  struct hostent *hp1, *hp2;
-  int status;
-
-  if (strcasecmp (h1, h2) == 0)
-    return 1;
-
-  if (!is_hostname (h1) || !is_hostname (h2))
-    return 0;
-
-  hp1 = gethostbyname (h1);
-  if (hp1 == NULL)
-    return 0;
-
-  hp1 = hostent_dup (hp1);
-
-  hp2 = gethostbyname (h2);
-  if (hp2)
-    {
-      if (strcasecmp (hp1->h_name, hp2->h_name) == 0)
-	status = 1;
-      else
-	{
-	  char **ap1, **ap2;
-
-	  status = 0;
-	  for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++)
-	    for (ap2 = hp2->h_addr_list; *ap2; ap2++)
-	      if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0)
-		{
-		  status = 1;
-		  break;
-		}
-	}
-    }
-  else
-    status = 0;
-
-  free (hp1);
-  return status;
-}
-
-
-/* Map IP to hostname, and then map back to addr to make sure it is a
- * reliable hostname
- */
-struct hostent *
-get_reliable_hostbyaddr(const char *addr, int len, int type)
-{
-	struct hostent *hp = NULL;
-
-	struct hostent *reverse;
-	struct hostent *forward;
-	char **sp;
-
-	reverse = gethostbyaddr (addr, len, type);
-	if (!reverse)
-		return NULL;
-
-	/* must make sure the hostent is authorative. */
-
-	reverse = hostent_dup (reverse);
-	forward = gethostbyname (reverse->h_name);
-
-	if (forward) {
-		/* now make sure the "addr" is in the list */
-		for (sp = forward->h_addr_list ; *sp ; sp++) {
-			if (memcmp (*sp, addr, forward->h_length) == 0)
-				break;
-		}
-
-		if (*sp) {
-			/* it's valid */
-			hp = hostent_dup (forward);
-		}
-		else {
-			/* it was a FAKE */
-			xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse",
-			      reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
-		}
-	}
-	else {
-		/* never heard of it. misconfigured DNS? */
-		xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist",
-		      reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
-	}
-
-	free (reverse);
-	return hp;
-}
-
-
-#ifdef TEST
-void
-print_host (struct hostent *hp)
-{
-  char **sp;
-
-  if (hp)
-    {
-      printf ("official hostname: %s\n", hp->h_name);
-      printf ("aliases:\n");
-      for (sp = hp->h_aliases; *sp; sp++)
-	printf ("  %s\n", *sp);
-      printf ("IP addresses:\n");
-      for (sp = hp->h_addr_list; *sp; sp++)
-	printf ("  %s\n", inet_ntoa (*(struct in_addr *) *sp));
-    }
-  else
-    printf ("Not host information\n");
-}
-
-int
-main (int argc, char **argv)
-{
-  struct hostent *hp = gethostbyname (argv [1]);
-  struct hostent *cp;
-  struct in_addr addr;
-
-  print_host (hp);
-
-  if (hp)
-    {
-      cp = hostent_dup (hp);
-      print_host (cp);
-      free (cp);
-    }
-  printf ("127.0.0.1 == %s: %d\n", argv [1],
-	  matchhostname ("127.0.0.1", argv [1]));
-  addr.s_addr = inet_addr(argv [2]);
-  printf ("%s\n", inet_ntoa (addr));
-  cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET);
-  print_host (cp);
-  return 0;
-}
-#endif
diff --git a/support/include/misc.h b/support/include/misc.h
index e817bc5..bc5ba23 100644
--- a/support/include/misc.h
+++ b/support/include/misc.h
@@ -15,11 +15,6 @@
 int	randomkey(unsigned char *keyout, int len);
 int	weakrandomkey(unsigned char *keyout, int len);
 
-struct hostent;
-struct hostent	*hostent_dup(struct hostent *hp);
-struct hostent	*get_hostent (const char *addr, int len, int type);
-struct hostent *get_reliable_hostbyaddr(const char *addr, int len, int type);
-
 extern int is_mountpoint(char *path);
 
 #endif /* MISC_H */
-------------------------------------------------------------------------------
exportfs-exportfs-c-no-longer-
-------------------------------------------------------------------------------
exportfs: exportfs.c no longer needs #include "xmalloc.h"

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

No calls to xmalloc() or xstrdup() here.  No need for the double
#include of xmalloc.h.

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

 utils/exportfs/exportfs.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)


diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index fae8310..c62716e 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -12,19 +12,21 @@
 #include <config.h>
 #endif
 
+#include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/vfs.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
 #include <getopt.h>
+#include <fcntl.h>
 #include <netdb.h>
 #include <errno.h>
-#include "xmalloc.h"
+
 #include "misc.h"
 #include "nfslib.h"
 #include "exportfs.h"
-#include "xmalloc.h"
 #include "xlog.h"
 
 static void	export_all(int verbose);
-------------------------------------------------------------------------------
exportfs-use-xlog-for-error-re
-------------------------------------------------------------------------------
exportfs: Use xlog() for error reporting

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

Clean up: libexport.a uses xlog() exclusively for error reporting and
debugging messages.  Enable xlog debugging messages in exportfs.  Use
xlog instead of fprintf(stderr) for reporting errors in exportfs.c, to
be consistent with libexport.a and other components of nfs-utils.

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

 utils/exportfs/exportfs.c |   57 ++++++++++++++++++++++++---------------------
 1 files changed, 30 insertions(+), 27 deletions(-)


diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index c62716e..b76958d 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -35,7 +35,7 @@ static void	unexportfs(char *arg, int verbose);
 static void	exports_update(int verbose);
 static void	dump(int verbose);
 static void	error(nfs_export *exp, int err);
-static void	usage(void);
+static void	usage(const char *progname);
 static void	validate_export(nfs_export *exp);
 static int	matchhostname(const char *hostname1, const char *hostname2);
 
@@ -43,6 +43,7 @@ int
 main(int argc, char **argv)
 {
 	char	*options = NULL;
+	char	*progname = NULL;
 	int	f_export = 1;
 	int	f_all = 0;
 	int	f_verbose = 0;
@@ -52,7 +53,14 @@ main(int argc, char **argv)
 	int	new_cache = 0;
 	int	force_flush = 0;
 
-	xlog_open("exportfs");
+	if ((progname = strrchr(argv[0], '/')) != NULL)
+		progname++;
+	else
+		progname = argv[0];
+
+	xlog_open(progname);
+	xlog_stderr(1);
+	xlog_syslog(0);
 
 	export_errno = 0;
 
@@ -76,26 +84,27 @@ main(int argc, char **argv)
 			break;
 		case 'v':
 			f_verbose = 1;
+			xlog_config(D_ALL, 1);
 			break;
 		case 'f':
 			force_flush = 1;
 			break;
 		default:
-			usage();
+			usage(progname);
 			break;
 		}
 	}
 
 	if (optind != argc && f_all) {
-		fprintf(stderr,"exportfs: extra arguments are not permitted with -a or -r.\n");
+		xlog(L_ERROR, "extra arguments are not permitted with -a or -r");
 		return 1;
 	}
 	if (f_ignore && (f_all || ! f_export)) {
-		fprintf(stderr,"exportfs: -i not meaningful with -a, -r or -u.\n");
+		xlog(L_ERROR, "-i not meaningful with -a, -r or -u");
 		return 1;
 	}
 	if (f_reexport && ! f_export) {
-		fprintf(stderr, "exportfs: -r and -u are incompatible.\n");
+		xlog(L_ERROR, "-r and -u are incompatible");
 		return 1;
 	}
 	new_cache = check_new_cache();
@@ -104,8 +113,10 @@ main(int argc, char **argv)
 			if (new_cache)
 				cache_flush(1);
 			else {
-				fprintf(stderr, "exportfs: -f: only available with new cache controls: mount /proc/fs/nfsd first\n");
-				exit(1);
+				xlog(L_ERROR, "-f is available only "
+					"with new cache controls. "
+					"Mount /proc/fs/nfsd first");
+				return 1;
 			}
 			return 0;
 		} else {
@@ -243,7 +254,7 @@ exportfs(char *arg, char *options, int verbose)
 		*path++ = '\0';
 
 	if (!path || *path != '/') {
-		fprintf(stderr, "Invalid exporting option: %s\n", arg);
+		xlog(L_ERROR, "Invalid exporting option: %s", arg);
 		return;
 	}
 
@@ -289,8 +300,7 @@ unexportfs(char *arg, int verbose)
 		*path++ = '\0';
 
 	if (!path || *path != '/') {
-		fprintf(stderr, "Invalid unexporting option: %s\n",
-			arg);
+		xlog(L_ERROR, "Invalid unexporting option: %s", arg);
 		return;
 	}
 
@@ -385,14 +395,12 @@ validate_export(nfs_export *exp)
 	int fs_has_fsid = 0;
 
 	if (stat(path, &stb) < 0) {
-		fprintf(stderr, "exportfs: Warning: %s does not exist\n",
-			path);
+		xlog(L_ERROR, "Failed to stat %s: %m \n", path);
 		return;
 	}
 	if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
-		fprintf(stderr, "exportfs: Warning: %s is neither "
-			"a directory nor a file.\n"
-			"     remote access will fail\n", path);
+		xlog(L_ERROR, "%s is neither a directory nor a file. "
+			"Remote access will fail", path);
 		return;
 	}
 	if (!can_test())
@@ -405,19 +413,14 @@ validate_export(nfs_export *exp)
 	if ((exp->m_export.e_flags & NFSEXP_FSID) || exp->m_export.e_uuid ||
 	    fs_has_fsid) {
 		if ( !test_export(path, 1)) {
-			fprintf(stderr, "exportfs: Warning: %s does not "
-				"support NFS export.\n",
-				path);
+			xlog(L_ERROR, "%s does not support NFS export", path);
 			return;
 		}
 	} else if ( ! test_export(path, 0)) {
 		if (test_export(path, 1))
-			fprintf(stderr, "exportfs: Warning: %s requires fsid= "
-				"for NFS export\n", path);
+			xlog(L_ERROR, "%s requires fsid= for NFS export", path);
 		else
-			fprintf(stderr, "exportfs: Warning: %s does not "
-				"support NFS export.\n",
-				path);
+			xlog(L_ERROR, "%s does not support NFS export", path);
 		return;
 
 	}
@@ -601,13 +604,13 @@ dump(int verbose)
 static void
 error(nfs_export *exp, int err)
 {
-	fprintf(stderr, "%s:%s: %s\n", exp->m_client->m_hostname, 
+	xlog(L_ERROR, "%s:%s: %s\n", exp->m_client->m_hostname, 
 		exp->m_export.e_path, strerror(err));
 }
 
 static void
-usage(void)
+usage(const char *progname)
 {
-	fprintf(stderr, "usage: exportfs [-aruv] [host:/path]\n");
+	fprintf(stderr, "usage: %s [-aruv] [host:/path]\n", progname);
 	exit(1);
 }
-------------------------------------------------------------------------------
libexport-a-prepare-to-recogni
-------------------------------------------------------------------------------
libexport.a: Prepare to recognize IPv6 addresses in client_gettype()

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

The current open-coded parsing logic in client_gettype() will be hard
to modify to recognize IPv6 addresses.  Use a more generic mechanism
for detecting IP presentation addresses.

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

 support/export/client.c |   23 ++++++++++++++---------
 1 files changed, 14 insertions(+), 9 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 712b252..5e481a8 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -582,7 +582,8 @@ client_check(const nfs_client *clp, const struct addrinfo *ai)
 int
 client_gettype(char *ident)
 {
-	char	*sp;
+	struct addrinfo *ai;
+	char *sp;
 
 	if (ident[0] == '\0' || strcmp(ident, "*")==0)
 		return MCL_ANONYMOUS;
@@ -602,12 +603,16 @@ client_gettype(char *ident)
 		if (*sp == '\\' && sp[1])
 			sp++;
 	}
-	/* check for N.N.N.N */
-	sp = ident;
-	if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN;
-	sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN;
-	sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '.') return MCL_FQDN;
-	sp++; if(!isdigit(*sp) || strtoul(sp, &sp, 10) > 255 || *sp != '\0') return MCL_FQDN;
-	/* we lie here a bit. but technically N.N.N.N == N.N.N.N/32 :) */
-	return MCL_SUBNETWORK;
+
+	/*
+	 * Treat unadorned host addresses as MCL_SUBNETWORK.
+	 * Everything else is MCL_FQDN.
+	 */
+	ai = host_pton(ident);
+	if (ai) {
+		freeaddrinfo(ai);
+		return MCL_SUBNETWORK;
+	}
+
+	return MCL_FQDN;
 }
-------------------------------------------------------------------------------
libexport-a-prepare-init_subne
-------------------------------------------------------------------------------
libexport.a: Prepare init_subnetwork() for IPv6 support

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

Retire the slash32 logic in inet_netmask() in favor of a more generic
netmask parser that can support IPv6 addresses.

If an invalid IP address string is given to inet_addr(3), it returns
INADDR_NONE, which is actually a valid address (255.255.255.255).
We're none the wiser to the substitution until something breaks later.
This patch provides better sanity checking of the parsed address, now
that such an error can be reported to client_init()'s callers.

We can also check the prefixlen value a little more carefully as well.

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

 support/export/client.c |   77 ++++++++++++++++++++++++++++++++++-------------
 1 files changed, 56 insertions(+), 21 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 5e481a8..b42bcde 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <netdb.h>
+#include <errno.h>
 
 #include "misc.h"
 #include "nfslib.h"
@@ -60,25 +61,55 @@ client_free(nfs_client *clp)
 }
 
 static int
-init_netmask(nfs_client *clp, const char *slash)
+init_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
 {
 	struct sockaddr_in sin = {
 		.sin_family	= AF_INET,
 	};
+	unsigned long prefixlen;
 
-	if (strchr(slash + 1, '.')) {
-		sin.sin_addr.s_addr = inet_addr(slash + 1);
+	/* No slash present; assume netmask is all ones */
+	if (!slash) {
+		switch (family) {
+		case AF_INET:
+			prefixlen = 32;
+			break;
+		default:
+			goto out_badfamily;
+		}
 	} else {
-		int prefixlen = atoi(slash + 1);
-		if (0 < prefixlen && prefixlen <= 32)
-			sin.sin_addr.s_addr =
-					htonl((uint32_t)~0 << (32 - prefixlen));
-		else
+		char *endptr;
+
+		/* A spelled out netmask address, perhaps? */
+		if (strchr(slash + 1, '.')) {
+			if (!inet_pton(AF_INET, slash + 1, &sin.sin_addr.s_addr))
+				goto out_badmask;
+			set_addrlist_in(clp, 1, &sin);
+			return 1;
+		}
+
+		/* A prefixlen was given */
+		prefixlen = strtoul(slash + 1, &endptr, 10);
+		if (*endptr != '\0' && prefixlen != ULONG_MAX && errno != ERANGE)
 			goto out_badprefix;
 	}
 
-	set_addrlist_in(clp, 1, &sin);
-	return 1;
+	switch (family) {
+	case AF_INET:
+		if (prefixlen > 32)
+			goto out_badprefix;
+		sin.sin_addr.s_addr = htonl((uint32_t)~0 << (32 - prefixlen));
+		set_addrlist_in(clp, 1, &sin);
+		return 1;
+	}
+
+out_badfamily:
+	xlog(L_ERROR, "Unsupported address family for %s", clp->m_hostname);
+	return 0;
+
+out_badmask:
+	xlog(L_ERROR, "Invalid netmask `%s' for %s", slash + 1, clp->m_hostname);
+	return 0;
 
 out_badprefix:
 	xlog(L_ERROR, "Invalid prefix `%s' for %s", slash + 1, clp->m_hostname);
@@ -88,22 +119,26 @@ out_badprefix:
 static int
 init_subnetwork(nfs_client *clp)
 {
-	struct sockaddr_in sin = {
-		.sin_family	= AF_INET,
-	};
-	static char slash32[] = "/32";
+	struct addrinfo *ai;
+	sa_family_t family;
 	char *cp;
 
 	cp = strchr(clp->m_hostname, '/');
-	if (!cp)
-		cp = slash32;
 
-	*cp = '\0';
-	sin.sin_addr.s_addr = inet_addr(clp->m_hostname);
-	set_addrlist_in(clp, 0, &sin);
-	*cp = '/';
+	if (cp)
+		*cp = '\0';
+	ai = host_pton(clp->m_hostname);
+	if (cp)
+		*cp = '/';
+	if (!ai) {
+		xlog(L_ERROR, "Invalid IP address %s", clp->m_hostname);
+		return 0;
+	}
+	set_addrlist(clp, 0, ai->ai_addr, ai->ai_addrlen);
+	family = ai->ai_addr->sa_family;
+	freeaddrinfo(ai);
 
-	return init_netmask(clp, cp);
+	return init_netmask(clp, cp, family);
 }
 
 static int
-------------------------------------------------------------------------------
libexport-a-ipv6-support-for-c
-------------------------------------------------------------------------------
libexport.a: IPv6 support for client_init_subnet()

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

To parse and store an IPv6 host or subnet address,
client_init_netmask() needs to handle 128 bit subnet masks.

To keep things neat, only the flags that determine whether
getaddrinfo(3) can return IPV6 addresses are changed when
IPV6_SUPPORTED is enabled.  If IPV6_SUPPORT is disabled,
IPv6 support here remains compiled in, but should never be
executed.

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

 support/export/client.c |   32 ++++++++++++++++++++++++++++++++
 1 files changed, 32 insertions(+), 0 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index b42bcde..01f9974 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -63,6 +63,12 @@ client_free(nfs_client *clp)
 static int
 init_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
 {
+#ifdef IPV6_SUPPORTED
+	int i;
+	struct sockaddr_in6 sin6 = {
+		.sin6_family	= AF_INET6,
+	};
+#endif
 	struct sockaddr_in sin = {
 		.sin_family	= AF_INET,
 	};
@@ -74,6 +80,11 @@ init_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
 		case AF_INET:
 			prefixlen = 32;
 			break;
+#ifdef IPV6_SUPPORTED
+		case AF_INET6:
+			prefixlen = 128;
+			break;
+#endif
 		default:
 			goto out_badfamily;
 		}
@@ -87,6 +98,14 @@ init_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
 			set_addrlist_in(clp, 1, &sin);
 			return 1;
 		}
+#ifdef IPV6_SUPPORTED
+		if (strchr(slash + 1, ':')) {
+			if (!inet_pton(AF_INET6, slash + 1, &sin6.sin6_addr))
+				goto out_badmask;
+			set_addrlist_in6(clp, 1, &sin6);
+			return 1;
+		}
+#endif
 
 		/* A prefixlen was given */
 		prefixlen = strtoul(slash + 1, &endptr, 10);
@@ -101,6 +120,19 @@ init_netmask(nfs_client *clp, const char *slash, const sa_family_t family)
 		sin.sin_addr.s_addr = htonl((uint32_t)~0 << (32 - prefixlen));
 		set_addrlist_in(clp, 1, &sin);
 		return 1;
+#ifdef IPV6_SUPPORTED
+	case AF_INET6:
+		if (prefixlen > 128)
+			goto out_badprefix;
+		for (i = 0; prefixlen > 32; i++) {
+			sin6.sin6_addr.s6_addr32[i] = 0xffffffff;
+			prefixlen -= 32;
+		}
+		sin6.sin6_addr.s6_addr32[i] =
+				htonl((uint32_t)~0 << (32 - prefixlen));
+		set_addrlist_in6(clp, 1, &sin6);
+		return 1;
+#endif
 	}
 
 out_badfamily:
-------------------------------------------------------------------------------
libexport-a-ipv6-support-in-cl
-------------------------------------------------------------------------------
libexport.a: IPv6 support in client_check()

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

Introduce support for IPv6 in client_check()'s helpers.

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

 support/export/client.c |   64 +++++++++++++++++++++++++++++++----------------
 1 files changed, 42 insertions(+), 22 deletions(-)


diff --git a/support/export/client.c b/support/export/client.c
index 01f9974..703f7d6 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -22,6 +22,7 @@
 #include "misc.h"
 #include "nfslib.h"
 #include "exportfs.h"
+#include "compaddr.h"
 
 /* netgroup stuff never seems to be defined in any header file. Linux is
  * not alone in this.
@@ -450,27 +451,6 @@ add_name(char *old, char *add)
 	return new;
 }
 
-static int
-addrs_match4(const struct sockaddr *sa1, const struct sockaddr *sa2)
-{
-	const struct sockaddr_in *si1 = (const struct sockaddr_in *)sa1;
-	const struct sockaddr_in *si2 = (const struct sockaddr_in *)sa2;
-
-	return si1->sin_addr.s_addr == si2->sin_addr.s_addr;
-}
-
-static int
-addrs_match(const struct sockaddr *sa1, const struct sockaddr *sa2)
-{
-	if (sa1->sa_family == sa2->sa_family)
-		switch (sa1->sa_family) {
-		case AF_INET:
-			return addrs_match4(sa1, sa2);
-		}
-
-	return 0;
-}
-
 /*
  * Check each address listed in @ai against each address
  * stored in @clp.  Return 1 if a match is found, otherwise
@@ -483,7 +463,7 @@ check_fqdn(const nfs_client *clp, const struct addrinfo *ai)
 
 	for (; ai; ai = ai->ai_next)
 		for (i = 0; i < clp->m_naddr; i++)
-			if (addrs_match(ai->ai_addr, get_addrlist(clp, i)))
+			if (compare_sockaddr(ai->ai_addr, get_addrlist(clp, i)))
 				return 1;
 
 	return 0;
@@ -513,6 +493,43 @@ check_subnet_v4(const struct sockaddr_in *address,
 	return 0;
 }
 
+#ifdef IPV6_SUPPORTED
+static int
+check_subnet_v6(const struct sockaddr_in6 *address,
+		const struct sockaddr_in6 *mask, const struct addrinfo *ai)
+{
+	for (; ai; ai = ai->ai_next) {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
+
+		if (sin6->sin6_family != AF_INET6)
+			continue;
+
+		if (mask_match(address->sin6_addr.s6_addr32[0],
+				sin6->sin6_addr.s6_addr[0],
+		      		mask->sin6_addr.s6_addr32[0]) &&
+		    mask_match(address->sin6_addr.s6_addr32[1],
+				sin6->sin6_addr.s6_addr[1],
+		      		mask->sin6_addr.s6_addr32[1]) &&
+		    mask_match(address->sin6_addr.s6_addr32[2],
+				sin6->sin6_addr.s6_addr[2],
+		      		mask->sin6_addr.s6_addr32[2]) &&
+		    mask_match(address->sin6_addr.s6_addr32[3],
+				sin6->sin6_addr.s6_addr[3],
+		      		mask->sin6_addr.s6_addr32[3]))
+			return 1;
+	}
+	return 0;
+}
+#else	/* !IPV6_SUPPORTED */
+static int
+check_subnet_v6(__attribute__((unused)) const struct sockaddr_in6 *address,
+		__attribute__((unused)) const struct sockaddr_in6 *mask,
+		__attribute__((unused)) const struct addrinfo *ai)
+{
+	return 0;
+}
+#endif	/* !IPV6_SUPPORTED */
+
 /*
  * Check each address listed in @ai against the subnetwork or
  * host address stored in @clp.  Return 1 if an address in @hp
@@ -525,6 +542,9 @@ check_subnetwork(const nfs_client *clp, const struct addrinfo *ai)
 	case AF_INET:
 		return check_subnet_v4(get_addrlist_in(clp, 0),
 				get_addrlist_in(clp, 1), ai);
+	case AF_INET6:
+		return check_subnet_v6(get_addrlist_in6(clp, 0),
+				get_addrlist_in6(clp, 1), ai);
 	}
 
 	return 0;
-------------------------------------------------------------------------------
libnfs-a-fix-api-for-getfh-fri
-------------------------------------------------------------------------------
libnfs.a: Fix API for getfh() & friends

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

POSIX requires that a "struct sockaddr" is the same size as a "struct
sockaddr_in".  Therefore, "struct sockaddr" cannot contain an AF_INET6
address.

The nfsctl_arg struct uses a struct sockaddr to pass the client's IP
address to the kernel, meaning the legacy nfsctl() API can never
support IPv6.  Fortunately for us, this legacy interface was replaced
by a text-based cache interface a few years back.  We don't need to
support non-AF_INET addresses here.

To document this, change the definitions of getfh() and friends to
take a struct sockaddr_in * instead of a struct sockaddr * .

Since mountd will support IPv6 in the near future, check that the
family of incoming client addresses is AF_INET, in order to prevent
non-AF_INET addresses from being passed to the legacy nfsctl()
interface.

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

 support/include/nfslib.h |    9 ++++++---
 support/nfs/getfh.c      |   48 ++++++++++++++++++++++++++++++++++++++++------
 utils/mountd/mountd.c    |    7 +++----
 3 files changed, 51 insertions(+), 13 deletions(-)


diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index e4777dd..edd3b32 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -135,9 +135,12 @@ int			nfsaddclient(struct nfsctl_client *clp);
 int			nfsdelclient(struct nfsctl_client *clp);
 int			nfsexport(struct nfsctl_export *exp);
 int			nfsunexport(struct nfsctl_export *exp);
-struct nfs_fh_len *	getfh_old(struct sockaddr *addr, dev_t dev, ino_t ino);
-struct nfs_fh_len *	getfh(struct sockaddr *addr, const char *);
-struct nfs_fh_len *	getfh_size(struct sockaddr *addr, const char *, int size);
+
+struct nfs_fh_len *	getfh_old(const struct sockaddr_in *sin,
+					const dev_t dev, const ino_t ino);
+struct nfs_fh_len *	getfh(const struct sockaddr_in *sin, const char *path);
+struct nfs_fh_len *	getfh_size(const struct sockaddr_in *sin,
+					const char *path, int const size);
 
 void qword_print(FILE *f, char *str);
 void qword_printhex(FILE *f, char *str, int slen);
diff --git a/support/nfs/getfh.c b/support/nfs/getfh.c
index 81266fd..f3023c5 100644
--- a/support/nfs/getfh.c
+++ b/support/nfs/getfh.c
@@ -19,8 +19,17 @@
 #include <errno.h>
 #include "nfslib.h"
 
+/**
+ * getfh_old - ask the kernel for an NFSv2 file handle via nfsctl()
+ * @sin: pointer to the AF_INET address of a client
+ * @dev: device number of device where requested object resides
+ * @ino: inode number of requested object
+ *
+ * Returns a pointer to an NFSv2 file handle, or NULL if some error
+ * occurred.  errno is set to reflect the specifics of the error.
+ */
 struct nfs_fh_len *
-getfh_old (struct sockaddr *addr, dev_t dev, ino_t ino)
+getfh_old (const struct sockaddr_in *sin, const dev_t dev, const ino_t ino)
 {
 	union nfsctl_res	res;
 	struct nfsctl_arg	arg;
@@ -30,7 +39,7 @@ getfh_old (struct sockaddr *addr, dev_t dev, ino_t ino)
 	arg.ca_getfh.gf_version = 2;	/* obsolete */
 	arg.ca_getfh.gf_dev = dev;
 	arg.ca_getfh.gf_ino = ino;
-	memcpy(&arg.ca_getfh.gf_addr, addr, sizeof(struct sockaddr_in));
+	memcpy(&arg.ca_getfh.gf_addr, sin, sizeof(*sin));
 
 	if (nfsctl(NFSCTL_GETFH, &arg, &res) < 0)
 		return NULL;
@@ -40,19 +49,32 @@ getfh_old (struct sockaddr *addr, dev_t dev, ino_t ino)
 	return &rfh;
 }
 
+/**
+ * getfh - ask the kernel for an NFSv2 file handle via nfsctl()
+ * @sin: pointer to the AF_INET address of a client
+ * @path: pointer to a '\0'-terminated ASCII string containing an pathname
+ *
+ * Returns a pointer to an NFSv2 file handle, or NULL if some error
+ * occurred.  errno is set to reflect the specifics of the error.
+ */
 struct nfs_fh_len *
-getfh(struct sockaddr *addr, const char *path)
+getfh(const struct sockaddr_in *sin, const char *path)
 {
 	static union nfsctl_res res;
         struct nfsctl_arg       arg;
 	static struct nfs_fh_len rfh;
 
+	if (sin->sin_family != AF_INET) {
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+
         arg.ca_version = NFSCTL_VERSION;
         arg.ca_getfd.gd_version = 2;    /* obsolete */
         strncpy(arg.ca_getfd.gd_path, path,
 		sizeof(arg.ca_getfd.gd_path) - 1);
 	arg.ca_getfd.gd_path[sizeof (arg.ca_getfd.gd_path) - 1] = '\0';
-        memcpy(&arg.ca_getfd.gd_addr, addr, sizeof(struct sockaddr_in));
+	memcpy(&arg.ca_getfd.gd_addr, sin, sizeof(*sin));
 
         if (nfsctl(NFSCTL_GETFD, &arg, &res) < 0)
                 return NULL;
@@ -62,17 +84,31 @@ getfh(struct sockaddr *addr, const char *path)
 	return &rfh;
 }
 
+/**
+ * getfh_size - ask the kernel for a file handle via nfsctl()
+ * @sin: pointer to the AF_INET address of a client
+ * @path: pointer to a '\0'-terminated ASCII string containing an pathname
+ * @size: maximum size, in bytes, of the returned file handle
+ *
+ * Returns a pointer to an NFSv3 file handle, or NULL if some error
+ * occurred.  errno is set to reflect the specifics of the error.
+ */
 struct nfs_fh_len *
-getfh_size(struct sockaddr *addr, const char *path, int size)
+getfh_size(const struct sockaddr_in *sin, const char *path, const int size)
 {
         static union nfsctl_res res;
         struct nfsctl_arg       arg;
 
+	if (sin->sin_family != AF_INET) {
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+
         arg.ca_version = NFSCTL_VERSION;
         strncpy(arg.ca_getfs.gd_path, path,
 		sizeof(arg.ca_getfs.gd_path) - 1);
 	arg.ca_getfs.gd_path[sizeof (arg.ca_getfs.gd_path) - 1] = '\0';
-        memcpy(&arg.ca_getfs.gd_addr, addr, sizeof(struct sockaddr_in));
+	memcpy(&arg.ca_getfs.gd_addr, sin, sizeof(*sin));
 	arg.ca_getfs.gd_maxlen = size;
 
         if (nfsctl(NFSCTL_GETFS, &arg, &res) < 0)
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 4c0c503..4955fb0 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -482,14 +482,13 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 			xtab_append(exp);
 
 		if (v3)
-			fh = getfh_size ((struct sockaddr *) sin, p, 64);
+			fh = getfh_size(sin, p, 64);
 		if (!v3 || (fh == NULL && errno == EINVAL)) {
 			/* We first try the new nfs syscall. */
-			fh = getfh ((struct sockaddr *) sin, p);
+			fh = getfh(sin, p);
 			if (fh == NULL && errno == EINVAL)
 				/* Let's try the old one. */
-				fh = getfh_old ((struct sockaddr *) sin,
-						stb.st_dev, stb.st_ino);
+				fh = getfh_old(sin, stb.st_dev, stb.st_ino);
 		}
 		if (fh == NULL && !did_export) {
 			exp->m_exported = 0;
-------------------------------------------------------------------------------
mountd-add-ipv6-support-in-aut
-------------------------------------------------------------------------------
mountd: add IPv6 support in auth_authenticate()

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

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

 utils/mountd/auth.c   |   62 +++++++++++++++++++++++++++++--------------------
 utils/mountd/mountd.c |    6 ++---
 utils/mountd/mountd.h |    5 ++--
 utils/mountd/rmtab.c  |    3 ++
 4 files changed, 45 insertions(+), 31 deletions(-)


diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
index 1eb78a3..d63b23a 100644
--- a/utils/mountd/auth.c
+++ b/utils/mountd/auth.c
@@ -21,6 +21,7 @@
 #include "mountd.h"
 #include "xmalloc.h"
 #include "v4root.h"
+#include "nfsrpc.h"
 
 enum auth_error
 {
@@ -112,9 +113,8 @@ auth_reload()
 }
 
 static nfs_export *
-auth_authenticate_internal(struct sockaddr_in *caller,
-			   char *path, struct addrinfo *ai,
-			   enum auth_error *error)
+auth_authenticate_internal(const struct sockaddr *caller, const char *path,
+		struct addrinfo *ai, enum auth_error *error)
 {
 	nfs_export		*exp;
 
@@ -123,10 +123,9 @@ auth_authenticate_internal(struct sockaddr_in *caller,
 		/* return static nfs_export with details filled in */
 		char *n;
 		free(my_client.m_hostname);
-		if (use_ipaddr) {
-			my_client.m_hostname =
-				strdup(inet_ntoa(caller->sin_addr));
-		} else {
+		if (use_ipaddr)
+			my_client.m_hostname = strdup(host_ntop(caller));
+		else {
 			n = client_compose(ai);
 			*error = unknown_host;
 			if (!n)
@@ -141,7 +140,21 @@ auth_authenticate_internal(struct sockaddr_in *caller,
 		if (my_client.m_hostname == NULL)
 			return NULL;
 		my_client.m_naddr = 1;
-		set_addrlist_in(&my_client, 0, caller);
+		switch (caller->sa_family) {
+		case AF_INET:
+			set_addrlist_in(&my_client, 0,
+						(struct sockaddr_in *)caller);
+			break;
+#ifdef IPV6_SUPPORTED
+		case AF_INET6:
+			set_addrlist_in6(&my_client, 0,
+						(struct sockaddr_in6 *)caller);
+			break;
+#endif
+		default:
+			free(n);
+			return NULL;
+		}
 		my_exp.m_client = &my_client;
 
 		exp = NULL;
@@ -173,7 +186,7 @@ auth_authenticate_internal(struct sockaddr_in *caller,
 		}
 	}
 	if (!(exp->m_export.e_flags & NFSEXP_INSECURE_PORT) &&
-		     ntohs(caller->sin_port) >= IPPORT_RESERVED) {
+		     nfs_get_port(caller) >= IPPORT_RESERVED) {
 		*error = illegal_port;
 		return NULL;
 	}
@@ -183,18 +196,18 @@ auth_authenticate_internal(struct sockaddr_in *caller,
 }
 
 nfs_export *
-auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
+auth_authenticate(const char *what, const struct sockaddr *caller,
+		const char *path)
 {
 	nfs_export	*exp = NULL;
 	char		epath[MAXPATHLEN+1];
 	char		*p = NULL;
 	struct addrinfo *ai = NULL;
-	struct in_addr	addr = caller->sin_addr;
 	enum auth_error	error = bad_path;
 
-	if (path [0] != '/') {
-		xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
-		     what, inet_ntoa(addr), path);
+	if (path[0] != '/') {
+		xlog(L_WARNING, "Bad path in %s request from %s: \"%s\"",
+			     what, host_ntop(caller), path);
 		return exp;
 	}
 
@@ -202,14 +215,13 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
 	epath[sizeof (epath) - 1] = '\0';
 	auth_fixpath(epath); /* strip duplicate '/' etc */
 
-	ai = client_resolve((struct sockaddr *)caller);
+	ai = client_resolve(caller);
 	if (!ai)
 		return exp;
 
 	/* Try the longest matching exported pathname. */
 	while (1) {
-		exp = auth_authenticate_internal(caller, epath,
-						 ai, &error);
+		exp = auth_authenticate_internal(caller, epath, ai, &error);
 		if (exp || (error != not_exported && error != no_entry))
 			break;
 		/* We have to treat the root, "/", specially. */
@@ -222,12 +234,12 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
 	switch (error) {
 	case bad_path:
 		xlog(L_WARNING, "bad path in %s request from %s: \"%s\"",
-		     what, inet_ntoa(addr), path);
+		     what, host_ntop(caller), path);
 		break;
 
 	case unknown_host:
 		xlog(L_WARNING, "refused %s request from %s for %s (%s): unmatched host",
-		     what, inet_ntoa(addr), path, epath);
+		     what, host_ntop(caller), path, epath);
 		break;
 
 	case no_entry:
@@ -241,17 +253,17 @@ auth_authenticate(char *what, struct sockaddr_in *caller, char *path)
 		break;
 
 	case illegal_port:
-		xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %d",
-		     what, ai->ai_canonname, path, epath, ntohs(caller->sin_port));
+		xlog(L_WARNING, "refused %s request from %s for %s (%s): illegal port %u",
+		     what, ai->ai_canonname, path, epath, nfs_get_port(caller));
 		break;
 
 	case success:
-		xlog(L_NOTICE, "authenticated %s request from %s:%d for %s (%s)",
-		     what, ai->ai_canonname, ntohs(caller->sin_port), path, epath);
+		xlog(L_NOTICE, "authenticated %s request from %s:%u for %s (%s)",
+		     what, ai->ai_canonname, nfs_get_port(caller), path, epath);
 		break;
 	default:
-		xlog(L_NOTICE, "%s request from %s:%d for %s (%s) gave %d",
-		     what, ai->ai_canonname, ntohs(caller->sin_port),
+		xlog(L_NOTICE, "%s request from %s:%u for %s (%s) gave %d",
+		     what, ai->ai_canonname, nfs_get_port(caller),
 			path, epath, error);
 	}
 
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 4955fb0..3d857f0 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -236,7 +236,7 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
 		p = rpath;
 	}
 
-	if (!(exp = auth_authenticate("unmount", sin, p))) {
+	if (!(exp = auth_authenticate("unmount", (struct sockaddr *)sin, p))) {
 		return 1;
 	}
 
@@ -311,7 +311,7 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
 	}
 
 	/* Now authenticate the intruder... */
-	exp = auth_authenticate("pathconf", sin, p);
+	exp = auth_authenticate("pathconf", (struct sockaddr *)sin, p);
 	if (!exp) {
 		return 1;
 	} else if (stat(p, &stb) < 0) {
@@ -413,7 +413,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 	}
 
 	/* Now authenticate the intruder... */
-	exp = auth_authenticate("mount", sin, p);
+	exp = auth_authenticate("mount", (struct sockaddr *)sin, p);
 	if (!exp) {
 		*error = NFSERR_ACCES;
 		return NULL;
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
index 31bacb5..96e9bf1 100644
--- a/utils/mountd/mountd.h
+++ b/utils/mountd/mountd.h
@@ -41,8 +41,9 @@ bool_t		mount_mnt_3_svc(struct svc_req *, dirpath *, mountres3 *);
 void		mount_dispatch(struct svc_req *, SVCXPRT *);
 void		auth_init(char *export_file);
 unsigned int	auth_reload(void);
-nfs_export *	auth_authenticate(char *what, struct sockaddr_in *sin,
-					char *path);
+nfs_export *	auth_authenticate(const char *what,
+					const struct sockaddr *caller,
+					const char *path);
 void		auth_export(nfs_export *exp);
 
 void		mountlist_add(char *host, const char *path);
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index 3b45d57..a6d553f 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -161,7 +161,8 @@ mountlist_del_all(struct sockaddr_in *sin)
 	}
 	while ((rep = getrmtabent(1, NULL)) != NULL) {
 		if (strcmp(rep->r_client, hostname) == 0 &&
-		    (exp = auth_authenticate("umountall", sin, rep->r_path)))
+		    (exp = auth_authenticate("umountall",
+				(struct sockaddr *)sin, rep->r_path)))
 			continue;
 		fputrmtabent(fp, rep, NULL);
 	}
-------------------------------------------------------------------------------
mountd-support-ipv6-in-mountd-
-------------------------------------------------------------------------------
mountd: Support IPv6 in mountd's svc routines

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

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

 utils/mountd/mountd.c |   35 ++++++++++++++++++-----------------
 1 files changed, 18 insertions(+), 17 deletions(-)


diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 3d857f0..ba4fcb7 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -212,9 +212,9 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
 bool_t
 mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
 {
-	struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt);
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 
-	xlog(D_CALL, "dump request from %s.", inet_ntoa(addr->sin_addr));
+	xlog(D_CALL, "dump request from %s.", host_ntop(sap));
 	*res = mountlist_list();
 
 	return 1;
@@ -223,7 +223,7 @@ mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
 bool_t
 mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
 {
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 	nfs_export	*exp;
 	char		*p = *argp;
 	char		rpath[MAXPATHLEN+1];
@@ -236,11 +236,11 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
 		p = rpath;
 	}
 
-	if (!(exp = auth_authenticate("unmount", (struct sockaddr *)sin, p))) {
+	if (!(exp = auth_authenticate("unmount", sap, p))) {
 		return 1;
 	}
 
-	mountlist_del(inet_ntoa(sin->sin_addr), p);
+	mountlist_del(host_ntop(sap), p);
 	return 1;
 }
 
@@ -257,9 +257,9 @@ mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
 bool_t
 mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
 {
-	struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt);
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 
-	xlog(D_CALL, "export request from %s.", inet_ntoa(addr->sin_addr));
+	xlog(D_CALL, "export request from %s.", host_ntop(sap));
 	*resp = get_exportlist();
 		
 	return 1;
@@ -268,9 +268,9 @@ mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
 bool_t
 mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
 {
-	struct sockaddr_in *addr = nfs_getrpccaller_in(rqstp->rq_xprt);
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 
-	xlog(D_CALL, "exportall request from %s.", inet_ntoa(addr->sin_addr));
+	xlog(D_CALL, "exportall request from %s.", host_ntop(sap));
 	*resp = get_exportlist();
 
 	return 1;
@@ -290,7 +290,7 @@ mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
 bool_t
 mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
 {
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 	struct stat	stb;
 	nfs_export	*exp;
 	char		rpath[MAXPATHLEN+1];
@@ -311,7 +311,7 @@ mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
 	}
 
 	/* Now authenticate the intruder... */
-	exp = auth_authenticate("pathconf", (struct sockaddr *)sin, p);
+	exp = auth_authenticate("pathconf", sap, p);
 	if (!exp) {
 		return 1;
 	} else if (stat(p, &stb) < 0) {
@@ -393,7 +393,7 @@ static struct nfs_fh_len *
 get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 		mountstat3 *error, int v3)
 {
-	struct sockaddr_in *sin = nfs_getrpccaller_in(rqstp->rq_xprt);
+	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 	struct stat	stb, estb;
 	nfs_export	*exp;
 	struct nfs_fh_len *fh;
@@ -413,7 +413,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 	}
 
 	/* Now authenticate the intruder... */
-	exp = auth_authenticate("mount", (struct sockaddr *)sin, p);
+	exp = auth_authenticate("mount", sap, p);
 	if (!exp) {
 		*error = NFSERR_ACCES;
 		return NULL;
@@ -482,13 +482,14 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 			xtab_append(exp);
 
 		if (v3)
-			fh = getfh_size(sin, p, 64);
+			fh = getfh_size((struct sockaddr_in *)sap, p, 64);
 		if (!v3 || (fh == NULL && errno == EINVAL)) {
 			/* We first try the new nfs syscall. */
-			fh = getfh(sin, p);
+			fh = getfh((struct sockaddr_in *)sap, p);
 			if (fh == NULL && errno == EINVAL)
 				/* Let's try the old one. */
-				fh = getfh_old(sin, stb.st_dev, stb.st_ino);
+				fh = getfh_old((struct sockaddr_in *)sap,
+						stb.st_dev, stb.st_ino);
 		}
 		if (fh == NULL && !did_export) {
 			exp->m_exported = 0;
@@ -502,7 +503,7 @@ get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
 		}
 	}
 	*error = NFS_OK;
-	mountlist_add(inet_ntoa(sin->sin_addr), p);
+	mountlist_add(host_ntop(sap), p);
 	if (expret)
 		*expret = exp;
 	return fh;
-------------------------------------------------------------------------------
mountd-support-ipv6-in-mountli-0
-------------------------------------------------------------------------------
mountd: support IPv6 in mountlist_del_all()

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

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

 utils/mountd/mountd.c |    2 +-
 utils/mountd/mountd.h |    2 +-
 utils/mountd/rmtab.c  |   10 ++++------
 3 files changed, 6 insertions(+), 8 deletions(-)


diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index ba4fcb7..cd3a024 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -250,7 +250,7 @@ mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
 	/* Reload /etc/xtab if necessary */
 	auth_reload();
 
-	mountlist_del_all(nfs_getrpccaller_in(rqstp->rq_xprt));
+	mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt));
 	return 1;
 }
 
diff --git a/utils/mountd/mountd.h b/utils/mountd/mountd.h
index 96e9bf1..0d31ca7 100644
--- a/utils/mountd/mountd.h
+++ b/utils/mountd/mountd.h
@@ -48,7 +48,7 @@ void		auth_export(nfs_export *exp);
 
 void		mountlist_add(char *host, const char *path);
 void		mountlist_del(char *host, const char *path);
-void		mountlist_del_all(struct sockaddr_in *sin);
+void		mountlist_del_all(const struct sockaddr *sap);
 mountlist	mountlist_list(void);
 
 
diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index a6d553f..b5d5bb7 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -130,7 +130,7 @@ mountlist_del(char *hname, const char *path)
 }
 
 void
-mountlist_del_all(struct sockaddr_in *sin)
+mountlist_del_all(const struct sockaddr *sap)
 {
 	char		*hostname;
 	struct rmtabent	*rep;
@@ -140,10 +140,9 @@ mountlist_del_all(struct sockaddr_in *sin)
 
 	if ((lockid = xflock(_PATH_RMTABLCK, "w")) < 0)
 		return;
-	hostname = host_canonname((struct sockaddr *)sin);
+	hostname = host_canonname(sap);
 	if (!hostname) {
-		xlog(L_ERROR, "can't get hostname of %s",
-				host_ntop((struct sockaddr *)sin));
+		xlog(L_ERROR, "can't get hostname of %s", host_ntop(sap));
 		xfunlock(lockid);
 		return;
 	}
@@ -161,8 +160,7 @@ mountlist_del_all(struct sockaddr_in *sin)
 	}
 	while ((rep = getrmtabent(1, NULL)) != NULL) {
 		if (strcmp(rep->r_client, hostname) == 0 &&
-		    (exp = auth_authenticate("umountall",
-				(struct sockaddr *)sin, rep->r_path)))
+		    (exp = auth_authenticate("umountall", sap, rep->r_path)))
 			continue;
 		fputrmtabent(fp, rep, NULL);
 	}
-------------------------------------------------------------------------------
mountd-add-mountlist_freeall
-------------------------------------------------------------------------------
mountd: Add mountlist_freeall()

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

Clean up: introduce a helper for freeing all mountlist records.

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

 utils/mountd/rmtab.c |   19 +++++++++++++------
 1 files changed, 13 insertions(+), 6 deletions(-)


diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index b5d5bb7..fd41dbf 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -174,6 +174,18 @@ mountlist_del_all(const struct sockaddr *sap)
 	free(hostname);
 }
 
+static void
+mountlist_freeall(mountlist mlist)
+{
+	while (mlist) {
+		mountlist m = mlist;
+		mlist = m->ml_next;
+		xfree(m->ml_hostname);
+		xfree(m->ml_directory);
+		xfree(m);
+	}
+}
+
 mountlist
 mountlist_list(void)
 {
@@ -193,12 +205,7 @@ mountlist_list(void)
 		return NULL;
 	}
 	if (stb.st_mtime != last_mtime) {
-		while (mlist) {
-			mlist = (m = mlist)->ml_next;
-			xfree(m->ml_hostname);
-			xfree(m->ml_directory);
-			xfree(m);
-		}
+		mountlist_freeall(mlist);
 		last_mtime = stb.st_mtime;
 
 		setrmtabent("r");
-------------------------------------------------------------------------------
mountd-handle-memory-exhaustio
-------------------------------------------------------------------------------
mountd: Handle memory exhaustion in mountlist_list()

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

I'm about to replace inet_aton(3)/gethostbyaddr(3) with
host_pton()/host_canonname().  Since host_canonname() returns a string
allocated with strdup(3) instead of xstrdup(), mountlist_list() will
now have to deal with memory exhaustion properly.

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

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


diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index fd41dbf..f08d5fd 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -16,7 +16,7 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
-#include "xmalloc.h"
+
 #include "misc.h"
 #include "exportfs.h"
 #include "xio.h"
@@ -180,9 +180,9 @@ mountlist_freeall(mountlist mlist)
 	while (mlist) {
 		mountlist m = mlist;
 		mlist = m->ml_next;
-		xfree(m->ml_hostname);
-		xfree(m->ml_directory);
-		xfree(m);
+		free(m->ml_hostname);
+		free(m->ml_directory);
+		free(m);
 	}
 }
 
@@ -210,16 +210,32 @@ mountlist_list(void)
 
 		setrmtabent("r");
 		while ((rep = getrmtabent(1, NULL)) != NULL) {
-			m = (mountlist) xmalloc(sizeof(*m));
+			m = calloc(1, sizeof(*m));
+			if (!m) {
+				mountlist_freeall(mlist);
+				mlist = NULL;
+				xlog(L_ERROR, "%s: memory allocation failed",
+						__func__);
+				break;
+			}
 
 			if (reverse_resolve &&
 			   inet_aton((const char *) rep->r_client, &addr) &&
 			   (he = gethostbyaddr(&addr, sizeof(addr), AF_INET)))
-				m->ml_hostname = xstrdup(he->h_name);
+				m->ml_hostname = strdup(he->h_name);
 			else
-				m->ml_hostname = xstrdup(rep->r_client);
+				m->ml_hostname = strdup(rep->r_client);
+
+			m->ml_directory = strdup(rep->r_path);
+
+			if (!m->ml_hostname || !m->ml_directory) {
+				mountlist_freeall(mlist);
+				mlist = NULL;
+				xlog(L_ERROR, "%s: memory allocation failed",
+						__func__);
+				break;
+			}
 
- 			m->ml_directory = xstrdup(rep->r_path);
 			m->ml_next = mlist;
 			mlist = m;
 		}
-------------------------------------------------------------------------------
mountd-support-ipv6-in-mountli
-------------------------------------------------------------------------------
mountd: Support IPv6 in mountlist_list()

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

Replace inet_aton(3) and gethostbyaddr(3) with calls to our shiny new
host_foo() DNS helpers.

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

 utils/mountd/rmtab.c |   16 +++++++++-------
 1 files changed, 9 insertions(+), 7 deletions(-)


diff --git a/utils/mountd/rmtab.c b/utils/mountd/rmtab.c
index f08d5fd..8e91ee4 100644
--- a/utils/mountd/rmtab.c
+++ b/utils/mountd/rmtab.c
@@ -195,8 +195,6 @@ mountlist_list(void)
 	struct rmtabent		*rep;
 	struct stat		stb;
 	int			lockid;
-	struct in_addr		addr;
-	struct hostent		*he;
 
 	if ((lockid = xflock(_PATH_RMTABLCK, "r")) < 0)
 		return NULL;
@@ -219,11 +217,15 @@ mountlist_list(void)
 				break;
 			}
 
-			if (reverse_resolve &&
-			   inet_aton((const char *) rep->r_client, &addr) &&
-			   (he = gethostbyaddr(&addr, sizeof(addr), AF_INET)))
-				m->ml_hostname = strdup(he->h_name);
-			else
+			if (reverse_resolve) {
+				struct addrinfo *ai;
+				ai = host_pton(rep->r_client);
+				if (ai) {
+					m->ml_hostname = host_canonname(ai->ai_addr);
+					freeaddrinfo(ai);
+				}
+			}
+			if (!m->ml_hostname)
 				m->ml_hostname = strdup(rep->r_client);
 
 			m->ml_directory = strdup(rep->r_path);
-------------------------------------------------------------------------------
exportfs-enable-ipv6-support-i
-------------------------------------------------------------------------------
exportfs: Enable IPv6 support in matchhostname()

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

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

 utils/exportfs/exportfs.c |   23 ++---------------------
 1 files changed, 2 insertions(+), 21 deletions(-)


diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index b76958d..5702864 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -28,6 +28,7 @@
 #include "nfslib.h"
 #include "exportfs.h"
 #include "xlog.h"
+#include "compaddr.h"
 
 static void	export_all(int verbose);
 static void	exportfs(char *arg, char *options, int verbose);
@@ -443,26 +444,6 @@ is_hostname(const char *sp)
 }
 
 static int
-compare_sockaddrs4(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 int
-compare_sockaddrs(const struct sockaddr *sa1, const struct sockaddr *sa2)
-{
-	if (sa1->sa_family == sa2->sa_family)
-		switch (sa1->sa_family) {
-		case AF_INET:
-			return compare_sockaddrs4(sa1, sa2);
-		}
-
-	return 0;
-}
-
-static int
 matchhostname(const char *hostname1, const char *hostname2)
 {
 	struct addrinfo *gai_results1 = NULL, *gai_results2 = NULL;
@@ -493,7 +474,7 @@ matchhostname(const char *hostname1, const char *hostname2)
 
 	for (ai1 = gai_results1; ai1; ai1 = ai1->ai_next)
 		for (ai2 = gai_results2; ai2; ai2 = ai2->ai_next)
-			if (compare_sockaddrs(ai1->ai_addr, ai2->ai_addr)) {
+			if (compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
 				result = 1;
 				goto out;
 			}
-------------------------------------------------------------------------------
mountd-handle-ipv6-addresses-i
-------------------------------------------------------------------------------
mountd: Handle IPv6 addresses in kernel auth_unix_ip upcalls

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

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

 utils/mountd/cache.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)


diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index cbe477d..255a67b 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -64,7 +64,7 @@ void auth_unix_ip(FILE *f)
 	 */
 	char *cp;
 	char class[20];
-	char ipaddr[20];
+	char ipaddr[INET6_ADDRSTRLEN];
 	char *client = NULL;
 	struct addrinfo *ai = NULL;
 	if (readline(fileno(f), &lbuf, &lbuflen) != 1)
@@ -78,7 +78,7 @@ void auth_unix_ip(FILE *f)
 	    strcmp(class, "nfsd") != 0)
 		return;
 
-	if (qword_get(&cp, ipaddr, 20) <= 0)
+	if (qword_get(&cp, ipaddr, sizeof(ipaddr)) <= 0)
 		return;
 
 	auth_reload();
-------------------------------------------------------------------------------
libexport-a-enable-ipv6-suppor
-------------------------------------------------------------------------------
libexport.a: Enable IPv6 support in hostname.c

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

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

 support/export/hostname.c |   30 +++++++++++++++---------------
 1 files changed, 15 insertions(+), 15 deletions(-)


diff --git a/support/export/hostname.c b/support/export/hostname.c
index 8d3a2bc..c92b241 100644
--- a/support/export/hostname.c
+++ b/support/export/hostname.c
@@ -33,16 +33,6 @@
 #define AI_ADDRCONFIG	0
 #endif
 
-#ifdef HAVE_GETNAMEINFO
-static socklen_t
-sockaddr_size(const struct sockaddr *sap)
-{
-	if (sap->sa_family != AF_INET)
-		return 0;
-	return sizeof(struct sockaddr_in);
-}
-#endif
-
 /**
  * host_ntop - generate presentation address given a sockaddr
  * @sap: pointer to socket address
@@ -55,7 +45,7 @@ char *
 host_ntop(const struct sockaddr *sap)
 {
 	static char buf[NI_MAXHOST];
-	socklen_t salen = sockaddr_size(sap);
+	socklen_t salen = sockaddr_length(sap);
 
 	if (salen == 0)
 		return strncpy(buf, "unknown family", sizeof(buf));
@@ -99,7 +89,7 @@ host_pton(const char *paddr)
 		.ai_flags	= AI_NUMERICHOST,
 	};
 	struct sockaddr_in sin;
-	int error;
+	int error, inet4;
 
 	/*
 	 * getaddrinfo(3) recognizes incomplete addresses like "10.4"
@@ -109,8 +99,9 @@ host_pton(const char *paddr)
 	 * inet_pton(3) is much stricter.  Use it to be certain we
 	 * have a real AF_INET presentation address.
 	 */
+	inet4 = 1;
 	if (!inet_pton(AF_INET, paddr, &sin.sin_addr))
-		return NULL;
+		inet4 = 0;
 
 	error = getaddrinfo(paddr, NULL, &hints, &ai);
 	if (error) {
@@ -119,6 +110,11 @@ host_pton(const char *paddr)
 		return NULL;
 	}
 
+	if (!inet4 && ai->ai_addr->sa_family == AF_INET) {
+		freeaddrinfo(ai);
+		return NULL;
+	}
+
 	return ai;
 }
 
@@ -134,7 +130,11 @@ struct addrinfo *
 host_addrinfo(const char *hostname)
 {
 	struct addrinfo hints = {
+#ifdef IPV6_SUPPORTED
+		.ai_family	= AF_UNSPEC,
+#else
 		.ai_family	= AF_INET,
+#endif
 		.ai_protocol	= IPPROTO_UDP,	/* don't return duplicates */
 		.ai_flags	= AI_ADDRCONFIG | AI_CANONNAME,
 	};
@@ -167,7 +167,7 @@ char *
 host_canonname(const struct sockaddr *sap)
 {
 	char buf[NI_MAXHOST];
-	socklen_t salen = sockaddr_size(sap);
+	socklen_t salen = sockaddr_length(sap);
 	int error;
 
 	if (salen == 0) {
@@ -246,7 +246,7 @@ host_numeric_addrinfo(const struct sockaddr *sap)
 {
 	char buf[NI_MAXHOST];
 	struct addrinfo *ai;
-	socklen_t salen = sockaddr_size(sap);
+	socklen_t salen = sockaddr_length(sap);
 	int error;
 
 	if (salen == 0) {
-------------------------------------------------------------------------------
mountd-support-ti-rpc-mountd-l
-------------------------------------------------------------------------------
mountd: Support TI-RPC mountd listener

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

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

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

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

 utils/mountd/mountd.c |   18 +++++++++---------
 1 files changed, 9 insertions(+), 9 deletions(-)


diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index cd3a024..8ff64c7 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -81,11 +81,11 @@ static void
 unregister_services (void)
 {
 	if (nfs_version & 0x1)
-		pmap_unset (MOUNTPROG, MOUNTVERS);
+		nfs_svc_unregister(MOUNTPROG, MOUNTVERS);
 	if (nfs_version & (0x1 << 1))
-		pmap_unset (MOUNTPROG, MOUNTVERS_POSIX);
+		nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX);
 	if (nfs_version & (0x1 << 2))
-		pmap_unset (MOUNTPROG, MOUNTVERS_NFSV3);
+		nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3);
 }
 
 static void
@@ -736,14 +736,14 @@ main(int argc, char **argv)
 		cache_open();
 
 	if (nfs_version & 0x1)
-		rpc_init("mountd", MOUNTPROG, MOUNTVERS,
-			 mount_dispatch, port);
+		(void)nfs_svc_create("mountd", MOUNTPROG, MOUNTVERS,
+					mount_dispatch, port);
 	if (nfs_version & (0x1 << 1))
-		rpc_init("mountd", MOUNTPROG, MOUNTVERS_POSIX,
-			 mount_dispatch, port);
+		(void)nfs_svc_create("mountd", MOUNTPROG, MOUNTVERS_POSIX,
+					mount_dispatch, port);
 	if (nfs_version & (0x1 << 2))
-		rpc_init("mountd", MOUNTPROG, MOUNTVERS_NFSV3,
-			 mount_dispatch, port);
+		(void)nfs_svc_create("mountd", MOUNTPROG, MOUNTVERS_NFSV3,
+					mount_dispatch, port);
 
 	sa.sa_handler = killer;
 	sigaction(SIGINT, &sa, NULL);
-------------------------------------------------------------------------------
mountd-fix-up-version-and-usag
-------------------------------------------------------------------------------
mountd: Fix up version and usage messages

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

Clean up: rpc.mountd is no longer known as kmountd.  Use the program's
basename rather than the full pathname for the usage message.  Display
a version message at start up similar to statd's.

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

 utils/mountd/mountd.c |   34 +++++++++++++++++++++-------------
 1 files changed, 21 insertions(+), 13 deletions(-)


diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 8ff64c7..8694bd5 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -608,6 +608,7 @@ main(int argc, char **argv)
 {
 	char	*export_file = _PATH_EXPORTS;
 	char    *state_dir = NFS_STATEDIR;
+	char	*progname;
 	int	foreground = 0;
 	int	port = 0;
 	int	descriptors = 0;
@@ -615,6 +616,12 @@ main(int argc, char **argv)
 	struct sigaction sa;
 	struct rlimit rlim;
 
+	/* Set the basename */
+	if ((progname = strrchr(argv[0], '/')) != NULL)
+		progname++;
+	else
+		progname = argv[0];
+
 	/* Parse the command line options and arguments. */
 	opterr = 0;
 	while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vrs:t:g", longopts, NULL)) != EOF)
@@ -626,8 +633,8 @@ main(int argc, char **argv)
 			descriptors = atoi(optarg);
 			if (descriptors <= 0) {
 				fprintf(stderr, "%s: bad descriptors: %s\n",
-					argv [0], optarg);
-				usage(argv [0], 1);
+					progname, optarg);
+				usage(progname, 1);
 			}
 			break;
 		case 'F':
@@ -643,15 +650,15 @@ main(int argc, char **argv)
 			ha_callout_prog = optarg;
 			break;
 		case 'h':
-			usage(argv [0], 0);
+			usage(progname, 0);
 			break;
 		case 'P':	/* XXX for nfs-server compatibility */
 		case 'p':
 			port = atoi(optarg);
 			if (port <= 0 || port > 65535) {
 				fprintf(stderr, "%s: bad port number: %s\n",
-					argv [0], optarg);
-				usage(argv [0], 1);
+					progname, optarg);
+				usage(progname, 1);
 			}
 			break;
 		case 'N':
@@ -666,7 +673,7 @@ main(int argc, char **argv)
 		case 's':
 			if ((state_dir = xstrdup(optarg)) == NULL) {
 				fprintf(stderr, "%s: xstrdup(%s) failed!\n",
-					argv[0], optarg);
+					progname, optarg);
 				exit(1);
 			}
 			break;
@@ -677,28 +684,28 @@ main(int argc, char **argv)
 			nfs_version |= 1 << (atoi (optarg) - 1);
 			break;
 		case 'v':
-			printf("kmountd %s\n", VERSION);
+			printf("%s version " VERSION "\n", progname);
 			exit(0);
 		case 0:
 			break;
 		case '?':
 		default:
-			usage(argv [0], 1);
+			usage(progname, 1);
 		}
 
 	/* No more arguments allowed. */
 	if (optind != argc || !(nfs_version & 0x7))
-		usage(argv [0], 1);
+		usage(progname, 1);
 
 	if (chdir(state_dir)) {
 		fprintf(stderr, "%s: chdir(%s) failed: %s\n",
-			argv [0], state_dir, strerror(errno));
+			progname, state_dir, strerror(errno));
 		exit(1);
 	}
 
 	if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
 		fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
-				argv [0], strerror(errno));
+				progname, strerror(errno));
 	else {
 		/* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
 		if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) ||
@@ -708,14 +715,15 @@ main(int argc, char **argv)
 			rlim.rlim_cur = descriptors;
 			if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
 				fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
-					argv [0], strerror(errno));
+					progname, strerror(errno));
 				exit(1);
 			}
 		}
 	}
 	/* Initialize logging. */
 	if (!foreground) xlog_stderr(0);
-	xlog_open("mountd");
+	xlog_open(progname);
+	xlog(L_NOTICE, "Version " VERSION " starting");
 
 	sa.sa_handler = SIG_IGN;
 	sa.sa_flags = 0;
-------------------------------------------------------------------------------
mountd-auth-c-no-longer-needs-
-------------------------------------------------------------------------------
mountd: auth.c no longer needs #include xmalloc.h

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

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

 utils/mountd/auth.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)


diff --git a/utils/mountd/auth.c b/utils/mountd/auth.c
index d63b23a..d2beb46 100644
--- a/utils/mountd/auth.c
+++ b/utils/mountd/auth.c
@@ -10,16 +10,18 @@
 #include <config.h>
 #endif
 
+#include <sys/types.h>
 #include <sys/stat.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <errno.h>
 #include <unistd.h>
+#include <fcntl.h>
+
 #include "misc.h"
 #include "nfslib.h"
 #include "exportfs.h"
 #include "mountd.h"
-#include "xmalloc.h"
 #include "v4root.h"
 #include "nfsrpc.h"
 
-------------------------------------------------------------------------------
mountd-cache-c-no-longer-needs
-------------------------------------------------------------------------------
mountd: cache.c no longer needs #include xmalloc.h

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

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

 utils/mountd/cache.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)


diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 255a67b..890a0fe 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -29,7 +29,6 @@
 #include "nfslib.h"
 #include "exportfs.h"
 #include "mountd.h"
-#include "xmalloc.h"
 #include "fsloc.h"
 #include "pseudoflavors.h"
 #include "v4root.h"
-------------------------------------------------------------------------------
libexport-a-rmtab-c-no-longer-
-------------------------------------------------------------------------------
libexport.a: rmtab.c no longer needs #include xmalloc.h

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

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

 support/export/rmtab.c |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)


diff --git a/support/export/rmtab.c b/support/export/rmtab.c
index ff348bd..15b6d06 100644
--- a/support/export/rmtab.c
+++ b/support/export/rmtab.c
@@ -1,7 +1,7 @@
 /*
- * support/export/rmntab.c
+ * support/export/rmtab.c
  *
- * Interface to the rmnt file.
+ * Interface to the rmtab file.
  *
  */
 
@@ -12,7 +12,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include "xmalloc.h"
+
 #include "misc.h"
 #include "nfslib.h"
 #include "exportfs.h"
-------------------------------------------------------------------------------
libexport-a-xtab-c-no-longer-n
-------------------------------------------------------------------------------
libexport.a: xtab.c no longer needs #include xmalloc.h

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

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

 support/export/xtab.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)


diff --git a/support/export/xtab.c b/support/export/xtab.c
index 2a43193..e953071 100644
--- a/support/export/xtab.c
+++ b/support/export/xtab.c
@@ -14,7 +14,7 @@
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
-#include "xmalloc.h"
+
 #include "nfslib.h"
 #include "exportfs.h"
 #include "xio.h"
-------------------------------------------------------------------------------
mountd-squelch-unused-paramete
-------------------------------------------------------------------------------
mountd: Squelch unused parameter warnings in mountd.c

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

Clean up: Eliminate these compiler warnings:

mountd.c: In function ‘mount_null_1_svc’:
mountd.c:195: warning: unused parameter ‘rqstp’
mountd.c:195: warning: unused parameter ‘argp’
mountd.c:195: warning: unused parameter ‘resp’
mountd.c: In function ‘mount_dump_1_svc’:
mountd.c:213: warning: unused parameter ‘argp’
mountd.c: In function ‘mount_umnt_1_svc’:
mountd.c:224: warning: unused parameter ‘resp’
mountd.c: In function ‘mount_umntall_1_svc’:
mountd.c:248: warning: unused parameter ‘argp’
mountd.c:248: warning: unused parameter ‘resp’
mountd.c: In function ‘mount_export_1_svc’:
mountd.c:258: warning: unused parameter ‘argp’
mountd.c: In function ‘mount_exportall_1_svc’:
mountd.c:269: warning: unused parameter ‘argp’

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

 utils/mountd/mountd.c |   19 +++++++++++++------
 1 files changed, 13 insertions(+), 6 deletions(-)


diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 8694bd5..d060286 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -192,7 +192,9 @@ sig_hup (int sig)
 }
 
 bool_t
-mount_null_1_svc(struct svc_req *rqstp, void *argp, void *resp)
+mount_null_1_svc(__attribute__((unused)) struct svc_req *rqstp,
+		__attribute__((unused)) void *argp,
+		__attribute__((unused)) void *resp)
 {
 	return 1;
 }
@@ -210,7 +212,8 @@ mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
 }
 
 bool_t
-mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
+mount_dump_1_svc(struct svc_req *rqstp, __attribute__((unused)) void *argp,
+		mountlist *res)
 {
 	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 
@@ -221,7 +224,8 @@ mount_dump_1_svc(struct svc_req *rqstp, void *argp, mountlist *res)
 }
 
 bool_t
-mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
+mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp,
+		__attribute__((unused)) void *resp)
 {
 	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 	nfs_export	*exp;
@@ -245,7 +249,8 @@ mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *resp)
 }
 
 bool_t
-mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
+mount_umntall_1_svc(struct svc_req *rqstp, __attribute__((unused)) void *argp,
+		__attribute__((unused))void *resp)
 {
 	/* Reload /etc/xtab if necessary */
 	auth_reload();
@@ -255,7 +260,8 @@ mount_umntall_1_svc(struct svc_req *rqstp, void *argp, void *resp)
 }
 
 bool_t
-mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+mount_export_1_svc(struct svc_req *rqstp, __attribute__((unused)) void *argp,
+		exports *resp)
 {
 	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 
@@ -266,7 +272,8 @@ mount_export_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
 }
 
 bool_t
-mount_exportall_1_svc(struct svc_req *rqstp, void *argp, exports *resp)
+mount_exportall_1_svc(struct svc_req *rqstp, __attribute__((unused)) void *argp,
+		exports *resp)
 {
 	struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
 
-------------------------------------------------------------------------------
mountd-make-e_fsid-unsigned
-------------------------------------------------------------------------------
mountd: Make e_fsid unsigned

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

Eliminate this compiler warning:

cache.c: In function ‘nfsd_fh’:
cache.c:441: warning: comparison between signed and unsigned integer
expressions

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

 support/include/nfslib.h  |    3 ++-
 support/nfs/cacheio.c     |    5 +++++
 support/nfs/exports.c     |    2 +-
 utils/exportfs/exportfs.c |    2 +-
 utils/mountd/cache.c      |    2 +-
 5 files changed, 10 insertions(+), 4 deletions(-)


diff --git a/support/include/nfslib.h b/support/include/nfslib.h
index edd3b32..7965e42 100644
--- a/support/include/nfslib.h
+++ b/support/include/nfslib.h
@@ -83,7 +83,7 @@ struct exportent {
 	int		e_nsquids;
 	int *		e_sqgids;
 	int		e_nsqgids;
-	int		e_fsid;
+	unsigned int	e_fsid;
 	char *		e_mountpoint;
 	int             e_fslocmethod;
 	char *          e_fslocdata;
@@ -145,6 +145,7 @@ struct nfs_fh_len *	getfh_size(const struct sockaddr_in *sin,
 void qword_print(FILE *f, char *str);
 void qword_printhex(FILE *f, char *str, int slen);
 void qword_printint(FILE *f, int num);
+void qword_printuint(FILE *f, unsigned int num);
 int qword_eol(FILE *f);
 int readline(int fd, char **buf, int *lenp);
 int qword_get(char **bpp, char *dest, int bufsize);
diff --git a/support/nfs/cacheio.c b/support/nfs/cacheio.c
index 6a6ed5a..7966005 100644
--- a/support/nfs/cacheio.c
+++ b/support/nfs/cacheio.c
@@ -148,6 +148,11 @@ void qword_printint(FILE *f, int num)
 	fprintf(f, "%d ", num);
 }
 
+void qword_printuint(FILE *f, unsigned int num)
+{
+	fprintf(f, "%u ", num);
+}
+
 int qword_eol(FILE *f)
 {
 	int err;
diff --git a/support/nfs/exports.c b/support/nfs/exports.c
index 1aaebf4..5e7cc0c 100644
--- a/support/nfs/exports.c
+++ b/support/nfs/exports.c
@@ -250,7 +250,7 @@ putexportent(struct exportent *ep)
 	fprintf(fp, "%sacl,", (ep->e_flags & NFSEXP_NOACL)?
 		"no_" : "");
 	if (ep->e_flags & NFSEXP_FSID) {
-		fprintf(fp, "fsid=%d,", ep->e_fsid);
+		fprintf(fp, "fsid=%u,", ep->e_fsid);
 	}
 	if (ep->e_uuid)
 		fprintf(fp, "fsid=%s,", ep->e_uuid);
diff --git a/utils/exportfs/exportfs.c b/utils/exportfs/exportfs.c
index 5702864..0856079 100644
--- a/utils/exportfs/exportfs.c
+++ b/utils/exportfs/exportfs.c
@@ -550,7 +550,7 @@ dump(int verbose)
 			if (ep->e_flags & NFSEXP_NOACL)
 				c = dumpopt(c, "no_acl");
 			if (ep->e_flags & NFSEXP_FSID)
-				c = dumpopt(c, "fsid=%d", ep->e_fsid);
+				c = dumpopt(c, "fsid=%u", ep->e_fsid);
 			if (ep->e_uuid)
 				c = dumpopt(c, "fsid=%s", ep->e_uuid);
 			if (ep->e_mountpoint)
diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 890a0fe..2f512f7 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -599,7 +599,7 @@ static int dump_to_cache(FILE *f, char *domain, char *path, struct exportent *ex
 		qword_printint(f, exp->e_flags & flag_mask);
 		qword_printint(f, exp->e_anonuid);
 		qword_printint(f, exp->e_anongid);
-		qword_printint(f, exp->e_fsid);
+		qword_printuint(f, exp->e_fsid);
 		write_fsloc(f, exp, path);
 		write_secinfo(f, exp, flag_mask);
  		if (exp->e_uuid == NULL || different_fs) {
-------------------------------------------------------------------------------
mountd-squelch-compiler-warnin-0
-------------------------------------------------------------------------------
mountd: Squelch compiler warnings in cache.c

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

Clean up: Eliminate these compiler warnings:

cache.c: In function ‘nfsd_fh’:
cache.c:441: warning: comparison between signed and unsigned integer
expressions
cache.c: At top level:
cache.c:713: warning: missing initializer
cache.c:713: warning: (near initialization for ‘cachelist[0].f’)
cache.c:714: warning: missing initializer
cache.c:714: warning: (near initialization for ‘cachelist[1].f’)
cache.c:715: warning: missing initializer
cache.c:715: warning: (near initialization for ‘cachelist[2].f’)
cache.c:716: warning: missing initializer
cache.c:716: warning: (near initialization for ‘cachelist[3].f’)
cache.c:717: warning: missing initializer
cache.c:717: warning: (near initialization for ‘cachelist[4].f’)
cache.c: In function ‘cache_export_ent’:
cache.c:788: warning: comparison between signed and unsigned integer
expressions
cache.c:808: warning: comparison between signed and unsigned integer
expressions

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

 utils/mountd/cache.c |   26 +++++++++++++++++++-------
 1 files changed, 19 insertions(+), 7 deletions(-)


diff --git a/utils/mountd/cache.c b/utils/mountd/cache.c
index 2f512f7..9b71521 100644
--- a/utils/mountd/cache.c
+++ b/utils/mountd/cache.c
@@ -747,11 +747,23 @@ struct {
 	void (*cache_handle)(FILE *f);
 	FILE *f;
 } cachelist[] = {
-	{ "auth.unix.ip", auth_unix_ip},
-	{ "auth.unix.gid", auth_unix_gid},
-	{ "nfsd.export", nfsd_export},
-	{ "nfsd.fh", nfsd_fh},
-	{ NULL, NULL }
+	{
+		.cache_name	= "auth.unix.ip",
+		.cache_handle	= auth_unix_ip,
+	},
+	{
+		.cache_name	= "auth.unix.gid",
+		.cache_handle	= auth_unix_gid,
+	},
+	{
+		.cache_name	= "nfsd.export",
+		.cache_handle	= nfsd_export,
+	},
+	{
+		.cache_name	= "nfsd.fh",
+		.cache_handle	= nfsd_fh,
+	},
+	{ NULL, NULL, NULL },
 };
 
 extern int manage_gids;
@@ -821,8 +833,8 @@ int cache_export_ent(char *domain, struct exportent *exp, char *path)
 		 * and export them with the same options
 		 */
 		struct stat stb;
-		int l = strlen(exp->e_path);
-		int dev;
+		size_t l = strlen(exp->e_path);
+		dev_t dev;
 
 		if (strlen(path) <= l || path[l] != '/' ||
 		    strncmp(exp->e_path, path, l) != 0)
-------------------------------------------------------------------------------
mountd-squelch-compiler-warnin
-------------------------------------------------------------------------------
mountd: Squelch compiler warning in fsloc.c

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

Clean up: Eliminate this compiler warning:

fsloc.c: In function ‘replicas_lookup’:
fsloc.c:149: warning: unused parameter ‘key’

I have a nagging sense that @key will be used at some point, so I'm
leaving it in.

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

 utils/mountd/fsloc.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)


diff --git a/utils/mountd/fsloc.c b/utils/mountd/fsloc.c
index 5b094b0..dcbe62a 100644
--- a/utils/mountd/fsloc.c
+++ b/utils/mountd/fsloc.c
@@ -146,7 +146,8 @@ static struct servers *method_list(char *data)
 }
 
 /* Returns appropriately filled struct servers, or NULL if had a problem */
-struct servers *replicas_lookup(int method, char *data, char *key)
+struct servers *replicas_lookup(int method, char *data,
+		__attribute__((unused)) char *key)
 {
 	struct servers *sp=NULL;
 	switch(method) {
-------------------------------------------------------------------------------
mountd-update-mountd-exportfs-
-------------------------------------------------------------------------------
mountd: Update mountd/exportfs man pages to reflect IPv6 changes

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

Document IPv6 support in rpc.mountd and exportfs, and clarify existing
language in the man page.

Clean up: Use bold consistently for program names, and italics
consistently for file names.  Use "rpc.mountd" consistently as the
name of the mountd daemon.

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

 utils/exportfs/exportfs.man |  281 ++++++++++++++++++++++---------------------
 utils/exportfs/exports.man  |   70 +++++++----
 utils/mountd/mountd.man     |  240 ++++++++++++++++++++++++-------------
 3 files changed, 342 insertions(+), 249 deletions(-)


diff --git a/utils/exportfs/exportfs.man b/utils/exportfs/exportfs.man
index c7b230a..9839ba5 100644
--- a/utils/exportfs/exportfs.man
+++ b/utils/exportfs/exportfs.man
@@ -1,11 +1,11 @@
+.\"@(#)exportfs.8"
 .\"
-.\" exportfs(8)
-.\" 
 .\" Copyright (C) 1995 Olaf Kirch <okir@monad.swb.de>
 .\" Modifications 1999-2003 Neil Brown <neilb@cse.unsw.edu.au>
-.TH exportfs 8 "18 July 2003"
+.\"
+.TH exportfs 8 "31 December 2009"
 .SH NAME
-exportfs \- maintain list of NFS exported file systems
+exportfs \- maintain table of exported NFS file systems
 .SH SYNOPSIS
 .BI "/usr/sbin/exportfs [-avi] [-o " "options,.." "] [" "client:/path" " ..]
 .br
@@ -18,65 +18,68 @@ exportfs \- maintain list of NFS exported file systems
 .BI "/usr/sbin/exportfs -f"
 .br
 .SH DESCRIPTION
+An NFS server maintains a table of local physical file systems
+that are accessible to NFS clients.
+Each file system in this table is  referred to as an
+.IR "exported file system" ,
+or
+.IR export ,
+for short.
+.PP
 The
 .B exportfs
-command is used to maintain the current table of exported file systems for
-NFS. This list is kept in a separate file named
-.BR /var/lib/nfs/etab
-which is read by
-.B mountd
-when a remote host requests access to mount a file tree, and parts of
-the list which are active are kept in the kernel's export table.
-.P
-Normally this 
-.B etab
-file is initialized with the list of all file systems named in
-.B /etc/exports 
+command maintains the current table of exports for the NFS server.
+The master export table is kept in a file named
+.IR /var/lib/nfs/etab .
+This file is read by
+.B rpc.mountd
+when a client sends an NFS MOUNT request.
+.PP
+Normally the master export table is initialized with the contents of
+.I /etc/exports 
 by invoking
 .BR "exportfs -a" .
-.P
-However, administrators can choose to add and delete individual file systems
-without modifying
-.B /etc/exports
-using
-.BR exportfs .
-.P
+However, a system administrator can choose to add or delete
+exports without modifying
+.I /etc/exports
+by using the
+.B exportfs
+command.
+.PP
 .B exportfs
-and it's partner program
-.B mountd
-work in one of two modes, a legacy mode which applies to 2.4 and
+and its partner program
+.B rpc.mountd
+work in one of two modes: a legacy mode which applies to 2.4 and
 earlier versions of the Linux kernel, and a new mode which applies to
-2.6 and later versions providing the
+2.6 and later versions, providing the
 .B nfsd
 virtual filesystem has been mounted at
-.B /proc/fs/nfsd
+.I /proc/fs/nfsd
 or
-.BR /proc/fs/nfs .
-If this filesystem is not mounted in 2.6, the legacy mode is used.
-.P
+.IR /proc/fs/nfs .
+On 2.6 kernels, if this filesystem is not mounted, the legacy mode is used.
+.PP
 In the new mode,
 .B exportfs
-does not give any information to the kernel but only provides it to
-.B mountd
+does not give any information to the kernel, but provides it only to
+.B rpc.mountd
 through the
-.B /var/lib/nfs/etab
+.I /var/lib/nfs/etab
 file.
-.B mountd
-will listen to requests from the kernel and will provide information
-as needed.
-.P
+.B rpc.mountd
+then manages kernel requests for information about exports, as needed.
+.PP
 In the legacy mode,
-any export requests which identify a specific host (rather than a
-subnet or netgroup etc) are entered directly into the kernel's export
-table as well as being written to
-.BR /var/lib/nfs/etab .
-Further, any mount points listed in
-.B /var/lib/nfs/rmtab
+exports which identify a specific host, rather than a subnet or netgroup,
+are entered directly into the kernel's export table,
+as well as being written to
+.IR /var/lib/nfs/etab .
+Further, exports listed in
+.I /var/lib/nfs/rmtab
 which match a non host-specific export request will cause an
 appropriate export entry for the host given in
-.B rmtab
-to be entered
-into the kernel's export table.
+.I rmtab
+to be added to the kernel's export table.
 .SH OPTIONS
 .TP 
 .B -a
@@ -84,163 +87,167 @@ Export or unexport all directories.
 .TP
 .BI "-o " options,...
 Specify a list of export options in the same manner as in
-.BR exports(5) .
+.BR exports (5).
 .TP
 .B -i
 Ignore the
-.B /etc/exports
-file, so that only default options and options given on the command
-line are used.
+.I /etc/exports
+file.  Only default options and options given on the command line are used.
 .TP
 .B -r
-Reexport all directories. It synchronizes /var/lib/nfs/etab
-with /etc/exports. It removes entries in /var/lib/nfs/etab
-which are deleted from /etc/exports, and remove any entries from the
+Reexport all directories, synchronizing
+.I /var/lib/nfs/etab
+with
+.IR /etc/exports .
+This option removes entries in
+.I /var/lib/nfs/etab
+which have been deleted from
+.I /etc/exports, and removes any entries from the
 kernel export table which are no longer valid.
 .TP
 .B -u
 Unexport one or more directories.
 .TP
 .B -f
-In 'new' mode, flush everything out of the kernels export table. Any
-clients that are active will get new entries added by
-.B mountd
-when they make their next request.
+If
+.I /proc/fs/nfsd
+or
+.I /proc/fs/nfs
+is mounted, flush everything out of the kernel's export table.
+Fresh entries for active clients are added to the kernel's export table by
+.B rpc.mountd
+when they make their next NFS mount request.
 .TP
 .B -v
 Be verbose. When exporting or unexporting, show what's going on. When
 displaying the current export list, also display the list of export
 options.
 .SH DISCUSSION
-.\" -------------------- Exporting Directories --------------------
 .SS Exporting Directories
-The first synopsis shows how to invoke the command when adding new
-entries to the export table.  When using 
+The first synopsis shows how to invoke
+.B exportfs
+when adding new entries to the export table.  When using 
 .BR "exportfs -a" ,
-all directories in
-.B exports(5)
+all exports listed in
+.I /etc/exports
 are added to
-.B etab
-and the resulting list is pushed into the kernel.
-.P
+.IR /var/lib/nfs/etab .
+The kernel's export table is also updated as needed.
+.PP
 The
 .I host:/path
-argument specifies the directory to export along with the host or hosts to
-export it to. All formats described in
+argument specifies a local directory to export,
+along with the client or clients who are permitted to access it.
+See
 .B exports(5)
-are supported; to export a directory to the world, simply specify
+for a description of supported options and access list formats.
+To export a directory to the world, simply specify
 .IR :/path .
-.P
+.PP
 The export options for a particular host/directory pair derive from
-several sources.  There is a set of default options which can be overridden by
-entries in
-.B /etc/exports
-(unless the
-.B -i
-option is given).
-In addition, the administrator may override any options from these sources
-using the
+several sources.
+The default export options are
+.BR sync,ro,root_squash,wdelay .
+These can be overridden by entries in
+.IR /etc/exports .
+.PP
+A system administrator may override options from these sources using the
 .B -o
-argument which takes a comma-separated list of options in the same fashion
+command-line option on
+.BR exportfs .
+This option takes a comma-separated list of options in the same fashion
 as one would specify them in
-.BR exports(5) .
-Thus,
+.IR /etc/exports .
+In this way
 .B exportfs
-can also be used to modify the export options of an already exported
-directory.
-.P
-Modifications of the kernel export table used by
-.B nfsd(8)
-take place immediately after parsing the command line and updating the
-.B etab
-file.
-.P
-The default export options are
-.BR sync,ro,root_squash,wdelay .
-.\" -------------------- Unexporting Directories ------------------
+can be used to modify the export options of an already exported directory.
 .SS Unexporting Directories
 The third synopsis shows how to unexported a currently exported directory.
 When using
 .BR "exportfs -ua" ,
 all entries listed in
-.B etab
+.I /var/lib/nfs/etab
 are removed from the kernel export tables, and the file is cleared. This
 effectively shuts down all NFS activity.
-.P
-To remove an export to a host, specify a
+.PP
+To remove an export, specify a
 .I host:/path
 pair. This deletes the specified entry from
-.B etab
+.I /var/lib/nfs/etab
 and removes the corresponding kernel entry (if any).
-To remove one or more exports to several hosts, use
-.BR "exportfs -ua" .
-.P
-.\" -------------------- Dumping the Export Table -----------------
+.PP
 .SS Dumping the Export Table 
 Invoking
 .B exportfs
-without further options shows the current list of exported file systems.
-When giving the
+without options shows the current list of exported file systems.
+Adding the
 .B -v
-option, the list of flags pertaining to each export are shown in addition.
-.\" -------------------- EXAMPLES ---------------------------------
+option causes
+.B exportfs
+to display the export options for each export.
 .SH EXAMPLES
 The following adds all directories listed in
-.B /etc/exports
+.I /etc/exports
 to
-.B /var/lib/nfs/etab
+.I /var/lib/nfs/etab
 and pushes the resulting export entries into the kernel:
-.P
+.PP
 .nf
 .B "# exportfs -a
 .fi
-.P
+.PP
 To export the
-.B /usr/tmp
+.I /usr/tmp
 directory to host 
 .BR django ,
-allowing asynchronous writes, one would do this:
-.P
+allowing insecure file locking requests from clients:
+.PP
 .nf
-.B "# exportfs -o async django:/usr/tmp
+.B "# exportfs -o insecure_locks django:/usr/tmp
 .fi
-.P
+.PP
 To unexport the
-.B /usr/tmp
+.I /usr/tmp
 directory:
-.P
+.PP
 .nf
 .B "# exportfs -u django:/usr/tmp
 .fi
-.P
-To unexport all the directories listed in
-.B /etc/exports:
-.P
+.PP
+To unexport all exports listed in
+.IR /etc/exports :
+.PP
 .nf
 .B "# exportfs -au
 .fi
-.\" -------------------- DEPENDENCIES -----------------------------
-.SH DEPENDENCIES
-Exporting to IP networks, DNS and NIS domains does not enable clients
-from these groups to access NFS immediately; rather, these sorts of
-exports are hints to
-.B mountd(8)
+.SH USAGE NOTES
+Exporting to IP networks or DNS and NIS domains does not enable clients
+from these groups to access NFS immediately.
+Rather, these sorts of exports are hints to
+.BR rpc.mountd (8)
 to grant any mount requests from these clients.
-This is usually not a big problem, because any existing mounts are preserved
-in
-.B rmtab
+This is usually not a problem, because any existing mounts are preserved in
+.I rmtab
 across reboots.
-.P
+.PP
 When unexporting a network or domain entry, any current exports to members
 of this group will be checked against the remaining valid exports and
 if they themselves are no longer valid they will be removed.
-.P
-.\" -------------------- SEE ALSO --------------------------------
+.SH FILES
+.TP 2.5i
+.I /etc/exports
+input file listing exports, export options, and access control lists
+.TP 2.5i
+.I /var/lib/nfs/etab
+master table of exports
+.TP 2.5i
+.I /var/lib/nfs/rmtab
+table of clients accessing server's exports
 .SH SEE ALSO
-.BR exports(5) ", " mountd(8)
-.\" -------------------- AUTHOR ----------------------------------
+.BR exports (5),
+.BR rpc.mountd (8),
+.BR netgroup (5)
 .SH AUTHORS
-Olaf Kirch, <okir@monad.swb.de>
+Olaf Kirch <okir@monad.swb.de>
 .br
-Neil Brown, <neilb@cse.unsw.edu.au>
-
+Neil Brown <neilb@cse.unsw.edu.au>
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index ea28ca8..90d2cf2 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -1,18 +1,22 @@
-.TH EXPORTS 5 "4 March 2005" "Linux" "Linux File Formats Manual"
+.\"@(#)exports.5"
+.\"
+.TH exports 5 "31 December 2009"
 .SH NAME
-exports \- NFS file systems being exported (for Kernel based NFS)
-.SH SYNOPSIS
-.B /etc/exports
+exports \- NFS server export table
 .SH DESCRIPTION
 The file
 .I /etc/exports
-serves as the access control list for file systems which may be
-exported to NFS clients.  It is used by
-.IR exportfs (8)
+contains a table of local physical file systems on an NFS server
+that are accessible to NFS clients.
+The contents of the file are maintained by the server's system
+administrator.
+.PP
+Each file system in this table has a list of options and an
+access control list.
+The table is used by
+.BR exportfs (8)
 to give information to
-.IR mountd (8)
-and to the kernel based NFS file server daemon
-.IR nfsd (8).
+.BR mountd (8).
 .PP
 The file format is similar to the SunOS
 .I exports
@@ -34,7 +38,9 @@ double quotes. You can also specify spaces or other unusual character in
 the export name using a backslash followed by the character code as three
 octal digits.
 .PP
-To apply changes to this file, run exportfs \-ra or restart the NFS server.
+To apply changes to this file, run
+.BR exportfs \-ra
+or restart the NFS server.
 .PP
 .SS Machine Name Formats
 NFS clients may be specified in a number of ways:
@@ -61,9 +67,10 @@ simultaneously. This is done by specifying an IP address and netmask pair
 as
 .IR address/netmask
 where the netmask can be specified in dotted-decimal format, or as a
-contiguous mask length (for example, either `/255.255.252.0' or `/22' appended
-to the network base address result in identical subnetworks with 10 bits of
-host). Wildcard characters generally do not work on IP addresses, though they
+contiguous mask length.
+For example, either `/255.255.252.0' or `/22' appended
+to the network base IPv4 address results in identical subnetworks with 10 bits of
+host. Wildcard characters generally do not work on IP addresses, though they
 may work by accident when reverse DNS lookups fail.
 '''.TP
 '''.B =public
@@ -106,7 +113,7 @@ preceding sec= option.  The only options that are permitted to vary in
 this way are ro, rw, no_root_squash, root_squash, and all_squash.
 .PP
 .SS General Options
-.IR exportfs
+.BR exportfs
 understands the following export options:
 .TP
 .IR secure "\*d
@@ -144,7 +151,8 @@ default.  In all releases after 1.0.0,
 is the default, and
 .I async
 must be explicitly requested if needed.
-To help make system administrators aware of this change, 'exportfs'
+To help make system administrators aware of this change,
+.B exportfs
 will issue a warning if neither
 .I sync
 nor
@@ -246,7 +254,7 @@ If you genuinely require subtree checking, you should explicitly put
 that option in the
 .B exports
 file.  If you put neither option,
-.I exportfs
+.B exportfs
 will warn you that the change is pending.
 
 .TP
@@ -272,7 +280,9 @@ or
 .TP
 .IR no_acl
 On some specially patched kernels, and when exporting filesystems that
-support ACLs, this option tells nfsd not to reveal ACLs to clients, so
+support ACLs, this option tells
+.B nfsd
+not to reveal ACLs to clients, so
 they will see only a subset of actual permissions on the given file
 system.  This option is safe for filesystems used by NFSv2 clients and
 old NFSv3 clients that perform access decisions locally.  Current
@@ -381,7 +391,7 @@ of the filesystem must be handled elsewhere.)
 
 .SS User ID Mapping
 .PP
-.I nfsd
+.B nfsd
 bases its access control to files on the server machine on the uid and
 gid provided in each NFS RPC request. The normal behavior a user would
 expect is that she can access her files on the server just as she would
@@ -399,11 +409,11 @@ and can be turned off with
 .IR no_root_squash .
 .PP
 By default,
-'''.I nfsd
+'''.B nfsd
 '''tries to obtain the anonymous uid and gid by looking up user
 '''.I nobody
 '''in the password file at startup time. If it isn't found, a uid and gid
-.I exportfs
+.B exportfs
 chooses a uid and gid
 of 65534 for squashed access. These values can also be overridden by
 the
@@ -411,7 +421,7 @@ the
 options.
 '''.PP
 '''In addition to this, 
-'''.I nfsd
+'''.B nfsd
 '''lets you specify arbitrary uids and gids that should be mapped to user
 '''nobody as well.
 Finally, you can map all user requests to the
@@ -478,7 +488,7 @@ all three mounts with the `sync' option enabled.
 '''access to the private directory.
 '''.SH CAVEATS
 '''Unlike other NFS server implementations, this
-'''.I nfsd
+'''.B nfsd
 '''allows you to export both a directory and a subdirectory thereof to
 '''the same host, for instance 
 '''.IR /usr " and " /usr/X11R6 .
@@ -499,7 +509,15 @@ all three mounts with the `sync' option enabled.
 .BR showmount (8).
 '''.SH DIAGNOSTICS
 '''An error parsing the file is reported using syslogd(8) as level NOTICE from
-'''a DAEMON whenever nfsd(8) or mountd(8) is started up.  Any unknown
+'''a DAEMON whenever
+'''.BR nfsd (8)
+'''or
+'''.BR mountd (8)
+'''is started up.  Any unknown
 '''host is reported at that time, but often not all hosts are not yet known
-'''to named(8) at boot time, thus as hosts are found they are reported
-'''with the same syslogd(8) parameters.
+'''to
+'''.BR named (8)
+'''at boot time, thus as hosts are found they are reported
+'''with the same
+'''.BR syslogd (8)
+'''parameters.
diff --git a/utils/mountd/mountd.man b/utils/mountd/mountd.man
index bfa06e0..f337590 100644
--- a/utils/mountd/mountd.man
+++ b/utils/mountd/mountd.man
@@ -1,9 +1,9 @@
-.\"
-.\" mountd(8)
+.\"@(#)rpc.mountd.8"
 .\"
 .\" Copyright (C) 1999 Olaf Kirch <okir@monad.swb.de>
 .\" Modified by Paul Clements, 2004.
-.TH rpc.mountd 8 "31 Aug 2004"
+.\"
+.TH rpc.mountd 8 "31 Dec 2009"
 .SH NAME
 rpc.mountd \- NFS mount daemon
 .SH SYNOPSIS
@@ -11,48 +11,73 @@ rpc.mountd \- NFS mount daemon
 .SH DESCRIPTION
 The
 .B rpc.mountd
-program implements the NFS mount protocol. When receiving a MOUNT
-request from an NFS client, it checks the request against the list of
-currently exported file systems. If the client is permitted to mount
-the file system,
-.B rpc.mountd
-obtains a file handle for requested directory and returns it to
-the client.
-.SS Exporting NFS File Systems
-Making file systems available to NFS clients is called
-.IR exporting .
-.P
-Usually, a file system and the hosts it should be made available to
-are listed in the
-.B /etc/exports
-file, and invoking
-.B exportfs -a
-whenever the system is booted. The
+daemon implements the server side of the NFS MOUNT protocol,
+an NFS side protocol used by NFS version 2 [RFC1094] and NFS version 3 [RFC1813].
+.PP
+An NFS server maintains a table of local physical file systems
+that are accessible to NFS clients.
+Each file system in this table is referred to as an
+.IR "exported file system" ,
+or
+.IR export ,
+for short.
+.PP
+Each file system in the export table has an access control list.
+.B rpc.mountd
+uses these access control lists to determine
+whether an NFS client is permitted to access a given file system.
+For details on how to manage your NFS server's export table, see the
+.BR exports (5)
+and
 .BR exportfs (8)
-command makes export information available to both the kernel NFS
-server module and the
-.B rpc.mountd
-daemon.
-.P
-Alternatively, you can export individual directories temporarily 
-using
-.BR exportfs 's
-.IB host : /directory
-syntax.
+man pages.
+.SS Mounting exported NFS File Systems
+The NFS MOUNT protocol has several procedures.
+The most important of these are
+MNT (mount an export) and
+UMNT (unmount an export).
+.PP
+A MNT request has two arguments: an explicit argument that
+contains the pathname of the root directory of the export to be mounted,
+and an implicit argument that is the sender's IP address.
+.PP
+When receiving a MNT request from an NFS client,
+.B rpc.mountd
+checks both the pathname and the sender's IP address against its export table.
+If the sender is permitted to access the requested export,
+.B rpc.mountd
+returns an NFS file handle for the export's root directory to the client.
+The client can then use the root file handle and NFS LOOKUP requests
+to navigate the directory structure of the export.
 .SS The rmtab File
-For every mount request received from an NFS client,
-.B rpc.mountd
-adds an entry to the
-.B /var/lib/nfs/rmtab
-file. When receiving an unmount request, that entry is removed.
-.P
-However, this file is mostly ornamental. One, the client can continue
-to use the file handle even after calling
-.B rpc.mountd 's
-UMOUNT procedure. And two, if a client reboots without notifying
-.B rpc.mountd ,
-a stale entry will remain in
-.BR rmtab .
+The
+.B rpc.mountd
+daemon registers every successful MNT request by adding an entry to the
+.I /var/lib/nfs/rmtab
+file.
+When receivng a UMNT request from an NFS client,
+.B rpc.mountd
+simply removes the matching entry from
+.IR /var/lib/nfs/rmtab ,
+as long as the access control list for that export allows that sender
+to access the export.
+.PP
+Clients can discover the list of file systems an NFS server is
+currently exporting, or the list of other clients that have mounted
+its exports, by using the
+.BR showmount (8)
+command.
+.BR showmount (8)
+uses other procedures in the NFS MOUNT protocol to report information
+about the server's exported file systems.
+.PP
+Note, however, that there is little to guarantee that the contents of
+.I /var/lib/nfs/rmtab
+are accurate.
+A client may continue accessing an export even after invoking UMNT.
+If the client reboots without sending a UMNT request, stale entries
+remain for that client in
+.IR /var/lib/nfs/rmtab .
 .SH OPTIONS
 .TP
 .B \-d kind " or " \-\-debug kind
@@ -94,22 +119,25 @@ Don't advertise TCP for mount.
 Ignored (compatibility with unfsd??).
 .TP
 .B \-p " or " \-\-port num
-Force
+Specifies the port number used for RPC listener sockets.
+If this option is not specified,
 .B rpc.mountd
-to bind to the specified port num, instead of using the random port
-number assigned by the portmapper.
+chooses a random ephemeral port for each listener socket.
+.IP
+This option can be used to fix the port value of
+.BR rpc.mountd 's
+listeners when NFS MOUNT requests must traverse a firewall
+between clients and servers.
 .TP
 .B \-H " or " \-\-ha-callout prog
-Specify a high availability callout program, which will receive callouts
-for all client mount and unmount requests. This allows 
-.B rpc.mountd
-to be used in a High Availability NFS (HA-NFS) environment. This callout is not
-needed (and should not be used) with 2.6 and later kernels (instead,
-mount the nfsd filesystem on
-.B /proc/fs/nfsd
-).
-The program will be called with 4 arguments.
-The first will be
+Specify a high availability callout program.
+This program receives callouts for all MOUNT and UNMOUNT requests.
+This allows 
+.B rpc.mountd
+to be used in a High Availability NFS (HA-NFS) environment.
+.IP
+The callout program is run with 4 arguments.
+The first is
 .B mount
 or
 .B unmount
@@ -118,19 +146,30 @@ The second will be the name of the client performing the mount.
 The third will be the path that the client is mounting.
 The last is the number of concurrent mounts that we believe the client
 has of that path.
+.IP
+This callout is not needed with 2.6 and later kernels.
+Instead, mount the nfsd filesystem on
+.IR /proc/fs/nfsd .
 .TP
 .BI "\-s," "" " \-\-state\-directory\-path "  directory
-specify a directory in which to place statd state information.
+Specify a directory in which to place statd state information.
 If this option is not specified the default of
-.BR /var/lib/nfs
+.I /var/lib/nfs
 is used.
 .TP
 .BI "\-r," "" " \-\-reverse\-lookup"
-mountd tracks IP addresses in the rmtab, and when a DUMP request is made (by
-someone running showmount -a, for instance), it returns IP addresses instead
-of hostnames by default. This option causes mountd to do a reverse
-lookup on each IP address and return that hostname instead. Enabling this can
-have a substantial negative effect on performance in some situations.
+.B rpc.mountd
+tracks IP addresses in the
+.I rmtab
+file.  When a DUMP request is made (by
+someone running
+.BR "showmount -a" ,
+for instance), it returns IP addresses instead
+of hostnames by default. This option causes
+.B rpc.mountd
+to perform a reverse lookup on each IP address and return that hostname instead.
+Enabling this can have a substantial negative effect on performance
+in some situations.
 .TP
 .BR "\-t N" " or " "\-\-num\-threads=N"
 This option specifies the number of worker threads that rpc.mountd
@@ -162,41 +201,70 @@ If you use the
 flag, then the list of group ids received from the client will be
 replaced by a list of group ids determined by an appropriate lookup on
 the server. Note that the 'primary' group id is not affected so a
-.I newgroup
+.B newgroup
 command on the client will still be effective.  This function requires
 a Linux Kernel with version at least 2.6.21.
-
 .SH TCP_WRAPPERS SUPPORT
-This
+You can protect your
 .B rpc.mountd
-version is protected by the
+listeners using the
+.B tcp_wrapper
+library or
+.BR iptables (8).
+.PP
+Note that the
 .B tcp_wrapper
-library. You have to give the clients access to
-.B rpc.mountd
-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:
-
-mountd: .bar.com
-
-You have to use the daemon name 
+library supports only IPv4 networking.
+.PP
+Add the hostnames of NFS peers that are allowed to access
+.B rpc.mountd
+to
+.IR /etc/hosts.allow .
+Use the daemon name
 .B mountd
-for the daemon name (even if the binary has a different name).
-.B Note:
-hostnames used in either access file will be ignored when
+even if the
+.B rpc.mountd
+binary has a different name.
+.PP
+Hostnames used in either access file will be ignored when
 they can not be resolved into IP addresses.
-
-For further information please have a look at the
+For further information see the
 .BR tcpd (8)
 and
 .BR hosts_access (5)
-manual pages.
+man pages.
+.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.mountd ,
+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.mountd
+will operate.
+.SH FILES
+.TP 2.5i
+.I /etc/exports
+input file for
+.BR exportfs ,
+listing exports, export options, and access control lists
+.TP 2.5i
+.I /var/lib/nfs/rmtab
+table of clients accessing server's exports
 .SH SEE ALSO
-.BR rpc.nfsd (8),
 .BR exportfs (8),
 .BR exports (5),
-.BR rpc.rquotad (8).
-.SH FILES
-.BR /etc/exports ,
-.BR /var/lib/nfs/xtab .
+.BR showmount (8),
+.BR rpc.nfsd (8),
+.BR rpc.rquotad (8),
+.BR nfs (5),
+.BR tcpd (8),
+.BR hosts_access (5),
+.BR iptables (8),
+.BR netconfig (5)
+.sp
+RFC 1094 - "NFS: Network File System Protocol Specification"
+.br
+RFC 1813 - "NFS Version 3 Protocol Specification"
 .SH AUTHOR
 Olaf Kirch, H. J. Lu, G. Allan Morris III, and a host of others.
-------------------------------------------------------------------------------
nfsd-revert-commit-b2a3cd59
-------------------------------------------------------------------------------
nfsd: Revert commit b2a3cd59

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

Enable IPv6 support in rpc.nfsd again.

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

 utils/nfsd/nfsd.c   |    9 ---------
 utils/nfsd/nfssvc.c |    9 ---------
 2 files changed, 0 insertions(+), 18 deletions(-)


diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
index 1cda1e5..650c593 100644
--- a/utils/nfsd/nfsd.c
+++ b/utils/nfsd/nfsd.c
@@ -27,15 +27,6 @@
 #include "nfssvc.h"
 #include "xlog.h"
 
-/*
- * IPv6 support for nfsd was finished before some of the other daemons (mountd
- * and statd in particular). That could be a problem in the future if someone
- * were to boot a kernel that supports IPv6 serving with  an older nfs-utils. For
- * now, hardcode the IPv6 switch into the off position until the other daemons
- * are functional.
- */
-#undef IPV6_SUPPORTED
-
 static void	usage(const char *);
 
 static struct option longopts[] =
diff --git a/utils/nfsd/nfssvc.c b/utils/nfsd/nfssvc.c
index b8028bb..119135e 100644
--- a/utils/nfsd/nfssvc.c
+++ b/utils/nfsd/nfssvc.c
@@ -22,15 +22,6 @@
 #include "nfslib.h"
 #include "xlog.h"
 
-/*
- * IPv6 support for nfsd was finished before some of the other daemons (mountd
- * and statd in particular). That could be a problem in the future if someone
- * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For
- * now, hardcode the IPv6 switch into the off position until the other daemons
- * are functional.
- */
-#undef IPV6_SUPPORTED
-
 #define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
 #define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
 #define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"