diff options
Diffstat (limited to 'support/nfs')
-rw-r--r-- | support/nfs/Makefile.am | 3 | ||||
-rw-r--r-- | support/nfs/svc_create.c | 252 |
2 files changed, 254 insertions, 1 deletions
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 */ |