nfs-utils: declare a static common buffer for nfssvc.c routines
[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/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <errno.h>
19
20 #include "nfslib.h"
21 #include "xlog.h"
22
23 #define NFSD_PORTS_FILE     "/proc/fs/nfsd/portlist"
24 #define NFSD_VERS_FILE    "/proc/fs/nfsd/versions"
25 #define NFSD_THREAD_FILE  "/proc/fs/nfsd/threads"
26
27 /*
28  * declaring a common static scratch buffer here keeps us from having to
29  * continually thrash the stack. The value of 128 bytes here is really just a
30  * SWAG and can be increased if necessary. It ought to be enough for the
31  * routines below however.
32  */
33 char buf[128];
34
35 static void
36 nfssvc_setfds(int port, unsigned int ctlbits, char *haddr)
37 {
38         int fd, n, on=1;
39         int udpfd = -1, tcpfd = -1;
40         struct sockaddr_in sin;
41
42         fd = open(NFSD_PORTS_FILE, O_RDONLY);
43         if (fd < 0)
44                 return;
45         n = read(fd, buf, sizeof(buf));
46         close(fd);
47         if (n != 0)
48                 return;
49         /* there are no ports currently open, so it is safe to
50          * try to open some and pass them through.
51          * Note: If the user explicitly asked for 'udp', then
52          * we should probably check if that is open, and should
53          * open it if not.  However we don't yet.  All sockets
54          * have to be opened when the first daemon is started.
55          */
56         fd = open(NFSD_PORTS_FILE, O_WRONLY);
57         if (fd < 0)
58                 return;
59         sin.sin_family = AF_INET;
60         sin.sin_port   = htons(port);
61         sin.sin_addr.s_addr =  inet_addr(haddr);
62
63         if (NFSCTL_UDPISSET(ctlbits)) {
64                 udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
65                 if (udpfd < 0) {
66                         xlog(L_ERROR, "unable to create UDP socket: "
67                                 "errno %d (%m)", errno);
68                         exit(1);
69                 }
70                 if (bind(udpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
71                         xlog(L_ERROR, "unable to bind UDP socket: "
72                                 "errno %d (%m)", errno);
73                         exit(1);
74                 }
75         }
76
77         if (NFSCTL_TCPISSET(ctlbits)) {
78                 tcpfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
79                 if (tcpfd < 0) {
80                         xlog(L_ERROR, "unable to create TCP socket: "
81                                 "errno %d (%m)", errno);
82                         exit(1);
83                 }
84                 if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
85                         xlog(L_ERROR, "unable to set SO_REUSEADDR: "
86                                 "errno %d (%m)", errno);
87                         exit(1);
88                 }
89                 if (bind(tcpfd, (struct  sockaddr  *)&sin, sizeof(sin)) < 0){
90                         xlog(L_ERROR, "unable to bind TCP socket: "
91                                 "errno %d (%m)", errno);
92                         exit(1);
93                 }
94                 if (listen(tcpfd, 64) < 0){
95                         xlog(L_ERROR, "unable to create listening socket: "
96                                 "errno %d (%m)", errno);
97                         exit(1);
98                 }
99         }
100         if (udpfd >= 0) {
101                 snprintf(buf, sizeof(buf), "%d\n", udpfd); 
102                 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
103                         xlog(L_ERROR, 
104                                "writing fds to kernel failed: errno %d (%m)", 
105                                errno);
106                 }
107                 close(fd);
108                 fd = -1;
109         }
110         if (tcpfd >= 0) {
111                 if (fd < 0)
112                         fd = open(NFSD_PORTS_FILE, O_WRONLY);
113                 snprintf(buf, sizeof(buf), "%d\n", tcpfd); 
114                 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
115                         xlog(L_ERROR, 
116                                "writing fds to kernel failed: errno %d (%m)", 
117                                errno);
118                 }
119         }
120         close(fd);
121
122         return;
123 }
124 static void
125 nfssvc_versbits(unsigned int ctlbits, int minorvers4)
126 {
127         int fd, n, off;
128         char *ptr;
129
130         ptr = buf;
131         off = 0;
132         fd = open(NFSD_VERS_FILE, O_WRONLY);
133         if (fd < 0)
134                 return;
135
136         for (n = NFSD_MINVERS; n <= NFSD_MAXVERS; n++) {
137                 if (NFSCTL_VERISSET(ctlbits, n))
138                     off += snprintf(ptr+off, sizeof(buf) - off, "+%d ", n);
139                 else
140                     off += snprintf(ptr+off, sizeof(buf) - off, "-%d ", n);
141         }
142         n = minorvers4 >= 0 ? minorvers4 : -minorvers4;
143         if (n >= NFSD_MINMINORVERS4 && n <= NFSD_MAXMINORVERS4)
144                     off += snprintf(ptr+off, sizeof(buf) - off, "%c4.%d",
145                                     minorvers4 > 0 ? '+' : '-',
146                                     n);
147         xlog(D_GENERAL, "Writing version string to kernel: %s", buf);
148         snprintf(ptr+off, sizeof(buf) - off, "\n");
149         if (write(fd, buf, strlen(buf)) != strlen(buf))
150                 xlog(L_ERROR, "Setting version failed: errno %d (%m)", errno);
151
152         close(fd);
153
154         return;
155 }
156 int
157 nfssvc(int port, int nrservs, unsigned int versbits, int minorvers4,
158         unsigned protobits, char *haddr)
159 {
160         struct nfsctl_arg       arg;
161         int fd;
162
163         /* Note: must set versions before fds so that
164          * the ports get registered with portmap against correct
165          * versions
166          */
167         nfssvc_versbits(versbits, minorvers4);
168         nfssvc_setfds(port, protobits, haddr);
169
170         fd = open(NFSD_THREAD_FILE, O_WRONLY);
171         if (fd < 0)
172                 fd = open("/proc/fs/nfs/threads", O_WRONLY);
173         if (fd >= 0) {
174                 /* 2.5+ kernel with nfsd filesystem mounted.
175                  * Just write the number in.
176                  * Cannot handle port number yet, but does anyone care?
177                  */
178                 int n;
179                 snprintf(buf, sizeof(buf), "%d\n", nrservs);
180                 n = write(fd, buf, strlen(buf));
181                 close(fd);
182                 if (n != strlen(buf))
183                         return -1;
184                 else
185                         return 0;
186         }
187
188         arg.ca_version = NFSCTL_VERSION;
189         arg.ca_svc.svc_nthreads = nrservs;
190         arg.ca_svc.svc_port = port;
191         return nfsctl(NFSCTL_SVC, &arg, NULL);
192 }