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