summaryrefslogtreecommitdiffstats
path: root/support/nfs/svc_create.c
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2010-01-14 12:24:39 -0500
committerSteve Dickson <steved@redhat.com>2010-01-15 14:55:52 -0500
commit7dd134204d88c22b414a4ecfcd986efb57fedebf (patch)
tree6bd69aac2d908848a019394121de166b9fa7548a /support/nfs/svc_create.c
parente2446fda6e7cdb1b9462162b81b0e50fd6efaf56 (diff)
downloadnfs-utils-7dd134204d88c22b414a4ecfcd986efb57fedebf.tar.gz
nfs-utils-7dd134204d88c22b414a4ecfcd986efb57fedebf.tar.xz
nfs-utils-7dd134204d88c22b414a4ecfcd986efb57fedebf.zip
statd: Support TI-RPC statd listener
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>
Diffstat (limited to 'support/nfs/svc_create.c')
-rw-r--r--support/nfs/svc_create.c252
1 files changed, 252 insertions, 0 deletions
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 */