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