public inbox for linux-nfs@vger.kernel.org
 help / color / mirror / Atom feed
From: Chuck Lever <chuck.lever@oracle.com>
To: steved@redhat.com
Cc: chris.mason@oracle.com, linux-nfs@vger.kernel.org
Subject: [PATCH 23/24] statd: Support TI-RPC statd listener
Date: Thu, 14 Jan 2010 12:32:12 -0500	[thread overview]
Message-ID: <20100114173212.26079.54866.stgit@localhost.localdomain> (raw)
In-Reply-To: <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>

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

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

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

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

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

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

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


  parent reply	other threads:[~2010-01-14 17:33 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-01-14 17:28 [PATCH 00/24] Remaining IPv6 patches for statd Chuck Lever
     [not found] ` <20100114172457.26079.66627.stgit-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2010-01-14 17:28   ` [PATCH 01/24] libnsm.a: Add RPC construction helper functions Chuck Lever
2010-01-14 17:29   ` [PATCH 02/24] sm-notify: Replace RPC code Chuck Lever
2010-01-14 17:29   ` [PATCH 03/24] statd: Update rmtcall.c Chuck Lever
2010-01-14 17:29   ` [PATCH 04/24] sm-notify: factor socket creation out of notify() Chuck Lever
2010-01-14 17:29   ` [PATCH 05/24] sm-notify: Support creating a PF_INET6 socket in smn_create_socket() Chuck Lever
2010-01-14 17:29   ` [PATCH 06/24] sm-notify: IPv6 support in reserved port binding " Chuck Lever
2010-01-14 17:29   ` [PATCH 07/24] sm-notify: Use getaddrinfo(3) to create bind address " Chuck Lever
2010-01-14 17:30   ` [PATCH 08/24] sm-notify: Support IPv6 DNS lookups in smn_lookup Chuck Lever
2010-01-14 17:30   ` [PATCH 09/24] nfs-utils: Collect socket address helpers into one location Chuck Lever
2010-01-14 17:30   ` [PATCH 10/24] statd: Introduce statd version of matchhostname() Chuck Lever
2010-01-14 17:30   ` [PATCH 11/24] statd: add nsm_present_address() API Chuck Lever
2010-01-14 17:30   ` [PATCH 12/24] statd: add IPv6 support in sm_notify_1_svc() Chuck Lever
2010-01-14 17:30   ` [PATCH 13/24] statd: Support IPv6 is caller_is_localhost() Chuck Lever
2010-01-14 17:30   ` [PATCH 14/24] statd: Support IPv6 in sm_simu_crash_1_svc Chuck Lever
2010-01-14 17:31   ` [PATCH 15/24] sm-notify: Save mon_name and my_name strings Chuck Lever
2010-01-14 17:31   ` [PATCH 16/24] libnsm.a: Factor atomic write code out of nsm_get_state() Chuck Lever
2010-01-14 17:31   ` [PATCH 17/24] libnsm.a: Add support for multiple lines in monitor record files Chuck Lever
2010-01-14 17:31   ` [PATCH 18/24] statd: Add API to canonicalize mon_names Chuck Lever
2010-01-14 17:31   ` [PATCH 19/24] statd: Support IPv6 in sm_mon_1_svc() Chuck Lever
2010-01-14 17:31   ` [PATCH 20/24] statd: Support IPv6 in sm_stat_1_svc() Chuck Lever
2010-01-14 17:31   ` [PATCH 21/24] statd: Remove NL_ADDR() macro Chuck Lever
2010-01-14 17:32   ` [PATCH 22/24] libnsm.a: retain CAP_NET_BIND when dropping privileges Chuck Lever
2010-01-14 17:32   ` Chuck Lever [this message]
2010-01-14 17:32   ` [PATCH 24/24] statd: update rpc.statd(8) and sm-notify(8) to reflect IPv6 support Chuck Lever
2010-01-16 13:22   ` [PATCH 00/24] Remaining IPv6 patches for statd Steve Dickson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20100114173212.26079.54866.stgit@localhost.localdomain \
    --to=chuck.lever@oracle.com \
    --cc=chris.mason@oracle.com \
    --cc=linux-nfs@vger.kernel.org \
    --cc=steved@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox