blob: 73d6a92800bd4694f98ce9d499ff480f4e3ef668 [file] [log] [blame]
/*
* 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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "nfslib.h"
#include "nfssvc.h"
#include "xlog.h"
#ifndef NFSD_NPROC
#define NFSD_NPROC 8
#endif
static void usage(const char *);
static struct option longopts[] =
{
{ "host", 1, 0, 'H' },
{ "help", 0, 0, 'h' },
{ "no-nfs-version", 1, 0, 'N' },
{ "nfs-version", 1, 0, 'V' },
{ "no-tcp", 0, 0, 'T' },
{ "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 }
};
/* given a family and ctlbits, disable any that aren't listed in netconfig */
#ifdef HAVE_LIBTIRPC
static void
nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6)
{
struct netconfig *nconf;
unsigned int *famproto;
void *handle;
xlog(D_GENERAL, "Checking netconfig for visible protocols.");
handle = setnetconfig();
while((nconf = getnetconfig(handle))) {
if (!(nconf->nc_flag & NC_VISIBLE))
continue;
if (!strcmp(nconf->nc_protofmly, NC_INET))
famproto = proto4;
else if (!strcmp(nconf->nc_protofmly, NC_INET6))
famproto = proto6;
else
continue;
if (!strcmp(nconf->nc_proto, NC_TCP))
NFSCTL_TCPSET(*famproto);
else if (!strcmp(nconf->nc_proto, NC_UDP))
NFSCTL_UDPSET(*famproto);
xlog(D_GENERAL, "Enabling %s %s.", nconf->nc_protofmly,
nconf->nc_proto);
}
endnetconfig(handle);
return;
}
#else /* HAVE_LIBTIRPC */
static void
nfsd_enable_protos(unsigned int *proto4, unsigned int *proto6)
{
/* Enable all IPv4 protocols if no TIRPC support */
*proto4 = NFSCTL_ALLBITS;
*proto6 = 0;
}
#endif /* HAVE_LIBTIRPC */
int
main(int argc, char **argv)
{
int count = NFSD_NPROC, c, i, error = 0, portnum = 0, fd, found_one;
char *p, *progname, *port, *rdma_port = NULL;
char **haddr = NULL;
unsigned int hcounter = 0;
int socket_up = 0;
unsigned int minorvers = 0;
unsigned int minorversset = 0;
unsigned int versbits = NFSCTL_VERDEFAULT;
unsigned int protobits = NFSCTL_ALLBITS;
unsigned int proto4 = 0;
unsigned int proto6 = 0;
int grace = -1;
int lease = -1;
progname = strdup(basename(argv[0]));
if (!progname) {
fprintf(stderr, "%s: unable to allocate memory.\n", argv[0]);
exit(1);
}
port = strdup("nfs");
if (!port) {
fprintf(stderr, "%s: unable to allocate memory.\n", progname);
exit(1);
}
haddr = malloc(sizeof(char *));
if (!haddr) {
fprintf(stderr, "%s: unable to allocate memory.\n", progname);
exit(1);
}
haddr[0] = NULL;
xlog_syslog(0);
xlog_stderr(1);
while ((c = getopt_long(argc, argv, "dH:hN:V:p:P:sTUrG:L:", longopts, NULL)) != EOF) {
switch(c) {
case 'd':
xlog_config(D_ALL, 1);
break;
case 'H':
if (hcounter) {
haddr = realloc(haddr, sizeof(char*) * hcounter+1);
if(!haddr) {
fprintf(stderr, "%s: unable to allocate "
"memory.\n", progname);
exit(1);
}
}
haddr[hcounter] = strdup(optarg);
if (!haddr[hcounter]) {
fprintf(stderr, "%s: unable to allocate "
"memory.\n", progname);
exit(1);
}
hcounter++;
break;
case 'P': /* XXX for nfs-server compatibility */
case 'p':
/* only the last -p option has any effect */
portnum = atoi(optarg);
if (portnum <= 0 || portnum > 65535) {
fprintf(stderr, "%s: bad port number: %s\n",
progname, optarg);
usage(progname);
}
free(port);
port = strdup(optarg);
if (!port) {
fprintf(stderr, "%s: unable to allocate "
"memory.\n", progname);
exit(1);
}
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 > NFS4_MAXMINOR) {
fprintf(stderr, "%s: unsupported minor version\n", optarg);
exit(1);
}
NFSCTL_VERSET(minorversset, i);
NFSCTL_VERUNSET(minorvers, i);
break;
}
case 3:
case 2:
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 > NFS4_MAXMINOR) {
fprintf(stderr, "%s: unsupported minor version\n", optarg);
exit(1);
}
NFSCTL_VERSET(minorversset, i);
NFSCTL_VERSET(minorvers, i);
break;
}
case 3:
case 2:
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_TCPUNSET(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);
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);
nfsd_enable_protos(&proto4, &proto6);
if (!NFSCTL_TCPISSET(protobits)) {
NFSCTL_TCPUNSET(proto4);
NFSCTL_TCPUNSET(proto6);
}
if (!NFSCTL_UDPISSET(protobits)) {
NFSCTL_UDPUNSET(proto4);
NFSCTL_UDPUNSET(proto6);
}
/* 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(proto4) &&
!NFSCTL_TCPISSET(proto6)) {
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);
if (grace > 0)
nfssvc_set_time("grace", grace);
if (lease > 0)
nfssvc_set_time("lease", lease);
i = 0;
do {
error = nfssvc_set_sockets(AF_INET, proto4, haddr[i], port);
if (!error)
socket_up = 1;
#ifdef IPV6_SUPPORTED
error = nfssvc_set_sockets(AF_INET6, proto6, haddr[i], port);
if (!error)
socket_up = 1;
#endif /* IPV6_SUPPORTED */
} 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(portnum, count)) < 0)
xlog(L_ERROR, "error starting threads: errno %d (%m)", errno);
out:
free(port);
for(i=0; i < hcounter; i++)
free(haddr[i]);
free(haddr);
free(progname);
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|--no-tcp] [-U|--no-udp] [-r|--rdma=]\n"
" [-G|--grace-time secs] [-L|--leasetime secs] nrservs\n",
prog);
exit(2);
}