All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/26] Basic IPv6 support in statd (take++)
@ 2009-10-13 14:54 Chuck Lever
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
  0 siblings, 1 reply; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:54 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

I think I've addressed the three major concerns expressed during the
last round of review.

a) The on-disk format (files and directories under /var/lib/nfs) is
   unchanged.

b) IPv6 support is implemented as a series of incremental changes to
   the current statd implementation, not as a full replacement with
   a fresh implementation.  The series can be broken up into chunks
   of 4 or 5 patches and applied over time across nfs-utils releases
   (although it need not be).

c) New features, such as support for sending SM_NOTIFY over TCP and
   support for handling downcalls on IPv6 transports, has been
   removed for now.  No additional support for handling multi-homed
   hosts is introduced.  No additional support for distinguishing
   between client and server peers is introduced.

There remains some clean up that has been discussed previously on this
mailing list.  The on-disk monitor records are now managed in code
that sm-notify and statd share, replacing two separate but equal
implementations.  Additionally, common RPC code is now used to manage
both NLM downcalls and sending SM_NOTIFY requests; again, replacing
separate but similar implementations.

Jeff and I have been testing this code, and it appears to work at
least as well as the IPv4-only version, plus providing support for
monitoring IPv6 hosts.  It is designed to build and work properly with
TI-RPC and without.

I'm expecting to see some additional minor refinements and clean ups
to these patches over the next couple of weeks.  Right now, I would
like some feedback about the architecture of this proposed change.  Is
this overall design acceptable?  Are there parts that are crazy or
unclear that could be improved or better documented?  Outright bugs,
omissions, or oversights?  Do the layering and library APIs make
sense?  Any new security exposures?

I'm cooking up a few additional minor changes, so I'm holding back on
presenting the man page refreshes.  Other than allowing network
addresses specified on the command line to be IPv6 addresses, the
command line synopses for both programs are not changed by this work.

---

Chuck Lever (26):
      statd: Support TI-RPC statd listener
      statd: retain CAP_NET_BIND when dropping privileges
      statd: Support IPv6 in sm_stat_1_svc()
      statd: Support IPv6 in sm_mon_1_svc()
      statd: Support IPv6 in sm_simu_crash_1_svc
      statd: Support IPv6 is caller_is_localhost()
      statd: add IPv6 support in sm_notify_1_svc()
      libnsm.a: add nsm_present_address() API
      statd: Introduce statd version of matchhostname()
      statd: squelch compiler warning in sm-notify.c
      statd: Support IPv6 DNS lookups in smn_lookup
      statd: Use getaddrinfo(3) to generate bind address in smn_create_socket()
      statd: IPv6 support in reserved port binding in smn_create_socket()
      statd: Support creating a PF_INET6 socket in smn_create_socket()
      statd: factor socket creation out of notify()
      statd: Update rmtcall.c
      statd: Support sending SM_NOTIFY requests to IPv6 remotes
      libnsm: Add RPC construction helper functions
      statd: Use the new nsm_ file.c calls in rpc.statd
      statd: Use the new nsm_ file.c calls in sm_notify
      statd: Introduce common routines to handle persistent storage
      statd: Move the sm_inter XDR pieces to libnsm.a
      statd: fix address copy in sm-notify.c
      statd: replace smn_{get,set}_port() with the shared equivalents
      statd: Replace nsm_log() with xlog() in sm-notify command
      statd: Replace note() with xlog() in rpc.statd


 .gitignore                   |    9 
 aclocal/libcap.m4            |   15 +
 configure.ac                 |    4 
 support/Makefile.am          |    2 
 support/include/Makefile.am  |    1 
 support/include/ha-callout.h |    4 
 support/include/nsm.h        |   87 ++++
 support/include/rpcmisc.h    |    5 
 support/nfs/Makefile.am      |    3 
 support/nfs/svc_create.c     |  213 +++++++++++
 support/nsm/Makefile.am      |   45 ++
 support/nsm/file.c           |  816 ++++++++++++++++++++++++++++++++++++++++++
 support/nsm/rpc.c            |  505 ++++++++++++++++++++++++++
 support/nsm/sm_inter.x       |  131 +++++++
 utils/statd/Makefile.am      |   22 -
 utils/statd/callback.c       |   76 +++-
 utils/statd/hostname.c       |  268 ++++++++++++++
 utils/statd/log.c            |   95 -----
 utils/statd/log.h            |   42 --
 utils/statd/misc.c           |   30 --
 utils/statd/monitor.c        |  237 +++++-------
 utils/statd/notlist.c        |    4 
 utils/statd/rmtcall.c        |  165 ++------
 utils/statd/simu.c           |   37 +-
 utils/statd/simulate.c       |   52 +--
 utils/statd/sm-notify.c      |  773 ++++++++++++++++------------------------
 utils/statd/sm-notify.man    |    6 
 utils/statd/sm_inter.x       |  131 -------
 utils/statd/stat.c           |   14 -
 utils/statd/statd.c          |  199 +++-------
 utils/statd/statd.h          |   38 --
 utils/statd/svc_run.c        |    7 
 utils/statd/version.h        |    7 
 33 files changed, 2747 insertions(+), 1296 deletions(-)
 create mode 100644 aclocal/libcap.m4
 create mode 100644 support/include/nsm.h
 create mode 100644 support/nfs/svc_create.c
 create mode 100644 support/nsm/Makefile.am
 create mode 100644 support/nsm/file.c
 create mode 100644 support/nsm/rpc.c
 create mode 100644 support/nsm/sm_inter.x
 create mode 100644 utils/statd/hostname.c
 delete mode 100644 utils/statd/log.c
 delete mode 100644 utils/statd/log.h
 delete mode 100644 utils/statd/sm_inter.x
 delete mode 100644 utils/statd/version.h

-- 
Chuck Lever <chuck.lever@oracle.com>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 01/26] statd: Replace note() with xlog() in rpc.statd
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2009-10-13 14:54   ` Chuck Lever
       [not found]     ` <20091013145416.2424.12787.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
  2009-10-13 14:54   ` [PATCH 02/26] statd: Replace nsm_log() with xlog() in sm-notify command Chuck Lever
                     ` (24 subsequent siblings)
  25 siblings, 1 reply; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:54 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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"


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 02/26] statd: Replace nsm_log() with xlog() in sm-notify command
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
  2009-10-13 14:54   ` [PATCH 01/26] statd: Replace note() with xlog() in rpc.statd Chuck Lever
@ 2009-10-13 14:54   ` Chuck Lever
  2009-10-13 14:54   ` [PATCH 03/26] statd: replace smn_{get, set}_port() with the shared equivalents Chuck Lever
                     ` (23 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:54 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 03/26] statd: replace smn_{get, set}_port() with the shared equivalents
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
  2009-10-13 14:54   ` [PATCH 01/26] statd: Replace note() with xlog() in rpc.statd Chuck Lever
  2009-10-13 14:54   ` [PATCH 02/26] statd: Replace nsm_log() with xlog() in sm-notify command Chuck Lever
@ 2009-10-13 14:54   ` Chuck Lever
  2009-10-13 14:54   ` [PATCH 04/26] statd: fix address copy in sm-notify.c Chuck Lever
                     ` (22 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:54 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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;
 		}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 04/26] statd: fix address copy in sm-notify.c
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (2 preceding siblings ...)
  2009-10-13 14:54   ` [PATCH 03/26] statd: replace smn_{get, set}_port() with the shared equivalents Chuck Lever
@ 2009-10-13 14:54   ` Chuck Lever
  2009-10-13 14:54   ` [PATCH 05/26] statd: Move the sm_inter XDR pieces to libnsm.a Chuck Lever
                     ` (21 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:54 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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
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;
 }
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 05/26] statd: Move the sm_inter XDR pieces to libnsm.a
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (3 preceding siblings ...)
  2009-10-13 14:54   ` [PATCH 04/26] statd: fix address copy in sm-notify.c Chuck Lever
@ 2009-10-13 14:54   ` Chuck Lever
  2009-10-13 14:55   ` [PATCH 06/26] statd: Introduce common routines to handle persistent storage Chuck Lever
                     ` (20 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:54 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 1b653e4..2b02d3b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -400,6 +400,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


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 06/26] statd: Introduce common routines to handle persistent storage
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (4 preceding siblings ...)
  2009-10-13 14:54   ` [PATCH 05/26] statd: Move the sm_inter XDR pieces to libnsm.a Chuck Lever
@ 2009-10-13 14:55   ` Chuck Lever
       [not found]     ` <20091013145506.2424.10505.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
  2009-10-13 14:55   ` [PATCH 07/26] statd: Use the new nsm_ file.c calls in sm_notify Chuck Lever
                     ` (19 subsequent siblings)
  25 siblings, 1 reply; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:55 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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          |  774 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 838 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..83680f9
--- /dev/null
+++ b/support/nsm/file.c
@@ -0,0 +1,774 @@
+/*
+ * 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"
+
+#ifdef NFS_STATEDIR
+#define NSM_DEFAULT_STATEDIR		NFS_STATEDIR NSM_PATH_EXTENSION
+#else	/* !defined(NFS_STATEDIR) */
+#define NSM_DEFAULT_STATEDIR		"/var/lib/nfs" NSM_PATH_EXTENSION
+#endif	/* !defined(NFS_STATEDIR) */
+
+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);
+}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 07/26] statd: Use the new nsm_ file.c calls in sm_notify
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (5 preceding siblings ...)
  2009-10-13 14:55   ` [PATCH 06/26] statd: Introduce common routines to handle persistent storage Chuck Lever
@ 2009-10-13 14:55   ` Chuck Lever
  2009-10-13 14:55   ` [PATCH 08/26] statd: Use the new nsm_ file.c calls in rpc.statd Chuck Lever
                     ` (18 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:55 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |  267 ++++++++---------------------------------------
 1 files changed, 46 insertions(+), 221 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 1983ef6..157cd34 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,44 @@ static struct addrinfo *smn_lookup(const char *name)
 	return ai;
 }
 
+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 = calloc(1, sizeof(*host));
+	if (host == NULL)
+		goto out_nomem;
+
+	host->name = strdup(hostname);
+	if (host->name == NULL) {
+		free(host);
+		goto out_nomem;
+	}
+
+	xlog(D_GENERAL, "Adding host %s to notify list", hostname);
+
+	host->last_used = timestamp;
+	host->timeout = NSM_TIMEOUT;
+	host->retries = 100; /* force address retry */
+
+	insert_host(host);
+	return 1;
+
+out_nomem:
+	xlog_warn("Unable to allocate memory");
+	return 0;
+}
+
 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);
@@ -157,20 +169,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 +196,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 +212,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 +315,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 +555,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 +601,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 +623,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);
-	}
-}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 08/26] statd: Use the new nsm_ file.c calls in rpc.statd
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (6 preceding siblings ...)
  2009-10-13 14:55   ` [PATCH 07/26] statd: Use the new nsm_ file.c calls in sm_notify Chuck Lever
@ 2009-10-13 14:55   ` Chuck Lever
  2009-10-13 14:55   ` [PATCH 09/26] libnsm: Add RPC construction helper functions Chuck Lever
                     ` (17 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:55 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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);
 
 /*


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 09/26] libnsm: Add RPC construction helper functions
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (7 preceding siblings ...)
  2009-10-13 14:55   ` [PATCH 08/26] statd: Use the new nsm_ file.c calls in rpc.statd Chuck Lever
@ 2009-10-13 14:55   ` Chuck Lever
       [not found]     ` <20091013145546.2424.83816.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
  2009-10-13 14:55   ` [PATCH 10/26] statd: Support sending SM_NOTIFY requests to IPv6 remotes Chuck Lever
                     ` (16 subsequent siblings)
  25 siblings, 1 reply; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:55 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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;
+}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 10/26] statd: Support sending SM_NOTIFY requests to IPv6 remotes
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (8 preceding siblings ...)
  2009-10-13 14:55   ` [PATCH 09/26] libnsm: Add RPC construction helper functions Chuck Lever
@ 2009-10-13 14:55   ` Chuck Lever
  2009-10-13 14:56   ` [PATCH 11/26] statd: Update rmtcall.c Chuck Lever
                     ` (15 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:55 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 157cd34..4c4e95d 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;
@@ -377,14 +376,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);
@@ -395,12 +386,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
@@ -435,48 +420,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;
 }
 
@@ -488,40 +438,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) {
@@ -542,16 +484,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);
 }
 
 /*


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 11/26] statd: Update rmtcall.c
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (9 preceding siblings ...)
  2009-10-13 14:55   ` [PATCH 10/26] statd: Support sending SM_NOTIFY requests to IPv6 remotes Chuck Lever
@ 2009-10-13 14:56   ` Chuck Lever
  2009-10-13 14:56   ` [PATCH 12/26] statd: factor socket creation out of notify() Chuck Lever
                     ` (14 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:56 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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));


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 12/26] statd: factor socket creation out of notify()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (10 preceding siblings ...)
  2009-10-13 14:56   ` [PATCH 11/26] statd: Update rmtcall.c Chuck Lever
@ 2009-10-13 14:56   ` Chuck Lever
  2009-10-13 14:56   ` [PATCH 13/26] statd: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
                     ` (13 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:56 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 4c4e95d..c57dd6c 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 *);
@@ -134,10 +134,74 @@ static void smn_forget_host(struct nsm_host *host)
 	free(host);
 }
 
+/*
+ * 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;
 
@@ -235,7 +299,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;
@@ -255,68 +326,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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 13/26] statd: Support creating a PF_INET6 socket in smn_create_socket()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (11 preceding siblings ...)
  2009-10-13 14:56   ` [PATCH 12/26] statd: factor socket creation out of notify() Chuck Lever
@ 2009-10-13 14:56   ` Chuck Lever
  2009-10-13 14:56   ` [PATCH 14/26] statd: IPv6 support in reserved port binding " Chuck Lever
                     ` (12 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:56 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |   67 +++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 62 insertions(+), 5 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index c57dd6c..cbf77c4 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;
@@ -134,6 +135,65 @@ static void smn_forget_host(struct nsm_host *host)
 	free(host);
 }
 
+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.
+	 */
+#ifdef IPV6_SUPPORTED
+	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;
+#else	/* !IPV6_SUPPORTED */
+	sock = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock < 0) {
+		xlog(L_ERROR, "Failed to create RPC socket: %m");
+		return -1;
+	}
+#endif	/* !IPV6_SUPPORTED */
+
+	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;
+}
+ 
 /*
  * Prepare a socket for sending RPC requests
  *
@@ -148,12 +208,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 */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 14/26] statd: IPv6 support in reserved port binding in smn_create_socket()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (12 preceding siblings ...)
  2009-10-13 14:56   ` [PATCH 13/26] statd: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
@ 2009-10-13 14:56   ` Chuck Lever
  2009-10-13 14:56   ` [PATCH 15/26] statd: Use getaddrinfo(3) to generate bind address " Chuck Lever
                     ` (11 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:56 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 cbf77c4..f58783d 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -194,6 +194,26 @@ out_close:
 	return -1;
 }
  
+#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
  *
@@ -241,13 +261,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;
 		}
 	}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 15/26] statd: Use getaddrinfo(3) to generate bind address in smn_create_socket()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (13 preceding siblings ...)
  2009-10-13 14:56   ` [PATCH 14/26] statd: IPv6 support in reserved port binding " Chuck Lever
@ 2009-10-13 14:56   ` Chuck Lever
  2009-10-13 14:56   ` [PATCH 16/26] statd: Support IPv6 DNS lookups in smn_lookup Chuck Lever
                     ` (10 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:56 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 f58783d..443fd46 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 *);
@@ -194,6 +194,40 @@ out_close:
 	return -1;
 }
  
+/*
+ * 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)
@@ -221,62 +255,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;
 }
 
@@ -308,7 +331,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;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 16/26] statd: Support IPv6 DNS lookups in smn_lookup
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (14 preceding siblings ...)
  2009-10-13 14:56   ` [PATCH 15/26] statd: Use getaddrinfo(3) to generate bind address " Chuck Lever
@ 2009-10-13 14:56   ` Chuck Lever
  2009-10-13 14:57   ` [PATCH 17/26] statd: squelch compiler warning in sm-notify.c Chuck Lever
                     ` (9 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:56 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 443fd46..78b4174 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -73,10 +73,12 @@ static struct nsm_host *	hosts = NULL;
 static struct addrinfo *smn_lookup(const char *name)
 {
 	struct addrinfo	*ai, hint = {
-#if HAVE_DECL_AI_ADDRCONFIG
+#ifdef IPV6_SUPPORTED
 		.ai_flags	= AI_ADDRCONFIG,
-#endif	/* HAVE_DECL_AI_ADDRCONFIG */
+		.ai_family	= AF_UNSPEC,
+#else	/* !IPV6_SUPPORTED */
 		.ai_family	= AF_INET,
+#endif	/* !IPV6_SUPPORTED */
 		.ai_protocol	= IPPROTO_UDP,
 	};
 	int error;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 17/26] statd: squelch compiler warning in sm-notify.c
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (15 preceding siblings ...)
  2009-10-13 14:56   ` [PATCH 16/26] statd: Support IPv6 DNS lookups in smn_lookup Chuck Lever
@ 2009-10-13 14:57   ` Chuck Lever
  2009-10-13 14:57   ` [PATCH 18/26] statd: Introduce statd version of matchhostname() Chuck Lever
                     ` (8 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:57 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

Get rid of a false positive compiler warning.

sm-notify.c: In function =E2=80=98record_pid=E2=80=99:
sm-notify.c:690: warning: comparison between signed and unsigned intege=
r
expressions

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

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

diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 78b4174..f50ff2d 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -675,16 +675,20 @@ find_host(uint32_t xid)
 static int record_pid(void)
 {
 	char pid[20];
+	ssize_t len;
 	int fd;
=20
 	snprintf(pid, 20, "%d\n", getpid());
 	fd =3D open("/var/run/sm-notify.pid", O_CREAT|O_EXCL|O_WRONLY, 0600);
 	if (fd < 0)
 		return 0;
-	if (write(fd, pid, strlen(pid)) !=3D strlen(pid))  {
+
+	len =3D write(fd, pid, strlen(pid));
+	if ((len < 0) || ((size_t)len !=3D strlen(pid))) {
 		xlog_warn("Writing to pid file failed: errno %d (%m)",
 				errno);
 	}
-	close(fd);
+
+	(void)close(fd);
 	return 1;
 }


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 18/26] statd: Introduce statd version of matchhostname()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (16 preceding siblings ...)
  2009-10-13 14:57   ` [PATCH 17/26] statd: squelch compiler warning in sm-notify.c Chuck Lever
@ 2009-10-13 14:57   ` Chuck Lever
  2009-10-13 14:57   ` [PATCH 19/26] libnsm.a: add nsm_present_address() API Chuck Lever
                     ` (7 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:57 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

For the near future, statd will support IPv6 but mountd/exportfs will
not.  Thus statd will need a version of matchhostname() that can deal
properly with IPv6 remotes.  To reduce the risk of breaking mountd
and exportfs, introduce a separate version of matchhostname() for
statd to use while mountd/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  |  165 +++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/monitor.c   |    5 +
 utils/statd/notlist.c   |    4 +
 utils/statd/statd.h     |    2 -
 6 files changed, 174 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..683c48b
--- /dev/null
+++ b/utils/statd/hostname.c
@@ -0,0 +1,165 @@
+/*
+ * 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"
+
+/*
+ * Before glibc 2.3.4, these flags are not defined
+ */
+#ifndef AI_IDN
+#define AI_IDN	0
+#endif
+#ifndef AI_IDN_USE_STD3_ASCII_RULES
+#define AI_IDN_USE_STD3_ASCII_RULES	0
+#endif
+
+#ifndef NI_IDN
+#define NI_IDN	0
+#endif
+#ifndef NI_IDN_USE_STD3_ASCII_RULES
+#define NI_IDN_USE_STD3_ASCII_RULES	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;
+}
+
+#ifdef IPV6_SUPPORTED
+static int
+compare_sockaddrs(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 int
+compare_sockaddrs(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;
+}
+#endif	/* !IPV6_SUPPORTED */
+
+/**
+ * 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_CANONNAME | AI_V4MAPPED |
+				  AI_IDN | AI_IDN_USE_STD3_ASCII_RULES,
+#else	/* !IPV6_SUPPORTED */
+		.ai_family	= AF_INET,
+		.ai_flags	= AI_CANONNAME |
+				  AI_IDN | AI_IDN_USE_STD3_ASCII_RULES,
+#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) {
+		freeaddrinfo(gai_results1);
+		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;
+}
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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 19/26] libnsm.a: add nsm_present_address() API
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (17 preceding siblings ...)
  2009-10-13 14:57   ` [PATCH 18/26] statd: Introduce statd version of matchhostname() Chuck Lever
@ 2009-10-13 14:57   ` Chuck Lever
  2009-10-13 14:57   ` [PATCH 20/26] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
                     ` (6 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:57 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |   73 ++++++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.h    |    2 +
 2 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index 683c48b..0148b9d 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>
 
@@ -52,6 +53,78 @@
 #define NI_IDN_USE_STD3_ASCII_RULES	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)
+		switch (sap->sa_family) {
+		case AF_INET:
+			salen = sizeof(struct sockaddr_in);
+			break;
+		case AF_INET6:
+			salen = sizeof(struct sockaddr_in6);
+			break;
+		default:
+			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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 20/26] statd: add IPv6 support in sm_notify_1_svc()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (18 preceding siblings ...)
  2009-10-13 14:57   ` [PATCH 19/26] libnsm.a: add nsm_present_address() API Chuck Lever
@ 2009-10-13 14:57   ` Chuck Lever
  2009-10-13 14:57   ` [PATCH 21/26] statd: Support IPv6 is caller_is_localhost() Chuck Lever
                     ` (5 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:57 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 21/26] statd: Support IPv6 is caller_is_localhost()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (19 preceding siblings ...)
  2009-10-13 14:57   ` [PATCH 20/26] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
@ 2009-10-13 14:57   ` Chuck Lever
  2009-10-13 14:58   ` [PATCH 22/26] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
                     ` (4 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:57 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |   22 +++++++++++++++-------
 1 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index a70b848..3db7ce8 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -32,20 +32,28 @@ 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;
+	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 (sin->sin_family != AF_INET)
+		goto out_nonlocal;
+	if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
+		goto out_nonlocal;
 	return 1;
+
+out_nonlocal:
+	if (!nsm_present_address((struct sockaddr *)sin, 0, buf, sizeof(buf)))
+		buf[0] = '\0';
+	xlog_warn("SM_MON/SM_UNMON call from non-local host %s", buf);
+	return 0;
 }
 
 /*


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 22/26] statd: Support IPv6 in sm_simu_crash_1_svc
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (20 preceding siblings ...)
  2009-10-13 14:57   ` [PATCH 21/26] statd: Support IPv6 is caller_is_localhost() Chuck Lever
@ 2009-10-13 14:58   ` Chuck Lever
  2009-10-13 14:58   ` [PATCH 23/26] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
                     ` (3 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:58 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |   35 ++++++++++++++++++++---------------
 1 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/utils/statd/simu.c b/utils/statd/simu.c
index 7df04d9..0902b63 100644
--- a/utils/statd/simu.c
+++ b/utils/statd/simu.c
@@ -8,41 +8,40 @@
 #include <config.h>
 #endif
 
+#include <netdb.h>
 #include <arpa/inet.h>
 
 #include "rpcmisc.h"
 #include "statd.h"
 #include "notlist.h"
+#include "nfsrpc.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);
+  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 (sin->sin_family != AF_INET)
+    goto out_nonlocal;
+  if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
+    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((struct sockaddr *)sin) >= IPPORT_RESERVED) {
+    xlog_warn("SM_SIMU_CRASH call from unprivileged port");
     goto failure;
   }
 
@@ -54,4 +53,10 @@ sm_simu_crash_1_svc (void *argp, struct svc_req *rqstp)
 
  failure:
   return ((void *)&result);
+
+ out_nonlocal:
+  if (!nsm_present_address((struct sockaddr *)sin, 0, buf, sizeof(buf)))
+    buf[0] = '\0';
+  xlog_warn("SM_SIMU_CRASH call from non-local host %s", buf);
+  goto failure;
 }


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 23/26] statd: Support IPv6 in sm_mon_1_svc()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (21 preceding siblings ...)
  2009-10-13 14:58   ` [PATCH 22/26] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
@ 2009-10-13 14:58   ` Chuck Lever
  2009-10-13 14:58   ` [PATCH 24/26] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
                     ` (2 subsequent siblings)
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:58 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

Replace deprecated gethostbyname(3) and gethostbyaddr(3) calls.

Handle IPv6 remotes and IDN labels properly.  At least with fairly
recent glibc's we are guaranteed that the returned hostnames will
contain only ASCII characters, allowing DNS labels to continue to be
used as file names as IDNs become commonplace.

Also address a couple of memory leaks.

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

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

diff --git a/utils/statd/hostname.c b/utils/statd/hostname.c
index 0148b9d..7d9ed45 100644
--- a/utils/statd/hostname.c
+++ b/utils/statd/hostname.c
@@ -149,6 +149,36 @@ get_addrinfo(const char *hostname, const struct addrinfo* gai_hint)
 	return NULL;
 }
 
+/**
+ * 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,
+		.ai_flags	= AI_CANONNAME | AI_ADDRCONFIG |
+				  AI_IDN | AI_IDN_USE_STD3_ASCII_RULES,
+#else	/* !IPV6_SUPPORTED */
+		.ai_family	= AF_INET,
+		.ai_flags	= AI_CANONNAME |
+				  AI_IDN | AI_IDN_USE_STD3_ASCII_RULES,
+#endif	/* !IPV6_SUPPORTED */
+	};
+
+	return get_addrinfo(hostname, &gai_hint);
+}
+
 #ifdef IPV6_SUPPORTED
 static int
 compare_sockaddrs(const struct sockaddr *sa1, const struct sockaddr *sa2)
diff --git a/utils/statd/monitor.c b/utils/statd/monitor.c
index 3db7ce8..df3206a 100644
--- a/utils/statd/monitor.c
+++ b/utils/statd/monitor.c
@@ -72,8 +72,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);
 
@@ -115,9 +115,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 */
@@ -130,15 +127,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
@@ -164,6 +167,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);
@@ -174,6 +178,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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 24/26] statd: Support IPv6 in sm_stat_1_svc()
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (22 preceding siblings ...)
  2009-10-13 14:58   ` [PATCH 23/26] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
@ 2009-10-13 14:58   ` Chuck Lever
  2009-10-13 14:58   ` [PATCH 25/26] statd: retain CAP_NET_BIND when dropping privileges Chuck Lever
  2009-10-13 14:58   ` [PATCH 26/26] statd: Support TI-RPC statd listener Chuck Lever
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:58 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 25/26] statd: retain CAP_NET_BIND when dropping privileges
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (23 preceding siblings ...)
  2009-10-13 14:58   ` [PATCH 24/26] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
@ 2009-10-13 14:58   ` Chuck Lever
  2009-10-13 14:58   ` [PATCH 26/26] statd: Support TI-RPC statd listener Chuck Lever
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:58 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 2b02d3b..d8ba6b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -164,6 +164,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 83680f9..36cddd7 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>
@@ -241,6 +243,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
@@ -288,6 +321,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;
@@ -305,7 +346,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
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 26/26] statd: Support TI-RPC statd listener
       [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
                     ` (24 preceding siblings ...)
  2009-10-13 14:58   ` [PATCH 25/26] statd: retain CAP_NET_BIND when dropping privileges Chuck Lever
@ 2009-10-13 14:58   ` Chuck Lever
  25 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-13 14:58 UTC (permalink / raw)
  To: linux-nfs; +Cc: chris.mason

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 |    5 +
 support/nfs/Makefile.am   |    3 -
 support/nfs/svc_create.c  |  213 +++++++++++++++++++++++++++++++++++++++++++++
 utils/statd/statd.c       |   42 +++++++--
 4 files changed, 254 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..7a39f13 100644
--- a/support/include/rpcmisc.h
+++ b/support/include/rpcmisc.h
@@ -41,7 +41,10 @@ struct rpc_dtable {
 		(xdrproc_t)xdr_##res_type, sizeof(res_type), \
 	}
 
-
+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..ca1d1bf
--- /dev/null
+++ b/support/nfs/svc_create.c
@@ -0,0 +1,213 @@
+/*
+ * 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 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 (statd, %u, %s).",
+				version, nconf->nc_proto);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * svc_create - start up NSM 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 NSM 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(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;
+}
+
+#else	/* !HAVE_LIBTIRPC */
+
+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;
+}
+
+#endif	/* !HAVE_LIBTIRPC */
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 72c9b41..19823fb 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -90,13 +90,22 @@ 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) {
+#ifdef HAVE_LIBTIRPC
+	(void)rpcb_unset(SM_PROG, SM_VERS, NULL);
+#else
+	(void)pmap_unset(SM_PROG, SM_VERS);
+#endif
+}
+
 /*
  * 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 +134,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 +436,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();
 
-	/* this registers both UDP and TCP services */
-	rpc_init("statd", SM_PROG, SM_VERS, sm_prog_1, port);
+	/*
+	 * ORDER
+	 */
+	if (!nsm_drop_privileges(pidfd))
+		exit(1);
+
+	/*
+	 * ORDER
+	 * Create RPC listeners after dropping privileges.  This permits
+	 * statd to unregister its own listeners when it exits.
+	 */
+	if (nfs_svc_create("statd", SM_PROG, SM_VERS, sm_prog_1, port) == 0) {
+		xlog(L_ERROR, "failed to create RPC listeners, exiting");
+		exit(1);
+	}
+	atexit(statd_unregister);
 
 	/* If we got this far, we have successfully started, so notify parent */
 	if (pipefds[1] > 0) {
@@ -440,9 +471,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


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/26] statd: Replace note() with xlog() in rpc.statd
       [not found]     ` <20091013145416.2424.12787.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2009-10-14 16:57       ` J. Bruce Fields
  2009-10-15 14:58         ` Chuck Lever
  0 siblings, 1 reply; 34+ messages in thread
From: J. Bruce Fields @ 2009-10-14 16:57 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, chris.mason

On Tue, Oct 13, 2009 at 10:54:16AM -0400, Chuck Lever wrote:
> 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");

Minor comment, no need to fix now, but: with the name "die" it was a lot
more obvious that this function never returned.

Also future work (not a problem with this patch): it's irritating that
we have long-running daemons dying on memory allocation failures.
Wouldn't it be better if they failed just the current operation and
tried to keep going?

--b.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 09/26] libnsm: Add RPC construction helper functions
       [not found]     ` <20091013145546.2424.83816.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2009-10-14 17:21       ` J. Bruce Fields
  2009-10-15 15:21         ` Chuck Lever
  0 siblings, 1 reply; 34+ messages in thread
From: J. Bruce Fields @ 2009-10-14 17:21 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, chris.mason

On Tue, Oct 13, 2009 at 10:55:46AM -0400, Chuck Lever wrote:
> 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.

Possibly dumb question: so we have to do this just because none of the
rpc libraries support asynchronous calls?

Would it be possible to add an asynchronous interface to tirpc some day?

--b.

> 
> 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;
> +}
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 01/26] statd: Replace note() with xlog() in rpc.statd
  2009-10-14 16:57       ` J. Bruce Fields
@ 2009-10-15 14:58         ` Chuck Lever
  0 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-15 14:58 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: linux-nfs, chris.mason


On Oct 14, 2009, at 12:57 PM, J. Bruce Fields wrote:

> On Tue, Oct 13, 2009 at 10:54:16AM -0400, Chuck Lever wrote:
>> 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");
>
> Minor comment, no need to fix now, but: with the name "die" it was a  
> lot
> more obvious that this function never returned.

This is going away, eventually.  See below.

> Also future work (not a problem with this patch): it's irritating that
> we have long-running daemons dying on memory allocation failures.
> Wouldn't it be better if they failed just the current operation and
> tried to keep going?

Yes.  One of the goals of this rework was to replace xmalloc and  
xstrdup in statd, but I've left that behind in the interest of  
minimizing scope creep.

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 09/26] libnsm: Add RPC construction helper functions
  2009-10-14 17:21       ` J. Bruce Fields
@ 2009-10-15 15:21         ` Chuck Lever
  0 siblings, 0 replies; 34+ messages in thread
From: Chuck Lever @ 2009-10-15 15:21 UTC (permalink / raw)
  To: J. Bruce Fields; +Cc: linux-nfs, chris.mason


On Oct 14, 2009, at 1:21 PM, J. Bruce Fields wrote:

> On Tue, Oct 13, 2009 at 10:55:46AM -0400, Chuck Lever wrote:
>> 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.
>
> Possibly dumb question: so we have to do this just because none of the
> rpc libraries support asynchronous calls?

Not dumb at all:  yes, statd (and sm-notify) set up their own  
asynchronous RPC call schedulers because the client-side RPC APIs  
provide only synchronous services.  statd also wires its NLM downcall  
scheduler into its RPC server loop (my_svc_run).  It's kind of a giant  
kludge, but it is simple, and allows statd and sm-notify to avoid a  
lot of forking.

The schedulers as they stand prevent us from using connection-oriented  
transports to deliver SM_NOTIFY calls and NLM downcalls.  In the  
downcall case, this is why statd broke when lockd started only a TCP  
listener (fixed in recent kernels).

NLM downcalls could be implemented with a synchronous RPC call in the  
server's SM_NOTIFY request handler instead.  But if the kernel isn't  
responding, statd's NSM listener would be made unavailable until the  
downcall completed.  statd's listener queue would have to take up that  
slack, I think.  This, and the fact that this scheduler code used to  
be shared with SM_NOTIFY delivery, is why we still have the NLM  
downcall scheduler.

> Would it be possible to add an asynchronous interface to tirpc some  
> day?

I think Sun's solution was to embrace pthreads in TI-RPC (and continue  
to support only a synchronous client API).  That would be a choice for  
Linux as well.

For an application like statd that has to use the standard glibc RPC  
implementation if TI-RPC isn't available, pthreads would likely be a  
non-starter.

> --b.
>
>>
>> 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;
>> +}
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux- 
>> nfs" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 06/26] statd: Introduce common routines to handle persistent storage
       [not found]     ` <20091013145506.2424.10505.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
@ 2009-10-16 14:05       ` Jeff Layton
       [not found]         ` <20091016100544.25f686c4-9yPaYZwiELC+kQycOl6kW4xkIHaj4LzF@public.gmane.org>
  0 siblings, 1 reply; 34+ messages in thread
From: Jeff Layton @ 2009-10-16 14:05 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, chris.mason

On Tue, 13 Oct 2009 10:55:06 -0400
Chuck Lever <chuck.lever@oracle.com> wrote:

> 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          |  774 +++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 838 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..83680f9
> --- /dev/null
> +++ b/support/nsm/file.c
> @@ -0,0 +1,774 @@
> +/*
> + * 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"
> +
> +#ifdef NFS_STATEDIR
> +#define NSM_DEFAULT_STATEDIR		NFS_STATEDIR NSM_PATH_EXTENSION
> +#else	/* !defined(NFS_STATEDIR) */
> +#define NSM_DEFAULT_STATEDIR		"/var/lib/nfs" NSM_PATH_EXTENSION
> +#endif	/* !defined(NFS_STATEDIR) */
> +
> +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;
> +	}
> +

^^^
I think we need some bounds checking here. There doesn't seem to be any
guarantee that the line we read in from the "db" is a particular
length. A check to verify that before trying to convert the cookie
would be good.

> +	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);
> +}
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 06/26] statd: Introduce common routines to handle persistent storage
       [not found]         ` <20091016100544.25f686c4-9yPaYZwiELC+kQycOl6kW4xkIHaj4LzF@public.gmane.org>
@ 2009-10-16 22:46           ` Chuck Lever
  2009-10-16 23:27             ` Jeff Layton
  0 siblings, 1 reply; 34+ messages in thread
From: Chuck Lever @ 2009-10-16 22:46 UTC (permalink / raw)
  To: Jeff Layton; +Cc: linux-nfs, chris.mason


On Oct 16, 2009, at 11:05 PM, Jeff Layton wrote:

> On Tue, 13 Oct 2009 10:55:06 -0400
> Chuck Lever <chuck.lever@oracle.com> wrote:
>> +
>> +/*
>> + * 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;
>> +	}
>> +
>
> ^^^
> I think we need some bounds checking here. There doesn't seem to be  
> any
> guarantee that the line we read in from the "db" is a particular
> length. A check to verify that before trying to convert the cookie
> would be good.

You mean the line read from the file is possibly not long enough?  I  
think sscanf(3) will bail and we'll hit the "return 0;" if it can't  
find enough bytes in the cookie.  It would be worth adding a specific  
test or two for short db lines in the nascent statd regression/unit  
test suite.

However, any new checks we want in this particular spot should  
probably be added by subsequent patches (ie after the IPv6 support  
series is committed), so we can verify that the IPv6 changes don't  
alter the existing on-disk format logic -- they only copy it to a new  
location.  No telling what kind of odd side effects other code outside  
of nfs-utils may depend on here.

>> +	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);
>> +}

--
Chuck Lever
chuck[dot]lever[at]oracle[dot]com




^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 06/26] statd: Introduce common routines to handle persistent storage
  2009-10-16 22:46           ` Chuck Lever
@ 2009-10-16 23:27             ` Jeff Layton
  0 siblings, 0 replies; 34+ messages in thread
From: Jeff Layton @ 2009-10-16 23:27 UTC (permalink / raw)
  To: Chuck Lever; +Cc: linux-nfs, chris.mason

On Sat, 17 Oct 2009 07:46:21 +0900
Chuck Lever <chuck.lever@oracle.com> wrote:

> 
> On Oct 16, 2009, at 11:05 PM, Jeff Layton wrote:
> 
> > On Tue, 13 Oct 2009 10:55:06 -0400
> > Chuck Lever <chuck.lever@oracle.com> wrote:
> >> +
> >> +/*
> >> + * 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;
> >> +	}
> >> +
> >
> > ^^^
> > I think we need some bounds checking here. There doesn't seem to be  
> > any
> > guarantee that the line we read in from the "db" is a particular
> > length. A check to verify that before trying to convert the cookie
> > would be good.
> 
> You mean the line read from the file is possibly not long enough?  I  
> think sscanf(3) will bail and we'll hit the "return 0;" if it can't  
> find enough bytes in the cookie.  It would be worth adding a specific  
> test or two for short db lines in the nascent statd regression/unit  
> test suite.
> 

Ok. Now that I look again, I think you're correct. In the event that
the line ends on the single space after the 4th field, c will point to
a NULL byte and sscanf will just bail out.

> However, any new checks we want in this particular spot should  
> probably be added by subsequent patches (ie after the IPv6 support  
> series is committed), so we can verify that the IPv6 changes don't  
> alter the existing on-disk format logic -- they only copy it to a new  
> location.  No telling what kind of odd side effects other code outside  
> of nfs-utils may depend on here.
> 

Fair enough, that sounds reasonable. My main concern at this point was
simply to make sure that we didn't segfault if the DB file turned out
to be corrupt. I don't think that'll happen, AFAICT.

> >> +	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);
> >> +}
> 
> --
> Chuck Lever
> chuck[dot]lever[at]oracle[dot]com
> 
> 
> 


-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2009-10-16 23:27 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-13 14:54 [PATCH 00/26] Basic IPv6 support in statd (take++) Chuck Lever
     [not found] ` <20091013142257.2424.76946.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2009-10-13 14:54   ` [PATCH 01/26] statd: Replace note() with xlog() in rpc.statd Chuck Lever
     [not found]     ` <20091013145416.2424.12787.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2009-10-14 16:57       ` J. Bruce Fields
2009-10-15 14:58         ` Chuck Lever
2009-10-13 14:54   ` [PATCH 02/26] statd: Replace nsm_log() with xlog() in sm-notify command Chuck Lever
2009-10-13 14:54   ` [PATCH 03/26] statd: replace smn_{get, set}_port() with the shared equivalents Chuck Lever
2009-10-13 14:54   ` [PATCH 04/26] statd: fix address copy in sm-notify.c Chuck Lever
2009-10-13 14:54   ` [PATCH 05/26] statd: Move the sm_inter XDR pieces to libnsm.a Chuck Lever
2009-10-13 14:55   ` [PATCH 06/26] statd: Introduce common routines to handle persistent storage Chuck Lever
     [not found]     ` <20091013145506.2424.10505.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2009-10-16 14:05       ` Jeff Layton
     [not found]         ` <20091016100544.25f686c4-9yPaYZwiELC+kQycOl6kW4xkIHaj4LzF@public.gmane.org>
2009-10-16 22:46           ` Chuck Lever
2009-10-16 23:27             ` Jeff Layton
2009-10-13 14:55   ` [PATCH 07/26] statd: Use the new nsm_ file.c calls in sm_notify Chuck Lever
2009-10-13 14:55   ` [PATCH 08/26] statd: Use the new nsm_ file.c calls in rpc.statd Chuck Lever
2009-10-13 14:55   ` [PATCH 09/26] libnsm: Add RPC construction helper functions Chuck Lever
     [not found]     ` <20091013145546.2424.83816.stgit-RytpoXr2tKZ9HhUboXbp9zCvJB+x5qRC@public.gmane.org>
2009-10-14 17:21       ` J. Bruce Fields
2009-10-15 15:21         ` Chuck Lever
2009-10-13 14:55   ` [PATCH 10/26] statd: Support sending SM_NOTIFY requests to IPv6 remotes Chuck Lever
2009-10-13 14:56   ` [PATCH 11/26] statd: Update rmtcall.c Chuck Lever
2009-10-13 14:56   ` [PATCH 12/26] statd: factor socket creation out of notify() Chuck Lever
2009-10-13 14:56   ` [PATCH 13/26] statd: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
2009-10-13 14:56   ` [PATCH 14/26] statd: IPv6 support in reserved port binding " Chuck Lever
2009-10-13 14:56   ` [PATCH 15/26] statd: Use getaddrinfo(3) to generate bind address " Chuck Lever
2009-10-13 14:56   ` [PATCH 16/26] statd: Support IPv6 DNS lookups in smn_lookup Chuck Lever
2009-10-13 14:57   ` [PATCH 17/26] statd: squelch compiler warning in sm-notify.c Chuck Lever
2009-10-13 14:57   ` [PATCH 18/26] statd: Introduce statd version of matchhostname() Chuck Lever
2009-10-13 14:57   ` [PATCH 19/26] libnsm.a: add nsm_present_address() API Chuck Lever
2009-10-13 14:57   ` [PATCH 20/26] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
2009-10-13 14:57   ` [PATCH 21/26] statd: Support IPv6 is caller_is_localhost() Chuck Lever
2009-10-13 14:58   ` [PATCH 22/26] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
2009-10-13 14:58   ` [PATCH 23/26] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
2009-10-13 14:58   ` [PATCH 24/26] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
2009-10-13 14:58   ` [PATCH 25/26] statd: retain CAP_NET_BIND when dropping privileges Chuck Lever
2009-10-13 14:58   ` [PATCH 26/26] statd: Support TI-RPC statd listener Chuck Lever

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.