]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/svc_create.c
fa7eab6711f49f3fa4cf3f5b726dbde67a4415cd
[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(L_ERROR, "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(L_ERROR, "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         switch (error != 0) {
93         case 0:
94                 return ai;
95         case EAI_SYSTEM:
96                 xlog(L_ERROR, "Failed to construct bind address: %m");
97                 break;
98         default:
99                 xlog(L_ERROR, "Failed to construct bind address: %s",
100                         gai_strerror(error));
101                 break;
102         }
103
104         return NULL;
105 }
106
107 static unsigned int
108 svc_create_nconf(const char *name, const rpcprog_t program,
109                 const rpcvers_t version,
110                 void (*dispatch)(struct svc_req *, SVCXPRT *),
111                 const uint16_t port, struct netconfig *nconf)
112 {
113         struct t_bind bindaddr;
114         struct addrinfo *ai;
115         SVCXPRT *xprt;
116
117         ai = svc_create_bindaddr(nconf, port);
118         if (ai == NULL)
119                 return 0;
120
121         bindaddr.addr.buf = ai->ai_addr;
122         bindaddr.qlen = SOMAXCONN;
123
124         xprt = svc_tli_create(RPC_ANYFD, nconf, &bindaddr, 0, 0);
125         freeaddrinfo(ai);
126         if (xprt == NULL) {
127                 xlog(D_GENERAL, "Failed to create listener xprt "
128                                 "(%s, %u, %s)", name, version, nconf->nc_netid);
129                 return 0;
130         }
131
132         if (!svc_reg(xprt, program, version, dispatch, nconf)) {
133                 /* svc_reg(3) destroys @xprt in this case */
134                 xlog(D_GENERAL, "Failed to register (%s, %u, %s)",
135                                 name, version, nconf->nc_netid);
136                 return 0;
137         }
138
139         return 1;
140 }
141
142 /**
143  * nfs_svc_create - start up RPC svc listeners
144  * @name: C string containing name of new service
145  * @program: RPC program number to register
146  * @version: RPC version number to register
147  * @dispatch: address of function that handles incoming RPC requests
148  * @port: if not zero, transport listens on this port
149  *
150  * Sets up network transports for receiving RPC requests, and starts
151  * the RPC dispatcher.  Returns the number of started network transports.
152  */
153 unsigned int
154 nfs_svc_create(__attribute__((unused)) char *name,
155                 const rpcprog_t program, const rpcvers_t version,
156                 void (*dispatch)(struct svc_req *, SVCXPRT *),
157                 const uint16_t port)
158 {
159         const struct sigaction create_sigaction = {
160                 .sa_handler     = SIG_IGN,
161         };
162         unsigned int visible, up;
163         struct netconfig *nconf;
164         void *handlep;
165
166         /*
167          * Ignore SIGPIPE to avoid exiting sideways when peers
168          * close their TCP connection while we're trying to reply
169          * to them.
170          */
171         (void)sigaction(SIGPIPE, &create_sigaction, NULL);
172
173         handlep = setnetconfig();
174         if (handlep == NULL) {
175                 xlog(L_ERROR, "Failed to access local netconfig database: %s",
176                         nc_sperror());
177                 return 0;
178         }
179
180         visible = 0;
181         up = 0;
182         while ((nconf = getnetconfig(handlep)) != NULL) {
183                 if (!(nconf->nc_flag & NC_VISIBLE))
184                         continue;
185                 visible++;
186                 up += svc_create_nconf(name, program, version, dispatch,
187                                                 port, nconf);
188         }
189
190         if (visible == 0)
191                 xlog(L_ERROR, "Failed to find any visible netconfig entries");
192
193         if (endnetconfig(handlep) == -1)
194                 xlog(L_ERROR, "Failed to close local netconfig database: %s",
195                         nc_sperror());
196
197         return up;
198 }
199
200 /**
201  * nfs_svc_unregister - remove service registrations from local rpcbind database
202  * @program: RPC program number to unregister
203  * @version: RPC version number to unregister
204  *
205  * Removes all registrations for [ @program, @version ] .
206  */
207 void
208 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
209 {
210         if (rpcb_unset(program, version, NULL) == FALSE)
211                 xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
212                         (unsigned long)program, (unsigned long)version);
213 }
214
215 #else   /* !HAVE_LIBTIRPC */
216
217 /**
218  * nfs_svc_create - start up RPC svc listeners
219  * @name: C string containing name of new service
220  * @program: RPC program number to register
221  * @version: RPC version number to register
222  * @dispatch: address of function that handles incoming RPC requests
223  * @port: if not zero, transport listens on this port
224  *
225  * Sets up network transports for receiving RPC requests, and starts
226  * the RPC dispatcher.  Returns the number of started network transports.
227  */
228 unsigned int
229 nfs_svc_create(char *name, const rpcprog_t program, const rpcvers_t version,
230                 void (*dispatch)(struct svc_req *, SVCXPRT *),
231                 const uint16_t port)
232 {
233         rpc_init(name, (int)program, (int)version, dispatch, (int)port);
234         return 1;
235 }
236
237 /**
238  * nfs_svc_unregister - remove service registrations from local rpcbind database
239  * @program: RPC program number to unregister
240  * @version: RPC version number to unregister
241  *
242  * Removes all registrations for [ @program, @version ] .
243  */
244 void
245 nfs_svc_unregister(const rpcprog_t program, const rpcvers_t version)
246 {
247         if (pmap_unset((unsigned long)program, (unsigned long)version) == FALSE)
248                 xlog(D_GENERAL, "Failed to unregister program %lu, version %lu",
249                         (unsigned long)program, (unsigned long)version);
250 }
251
252 #endif  /* !HAVE_LIBTIRPC */