nfsd: Add support for the -V and --nfs-version optional arguments
[nfs-utils.git] / utils / nfsd / nfsd.c
1 /*
2  * nfsd
3  *
4  * This is the user level part of nfsd. This is very primitive, because
5  * all the work is now done in the kernel module.
6  *
7  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <getopt.h>
21 #include <netdb.h>
22 #include <sys/socket.h>
23 #include <netinet/in.h>
24 #include <arpa/inet.h>
25
26 #include "nfslib.h"
27 #include "nfssvc.h"
28 #include "xlog.h"
29
30 #ifndef NFSD_NPROC
31 #define NFSD_NPROC 8
32 #endif
33
34 static void     usage(const char *);
35
36 static struct option longopts[] =
37 {
38         { "host", 1, 0, 'H' },
39         { "help", 0, 0, 'h' },
40         { "no-nfs-version", 1, 0, 'N' },
41         { "nfs-version", 1, 0, 'V' },
42         { "no-tcp", 0, 0, 'T' },
43         { "no-udp", 0, 0, 'U' },
44         { "port", 1, 0, 'P' },
45         { "port", 1, 0, 'p' },
46         { "debug", 0, 0, 'd' },
47         { "syslog", 0, 0, 's' },
48         { NULL, 0, 0, 0 }
49 };
50
51 /* given a family and ctlbits, disable any that aren't listed in netconfig */
52 #ifdef HAVE_LIBTIRPC
53 static void
54 nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6)
55 {
56         struct netconfig *nconf;
57         unsigned int *famproto;
58         void *handle;
59
60         xlog(D_GENERAL, "Checking netconfig for visible protocols.");
61
62         handle = setnetconfig();
63         while((nconf = getnetconfig(handle))) {
64                 if (!(nconf->nc_flag & NC_VISIBLE))
65                         continue;
66
67                 if (!strcmp(nconf->nc_protofmly, NC_INET))
68                         famproto = proto4;
69                 else if (!strcmp(nconf->nc_protofmly, NC_INET6))
70                         famproto = proto6;
71                 else
72                         continue;
73
74                 if (!strcmp(nconf->nc_proto, NC_TCP))
75                         NFSCTL_TCPSET(*famproto);
76                 else if (!strcmp(nconf->nc_proto, NC_UDP))
77                         NFSCTL_UDPSET(*famproto);
78
79                 xlog(D_GENERAL, "Enabling %s %s.", nconf->nc_protofmly,
80                         nconf->nc_proto);
81         }
82         endnetconfig(handle);
83         return;
84 }
85 #else /* HAVE_LIBTIRPC */
86 static void
87 nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6)
88 {
89         /* Enable all IPv4 protocols if no TIRPC support */
90         *proto4 = NFSCTL_ALLBITS;
91         *proto6 = 0;
92 }
93 #endif /* HAVE_LIBTIRPC */
94
95 int
96 main(int argc, char **argv)
97 {
98         int     count = NFSD_NPROC, c, error = 0, portnum = 0, fd, found_one;
99         char *p, *progname, *port;
100         char *haddr = NULL;
101         int     socket_up = 0;
102         int minorvers41 = 0;    /* nfsv4 minor version */
103         unsigned int versbits = NFSCTL_ALLBITS;
104         unsigned int protobits = NFSCTL_ALLBITS;
105         unsigned int proto4 = 0;
106         unsigned int proto6 = 0;
107
108         progname = strdup(basename(argv[0]));
109         if (!progname) {
110                 fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
111                 exit(1);
112         }
113
114         port = strdup("nfs");
115         if (!port) {
116                 fprintf(stderr, "%s: unable to allocate memory.\n", progname);
117                 exit(1);
118         }
119
120         xlog_syslog(0);
121         xlog_stderr(1);
122
123         while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:sTU", longopts, NULL)) != EOF) {
124                 switch(c) {
125                 case 'd':
126                         xlog_config(D_ALL, 1);
127                         break;
128                 case 'H':
129                         /*
130                          * for now, this only handles one -H option. Use the
131                          * last one specified.
132                          */
133                         free(haddr);
134                         haddr = strdup(optarg);
135                         if (!haddr) {
136                                 fprintf(stderr, "%s: unable to allocate "
137                                         "memory.\n", progname);
138                                 exit(1);
139                         }
140                         break;
141                 case 'P':       /* XXX for nfs-server compatibility */
142                 case 'p':
143                         /* only the last -p option has any effect */
144                         portnum = atoi(optarg);
145                         if (portnum <= 0 || portnum > 65535) {
146                                 fprintf(stderr, "%s: bad port number: %s\n",
147                                         progname, optarg);
148                                 usage(progname);
149                         }
150                         free(port);
151                         port = strdup(optarg);
152                         if (!port) {
153                                 fprintf(stderr, "%s: unable to allocate "
154                                                 "memory.\n", progname);
155                                 exit(1);
156                         }
157                         break;
158                 case 'N':
159                         switch((c = strtol(optarg, &p, 0))) {
160                         case 4:
161                                 if (*p == '.') {
162                                         int i = atoi(p+1);
163                                         if (i != 1) {
164                                                 fprintf(stderr, "%s: unsupported minor version\n", optarg);
165                                                 exit(1);
166                                         }
167                                         minorvers41 = -1;
168                                         break;
169                                 }
170                         case 3:
171                         case 2:
172                                 NFSCTL_VERUNSET(versbits, c);
173                                 break;
174                         default:
175                                 fprintf(stderr, "%s: Unsupported version\n", optarg);
176                                 exit(1);
177                         }
178                         break;
179                 case 'V':
180                         switch((c = strtol(optarg, &p, 0))) {
181                         case 4:
182                                 if (*p == '.') {
183                                         int i = atoi(p+1);
184                                         if (i != 1) {
185                                                 fprintf(stderr, "%s: unsupported minor version\n", optarg);
186                                                 exit(1);
187                                         }
188                                         minorvers41 = 1;
189                                         break;
190                                 }
191                         case 3:
192                         case 2:
193                                 NFSCTL_VERSET(versbits, c);
194                                 break;
195                         default:
196                                 fprintf(stderr, "%s: Unsupported version\n", optarg);
197                                 exit(1);
198                         }
199                         break;
200                 case 's':
201                         xlog_syslog(1);
202                         xlog_stderr(0);
203                         break;
204                 case 'T':
205                         NFSCTL_TCPUNSET(protobits);
206                         break;
207                 case 'U':
208                         NFSCTL_UDPUNSET(protobits);
209                         break;
210                 default:
211                         fprintf(stderr, "Invalid argument: '%c'\n", c);
212                 case 'h':
213                         usage(progname);
214                 }
215         }
216
217         if (optind < argc) {
218                 if ((count = atoi(argv[optind])) < 0) {
219                         /* insane # of servers */
220                         fprintf(stderr,
221                                 "%s: invalid server count (%d), using 1\n",
222                                 argv[0], count);
223                         count = 1;
224                 } else if (count == 0) {
225                         /*
226                          * don't bother setting anything else if the threads
227                          * are coming down anyway.
228                          */
229                         socket_up = 1;
230                         goto set_threads;
231                 }
232         }
233
234         xlog_open(progname);
235
236         nfsd_enable_protos(&proto4, &proto6);
237
238         if (!NFSCTL_TCPISSET(protobits)) {
239                 NFSCTL_TCPUNSET(proto4);
240                 NFSCTL_TCPUNSET(proto6);
241         }
242
243         if (!NFSCTL_UDPISSET(protobits)) {
244                 NFSCTL_UDPUNSET(proto4);
245                 NFSCTL_UDPUNSET(proto6);
246         }
247
248         /* make sure that at least one version is enabled */
249         found_one = 0;
250         for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) {
251                 if (NFSCTL_VERISSET(versbits, c))
252                         found_one = 1;
253         }
254         if (!found_one) {
255                 xlog(L_ERROR, "no version specified");
256                 exit(1);
257         }                       
258
259         if (NFSCTL_VERISSET(versbits, 4) &&
260             !NFSCTL_TCPISSET(proto4) &&
261             !NFSCTL_TCPISSET(proto6)) {
262                 xlog(L_ERROR, "version 4 requires the TCP protocol");
263                 exit(1);
264         }
265
266         if (chdir(NFS_STATEDIR)) {
267                 xlog(L_ERROR, "chdir(%s) failed: %m", NFS_STATEDIR);
268                 exit(1);
269         }
270
271         /* make sure nfsdfs is mounted if it's available */
272         nfssvc_mount_nfsdfs(progname);
273
274         /* can only change number of threads if nfsd is already up */
275         if (nfssvc_inuse()) {
276                 socket_up = 1;
277                 goto set_threads;
278         }
279
280         /*
281          * must set versions before the fd's so that the right versions get
282          * registered with rpcbind. Note that on older kernels w/o the right
283          * interfaces, these are a no-op.
284          */
285         nfssvc_setvers(versbits, minorvers41);
286  
287         error = nfssvc_set_sockets(AF_INET, proto4, haddr, port);
288         if (!error)
289                 socket_up = 1;
290
291 #ifdef IPV6_SUPPORTED
292         error = nfssvc_set_sockets(AF_INET6, proto6, haddr, port);
293         if (!error)
294                 socket_up = 1;
295 #endif /* IPV6_SUPPORTED */
296
297 set_threads:
298         /* don't start any threads if unable to hand off any sockets */
299         if (!socket_up) {
300                 xlog(L_ERROR, "unable to set any sockets for nfsd");
301                 goto out;
302         }
303         error = 0;
304
305         /*
306          * KLUDGE ALERT:
307          * Some kernels let nfsd kernel threads inherit open files
308          * from the program that spawns them (i.e. us).  So close
309          * everything before spawning kernel threads.  --Chip
310          */
311         fd = open("/dev/null", O_RDWR);
312         if (fd == -1)
313                 xlog(L_ERROR, "Unable to open /dev/null: %m");
314         else {
315                 /* switch xlog output to syslog since stderr is being closed */
316                 xlog_syslog(1);
317                 xlog_stderr(0);
318                 (void) dup2(fd, 0);
319                 (void) dup2(fd, 1);
320                 (void) dup2(fd, 2);
321         }
322         closeall(3);
323
324         if ((error = nfssvc_threads(portnum, count)) < 0)
325                 xlog(L_ERROR, "error starting threads: errno %d (%m)", errno);
326 out:
327         free(port);
328         free(haddr);
329         free(progname);
330         return (error != 0);
331 }
332
333 static void
334 usage(const char *prog)
335 {
336         fprintf(stderr, "Usage:\n"
337                 "%s [-d|--debug] [-H hostname] [-p|-P|--port port] [-N|--no-nfs-version version] [-V|--nfs-version version] [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] nrservs\n", 
338                 prog);
339         exit(2);
340 }