]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/mountd/mountd.c
mountd: Clear mountd registrations at start up
[nfs-utils.git] / utils / mountd / mountd.c
1 /*
2  * utils/mountd/mountd.c
3  *
4  * Authenticate mount requests and retrieve file handle.
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 <signal.h>
14 #include <sys/stat.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <getopt.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/resource.h>
24 #include <sys/wait.h>
25 #include "xmalloc.h"
26 #include "misc.h"
27 #include "mountd.h"
28 #include "rpcmisc.h"
29 #include "pseudoflavors.h"
30
31 extern void my_svc_run(void);
32
33 static void             usage(const char *, int exitcode);
34 static exports          get_exportlist(void);
35 static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **, mountstat3 *, int v3);
36
37 int reverse_resolve = 0;
38 int new_cache = 0;
39 int manage_gids;
40 int use_ipaddr = -1;
41
42 /* PRC: a high-availability callout program can be specified with -H
43  * When this is done, the program will receive callouts whenever clients
44  * send mount or unmount requests -- the callout is not needed for 2.6 kernel */
45 char *ha_callout_prog = NULL;
46
47 /* Number of mountd threads to start.   Default is 1 and
48  * that's probably enough unless you need hundreds of
49  * clients to be able to mount at once.  */
50 static int num_threads = 1;
51 /* Arbitrary limit on number of threads */
52 #define MAX_THREADS 64
53
54 static struct option longopts[] =
55 {
56         { "foreground", 0, 0, 'F' },
57         { "descriptors", 1, 0, 'o' },
58         { "debug", 1, 0, 'd' },
59         { "help", 0, 0, 'h' },
60         { "exports-file", 1, 0, 'f' },
61         { "nfs-version", 1, 0, 'V' },
62         { "no-nfs-version", 1, 0, 'N' },
63         { "version", 0, 0, 'v' },
64         { "port", 1, 0, 'p' },
65         { "no-tcp", 0, 0, 'n' },
66         { "ha-callout", 1, 0, 'H' },
67         { "state-directory-path", 1, 0, 's' },
68         { "num-threads", 1, 0, 't' },
69         { "reverse-lookup", 0, 0, 'r' },
70         { "manage-gids", 0, 0, 'g' },
71         { NULL, 0, 0, 0 }
72 };
73
74 #define NFSVERSBIT(vers)        (0x1 << (vers - 1))
75 #define NFSVERSBIT_ALL          (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4))
76
77 static int nfs_version = NFSVERSBIT_ALL;
78
79 static int version2(void)
80 {
81         return nfs_version & NFSVERSBIT(2);
82 }
83
84 static int version3(void)
85 {
86         return nfs_version & NFSVERSBIT(3);
87 }
88
89 static int version23(void)
90 {
91         return nfs_version & (NFSVERSBIT(2) | NFSVERSBIT(3));
92 }
93
94 static int version_any(void)
95 {
96         return nfs_version & NFSVERSBIT_ALL;
97 }
98
99 static void
100 unregister_services (void)
101 {
102         nfs_svc_unregister(MOUNTPROG, MOUNTVERS);
103         nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX);
104         nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3);
105 }
106
107 static void
108 cleanup_lockfiles (void)
109 {
110         unlink(_PATH_XTABLCK);
111         unlink(_PATH_ETABLCK);
112         unlink(_PATH_RMTABLCK);
113 }
114
115 /* Wait for all worker child processes to exit and reap them */
116 static void
117 wait_for_workers (void)
118 {
119         int status;
120         pid_t pid;
121
122         for (;;) {
123
124                 pid = waitpid(0, &status, 0);
125
126                 if (pid < 0) {
127                         if (errno == ECHILD)
128                                 return; /* no more children */
129                         xlog(L_FATAL, "mountd: can't wait: %s\n",
130                                         strerror(errno));
131                 }
132
133                 /* Note: because we SIG_IGN'd SIGCHLD earlier, this
134                  * does not happen on 2.6 kernels, and waitpid() blocks
135                  * until all the children are dead then returns with
136                  * -ECHILD.  But, we don't need to do anything on the
137                  * death of individual workers, so we don't care. */
138                 xlog(L_NOTICE, "mountd: reaped child %d, status %d\n",
139                                 (int)pid, status);
140         }
141 }
142
143 /* Fork num_threads worker children and wait for them */
144 static void
145 fork_workers(void)
146 {
147         int i;
148         pid_t pid;
149
150         xlog(L_NOTICE, "mountd: starting %d threads\n", num_threads);
151
152         for (i = 0 ; i < num_threads ; i++) {
153                 pid = fork();
154                 if (pid < 0) {
155                         xlog(L_FATAL, "mountd: cannot fork: %s\n",
156                                         strerror(errno));
157                 }
158                 if (pid == 0) {
159                         /* worker child */
160
161                         /* Re-enable the default action on SIGTERM et al
162                          * so that workers die naturally when sent them.
163                          * Only the parent unregisters with pmap and
164                          * hence needs to do special SIGTERM handling. */
165                         struct sigaction sa;
166                         sa.sa_handler = SIG_DFL;
167                         sa.sa_flags = 0;
168                         sigemptyset(&sa.sa_mask);
169                         sigaction(SIGHUP, &sa, NULL);
170                         sigaction(SIGINT, &sa, NULL);
171                         sigaction(SIGTERM, &sa, NULL);
172
173                         /* fall into my_svc_run in caller */
174                         return;
175                 }
176         }
177
178         /* in parent */
179         wait_for_workers();
180         unregister_services();
181         cleanup_lockfiles();
182         xlog(L_NOTICE, "mountd: no more workers, exiting\n");
183         exit(0);
184 }
185
186 /*
187  * Signal handler.
188  */
189 static void 
190 killer (int sig)
191 {
192         unregister_services();
193         if (num_threads > 1) {
194                 /* play Kronos and eat our children */
195                 kill(0, SIGTERM);
196                 wait_for_workers();
197         }
198         cleanup_lockfiles();
199         xlog (L_FATAL, "Caught signal %d, un-registering and exiting.", sig);
200 }
201
202 static void
203 sig_hup (int sig)
204 {
205         /* don't exit on SIGHUP */
206         xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n", sig);
207         return;
208 }
209
210 bool_t
211 mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp), 
212         void *UNUSED(resp))
213 {
214         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
215         char buf[INET6_ADDRSTRLEN];
216
217         xlog(D_CALL, "Received NULL request from %s",
218                 host_ntop(sap, buf, sizeof(buf)));
219
220         return 1;
221 }
222
223 bool_t
224 mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res)
225 {
226         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
227         char buf[INET6_ADDRSTRLEN];
228         struct nfs_fh_len *fh;
229
230         xlog(D_CALL, "Received MNT1(%s) request from %s", *path,
231                 host_ntop(sap, buf, sizeof(buf)));
232
233         fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0);
234         if (fh)
235                 memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32);
236         return 1;
237 }
238
239 bool_t
240 mount_dump_1_svc(struct svc_req *rqstp, void *UNUSED(argp), mountlist *res)
241 {
242         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
243         char buf[INET6_ADDRSTRLEN];
244
245         xlog(D_CALL, "Received DUMP request from %s",
246                 host_ntop(sap, buf, sizeof(buf)));
247
248         *res = mountlist_list();
249
250         return 1;
251 }
252
253 bool_t
254 mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *UNUSED(resp))
255 {
256         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
257         nfs_export      *exp;
258         char            *p = *argp;
259         char            rpath[MAXPATHLEN+1];
260         char            buf[INET6_ADDRSTRLEN];
261
262         if (*p == '\0')
263                 p = "/";
264
265         if (realpath(p, rpath) != NULL) {
266                 rpath[sizeof (rpath) - 1] = '\0';
267                 p = rpath;
268         }
269
270         xlog(D_CALL, "Received UMNT(%s) request from %s", p,
271                 host_ntop(sap, buf, sizeof(buf)));
272
273         exp = auth_authenticate("unmount", sap, p);
274         if (exp == NULL)
275                 return 1;
276
277         mountlist_del(host_ntop(sap, buf, sizeof(buf)), p);
278         return 1;
279 }
280
281 bool_t
282 mount_umntall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), 
283         void *UNUSED(resp))
284 {
285         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
286         char            buf[INET6_ADDRSTRLEN];
287
288         xlog(D_CALL, "Received UMNTALL request from %s",
289                 host_ntop(sap, buf, sizeof(buf)));
290
291         /* Reload /etc/xtab if necessary */
292         auth_reload();
293
294         mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt));
295         return 1;
296 }
297
298 bool_t
299 mount_export_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp)
300 {
301         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
302         char buf[INET6_ADDRSTRLEN];
303
304         xlog(D_CALL, "Received EXPORT request from %s.",
305                 host_ntop(sap, buf, sizeof(buf)));
306
307         *resp = get_exportlist();
308                 
309         return 1;
310 }
311
312 bool_t
313 mount_exportall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp)
314 {
315         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
316         char buf[INET6_ADDRSTRLEN];
317
318         xlog(D_CALL, "Received EXPORTALL request from %s.",
319                 host_ntop(sap, buf, sizeof(buf)));
320
321         *resp = get_exportlist();
322
323         return 1;
324 }
325
326 /*
327  * MNTv2 pathconf procedure
328  *
329  * The protocol doesn't include a status field, so Sun apparently considers
330  * it good practice to let anyone snoop on your system, even if it's
331  * pretty harmless data such as pathconf. We don't.
332  *
333  * Besides, many of the pathconf values don't make much sense on NFS volumes.
334  * FIFOs and tty device files represent devices on the *client*, so there's
335  * no point in getting the server's buffer sizes etc.
336  */
337 bool_t
338 mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res)
339 {
340         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
341         struct stat     stb;
342         nfs_export      *exp;
343         char            rpath[MAXPATHLEN+1];
344         char            *p = *path;
345         char buf[INET6_ADDRSTRLEN];
346
347         memset(res, 0, sizeof(*res));
348
349         if (*p == '\0')
350                 p = "/";
351
352         /* Reload /etc/xtab if necessary */
353         auth_reload();
354
355         /* Resolve symlinks */
356         if (realpath(p, rpath) != NULL) {
357                 rpath[sizeof (rpath) - 1] = '\0';
358                 p = rpath;
359         }
360
361         xlog(D_CALL, "Received PATHCONF(%s) request from %s", p,
362                 host_ntop(sap, buf, sizeof(buf)));
363
364         /* Now authenticate the intruder... */
365         exp = auth_authenticate("pathconf", sap, p);
366         if (exp == NULL)
367                 return 1;
368         else if (stat(p, &stb) < 0) {
369                 xlog(L_WARNING, "can't stat exported dir %s: %s",
370                                 p, strerror(errno));
371                 return 1;
372         }
373
374         res->pc_link_max  = pathconf(p, _PC_LINK_MAX);
375         res->pc_max_canon = pathconf(p, _PC_MAX_CANON);
376         res->pc_max_input = pathconf(p, _PC_MAX_INPUT);
377         res->pc_name_max  = pathconf(p, _PC_NAME_MAX);
378         res->pc_path_max  = pathconf(p, _PC_PATH_MAX);
379         res->pc_pipe_buf  = pathconf(p, _PC_PIPE_BUF);
380         res->pc_vdisable  = pathconf(p, _PC_VDISABLE);
381
382         /* Can't figure out what to do with pc_mask */
383         res->pc_mask[0]   = 0;
384         res->pc_mask[1]   = 0;
385
386         return 1;
387 }
388
389 /*
390  * We should advertise the preferred flavours first. (See RFC 2623
391  * section 2.7.)  We leave that to the administrator, by advertising
392  * flavours in the order they were listed in /etc/exports.  AUTH_NULL is
393  * dropped from the list to avoid backward compatibility issue with
394  * older Linux clients, who inspect the list in reversed order.
395  *
396  * XXX: It might be more helpful to rearrange these so that flavors
397  * giving more access (as determined from readonly and id-squashing
398  * options) come first.  (If we decide to do that we should probably do
399  * that when reading the exports rather than here.)
400  */
401 static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp)
402 {
403         struct sec_entry *s;
404         static int flavors[SECFLAVOR_COUNT];
405         int i = 0;
406
407         for (s = exp->m_export.e_secinfo; s->flav; s++) {
408                 if (s->flav->fnum == AUTH_NULL)
409                         continue;
410                 flavors[i] = s->flav->fnum;
411                 i++;
412         }
413         if (i == 0) {
414                 /* default when there is no sec= option: */
415                 i = 1;
416                 flavors[0] = AUTH_UNIX;
417         }
418         ok->auth_flavors.auth_flavors_val = flavors;
419         ok->auth_flavors.auth_flavors_len = i;
420 }
421
422 /*
423  * NFSv3 MOUNT procedure
424  */
425 bool_t
426 mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res)
427 {
428         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
429         struct mountres3_ok *ok = &res->mountres3_u.mountinfo;
430         char buf[INET6_ADDRSTRLEN];
431         nfs_export *exp;
432         struct nfs_fh_len *fh;
433
434         xlog(D_CALL, "Received MNT3(%s) request from %s", *path,
435                 host_ntop(sap, buf, sizeof(buf)));
436
437         fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1);
438         if (!fh)
439                 return 1;
440
441         ok->fhandle.fhandle3_len = fh->fh_size;
442         ok->fhandle.fhandle3_val = (char *)fh->fh_handle;
443         set_authflavors(ok, exp);
444         return 1;
445 }
446
447 static struct nfs_fh_len *
448 get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret,
449                 mountstat3 *error, int v3)
450 {
451         struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt);
452         struct stat     stb, estb;
453         nfs_export      *exp;
454         struct nfs_fh_len *fh;
455         char            rpath[MAXPATHLEN+1];
456         char            *p = *path;
457         char            buf[INET6_ADDRSTRLEN];
458
459         if (*p == '\0')
460                 p = "/";
461
462         /* Reload /var/lib/nfs/etab if necessary */
463         auth_reload();
464
465         /* Resolve symlinks */
466         if (realpath(p, rpath) != NULL) {
467                 rpath[sizeof (rpath) - 1] = '\0';
468                 p = rpath;
469         }
470
471         /* Now authenticate the intruder... */
472         exp = auth_authenticate("mount", sap, p);
473         if (exp == NULL) {
474                 *error = MNT3ERR_ACCES;
475                 return NULL;
476         }
477         if (stat(p, &stb) < 0) {
478                 xlog(L_WARNING, "can't stat exported dir %s: %s",
479                                 p, strerror(errno));
480                 if (errno == ENOENT)
481                         *error = MNT3ERR_NOENT;
482                 else
483                         *error = MNT3ERR_ACCES;
484                 return NULL;
485         }
486         if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) {
487                 xlog(L_WARNING, "%s is not a directory or regular file", p);
488                 *error = MNT3ERR_NOTDIR;
489                 return NULL;
490         }
491         if (stat(exp->m_export.e_path, &estb) < 0) {
492                 xlog(L_WARNING, "can't stat export point %s: %s",
493                      p, strerror(errno));
494                 *error = MNT3ERR_NOENT;
495                 return NULL;
496         }
497         if (estb.st_dev != stb.st_dev
498                    && (!new_cache
499                            || !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT))) {
500                 xlog(L_WARNING, "request to export directory %s below nearest filesystem %s",
501                      p, exp->m_export.e_path);
502                 *error = MNT3ERR_ACCES;
503                 return NULL;
504         }
505         if (exp->m_export.e_mountpoint &&
506                    !is_mountpoint(exp->m_export.e_mountpoint[0]?
507                                   exp->m_export.e_mountpoint:
508                                   exp->m_export.e_path)) {
509                 xlog(L_WARNING, "request to export an unmounted filesystem: %s",
510                      p);
511                 *error = MNT3ERR_NOENT;
512                 return NULL;
513         }
514
515         if (new_cache) {
516                 /* This will be a static private nfs_export with just one
517                  * address.  We feed it to kernel then extract the filehandle,
518                  * 
519                  */
520
521                 if (cache_export(exp, p)) {
522                         *error = MNT3ERR_ACCES;
523                         return NULL;
524                 }
525                 fh = cache_get_filehandle(exp, v3?64:32, p);
526                 if (fh == NULL) {
527                         *error = MNT3ERR_ACCES;
528                         return NULL;
529                 }
530         } else {
531                 int did_export = 0;
532         retry:
533                 if (exp->m_exported<1) {
534                         export_export(exp);
535                         did_export = 1;
536                 }
537                 if (!exp->m_xtabent)
538                         xtab_append(exp);
539
540                 if (v3)
541                         fh = getfh_size((struct sockaddr_in *)sap, p, 64);
542                 if (!v3 || (fh == NULL && errno == EINVAL)) {
543                         /* We first try the new nfs syscall. */
544                         fh = getfh((struct sockaddr_in *)sap, p);
545                         if (fh == NULL && errno == EINVAL)
546                                 /* Let's try the old one. */
547                                 fh = getfh_old((struct sockaddr_in *)sap,
548                                                 stb.st_dev, stb.st_ino);
549                 }
550                 if (fh == NULL && !did_export) {
551                         exp->m_exported = 0;
552                         goto retry;
553                 }
554
555                 if (fh == NULL) {
556                         xlog(L_WARNING, "getfh failed: %s", strerror(errno));
557                         *error = MNT3ERR_ACCES;
558                         return NULL;
559                 }
560         }
561         *error = MNT_OK;
562         mountlist_add(host_ntop(sap, buf, sizeof(buf)), p);
563         if (expret)
564                 *expret = exp;
565         return fh;
566 }
567
568 static void remove_all_clients(exportnode *e)
569 {
570         struct groupnode *g, *ng;
571
572         for (g = e->ex_groups; g; g = ng) {
573                 ng = g->gr_next;
574                 xfree(g->gr_name);
575                 xfree(g);
576         }
577         e->ex_groups = NULL;
578 }
579
580 static void free_exportlist(exports *elist)
581 {
582         struct exportnode *e, *ne;
583
584         for (e = *elist; e != NULL; e = ne) {
585                 ne = e->ex_next;
586                 remove_all_clients(e);
587                 xfree(e->ex_dir);
588                 xfree(e);
589         }
590         *elist = NULL;
591 }
592
593 static void prune_clients(nfs_export *exp, struct exportnode *e)
594 {
595         struct addrinfo *ai = NULL;
596         struct groupnode *c, **cp;
597
598         cp = &e->ex_groups;
599         while ((c = *cp) != NULL) {
600                 if (client_gettype(c->gr_name) == MCL_FQDN
601                     && (ai = host_addrinfo(c->gr_name))) {
602                         if (client_check(exp->m_client, ai)) {
603                                 *cp = c->gr_next;
604                                 xfree(c->gr_name);
605                                 xfree(c);
606                                 freeaddrinfo(ai);
607                                 continue;
608                         }
609                         freeaddrinfo(ai);
610                 }
611                 cp = &(c->gr_next);
612         }
613 }
614
615 static exportnode *lookup_or_create_elist_entry(exports *elist, nfs_export *exp)
616 {
617         exportnode *e;
618
619         for (e = *elist; e != NULL; e = e->ex_next) {
620                 if (!strcmp(exp->m_export.e_path, e->ex_dir))
621                         return e;
622         }
623         e = xmalloc(sizeof(*e));
624         e->ex_next = *elist;
625         e->ex_groups = NULL;
626         e->ex_dir = xstrdup(exp->m_export.e_path);
627         *elist = e;
628         return e;
629 }
630
631 static void insert_group(struct exportnode *e, char *newname)
632 {
633         struct groupnode *g;
634
635         for (g = e->ex_groups; g; g = g->gr_next)
636                 if (strcmp(g->gr_name, newname))
637                         return;
638
639         g = xmalloc(sizeof(*g));
640         g->gr_name = xstrdup(newname);
641         g->gr_next = e->ex_groups;
642         e->ex_groups = g;
643 }
644
645 static exports
646 get_exportlist(void)
647 {
648         static exports          elist = NULL;
649         struct exportnode       *e;
650         nfs_export              *exp;
651         int                     i;
652         static unsigned int     ecounter;
653         unsigned int            acounter;
654
655         acounter = auth_reload();
656         if (elist && acounter == ecounter)
657                 return elist;
658
659         ecounter = acounter;
660
661         free_exportlist(&elist);
662
663         for (i = 0; i < MCL_MAXTYPES; i++) {
664                 for (exp = exportlist[i].p_head; exp; exp = exp->m_next) {
665                          /* Don't show pseudo exports */
666                         if (exp->m_export.e_flags & NFSEXP_V4ROOT)
667                                 continue;
668                         e = lookup_or_create_elist_entry(&elist, exp);
669
670                         /* exports to "*" absorb any others */
671                         if (i == MCL_ANONYMOUS && e->ex_groups) {
672                                 remove_all_clients(e);
673                                 continue;
674                         }
675                         /* non-FQDN's absorb FQDN's they contain: */
676                         if (i != MCL_FQDN && e->ex_groups)
677                                 prune_clients(exp, e);
678
679                         if (exp->m_export.e_hostname[0] != '\0')
680                                 insert_group(e, exp->m_export.e_hostname);
681                 }
682         }
683
684         return elist;
685 }
686
687 int
688 main(int argc, char **argv)
689 {
690         char    *export_file = _PATH_EXPORTS;
691         char    *state_dir = NFS_STATEDIR;
692         char    *progname;
693         unsigned int listeners = 0;
694         int     foreground = 0;
695         int     port = 0;
696         int     descriptors = 0;
697         int     c;
698         int     vers;
699         struct sigaction sa;
700         struct rlimit rlim;
701
702         /* Set the basename */
703         if ((progname = strrchr(argv[0], '/')) != NULL)
704                 progname++;
705         else
706                 progname = argv[0];
707
708         /* Parse the command line options and arguments. */
709         opterr = 0;
710         while ((c = getopt_long(argc, argv, "o:nFd:f:p:P:hH:N:V:vrs:t:g", longopts, NULL)) != EOF)
711                 switch (c) {
712                 case 'g':
713                         manage_gids = 1;
714                         break;
715                 case 'o':
716                         descriptors = atoi(optarg);
717                         if (descriptors <= 0) {
718                                 fprintf(stderr, "%s: bad descriptors: %s\n",
719                                         progname, optarg);
720                                 usage(progname, 1);
721                         }
722                         break;
723                 case 'F':
724                         foreground = 1;
725                         break;
726                 case 'd':
727                         xlog_sconfig(optarg, 1);
728                         break;
729                 case 'f':
730                         export_file = optarg;
731                         break;
732                 case 'H': /* PRC: specify a high-availability callout program */
733                         ha_callout_prog = optarg;
734                         break;
735                 case 'h':
736                         usage(progname, 0);
737                         break;
738                 case 'P':       /* XXX for nfs-server compatibility */
739                 case 'p':
740                         port = atoi(optarg);
741                         if (port <= 0 || port > 65535) {
742                                 fprintf(stderr, "%s: bad port number: %s\n",
743                                         progname, optarg);
744                                 usage(progname, 1);
745                         }
746                         break;
747                 case 'N':
748                         vers = atoi(optarg);
749                         if (vers < 2 || vers > 4) {
750                                 fprintf(stderr, "%s: bad version number: %s\n",
751                                         argv[0], optarg);
752                                 usage(argv[0], 1);
753                         }
754                         nfs_version &= ~NFSVERSBIT(vers);
755                         break;
756                 case 'n':
757                         _rpcfdtype = SOCK_DGRAM;
758                         break;
759                 case 'r':
760                         reverse_resolve = 1;
761                         break;
762                 case 's':
763                         if ((state_dir = xstrdup(optarg)) == NULL) {
764                                 fprintf(stderr, "%s: xstrdup(%s) failed!\n",
765                                         progname, optarg);
766                                 exit(1);
767                         }
768                         break;
769                 case 't':
770                         num_threads = atoi (optarg);
771                         break;
772                 case 'V':
773                         vers = atoi(optarg);
774                         if (vers < 2 || vers > 4) {
775                                 fprintf(stderr, "%s: bad version number: %s\n",
776                                         argv[0], optarg);
777                                 usage(argv[0], 1);
778                         }
779                         nfs_version |= NFSVERSBIT(vers);
780                         break;
781                 case 'v':
782                         printf("%s version " VERSION "\n", progname);
783                         exit(0);
784                 case 0:
785                         break;
786                 case '?':
787                 default:
788                         usage(progname, 1);
789                 }
790
791         /* No more arguments allowed. */
792         if (optind != argc || !version_any())
793                 usage(progname, 1);
794
795         if (chdir(state_dir)) {
796                 fprintf(stderr, "%s: chdir(%s) failed: %s\n",
797                         progname, state_dir, strerror(errno));
798                 exit(1);
799         }
800
801         if (getrlimit (RLIMIT_NOFILE, &rlim) != 0)
802                 fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n",
803                                 progname, strerror(errno));
804         else {
805                 /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */
806                 if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) ||
807                     descriptors > FD_SETSIZE)
808                         descriptors = FD_SETSIZE;
809                 if (descriptors) {
810                         rlim.rlim_cur = descriptors;
811                         if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) {
812                                 fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n",
813                                         progname, strerror(errno));
814                                 exit(1);
815                         }
816                 }
817         }
818         /* Initialize logging. */
819         if (!foreground) xlog_stderr(0);
820         xlog_open(progname);
821
822         sa.sa_handler = SIG_IGN;
823         sa.sa_flags = 0;
824         sigemptyset(&sa.sa_mask);
825         sigaction(SIGHUP, &sa, NULL);
826         sigaction(SIGINT, &sa, NULL);
827         sigaction(SIGTERM, &sa, NULL);
828         sigaction(SIGPIPE, &sa, NULL);
829         /* WARNING: the following works on Linux and SysV, but not BSD! */
830         sigaction(SIGCHLD, &sa, NULL);
831
832         /* Daemons should close all extra filehandles ... *before* RPC init. */
833         if (!foreground)
834                 closeall(3);
835
836         new_cache = check_new_cache();
837         if (new_cache)
838                 cache_open();
839
840         unregister_services();
841         if (version2()) {
842                 listeners += nfs_svc_create("mountd", MOUNTPROG,
843                                         MOUNTVERS, mount_dispatch, port);
844                 listeners += nfs_svc_create("mountd", MOUNTPROG,
845                                         MOUNTVERS_POSIX, mount_dispatch, port);
846         }
847         if (version3())
848                 listeners += nfs_svc_create("mountd", MOUNTPROG,
849                                         MOUNTVERS_NFSV3, mount_dispatch, port);
850         if (version23() && listeners == 0)
851                 xlog(L_FATAL, "mountd: could not create listeners\n");
852
853         sa.sa_handler = killer;
854         sigaction(SIGINT, &sa, NULL);
855         sigaction(SIGTERM, &sa, NULL);
856         sa.sa_handler = sig_hup;
857         sigaction(SIGHUP, &sa, NULL);
858
859         auth_init(export_file);
860
861         if (!foreground) {
862                 /* We first fork off a child. */
863                 if ((c = fork()) > 0)
864                         exit(0);
865                 if (c < 0) {
866                         xlog(L_FATAL, "mountd: cannot fork: %s\n",
867                                                 strerror(errno));
868                 }
869                 /* Now we remove ourselves from the foreground.
870                    Redirect stdin/stdout/stderr first. */
871                 {
872                         int fd = open("/dev/null", O_RDWR);
873                         (void) dup2(fd, 0);
874                         (void) dup2(fd, 1);
875                         (void) dup2(fd, 2);
876                         if (fd > 2) (void) close(fd);
877                 }
878                 setsid();
879         }
880
881         /* silently bounds check num_threads */
882         if (foreground)
883                 num_threads = 1;
884         else if (num_threads < 1)
885                 num_threads = 1;
886         else if (num_threads > MAX_THREADS)
887                 num_threads = MAX_THREADS;
888
889         if (num_threads > 1)
890                 fork_workers();
891
892         xlog(L_NOTICE, "Version " VERSION " starting");
893         my_svc_run();
894
895         xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n");
896         unregister_services();
897         exit(1);
898 }
899
900 static void
901 usage(const char *prog, int n)
902 {
903         fprintf(stderr,
904 "Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n"
905 "       [-o num|--descriptors num] [-f exports-file|--exports-file=file]\n"
906 "       [-p|--port port] [-V version|--nfs-version version]\n"
907 "       [-N version|--no-nfs-version version] [-n|--no-tcp]\n"
908 "       [-H ha-callout-prog] [-s|--state-directory-path path]\n"
909 "       [-g|--manage-gids] [-t num|--num-threads=num]\n", prog);
910         exit(n);
911 }