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