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