/* * 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 */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }