diff options
Diffstat (limited to '')
-rw-r--r-- | utils/nfsd/nfsd.c | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c new file mode 100644 index 0000000..249df00 --- /dev/null +++ b/utils/nfsd/nfsd.c @@ -0,0 +1,441 @@ +/* + * nfsd + * + * This is the user level part of nfsd. This is very primitive, because + * all the work is now done in the kernel module. + * + * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> +#include <netdb.h> +#include <libgen.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sched.h> + +#include "conffile.h" +#include "nfslib.h" +#include "nfssvc.h" +#include "xlog.h" +#include "xcommon.h" + +#ifndef NFSD_NPROC +#define NFSD_NPROC 8 +#endif + +static void usage(const char *); + +static struct option longopts[] = +{ + { "host", 1, 0, 'H' }, + { "scope", 1, 0, 'S'}, + { "help", 0, 0, 'h' }, + { "no-nfs-version", 1, 0, 'N' }, + { "nfs-version", 1, 0, 'V' }, + { "tcp", 0, 0, 't' }, + { "no-tcp", 0, 0, 'T' }, + { "udp", 0, 0, 'u' }, + { "no-udp", 0, 0, 'U' }, + { "port", 1, 0, 'P' }, + { "port", 1, 0, 'p' }, + { "debug", 0, 0, 'd' }, + { "syslog", 0, 0, 's' }, + { "rdma", 2, 0, 'R' }, + { "grace-time", 1, 0, 'G'}, + { "lease-time", 1, 0, 'L'}, + { NULL, 0, 0, 0 } +}; + +inline static void +read_nfsd_conf(void) +{ + conf_init_file(NFS_CONFFILE); + xlog_set_debug("nfsd"); +} + +int +main(int argc, char **argv) +{ + int count = NFSD_NPROC, c, i, error = 0, portnum, fd, found_one; + char *p, *progname, *port, *rdma_port = NULL; + char **haddr = NULL; + char *scope = NULL; + int hcounter = 0; + struct conf_list *hosts; + int socket_up = 0; + unsigned int minorvers = NFSCTL_MINDEFAULT; + unsigned int minorversset = NFSCTL_MINDEFAULT; + unsigned int minormask = 0; + unsigned int versbits = NFSCTL_VERDEFAULT; + unsigned int protobits = NFSCTL_PROTODEFAULT; + int grace = -1; + int lease = -1; + int force4dot0 = 0; + + progname = basename(argv[0]); + haddr = xmalloc(sizeof(char *)); + haddr[0] = NULL; + + xlog_syslog(0); + xlog_stderr(1); + + /* Read in config setting */ + read_nfsd_conf(); + + nfssvc_get_minormask(&minormask); + + count = conf_get_num("nfsd", "threads", count); + grace = conf_get_num("nfsd", "grace-time", grace); + lease = conf_get_num("nfsd", "lease-time", lease); + port = conf_get_str("nfsd", "port"); + if (!port) + port = "nfs"; + if (conf_get_bool("nfsd", "rdma", false)) { + rdma_port = conf_get_str("nfsd", "rdma-port"); + if (!rdma_port) + rdma_port = "nfsrdma"; + } + /* backward compatibility - nfs.conf used to set rdma port directly */ + if (!rdma_port) + rdma_port = conf_get_str("nfsd", "rdma"); + if (conf_get_bool("nfsd", "udp", NFSCTL_UDPISSET(protobits))) + NFSCTL_UDPSET(protobits); + else + NFSCTL_UDPUNSET(protobits); + if (conf_get_bool("nfsd", "tcp", NFSCTL_TCPISSET(protobits))) + NFSCTL_TCPSET(protobits); + else + NFSCTL_TCPUNSET(protobits); + for (i = 2; i <= 4; i++) { + char tag[20]; + sprintf(tag, "vers%d", i); + if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(versbits, i))) { + NFSCTL_VERSET(versbits, i); + if (i == 4) + minorvers = minorversset = minormask; + } else { + NFSCTL_VERUNSET(versbits, i); + if (i == 4) { + minorvers = 0; + minorversset = minormask; + } + } + } + + /* We assume the kernel will default all minor versions to 'on', + * and allow the config file to disable some. + */ + for (i = NFS4_MINMINOR; i <= NFS4_MAXMINOR; i++) { + char tag[20]; + sprintf(tag, "vers4.%d", i); + /* The default for minor version support is to let the + * kernel decide. We could ask the kernel what that choice + * will be, but that is needlessly complex. + * Instead, perform a config-file lookup using each of the + * two possible default. If the result is different from the + * default, then impose that value, else don't make a change + * (i.e. don't set the bit in minorversset). + */ + if (!conf_get_bool("nfsd", tag, 1)) { + NFSCTL_MINORSET(minorversset, i); + NFSCTL_MINORUNSET(minorvers, i); + if (i == 0) + force4dot0 = 1; + } + if (conf_get_bool("nfsd", tag, 0)) { + NFSCTL_MINORSET(minorversset, i); + NFSCTL_MINORSET(minorvers, i); + if (i == 0) + force4dot0 = 1; + } + } + + hosts = conf_get_list("nfsd", "host"); + if (hosts && hosts->cnt) { + struct conf_list_node *n; + haddr = realloc(haddr, sizeof(char*) * hosts->cnt); + TAILQ_FOREACH(n, &(hosts->fields), link) { + haddr[hcounter] = n->field; + hcounter++; + } + } + scope = conf_get_str("nfsd", "scope"); + + while ((c = getopt_long(argc, argv, "dH:S:hN:V:p:P:stTuUrG:L:", longopts, NULL)) != EOF) { + switch(c) { + case 'd': + xlog_config(D_ALL, 1); + break; + case 'H': + if (hosts) { + hosts = NULL; + hcounter = 0; + } + if (hcounter) { + haddr = realloc(haddr, sizeof(char*) * hcounter+1); + if(!haddr) { + fprintf(stderr, "%s: unable to allocate " + "memory.\n", progname); + exit(1); + } + } + haddr[hcounter] = optarg; + hcounter++; + break; + case 'S': + scope = optarg; + break; + case 'P': /* XXX for nfs-server compatibility */ + case 'p': + /* only the last -p option has any effect */ + port = optarg; + break; + case 'r': + rdma_port = "nfsrdma"; + break; + case 'R': /* --rdma */ + if (optarg) + rdma_port = optarg; + else + rdma_port = "nfsrdma"; + break; + + case 'N': + switch((c = strtol(optarg, &p, 0))) { + case 4: + if (*p == '.') { + int i = atoi(p+1); + if (i < 0 || i > NFS4_MAXMINOR) { + fprintf(stderr, "%s: unsupported minor version\n", optarg); + exit(1); + } + NFSCTL_MINORSET(minorversset, i); + NFSCTL_MINORUNSET(minorvers, i); + if (i == 0) + force4dot0 = 1; + if (minorvers != 0) + break; + } else { + minorvers = 0; + minorversset = minormask; + } + /* FALLTHRU */ + case 3: + NFSCTL_VERUNSET(versbits, c); + break; + default: + fprintf(stderr, "%s: Unsupported version\n", optarg); + exit(1); + } + break; + case 'V': + switch((c = strtol(optarg, &p, 0))) { + case 4: + if (*p == '.') { + int i = atoi(p+1); + if (i < 0 || i > NFS4_MAXMINOR) { + fprintf(stderr, "%s: unsupported minor version\n", optarg); + exit(1); + } + NFSCTL_MINORSET(minorversset, i); + NFSCTL_MINORSET(minorvers, i); + if (i == 0) + force4dot0 = 1; + } else + minorvers = minorversset = minormask; + /* FALLTHRU */ + case 3: + NFSCTL_VERSET(versbits, c); + break; + default: + fprintf(stderr, "%s: Unsupported version\n", optarg); + exit(1); + } + break; + case 's': + xlog_syslog(1); + xlog_stderr(0); + break; + case 't': + NFSCTL_TCPSET(protobits); + break; + case 'T': + NFSCTL_TCPUNSET(protobits); + break; + case 'u': + NFSCTL_UDPSET(protobits); + break; + case 'U': + NFSCTL_UDPUNSET(protobits); + break; + case 'G': + grace = strtol(optarg, &p, 0); + if (*p || grace <= 0) { + fprintf(stderr, "%s: Unrecognized grace time.\n", optarg); + exit(1); + } + break; + case 'L': + lease = strtol(optarg, &p, 0); + if (*p || lease <= 0) { + fprintf(stderr, "%s: Unrecognized lease time.\n", optarg); + exit(1); + } + break; + default: + fprintf(stderr, "Invalid argument: '%c'\n", c); + /* FALLTHRU */ + case 'h': + usage(progname); + } + } + + if (optind < argc) { + if ((count = atoi(argv[optind])) < 0) { + /* insane # of servers */ + fprintf(stderr, + "%s: invalid server count (%d), using 1\n", + argv[0], count); + count = 1; + } else if (count == 0) { + /* + * don't bother setting anything else if the threads + * are coming down anyway. + */ + socket_up = 1; + goto set_threads; + } + } + + xlog_open(progname); + + portnum = strtol(port, &p, 0); + if (!*p && (portnum <= 0 || portnum > 65535)) { + /* getaddrinfo will catch other errors, but not + * out-of-range numbers. + */ + xlog(L_ERROR, "invalid port number: %s", port); + exit(1); + } + + /* make sure that at least one version is enabled */ + found_one = 0; + for (c = NFSD_MINVERS; c <= NFSD_MAXVERS; c++) { + if (NFSCTL_VERISSET(versbits, c)) + found_one = 1; + } + if (!found_one) { + xlog(L_ERROR, "no version specified"); + exit(1); + } + + if (NFSCTL_VERISSET(versbits, 4) && + !NFSCTL_TCPISSET(protobits)) { + xlog(L_ERROR, "version 4 requires the TCP protocol"); + exit(1); + } + + if (chdir(NFS_STATEDIR)) { + xlog(L_ERROR, "chdir(%s) failed: %m", NFS_STATEDIR); + exit(1); + } + + /* make sure nfsdfs is mounted if it's available */ + nfssvc_mount_nfsdfs(progname); + + /* can only change number of threads if nfsd is already up */ + if (nfssvc_inuse()) { + socket_up = 1; + goto set_threads; + } + + /* + * Must set versions before the fd's so that the right versions get + * registered with rpcbind. Note that on older kernels w/o the right + * interfaces, these are a no-op. + * Timeouts must also be set before ports are created else we get + * EBUSY. + */ + nfssvc_setvers(versbits, minorvers, minorversset, force4dot0); + if (grace > 0) + nfssvc_set_time("grace", grace); + if (lease > 0) + nfssvc_set_time("lease", lease); + + if (scope) { + if (unshare(CLONE_NEWUTS) < 0 || + sethostname(scope, strlen(scope)) < 0) { + xlog(L_ERROR, "Unable to set server scope: %m"); + error = -1; + goto out; + } + } + i = 0; + do { + error = nfssvc_set_sockets(protobits, haddr[i], port); + if (!error) + socket_up = 1; + } while (++i < hcounter); + + if (rdma_port) { + error = nfssvc_set_rdmaport(rdma_port); + if (!error) + socket_up = 1; + } +set_threads: + /* don't start any threads if unable to hand off any sockets */ + if (!socket_up) { + xlog(L_ERROR, "unable to set any sockets for nfsd"); + goto out; + } + error = 0; + + /* + * KLUDGE ALERT: + * Some kernels let nfsd kernel threads inherit open files + * from the program that spawns them (i.e. us). So close + * everything before spawning kernel threads. --Chip + */ + fd = open("/dev/null", O_RDWR); + if (fd == -1) + xlog(L_ERROR, "Unable to open /dev/null: %m"); + else { + /* switch xlog output to syslog since stderr is being closed */ + xlog_syslog(1); + xlog_stderr(0); + (void) dup2(fd, 0); + (void) dup2(fd, 1); + (void) dup2(fd, 2); + } + closeall(3); + + if ((error = nfssvc_threads(count)) < 0) + xlog(L_ERROR, "error starting threads: errno %d (%m)", errno); +out: + free(haddr); + return (error != 0); +} + +static void +usage(const char *prog) +{ + fprintf(stderr, "Usage:\n" + "%s [-d|--debug] [-H hostname] [-p|-P|--port port]\n" + " [-N|--no-nfs-version version] [-V|--nfs-version version]\n" + " [-s|--syslog] [-t|--tcp] [-T|--no-tcp] [-u|--udp] [-U|--no-udp]\n" + " [-r|--rdma=] [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n", + prog); + exit(2); +} |