]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/nfsd/nfssvc.c
34c67ca11b927ff6201e2d6b61a2e9c8c605ae7b
[nfs-utils.git] / utils / nfsd / nfssvc.c
1 /*
2  * utils/nfsd/nfssvc.c
3  *
4  * Run an NFS daemon.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <sys/types.h>
14 #include <sys/socket.h>
15 #include <netdb.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <errno.h>
21
22 #include "nfslib.h"
23 #include "xlog.h"
24
25 /*
26  * IPv6 support for nfsd was finished before some of the other daemons (mountd
27  * and statd in particular). That could be a problem in the future if someone
28  * were to boot a kernel that supports IPv6 serving with an older nfs-utils. For
29  * now, hardcode the IPv6 switch into the off position until the other daemons
30  * are functional.
31  */
32 #undef IPV6_SUPPORTED
33
34 #define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
35 #define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
36 #define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"
37
38 /*
39  * declaring a common static scratch buffer here keeps us from having to
40  * continually thrash the stack. The value of 128 bytes here is really just a
41  * SWAG and can be increased if necessary. It ought to be enough for the
42  * routines below however.
43  */
44 char buf[128];
45
46 /*
47  * Are there already sockets configured? If not, then it is safe to try to
48  * open some and pass them through.
49  *
50  * Note: If the user explicitly asked for 'udp', then we should probably check
51  * if that is open, and should open it if not. However we don't yet. All
52  * sockets have to be opened when the first daemon is started.
53  */
54 int
55 nfssvc_inuse(void)
56 {
57         int fd, n;
58
59         fd = open(NFSD_PORTS_FILE, O_RDONLY);
60
61         /* problem opening file, assume that nothing is configured */
62         if (fd < 0)
63                 return 0;
64
65         n = read(fd, buf, sizeof(buf));
66         close(fd);
67
68         xlog(D_GENERAL, "knfsd is currently %s", (n > 0) ? "up" : "down");
69
70         return (n > 0);
71 }
72
73 static int
74 nfssvc_setfds(const struct addrinfo *hints, const char *node, const char *port)
75 {
76         int fd, on = 1, fac = L_ERROR;
77         int sockfd = -1, rc = 0;
78         struct addrinfo *addrhead = NULL, *addr;
79         char *proto, *family;
80
81         /*
82          * if file can't be opened, then assume that it's not available and
83          * that the caller should just fall back to the old nfsctl interface
84          */
85         fd = open(NFSD_PORTS_FILE, O_WRONLY);
86         if (fd < 0)
87                 return 0;
88
89         switch(hints->ai_family) {
90         case AF_INET:
91                 family = "inet";
92                 break;
93 #ifdef IPV6_SUPPORTED
94         case AF_INET6:
95                 family = "inet6";
96                 break;
97 #endif /* IPV6_SUPPORTED */
98         default:
99                 xlog(L_ERROR, "Unknown address family specified: %d\n",
100                                 hints->ai_family);
101                 rc = EAFNOSUPPORT;
102                 goto error;
103         }
104
105         rc = getaddrinfo(node, port, hints, &addrhead);
106         if (rc == EAI_NONAME && !strcmp(port, "nfs")) {
107                 snprintf(buf, sizeof(buf), "%d", NFS_PORT);
108                 rc = getaddrinfo(node, buf, hints, &addrhead);
109         }
110
111         if (rc != 0) {
112                 xlog(L_ERROR, "unable to resolve %s:%s to %s address: "
113                                 "%s", node ? node : "ANYADDR", port, family,
114                                 rc == EAI_SYSTEM ? strerror(errno) :
115                                         gai_strerror(rc));
116                 goto error;
117         }
118
119         addr = addrhead;
120         while(addr) {
121                 /* skip non-TCP / non-UDP sockets */
122                 switch(addr->ai_protocol) {
123                 case IPPROTO_UDP:
124                         proto = "UDP";
125                         break;
126                 case IPPROTO_TCP:
127                         proto = "TCP";
128                         break;
129                 default:
130                         addr = addr->ai_next;
131                         continue;
132                 }
133
134                 xlog(D_GENERAL, "Creating %s %s socket.", family, proto);
135
136                 /* open socket and prepare to hand it off to kernel */
137                 sockfd = socket(addr->ai_family, addr->ai_socktype,
138                                 addr->ai_protocol);
139                 if (sockfd < 0) {
140                         xlog(L_ERROR, "unable to create %s %s socket: "
141                                 "errno %d (%m)", family, proto, errno);
142                         rc = errno;
143                         goto error;
144                 }
145 #ifdef IPV6_SUPPORTED
146                 if (addr->ai_family == AF_INET6 &&
147                     setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on))) {
148                         xlog(L_ERROR, "unable to set IPV6_V6ONLY: "
149                                 "errno %d (%m)\n", errno);
150                         rc = errno;
151                         goto error;
152                 }
153 #endif /* IPV6_SUPPORTED */
154                 if (addr->ai_protocol == IPPROTO_TCP &&
155                     setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
156                         xlog(L_ERROR, "unable to set SO_REUSEADDR on %s "
157                                 "socket: errno %d (%m)", family, errno);
158                         rc = errno;
159                         goto error;
160                 }
161                 if (bind(sockfd, addr->ai_addr, addr->ai_addrlen)) {
162                         xlog(L_ERROR, "unable to bind %s %s socket: "
163                                 "errno %d (%m)", family, proto, errno);
164                         rc = errno;
165                         goto error;
166                 }
167                 if (addr->ai_protocol == IPPROTO_TCP && listen(sockfd, 64)) {
168                         xlog(L_ERROR, "unable to create listening socket: "
169                                 "errno %d (%m)", errno);
170                         rc = errno;
171                         goto error;
172                 }
173
174                 if (fd < 0)
175                         fd = open(NFSD_PORTS_FILE, O_WRONLY);
176
177                 if (fd < 0) {
178                         xlog(L_ERROR, "couldn't open ports file: errno "
179                                       "%d (%m)", errno);
180                         goto error;
181                 }
182
183                 snprintf(buf, sizeof(buf), "%d\n", sockfd); 
184                 if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) {
185                         /*
186                          * this error may be common on older kernels that don't
187                          * support IPv6, so turn into a debug message.
188                          */
189                         if (errno == EAFNOSUPPORT)
190                                 fac = D_ALL;
191                         xlog(fac, "writing fd to kernel failed: errno %d (%m)",
192                                   errno);
193                         rc = errno;
194                         goto error;
195                 }
196                 close(fd);
197                 close(sockfd);
198                 sockfd = fd = -1;
199                 addr = addr->ai_next;
200         }
201 error:
202         if (fd >= 0)
203                 close(fd);
204         if (sockfd >= 0)
205                 close(sockfd);
206         if (addrhead)
207                 freeaddrinfo(addrhead);
208         return rc;
209 }
210
211 int
212 nfssvc_set_sockets(const int family, const unsigned int protobits,
213                    const char *host, const char *port)
214 {
215         struct addrinfo hints = { .ai_flags = AI_PASSIVE };
216
217         hints.ai_family = family;
218
219         if (!NFSCTL_ANYPROTO(protobits))
220                 return EPROTOTYPE;
221         else if (!NFSCTL_UDPISSET(protobits))
222                 hints.ai_protocol = IPPROTO_TCP;
223         else if (!NFSCTL_TCPISSET(protobits))
224                 hints.ai_protocol = IPPROTO_UDP;
225
226         return nfssvc_setfds(&hints, host, port);
227 }
228
229 void
230 nfssvc_setvers(unsigned int ctlbits, int minorvers4)
231 {
232         int fd, n, off;
233         char *ptr;
234
235         ptr = buf;
236         off = 0;
237         fd = open(NFSD_VERS_FILE, O_WRONLY);
238         if (fd < 0)
239                 return;
240
241         n = minorvers4 >= 0 ? minorvers4 : -minorvers4;
242         if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4)
243                     off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d ",
244                                     minorvers4 > 0 ? '+' : '-',
245                                     n);
246         for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) {
247                 if (NFSCTL_VERISSET(ctlbits, n))
248                     off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n);
249                 else
250                     off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n);
251         }
252         xlog(D_GENERAL, "Writing version string to kernel: %s", buf);
253         snprintf(ptr+off, sizeof(buf) - off, "\n");
254         if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
255                 xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno);
256
257         close(fd);
258
259         return;
260 }
261
262 int
263 nfssvc_threads(unsigned short port, const int nrservs)
264 {
265         struct nfsctl_arg       arg;
266         struct servent *ent;
267         ssize_t n;
268         int fd;
269
270         fd = open(NFSD_THREAD_FILE, O_WRONLY);
271         if (fd < 0)
272                 fd = open("/proc/fs/nfs/threads", O_WRONLY);
273         if (fd >= 0) {
274                 /* 2.5+ kernel with nfsd filesystem mounted.
275                  * Just write the number of threads.
276                  */
277                 snprintf(buf, sizeof(buf), "%d\n", nrservs);
278                 n = write(fd, buf, strlen(buf));
279                 close(fd);
280                 if (n != (ssize_t)strlen(buf))
281                         return -1;
282                 else
283                         return 0;
284         }
285
286         if (!port) {
287                 ent = getservbyname("nfs", "udp");
288                 if (ent != NULL)
289                         port = ntohs(ent->s_port);
290                 else
291                         port = NFS_PORT;
292         }
293
294         arg.ca_version = NFSCTL_VERSION;
295         arg.ca_svc.svc_nthreads = nrservs;
296         arg.ca_svc.svc_port = port;
297         return nfsctl(NFSCTL_SVC, &arg, NULL);
298 }