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