blob: 1cda1e59ca0521b4b6203de8b5d33edb94dc1559 [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"
/*
* IPv6 support for nfsd was finished before some of the other daemons (mountd
* and statd in particular). That could be a problem in the future if someone
* were to boot a kernel that supports IPv6 serving with an older nfs-utils. For
* now, hardcode the IPv6 switch into the off position until the other daemons
* are functional.
*/
#undef IPV6_SUPPORTED
static void usage(const char *);
static struct option longopts[] =
{
{ "host", 1, 0, 'H' },
{ "help", 0, 0, 'h' },
{ "no-nfs-version", 1, 0, 'N' },
{ "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' },
{ 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 = 1, c, error = 0, portnum = 0, fd, found_one;
char *p, *progname, *port;
char *haddr = NULL;
int socket_up = 0;
int minorvers4 = NFSD_MAXMINORVERS4; /* nfsv4 minor version */
unsigned int versbits = NFSCTL_ALLBITS;
unsigned int protobits = NFSCTL_ALLBITS;
unsigned int proto4 = 0;
unsigned int proto6 = 0;
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);
}
xlog_syslog(0);
xlog_stderr(1);
while ((c = getopt_long(argc, argv, "dH:hN:p:P:sTU", longopts, NULL)) != EOF) {
switch(c) {
case 'd':
xlog_config(D_ALL, 1);
break;
case 'H':
/*
* for now, this only handles one -H option. Use the
* last one specified.
*/
free(haddr);
haddr = strdup(optarg);
if (!haddr) {
fprintf(stderr, "%s: unable to allocate "
"memory.\n", progname);
exit(1);
}
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 'N':
switch((c = strtol(optarg, &p, 0))) {
case 4:
if (*p == '.') {
minorvers4 = -atoi(p + 1);
break;
}
case 3:
case 2:
NFSCTL_VERUNSET(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;
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);
}
/* 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.
*/
nfssvc_setvers(versbits, minorvers4);
error = nfssvc_set_sockets(AF_INET, proto4, haddr, port);
if (!error)
socket_up = 1;
#ifdef IPV6_SUPPORTED
error = nfssvc_set_sockets(AF_INET6, proto6, haddr, port);
if (!error)
socket_up = 1;
#endif /* IPV6_SUPPORTED */
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);
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|--no-nfs-version version ] [-s|--syslog] [-T|--no-tcp] [-U|--no-udp] nrservs\n",
prog);
exit(2);
}