]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/svc_create.c
libexport.a: Allow client_init() to fail instead of exit
[nfs-utils.git] / support / nfs / svc_create.c
1 /*
2  * Copyright 2009 Oracle.  All rights reserved.
3  *
4  * This file is part of nfs-utils.
5  *
6  * nfs-utils is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * nfs-utils is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <memory.h>
28 #include <signal.h>
29 #include <unistd.h>
30 #include <netdb.h>
31
32 #include <netinet/in.h>
33
34 #include <sys/socket.h>
35 #include <sys/resource.h>
36
37 #include <rpc/rpc.h>
38 #include <rpc/svc.h>
39
40 #ifdef HAVE_TCP_WRAPPER
41 #include "tcpwrapper.h"
42 #endif
43
44 #include "rpcmisc.h"
45 #include "xlog.h"
46
47 #ifdef HAVE_LIBTIRPC
48
49 /*
50  * Set up an appropriate bind address, given @port and @nconf.
51  *
52  * Returns getaddrinfo(3) results if successful.  Caller must
53  * invoke freeaddrinfo(3) on these results.
54  *
55  * Otherwise NULL is returned if an error occurs.
56  */
57 __attribute_malloc__
58 static struct addrinfo *
59 svc_create_bindaddr(struct netconfig *nconf, const uint16_t port)
60 {
61         struct addrinfo *ai = NULL;
62         struct addrinfo hint = {
63                 .ai_flags       = AI_PASSIVE | AI_NUMERICSERV,
64         };
65         char buf[8];
66         int error;
67
68         if (strcmp(nconf->nc_protofmly, NC_INET) == 0)
69                 hint.ai_family = AF_INET;
70 #ifdef IPV6_SUPPORTED
71         else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0)
72                 hint.ai_family = AF_INET6;
73 #endif  /* IPV6_SUPPORTED */
74         else {
75                 xlog(D_GENERAL, "Unrecognized bind address family: %s",
76                         nconf->nc_protofmly);
77                 return NULL;
78         }
79
80         if (strcmp(nconf->nc_proto, NC_UDP) == 0)
81                 hint.ai_protocol = (int)IPPROTO_UDP;
82         else if (strcmp(nconf->nc_proto, NC_TCP) == 0)
83                 hint.ai_protocol = (int)IPPROTO_TCP;
84         else {
85                 xlog(D_GENERAL, "Unrecognized bind address protocol: %s",
86                         nconf->nc_proto);
87                 return NULL;
88         }
89
90         (void)snprintf(buf, sizeof(buf), "%u", port);
91         error = getaddrinfo(NULL, buf, &hint, &ai);
92         if (error != 0) {
93                 xlog(L_ERROR, "Failed to construct bind address: %s",
94                         gai_strerror(error));
95                 return NULL;
96         }
97
98         return ai;
99 }
100
101 static unsigned int
102 svc_create_nconf(const char *name, const rpcprog_t program,
103                 const rpcvers_t version,
104                 void (*dispatch)(struct svc_req *, SVCXPRT *),
105                 const uint16_t port, struct netconfig *nconf)
106 {
107         struct t_bind bindaddr;
108         struct addrinfo *ai;
109         SVCXPRT *xprt;
110
111         ai = svc_create_bindaddr(nconf, port);
112         if (ai == NULL)
113                 return 0;
114
115         bindaddr.addr.buf = ai->ai_addr;
116         bindaddr.qlen = SOMAXCONN;
117
118         xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
119         freeaddrinfo(ai);
120         if (xprt == NULL) {
121                 xlog(D_GENERAL, "Failed to create listener xprt "
122                                 "(%s, %u, %s)", name, version, nconf->nc_netid);
123                 return 0;
124         }
125
126         if (!svc_reg(xprt, program, version, dispatch, nconf)) {
127                 /* svc_reg(3) destroys @xprt in this case */
128                 xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
129                                 name, version, nconf->nc_netid);
130                 return 0;
131         }
132
133         return 1;
134 }
135
136 /**
137  * nfs_svc_create - start up RPC svc listeners
138  * @name: C string containing name of new service
139  * @program: RPC program number to register
140  * @version: RPC version number to register
141  * @dispatch: address of function that handles incoming RPC requests
142  * @port: if not zero, transport listens on this port
143  *
144  * Sets up network transports for receiving RPC requests, and starts
145  * the RPC dispatcher.  Returns the number of started network transports.
146  */
147 unsigned int
148 nfs_svc_create(__attribute__((unused)) char *name,
149                 const rpcprog_t program, const rpcvers_t version,
150                 void (*dispatch)(struct svc_req *, SVCXPRT *),
151                 const uint16_t port)
152 {
153         const struct sigaction create_sigaction = {
154                 .sa_handler     = SIG_IGN,
155         };
156         unsigned int visible, up;
157         struct netconfig *nconf;
158         void *handlep;
159
160         /*
161          * Ignore SIGPIPE to avoid exiting sideways when peers
162          * close their TCP connection while we're trying to reply
163          * to them.
164          */
165         (void)sigaction(SIGPIPE, &create_sigaction, NULL);
166
167         handlep = setnetconfig();
168         if (handlep == NULL) {
169                 xlog(L_ERROR, "Failed to access local netconfig database: %s",
170                         nc_sperror());
171                 return 0;
172         }
173
174         visible = 0;
175         up = 0;
176         while ((nconf = getnetconfig(handlep)) != NULL) {
177                 if (!(nconf->nc_flag & NC_VISIBLE))
178                         continue;
179                 visible++;
180                 up += svc_create_nconf(name, program, version, dispatch,
181                                                 port, nconf);
182         }
183
184         if (visible == 0)
185                 xlog(L_ERROR, "Failed to find any visible netconfig entries");
186
187         if (endnetconfig(handlep) == -1)
188                 xlog(L_ERROR, "Failed to close local netconfig database: %s",
189                         nc_sperror());
190
191         return up;
192 }
193
194 /**
195  * nfs_svc_unregister - remove service registrations from local rpcbind database
196  * @program: RPC program number to unregister
197  * @version: RPC version number to unregister
198  *
199  * Removes all registrations for [ @program, @version ] .
200  */
201 void
202 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
203 {
204         if (rpcb_unset(program, version, NULL) == FALSE)
205                 xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
206                         (unsigned long)program, (unsigned long)version);
207 }
208
209 #else   /* !HAVE_LIBTIRPC */
210
211 /**
212  * nfs_svc_create - start up RPC svc listeners
213  * @name: C string containing name of new service
214  * @program: RPC program number to register
215  * @version: RPC version number to register
216  * @dispatch: address of function that handles incoming RPC requests
217  * @port: if not zero, transport listens on this port
218  *
219  * Sets up network transports for receiving RPC requests, and starts
220  * the RPC dispatcher.  Returns the number of started network transports.
221  */
222 unsigned int
223 nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
224                 void (*dispatch)(struct svc_req *, SVCXPRT *),
225                 const uint16_t port)
226 {
227         rpc_init(name, (int)program, (int)version, dispatch, (int)port);
228         return 1;
229 }
230
231 /**
232  * nfs_svc_unregister - remove service registrations from local rpcbind database
233  * @program: RPC program number to unregister
234  * @version: RPC version number to unregister
235  *
236  * Removes all registrations for [ @program, @version ] .
237  */
238 void
239 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
240 {
241         if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
242                 xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
243                         (unsigned long)program, (unsigned long)version);
244 }
245
246 #endif  /* !HAVE_LIBTIRPC */