nfsd.c revision cee8668251d5ec44fd1c6d6ddeb9c1d1821a57d2
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/* LINTLIBRARY */
/* PROTOLIB1 */
#pragma ident "%Z%%M% %I% %E% SMI"
/* NFS server */
#include <syslog.h>
#include <tiuser.h>
#include <errno.h>
#include <thread.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <signal.h>
#include <netconfig.h>
#include <netdir.h>
#include <string.h>
#include <unistd.h>
#include <stropts.h>
#include <poll.h>
#include <priv_utils.h>
#include <deflt.h>
#include <rpcsvc/daemon_utils.h>
#include <rpcsvc/nfs4_prot.h>
#include <libnvpair.h>
#include "nfs_tbind.h"
#include "thrpool.h"
/* quiesce requests will be ignored if nfs_server_vers_max < QUIESCE_VERSMIN */
#define QUIESCE_VERSMIN 4
/* DSS: distributed stable storage */
#define DSS_VERSMIN 4
static int nfssvcpool(int maxservers);
static void usage(void);
extern int _nfssys(int, void *);
/* signal handlers */
static void sigflush(int);
static void quiesce(int);
static char *MyName;
/* static NETSELDECL(defaultprotos)[] = { NC_UDP, NC_TCP, NULL }; */
/*
* The following are all globals used by routines in nfs_tbind.c.
*/
int num_servers; /* used by cots_listen_event() */
/* used by cots_listen_event() */
/*
* Start with the defaults (min == 2, max == 3). We have the
* capability of starting vers=4 but only if the user requests it.
*/
/*
* Set the default for server delegation enablement and set per
*/
int
{
char *dir = "/";
int allflag = 0;
int df_allflag = 0;
int opt_cnt = 0;
int maxservers_set = 0;
int logmaxservers = 0;
int pid;
int i;
char *df_provider = (char *)NULL;
char *defval;
uint_t dss_npaths = 0;
char **dss_pathnames = NULL;
/*
* Initializations that require more privileges than we need to run.
*/
svcsetprio();
" sufficient privileges\n", av[0]);
exit(1);
}
/*
* Read in the values from config file first before we check
* commandline options so the options override the file.
*/
errno = 0;
if (errno != 0) {
max_conns_allowed = -1;
}
}
errno = 0;
if (errno != 0) {
listen_backlog = 32;
}
}
opt_cnt++;
df_allflag = 1;
}
}
opt_cnt++;
}
errno = 0;
if (errno != 0) {
maxservers = 1;
} else {
maxservers_set = 1;
}
}
errno = 0;
if (errno != 0) {
}
}
errno = 0;
if (errno != 0) {
}
}
}
}
/* close defaults file */
}
/*
* Conflict options error messages.
*/
if (opt_cnt > 1) {
"the following options can be specified\n"
"\tNFSD_PROTOCOL=ALL\n"
"\tNFSD_PROTOCOL=protocol\n"
"\tNFSD_DEVICE=device\n\n");
usage();
}
opt_cnt = 0;
switch (i) {
case 'a':
df_provider = NULL;
allflag = 1;
opt_cnt++;
break;
case 'c':
break;
case 'p':
df_allflag = 0;
opt_cnt++;
break;
/*
* DSS: NFSv4 distributed stable storage.
*
* This is a Contracted Project Private interface, for
* the sole use of Sun Cluster HA-NFS. See PSARC/2006/313.
*/
case 's':
/* first "-s" option encountered? */
if (dss_pathnames == NULL) {
/*
* Allocate maximum possible space
* required given cmdline arg count;
* "-s <path>" consumes two args.
*/
if (dss_pathnames == NULL) {
"dss paths malloc failed\n",
av[0]);
exit(1);
}
}
dss_npaths++;
} else {
"%s: -s pathname too long.\n", av[0]);
}
break;
case 't':
df_allflag = 0;
opt_cnt++;
break;
case 'l':
break;
case '?':
usage();
/* NOTREACHED */
}
}
/*
* Conflict options error messages.
*/
if (opt_cnt > 1) {
"the following options can be specified\n"
"on the command line:\n"
"\t-a\n"
"\t-p protocol\n"
"\t-t transport\n\n");
usage();
}
if (nfs_server_vers_max == NFS_V4) {
if (nfs_server_vers_min == NFS_V4) {
"NFS version 4 is not supported "
"with the UDP protocol. Exiting\n");
"NFS version 4 is not supported "
"with the UDP protocol. Exiting\n");
exit(3);
} else {
"NFS version 4 is not supported "
"with the UDP protocol.\n");
}
}
}
/*
* If there is exactly one more argument, it is the number of
* servers.
*/
maxservers_set = 1;
}
/*
* If there are two or more arguments, then this is a usage error.
*/
usage();
/*
*/
else if ((nfs_server_vers_min > nfs_server_vers_max) ||
(nfs_server_vers_min < NFS_VERSMIN) ||
usage();
/*
* There are no additional arguments, and we haven't set maxservers
* explicitly via the config file, we use a default number of
* servers. We will log this.
*/
else if (maxservers_set == 0)
logmaxservers = 1;
/*
* Basic Sanity checks on options
*
* max_conns_allowed must be positive, except for the special
* value of -1 which is used internally to mean unlimited, -1 isn't
* documented but we allow it anyway.
*
* maxservers must be positive
* listen_backlog must be positive or zero
*/
(listen_backlog < 0) || (maxservers <= 0)) {
usage();
}
/*
* Set current dir to server root
*/
exit(1);
}
#ifndef DEBUG
/*
* Background
*/
if (pid < 0) {
perror("nfsd: fork");
exit(1);
}
if (pid != 0)
exit(0);
/*
* standard input, output, and error, and detach from
* controlling terminal.
*/
closefrom(0);
(void) dup(1);
(void) setsid();
#endif
/*
* establish our lock on the lock file and write our pid to it.
* exit if some other process holds the lock, or if there's any
*/
switch (pid) {
case 0:
break;
case -1:
exit(2);
default:
/* daemon was already running */
exit(0);
}
/*
* If we've been given a list of paths to be used for distributed
* stable storage, and provided we're going to run a version
* that supports it, setup the DSS paths.
*/
exit(1);
}
}
if (logmaxservers) {
"Number of servers not specified. Using default of %d.",
}
/*
* Make sure to unregister any previous versions in case the
* user is reconfiguring the server in interesting ways.
*/
/*
* Set up kernel RPC thread pool for the NFS server.
*/
if (nfssvcpool(maxservers)) {
"Can't set up kernel NFS service: %m. Exiting");
exit(1);
}
/*
* Set up blocked thread to do LWP creation on behalf of the kernel.
*/
if (svcwait(NFS_SVCPOOL_ID)) {
"Can't set up NFS pool creator: %m, Exiting");
exit(1);
}
/*
* RDMA start and stop thread.
* Per pool RDMA listener creation and
* destructor thread.
*
* start rdma services and block in the kernel.
*/
"Can't set up RDMA creator thread : %m.");
}
/*
* Build a protocol block list for registration.
*/
/* XXX - this needs work to get the version just right */
if (allflag) {
exit(1);
} else if (proto) {
/* there's more than one match for the same protocol */
goto done;
}
protoFound = TRUE;
}
}
(void) endnetconfig(nc);
if (protoFound == FALSE)
for protocol %s", proto);
} else if (provider)
else {
for (providerp = defaultproviders;
}
}
done:
if (num_fds == 0) {
"Could not start NFS service for any protocol. Exiting");
exit(1);
}
/*
* Get rid of unneeded privileges.
*/
/*
* Poll for non-data control events on the transport descriptors.
*/
/*
* If we get here, something failed in poll_for_action().
*/
return (1);
}
static int
nfssvcpool(int maxservers)
{
struct svcpool_args npa;
npa.max_same_xprt = 0;
}
/*
* Establish NFS service thread.
*/
static int
{
struct nfs_svc_args nsa;
/*
* If no version left, silently do nothing, previous
* checks will have assured at least TCP is available.
*/
return (0);
} else {
}
}
static void
usage(void)
{
"usage: %s [ -a ] [ -c max_conns ] [ -p protocol ] [ -t transport ] ", MyName);
"\twhere -a causes <nservers> to be started on each appropriate transport,\n");
"\tmax_conns is the maximum number of concurrent connections allowed,\n");
"\ttransport is a transport provider name (i.e. device),\n");
"\tlisten_backlog is the TCP listen backlog,\n");
"\tand <nservers> must be a decimal number > zero.\n");
exit(1);
}
/*
* Issue nfssys system call to flush all logging buffers asynchronously.
*
* NOTICE: It is extremely important to flush NFS logging buffers when
* nfsd exits. When the system is halted or rebooted nfslogd
* may not have an opportunity to flush the buffers.
*/
static void
{
struct nfsl_flush_args nfa;
}
/*
* SIGTERM handler.
* Flush logging buffers and exit.
*/
static void
{
nfsl_flush();
exit(0);
}
/*
* SIGUSR1 handler.
*
* Request that server quiesce, then (nfsd) exit. For subsequent warm start.
*
* This is a Contracted Project Private interface, for the sole use
* of Sun Cluster HA-NFS. See PSARC/2004/497.
*
* Equivalent to SIGTERM handler if nfs_server_vers_max < QUIESCE_VERSMIN.
*/
static void
{
int error;
int id = NFS_SVCPOOL_ID;
if (nfs_server_vers_max >= QUIESCE_VERSMIN) {
/* Request server quiesce at next shutdown */
if (error) {
"_nfssys(NFS4_SVC_REQUEST_QUIESCE) failed: %s",
return;
}
}
/* Flush logging buffers */
nfsl_flush();
exit(0);
}
/*
* DSS: distributed stable storage.
* Create leaf directories as required, keeping an eye on path
* lengths. Calls exit(1) on failure.
* The pathnames passed in must already exist, and must be writeable by nfsd.
* Note: the leaf directories under NFS4_VAR_DIR are not created here;
* they're created at pkg install.
*/
static void
{
int i;
/*
* Create the temporary storage used by dss_mkleafdir() here,
* rather than in that function, so that it only needs to be
* done once, rather than once for each call. Too big to put
* on the function's stack.
*/
exit(1);
}
for (i = 0; i < npaths; i++) {
char *p = pathnames[i];
}
}
/*
* Create "leaf" in "dir" (which must already exist).
* leaf: should start with a '/'
*/
static void
{
/* MAXPATHLEN includes the terminating NUL */
exit(1);
}
/* the directory may already exist: that's OK */
exit(1);
}
}
/*
* Create the storage dirs, and pass the path list to the kernel.
* This requires the nfssrv module to be loaded; the _nfssys() syscall
* will fail ENOTSUP if it is not.
* Use libnvpair(3LIB) to pass the data to the kernel.
*/
static int
{
char *bufp;
if (npaths > 1) {
/*
* We need to remove duplicate paths; this might be user error
* in the general case, but HA-NFSv4 can also cause this.
* Sort the pathnames array, and NULL out duplicates,
* then write the non-NULL entries to a new array.
* Sorting will also allow the kernel to optimise its searches.
*/
/* now NULL out any duplicates */
i = 0; j = 1; nskipped = 0;
while (j < npaths) {
j++;
nskipped++;
continue;
}
/* skip i over any of its NULLed duplicates */
i = j++;
}
/* finally, write the non-NULL entries to a new array */
if (nskipped > 0) {
int nreal;
char **tmp_pathnames;
if (tmp_pathnames == NULL) {
exit(1);
}
for (i = 0, j = 0; i < npaths; i++)
tmp_pathnames[j++] = pathnames[i];
}
}
/* Create directories to store the distributed state files */
/* Create the name-value pair list */
if (error) {
return (1);
}
/* Add the pathnames array as a single name-value pair */
if (error) {
return (1);
}
/*
* Pack list into contiguous memory, for passing to kernel.
* nvlist_pack() will allocate the memory for the buffer,
* which we should free() when no longer needed.
*/
if (error) {
return (1);
}
/* Now we have the packed buffer, we no longer need the list */
/*
* Let the kernel know in advance how big the buffer is.
* NOTE: we cannot just pass buflen, since size_t is a long, and
* thus a different size between ILP32 userland and LP64 kernel.
* Use an int for the transfer, since that should be big enough;
* this is a no-op at the moment, here, since nfsd is 32-bit, but
* that could change.
*/
if (error) {
"_nfssys(NFS4_DSS_SETPATHS_SIZE) failed: %s. ",
return (1);
}
/* Pass the packed buffer to the kernel */
if (error) {
return (1);
}
/*
* The kernel has now unpacked the buffer and extracted the
* pathnames array, we no longer need the buffer.
*/
return (0);
}
/*
* Quick sort string compare routine, for qsort.
* Needed to make arg types correct.
*/
int
{
}