diff options
Diffstat (limited to '')
-rw-r--r-- | utils/mountd/mountd.c | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c new file mode 100644 index 0000000..dbd5546 --- /dev/null +++ b/utils/mountd/mountd.c @@ -0,0 +1,887 @@ +/* + * utils/mountd/mountd.c + * + * Authenticate mount requests and retrieve file handle. + * + * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <signal.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/resource.h> + +#include "conffile.h" +#include "xmalloc.h" +#include "misc.h" +#include "mountd.h" +#include "rpcmisc.h" +#include "pseudoflavors.h" +#include "nfsd_path.h" +#include "nfslib.h" +#include "export.h" + +extern void my_svc_run(void); + +static void usage(const char *, int exitcode); +static exports get_exportlist(void); +static struct nfs_fh_len *get_rootfh(struct svc_req *, dirpath *, nfs_export **, mountstat3 *, int v3); + +int reverse_resolve = 0; +int manage_gids; +int use_ipaddr = -1; + +/* PRC: a high-availability callout program can be specified with -H + * When this is done, the program will receive callouts whenever clients + * send mount or unmount requests -- the callout is not needed for 2.6 kernel */ +char *ha_callout_prog = NULL; + +/* Number of mountd threads to start. Default is 1 and + * that's probably enough unless you need hundreds of + * clients to be able to mount at once. */ +static int num_threads = 1; +/* Arbitrary limit on number of threads */ +#define MAX_THREADS 64 + +static struct option longopts[] = +{ + { "foreground", 0, 0, 'F' }, + { "descriptors", 1, 0, 'o' }, + { "debug", 1, 0, 'd' }, + { "help", 0, 0, 'h' }, + { "nfs-version", 1, 0, 'V' }, + { "no-nfs-version", 1, 0, 'N' }, + { "version", 0, 0, 'v' }, + { "port", 1, 0, 'p' }, + { "no-tcp", 0, 0, 'n' }, + { "ha-callout", 1, 0, 'H' }, + { "state-directory-path", 1, 0, 's' }, + { "num-threads", 1, 0, 't' }, + { "reverse-lookup", 0, 0, 'r' }, + { "manage-gids", 0, 0, 'g' }, + { "no-udp", 0, 0, 'u' }, + { "log-auth", 0, 0, 'l'}, + { "cache-use-ipaddr", 0, 0, 'i'}, + { "ttl", 1, 0, 'T'}, + { NULL, 0, 0, 0 } +}; +static char shortopts[] = "o:nFd:p:P:hH:N:V:vurs:t:gliT:"; + +#define NFSVERSBIT(vers) (0x1 << (vers - 1)) +#define NFSVERSBIT_ALL (NFSVERSBIT(2) | NFSVERSBIT(3) | NFSVERSBIT(4)) + +static int nfs_version = NFSVERSBIT_ALL; + +static int version2(void) +{ + return nfs_version & NFSVERSBIT(2); +} + +static int version3(void) +{ + return nfs_version & NFSVERSBIT(3); +} + +static int version23(void) +{ + return nfs_version & (NFSVERSBIT(2) | NFSVERSBIT(3)); +} + +static int version_any(void) +{ + return nfs_version & NFSVERSBIT_ALL; +} + +static void +unregister_services (void) +{ + nfs_svc_unregister(MOUNTPROG, MOUNTVERS); + nfs_svc_unregister(MOUNTPROG, MOUNTVERS_POSIX); + nfs_svc_unregister(MOUNTPROG, MOUNTVERS_NFSV3); +} + +static void +cleanup_lockfiles (void) +{ + unlink(etab.lockfn); + unlink(rmtab.lockfn); +} + +/* + * Signal handler. + */ +static void +killer (int sig) +{ + unregister_services(); + if (num_threads > 1) { + /* play Kronos and eat our children */ + kill(0, SIGTERM); + cache_wait_for_workers("mountd"); + } + cleanup_lockfiles(); + free_state_path_names(&etab); + free_state_path_names(&rmtab); + xlog (L_NOTICE, "Caught signal %d, un-registering and exiting.", sig); + exit(0); +} + +static void +sig_hup (int UNUSED(sig)) +{ + /* don't exit on SIGHUP */ + xlog (L_NOTICE, "Received SIGHUP... Ignoring.\n"); + return; +} + +bool_t +mount_null_1_svc(struct svc_req *rqstp, void *UNUSED(argp), + void *UNUSED(resp)) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received NULL request from %s", + host_ntop(sap, buf, sizeof(buf))); + + return 1; +} + +bool_t +mount_mnt_1_svc(struct svc_req *rqstp, dirpath *path, fhstatus *res) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + struct nfs_fh_len *fh; + + xlog(D_CALL, "Received MNT1(%s) request from %s", *path, + host_ntop(sap, buf, sizeof(buf))); + + fh = get_rootfh(rqstp, path, NULL, &res->fhs_status, 0); + if (fh) + memcpy(&res->fhstatus_u.fhs_fhandle, fh->fh_handle, 32); + return 1; +} + +bool_t +mount_dump_1_svc(struct svc_req *rqstp, void *UNUSED(argp), mountlist *res) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received DUMP request from %s", + host_ntop(sap, buf, sizeof(buf))); + + *res = mountlist_list(); + + return 1; +} + +bool_t +mount_umnt_1_svc(struct svc_req *rqstp, dirpath *argp, void *UNUSED(resp)) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + nfs_export *exp; + char *p = *argp; + char rpath[MAXPATHLEN+1]; + char buf[INET6_ADDRSTRLEN]; + + if (*p == '\0') + p = "/"; + + if (nfsd_realpath(p, rpath) != NULL) { + rpath[sizeof (rpath) - 1] = '\0'; + p = rpath; + } + + xlog(D_CALL, "Received UMNT(%s) request from %s", p, + host_ntop(sap, buf, sizeof(buf))); + + exp = auth_authenticate("unmount", sap, p); + if (exp == NULL) + return 1; + + mountlist_del(host_ntop(sap, buf, sizeof(buf)), p); + return 1; +} + +bool_t +mount_umntall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), + void *UNUSED(resp)) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received UMNTALL request from %s", + host_ntop(sap, buf, sizeof(buf))); + + /* Reload /etc/exports if necessary */ + auth_reload(); + + mountlist_del_all(nfs_getrpccaller(rqstp->rq_xprt)); + return 1; +} + +bool_t +mount_export_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received EXPORT request from %s.", + host_ntop(sap, buf, sizeof(buf))); + + *resp = get_exportlist(); + + return 1; +} + +bool_t +mount_exportall_1_svc(struct svc_req *rqstp, void *UNUSED(argp), exports *resp) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + char buf[INET6_ADDRSTRLEN]; + + xlog(D_CALL, "Received EXPORTALL request from %s.", + host_ntop(sap, buf, sizeof(buf))); + + *resp = get_exportlist(); + + return 1; +} + +/* + * MNTv2 pathconf procedure + * + * The protocol doesn't include a status field, so Sun apparently considers + * it good practice to let anyone snoop on your system, even if it's + * pretty harmless data such as pathconf. We don't. + * + * Besides, many of the pathconf values don't make much sense on NFS volumes. + * FIFOs and tty device files represent devices on the *client*, so there's + * no point in getting the server's buffer sizes etc. + */ +bool_t +mount_pathconf_2_svc(struct svc_req *rqstp, dirpath *path, ppathcnf *res) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + struct stat stb; + nfs_export *exp; + char rpath[MAXPATHLEN+1]; + char *p = *path; + char buf[INET6_ADDRSTRLEN]; + + memset(res, 0, sizeof(*res)); + + if (*p == '\0') + p = "/"; + + /* Reload /etc/exports if necessary */ + auth_reload(); + + /* Resolve symlinks */ + if (nfsd_realpath(p, rpath) != NULL) { + rpath[sizeof (rpath) - 1] = '\0'; + p = rpath; + } + + xlog(D_CALL, "Received PATHCONF(%s) request from %s", p, + host_ntop(sap, buf, sizeof(buf))); + + /* Now authenticate the intruder... */ + exp = auth_authenticate("pathconf", sap, p); + if (exp == NULL) + return 1; + else if (nfsd_path_stat(p, &stb) < 0) { + xlog(L_WARNING, "can't stat exported dir %s: %s", + p, strerror(errno)); + return 1; + } + + res->pc_link_max = pathconf(p, _PC_LINK_MAX); + res->pc_max_canon = pathconf(p, _PC_MAX_CANON); + res->pc_max_input = pathconf(p, _PC_MAX_INPUT); + res->pc_name_max = pathconf(p, _PC_NAME_MAX); + res->pc_path_max = pathconf(p, _PC_PATH_MAX); + res->pc_pipe_buf = pathconf(p, _PC_PIPE_BUF); + res->pc_vdisable = pathconf(p, _PC_VDISABLE); + + /* Can't figure out what to do with pc_mask */ + res->pc_mask[0] = 0; + res->pc_mask[1] = 0; + + return 1; +} + +/* + * We should advertise the preferred flavours first. (See RFC 2623 + * section 2.7.) We leave that to the administrator, by advertising + * flavours in the order they were listed in /etc/exports. AUTH_NULL is + * dropped from the list to avoid backward compatibility issue with + * older Linux clients, who inspect the list in reversed order. + * + * XXX: It might be more helpful to rearrange these so that flavors + * giving more access (as determined from readonly and id-squashing + * options) come first. (If we decide to do that we should probably do + * that when reading the exports rather than here.) + */ +static void set_authflavors(struct mountres3_ok *ok, nfs_export *exp) +{ + struct sec_entry *s; + static int flavors[SECFLAVOR_COUNT]; + int i = 0; + + for (s = exp->m_export.e_secinfo; s->flav; s++) { + if (s->flav->fnum == AUTH_NULL) + continue; + flavors[i] = s->flav->fnum; + i++; + } + if (i == 0) { + /* default when there is no sec= option: */ + i = 1; + flavors[0] = AUTH_UNIX; + } + ok->auth_flavors.auth_flavors_val = flavors; + ok->auth_flavors.auth_flavors_len = i; +} + +/* + * NFSv3 MOUNT procedure + */ +bool_t +mount_mnt_3_svc(struct svc_req *rqstp, dirpath *path, mountres3 *res) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + struct mountres3_ok *ok = &res->mountres3_u.mountinfo; + char buf[INET6_ADDRSTRLEN]; + nfs_export *exp; + struct nfs_fh_len *fh; + + xlog(D_CALL, "Received MNT3(%s) request from %s", *path, + host_ntop(sap, buf, sizeof(buf))); + + fh = get_rootfh(rqstp, path, &exp, &res->fhs_status, 1); + if (!fh) + return 1; + + ok->fhandle.fhandle3_len = fh->fh_size; + ok->fhandle.fhandle3_val = (char *)fh->fh_handle; + set_authflavors(ok, exp); + return 1; +} + +static struct nfs_fh_len * +get_rootfh(struct svc_req *rqstp, dirpath *path, nfs_export **expret, + mountstat3 *error, int v3) +{ + struct sockaddr *sap = nfs_getrpccaller(rqstp->rq_xprt); + struct stat stb, estb; + nfs_export *exp; + struct nfs_fh_len *fh; + char rpath[MAXPATHLEN+1]; + char *p = *path; + char buf[INET6_ADDRSTRLEN]; + + if (*p == '\0') + p = "/"; + + /* Reload /var/lib/nfs/etab if necessary */ + auth_reload(); + + /* Resolve symlinks */ + if (nfsd_realpath(p, rpath) != NULL) { + rpath[sizeof (rpath) - 1] = '\0'; + p = rpath; + } + + /* Now authenticate the intruder... */ + exp = auth_authenticate("mount", sap, p); + if (exp == NULL) { + *error = MNT3ERR_ACCES; + return NULL; + } + if (nfsd_path_stat(p, &stb) < 0) { + xlog(L_WARNING, "can't stat exported dir %s: %s", + p, strerror(errno)); + if (errno == ENOENT) + *error = MNT3ERR_NOENT; + else + *error = MNT3ERR_ACCES; + return NULL; + } + if (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) { + xlog(L_WARNING, "%s is not a directory or regular file", p); + *error = MNT3ERR_NOTDIR; + return NULL; + } + if (nfsd_path_stat(exp->m_export.e_path, &estb) < 0) { + xlog(L_WARNING, "can't stat export point %s: %s", + p, strerror(errno)); + *error = MNT3ERR_NOENT; + return NULL; + } + if (estb.st_dev != stb.st_dev + && !(exp->m_export.e_flags & NFSEXP_CROSSMOUNT)) { + xlog(L_WARNING, "request to export directory %s below nearest filesystem %s", + p, exp->m_export.e_path); + *error = MNT3ERR_ACCES; + return NULL; + } + if (exp->m_export.e_mountpoint && + !check_is_mountpoint(exp->m_export.e_mountpoint[0]? + exp->m_export.e_mountpoint: + exp->m_export.e_path, + nfsd_path_lstat)) { + xlog(L_WARNING, "request to export an unmounted filesystem: %s", + p); + *error = MNT3ERR_NOENT; + return NULL; + } + + /* This will be a static private nfs_export with just one + * address. We feed it to kernel then extract the filehandle, + */ + + if (cache_export(exp, p)) { + *error = MNT3ERR_ACCES; + return NULL; + } + fh = cache_get_filehandle(exp, v3?64:32, p); + if (fh == NULL) { + *error = MNT3ERR_ACCES; + return NULL; + } + *error = MNT_OK; + mountlist_add(host_ntop(sap, buf, sizeof(buf)), p); + if (expret) + *expret = exp; + return fh; +} + +static void remove_all_clients(exportnode *e) +{ + struct groupnode *g, *ng; + + for (g = e->ex_groups; g; g = ng) { + ng = g->gr_next; + xfree(g->gr_name); + xfree(g); + } + e->ex_groups = NULL; +} + +static void free_exportlist(exports *elist) +{ + struct exportnode *e, *ne; + + for (e = *elist; e != NULL; e = ne) { + ne = e->ex_next; + remove_all_clients(e); + xfree(e->ex_dir); + xfree(e); + } + *elist = NULL; +} + +static void prune_clients(nfs_export *exp, struct exportnode *e) +{ + struct addrinfo *ai = NULL; + struct groupnode *c, **cp; + + cp = &e->ex_groups; + while ((c = *cp) != NULL) { + if (client_gettype(c->gr_name) == MCL_FQDN + && (ai = host_addrinfo(c->gr_name))) { + if (client_check(exp->m_client, ai)) { + *cp = c->gr_next; + xfree(c->gr_name); + xfree(c); + nfs_freeaddrinfo(ai); + continue; + } + nfs_freeaddrinfo(ai); + } + cp = &(c->gr_next); + } +} + +static exportnode *lookup_or_create_elist_entry(exports *elist, nfs_export *exp) +{ + exportnode *e; + + for (e = *elist; e != NULL; e = e->ex_next) { + if (!strcmp(exp->m_export.e_path, e->ex_dir)) + return e; + } + e = xmalloc(sizeof(*e)); + e->ex_next = *elist; + e->ex_groups = NULL; + e->ex_dir = xstrdup(exp->m_export.e_path); + *elist = e; + return e; +} + +static void insert_group(struct exportnode *e, char *newname) +{ + struct groupnode *g; + + for (g = e->ex_groups; g; g = g->gr_next) + if (!strcmp(g->gr_name, newname)) + return; + + g = xmalloc(sizeof(*g)); + g->gr_name = xstrdup(newname); + g->gr_next = e->ex_groups; + e->ex_groups = g; +} + +static exports +get_exportlist(void) +{ + static exports elist = NULL; + struct exportnode *e; + nfs_export *exp; + int i; + static unsigned int ecounter; + unsigned int acounter; + + acounter = auth_reload(); + if (elist && acounter == ecounter) + return elist; + + ecounter = acounter; + + free_exportlist(&elist); + + for (i = 0; i < MCL_MAXTYPES; i++) { + for (exp = exportlist[i].p_head; exp; exp = exp->m_next) { + /* Don't show pseudo exports */ + if (exp->m_export.e_flags & NFSEXP_V4ROOT) + continue; + e = lookup_or_create_elist_entry(&elist, exp); + + /* exports to "*" absorb any others */ + if (i == MCL_ANONYMOUS && e->ex_groups) { + remove_all_clients(e); + continue; + } + /* non-FQDN's absorb FQDN's they contain: */ + if (i != MCL_FQDN && e->ex_groups) + prune_clients(exp, e); + + if (exp->m_export.e_hostname[0] != '\0') + insert_group(e, exp->m_export.e_hostname); + } + } + + return elist; +} + +int vers; +int port = 0; +int descriptors = 0; + +inline static void +read_mountd_conf(char **argv) +{ + char *s; + int ttl; + + conf_init_file(NFS_CONFFILE); + + xlog_set_debug("mountd"); + manage_gids = conf_get_bool("mountd", "manage-gids", manage_gids); + descriptors = conf_get_num("mountd", "descriptors", descriptors); + port = conf_get_num("mountd", "port", port); + num_threads = conf_get_num("mountd", "threads", num_threads); + reverse_resolve = conf_get_bool("mountd", "reverse-lookup", reverse_resolve); + ha_callout_prog = conf_get_str("mountd", "ha-callout"); + if (conf_get_bool("mountd", "cache-use-ipaddr", 0)) + use_ipaddr = 2; + + s = conf_get_str("mountd", "state-directory-path"); + if (s && !state_setup_basedir(argv[0], s)) + exit(1); + + /* NOTE: following uses "nfsd" section of nfs.conf !!!! */ + if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(_rpcprotobits))) + NFSCTL_UDPSET(_rpcprotobits); + else + NFSCTL_UDPUNSET(_rpcprotobits); + if (conf_get_bool("nfsd", "tcp", NFSCTL_TCPISSET(_rpcprotobits))) + NFSCTL_TCPSET(_rpcprotobits); + else + NFSCTL_TCPUNSET(_rpcprotobits); + for (vers = 2; vers <= 4; vers++) { + char tag[20]; + sprintf(tag, "vers%d", vers); + if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(nfs_version, vers))) + NFSCTL_VERSET(nfs_version, vers); + else + NFSCTL_VERUNSET(nfs_version, vers); + } + + ttl = conf_get_num("mountd", "ttl", default_ttl); + if (ttl > 0) + default_ttl = ttl; +} + +int +main(int argc, char **argv) +{ + char *progname; + unsigned int listeners = 0; + int foreground = 0; + int c; + int ttl; + struct sigaction sa; + struct rlimit rlim; + + /* Set the basename */ + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; + + /* Initialize logging. */ + xlog_open(progname); + + /* Read in config setting */ + read_mountd_conf(argv); + + /* Parse the command line options and arguments. */ + opterr = 0; + while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != EOF) + switch (c) { + case 'g': + manage_gids = 1; + break; + case 'o': + descriptors = atoi(optarg); + if (descriptors <= 0) { + fprintf(stderr, "%s: bad descriptors: %s\n", + progname, optarg); + usage(progname, 1); + } + break; + case 'F': + foreground = 1; + break; + case 'd': + xlog_sconfig(optarg, 1); + break; + case 'H': /* PRC: specify a high-availability callout program */ + ha_callout_prog = optarg; + break; + case 'h': + usage(progname, 0); + break; + case 'P': /* XXX for nfs-server compatibility */ + case 'p': + port = atoi(optarg); + if (port <= 0 || port > 65535) { + fprintf(stderr, "%s: bad port number: %s\n", + progname, optarg); + usage(progname, 1); + } + break; + case 'N': + vers = atoi(optarg); + if (vers < 2 || vers > 4) { + fprintf(stderr, "%s: bad version number: %s\n", + argv[0], optarg); + usage(argv[0], 1); + } + nfs_version &= ~NFSVERSBIT(vers); + break; + case 'n': + NFSCTL_TCPUNSET(_rpcprotobits); + break; + case 'r': + reverse_resolve = 1; + break; + case 's': + if (!state_setup_basedir(argv[0], optarg)) + exit(1); + break; + case 't': + num_threads = atoi (optarg); + break; + case 'V': + vers = atoi(optarg); + if (vers < 2 || vers > 4) { + fprintf(stderr, "%s: bad version number: %s\n", + argv[0], optarg); + usage(argv[0], 1); + } + nfs_version |= NFSVERSBIT(vers); + break; + case 'v': + printf("%s version " VERSION "\n", progname); + exit(0); + case 'u': + NFSCTL_UDPUNSET(_rpcprotobits); + break; + case 'l': + xlog_sconfig("auth", 1); + break; + case 'i': + use_ipaddr = 2; + break; + case 'T': + ttl = atoi(optarg); + if (ttl <= 0) { + fprintf(stderr, "%s: bad ttl number of seconds: %s\n", + argv[0], optarg); + usage(argv[0], 1); + } + default_ttl = ttl; + break; + case 0: + break; + case '?': + default: + usage(progname, 1); + } + + /* No more arguments allowed. */ + if (optind != argc || !version_any()) { + fprintf(stderr, "%s: No protocol versions specified!\n", progname); + usage(progname, 1); + } + if (!setup_state_path_names(progname, ETAB, ETABTMP, ETABLCK, &etab)) + return 1; + if (!setup_state_path_names(progname, RMTAB, RMTABTMP, RMTABLCK, &rmtab)) + return 1; + + if (getrlimit (RLIMIT_NOFILE, &rlim) != 0) + fprintf(stderr, "%s: getrlimit (RLIMIT_NOFILE) failed: %s\n", + progname, strerror(errno)); + else { + /* glibc sunrpc code dies if getdtablesize > FD_SETSIZE */ + if ((descriptors == 0 && rlim.rlim_cur > FD_SETSIZE) || + descriptors > FD_SETSIZE) + descriptors = FD_SETSIZE; + if (descriptors) { + rlim.rlim_cur = descriptors; + if (setrlimit (RLIMIT_NOFILE, &rlim) != 0) { + fprintf(stderr, "%s: setrlimit (RLIMIT_NOFILE) failed: %s\n", + progname, strerror(errno)); + exit(1); + } + } + } + if (!foreground) xlog_stderr(0); + + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGPIPE, &sa, NULL); + /* WARNING: the following works on Linux and SysV, but not BSD! */ + sigaction(SIGCHLD, &sa, NULL); + + unregister_services(); + if (version2()) { + listeners += nfs_svc_create("mountd", MOUNTPROG, + MOUNTVERS, mount_dispatch, port); + listeners += nfs_svc_create("mountd", MOUNTPROG, + MOUNTVERS_POSIX, mount_dispatch, port); + } + if (version3()) + listeners += nfs_svc_create("mountd", MOUNTPROG, + MOUNTVERS_NFSV3, mount_dispatch, port); + if (version23() && listeners == 0) + xlog(L_WARNING, "mountd: No V2 or V3 listeners created!"); + + sa.sa_handler = killer; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sa.sa_handler = sig_hup; + sigaction(SIGHUP, &sa, NULL); + + if (!foreground) { + /* We first fork off a child. */ + if ((c = fork()) > 0) + exit(0); + if (c < 0) { + xlog(L_FATAL, "mountd: cannot fork: %s\n", + strerror(errno)); + } + /* Now we remove ourselves from the foreground. + Redirect stdin/stdout/stderr first. */ + { + int fd = open("/dev/null", O_RDWR); + (void) dup2(fd, 0); + (void) dup2(fd, 1); + (void) dup2(fd, 2); + if (fd > 2) (void) close(fd); + } + setsid(); + } + + /* silently bounds check num_threads */ + if (foreground) + num_threads = 1; + else if (num_threads < 1) + num_threads = 1; + else if (num_threads > MAX_THREADS) + num_threads = MAX_THREADS; + + /* Open cache channel files BEFORE forking so each upcall is + * only handled by one thread. Kernel provides locking for both + * read and write. + */ + cache_open(); + + if (cache_fork_workers("mountd", num_threads) == 0) { + /* We forked, waited, and now need to clean up */ + unregister_services(); + cleanup_lockfiles(); + free_state_path_names(&etab); + free_state_path_names(&rmtab); + xlog(L_NOTICE, "mountd: no more workers, exiting\n"); + exit(0); + } + + nfsd_path_init(); + v4clients_init(); + + xlog(L_NOTICE, "Version " VERSION " starting"); + my_svc_run(); + + xlog(L_ERROR, "RPC service loop terminated unexpectedly. Exiting...\n"); + unregister_services(); + free_state_path_names(&etab); + free_state_path_names(&rmtab); + exit(1); +} + +static void +usage(const char *prog, int n) +{ + fprintf(stderr, +"Usage: %s [-F|--foreground] [-h|--help] [-v|--version] [-d kind|--debug kind]\n" +" [-l|--log-auth] [-i|--cache-use-ipaddr] [-T|--ttl ttl]\n" +" [-o num|--descriptors num]\n" +" [-p|--port port] [-V version|--nfs-version version]\n" +" [-N version|--no-nfs-version version] [-n|--no-tcp]\n" +" [-H prog |--ha-callout prog] [-r |--reverse-lookup]\n" +" [-s|--state-directory-path path] [-g|--manage-gids]\n" +" [-t num|--num-threads=num] [-u|--no-udp]\n", prog); + exit(n); +} |