xref: /illumos-gate/usr/src/cmd/fs.d/nfs/nfsd/nfsd.c (revision 32885d593baf8bac788fa78885893a51b3ad0f28)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T		*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 /* LINTLIBRARY */
40 /* PROTOLIB1 */
41 
42 #pragma ident	"%Z%%M%	%I%	%E% SMI"
43 
44 /* NFS server */
45 
46 #include <sys/param.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <syslog.h>
50 #include <tiuser.h>
51 #include <rpc/rpc.h>
52 #include <errno.h>
53 #include <thread.h>
54 #include <sys/resource.h>
55 #include <sys/time.h>
56 #include <sys/file.h>
57 #include <nfs/nfs.h>
58 #include <nfs/nfs_acl.h>
59 #include <nfs/nfssys.h>
60 #include <stdio.h>
61 #include <stdio_ext.h>
62 #include <stdlib.h>
63 #include <signal.h>
64 #include <netconfig.h>
65 #include <netdir.h>
66 #include <string.h>
67 #include <unistd.h>
68 #include <stropts.h>
69 #include <sys/tihdr.h>
70 #include <poll.h>
71 #include <priv_utils.h>
72 #include <sys/tiuser.h>
73 #include <netinet/tcp.h>
74 #include <deflt.h>
75 #include <rpcsvc/daemon_utils.h>
76 #include <rpcsvc/nfs4_prot.h>
77 #include <libnvpair.h>
78 #include "nfs_tbind.h"
79 #include "thrpool.h"
80 
81 /* quiesce requests will be ignored if nfs_server_vers_max < QUIESCE_VERSMIN */
82 #define	QUIESCE_VERSMIN	4
83 /* DSS: distributed stable storage */
84 #define	DSS_VERSMIN	4
85 
86 static	int	nfssvc(int, struct netbuf, struct netconfig *);
87 static	int	nfssvcpool(int maxservers);
88 static	int	dss_init(uint_t npaths, char **pathnames);
89 static	void	dss_mkleafdirs(uint_t npaths, char **pathnames);
90 static	void	dss_mkleafdir(char *dir, char *leaf, char *path);
91 static	void	usage(void);
92 int		qstrcmp(const void *s1, const void *s2);
93 
94 extern	int	_nfssys(int, void *);
95 
96 /* signal handlers */
97 static void sigflush(int);
98 static void quiesce(int);
99 
100 static	char	*MyName;
101 static	NETSELDECL(defaultproviders)[] = { "/dev/tcp6", "/dev/tcp", "/dev/udp",
102 					    "/dev/udp6", NULL };
103 /* static	NETSELDECL(defaultprotos)[] =	{ NC_UDP, NC_TCP, NULL }; */
104 /*
105  * The following are all globals used by routines in nfs_tbind.c.
106  */
107 size_t	end_listen_fds;		/* used by conn_close_oldest() */
108 size_t	num_fds = 0;		/* used by multiple routines */
109 int	listen_backlog = 32;	/* used by bind_to_{provider,proto}() */
110 int	num_servers;		/* used by cots_listen_event() */
111 int	(*Mysvc)(int, struct netbuf, struct netconfig *) = nfssvc;
112 				/* used by cots_listen_event() */
113 int	max_conns_allowed = -1;	/* used by cots_listen_event() */
114 
115 /*
116  * Keep track of min/max versions of NFS protocol to be started.
117  * Start with the defaults (min == 2, max == 3).  We have the
118  * capability of starting vers=4 but only if the user requests it.
119  */
120 int	nfs_server_vers_min = NFS_VERSMIN_DEFAULT;
121 int	nfs_server_vers_max = NFS_VERSMAX_DEFAULT;
122 
123 /*
124  * Set the default for server delegation enablement and set per
125  * /etc/default/nfs configuration (if present).
126  */
127 int	nfs_server_delegation = NFS_SERVER_DELEGATION_DEFAULT;
128 
129 int
130 main(int ac, char *av[])
131 {
132 	char *dir = "/";
133 	int allflag = 0;
134 	int df_allflag = 0;
135 	int opt_cnt = 0;
136 	int maxservers = 1;	/* zero allows inifinte number of threads */
137 	int maxservers_set = 0;
138 	int logmaxservers = 0;
139 	int pid;
140 	int i;
141 	char *provider = (char *)NULL;
142 	char *df_provider = (char *)NULL;
143 	struct protob *protobp0, *protobp;
144 	NETSELDECL(proto) = NULL;
145 	NETSELDECL(df_proto) = NULL;
146 	NETSELPDECL(providerp);
147 	char *defval;
148 	boolean_t can_do_mlp;
149 	uint_t dss_npaths = 0;
150 	char **dss_pathnames = NULL;
151 	sigset_t sgset;
152 
153 	MyName = *av;
154 
155 	/*
156 	 * Initializations that require more privileges than we need to run.
157 	 */
158 	(void) _create_daemon_lock(NFSD, DAEMON_UID, DAEMON_GID);
159 	svcsetprio();
160 
161 	can_do_mlp = priv_ineffect(PRIV_NET_BINDMLP);
162 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
163 	    DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS,
164 	    can_do_mlp ? PRIV_NET_BINDMLP : NULL, NULL) == -1) {
165 		(void) fprintf(stderr, "%s should be run with"
166 		    " sufficient privileges\n", av[0]);
167 		exit(1);
168 	}
169 
170 	(void) enable_extended_FILE_stdio(-1, -1);
171 
172 	/*
173 	 * Read in the values from config file first before we check
174 	 * commandline options so the options override the file.
175 	 */
176 	if ((defopen(NFSADMIN)) == 0) {
177 		if ((defval = defread("NFSD_MAX_CONNECTIONS=")) != NULL) {
178 			errno = 0;
179 			max_conns_allowed = strtol(defval, (char **)NULL, 10);
180 			if (errno != 0) {
181 				max_conns_allowed = -1;
182 			}
183 		}
184 		if ((defval = defread("NFSD_LISTEN_BACKLOG=")) != NULL) {
185 			errno = 0;
186 			listen_backlog = strtol(defval, (char **)NULL, 10);
187 			if (errno != 0) {
188 				listen_backlog = 32;
189 			}
190 		}
191 		if ((defval = defread("NFSD_PROTOCOL=")) != NULL) {
192 			df_proto = strdup(defval);
193 			opt_cnt++;
194 			if (strncasecmp("ALL", defval, 3) == 0) {
195 				free(df_proto);
196 				df_proto = NULL;
197 				df_allflag = 1;
198 			}
199 		}
200 		if ((defval = defread("NFSD_DEVICE=")) != NULL) {
201 			df_provider = strdup(defval);
202 			opt_cnt++;
203 		}
204 		if ((defval = defread("NFSD_SERVERS=")) != NULL) {
205 			errno = 0;
206 			maxservers = strtol(defval, (char **)NULL, 10);
207 			if (errno != 0) {
208 				maxservers = 1;
209 			} else {
210 				maxservers_set = 1;
211 			}
212 		}
213 		if ((defval = defread("NFS_SERVER_VERSMIN=")) != NULL) {
214 			errno = 0;
215 			nfs_server_vers_min =
216 			    strtol(defval, (char **)NULL, 10);
217 			if (errno != 0) {
218 				nfs_server_vers_min = NFS_VERSMIN_DEFAULT;
219 			}
220 		}
221 		if ((defval = defread("NFS_SERVER_VERSMAX=")) != NULL) {
222 			errno = 0;
223 			nfs_server_vers_max =
224 			    strtol(defval, (char **)NULL, 10);
225 			if (errno != 0) {
226 				nfs_server_vers_max = NFS_VERSMAX_DEFAULT;
227 			}
228 		}
229 		if ((defval = defread("NFS_SERVER_DELEGATION=")) != NULL) {
230 			if (strcmp(defval, "off") == 0) {
231 				nfs_server_delegation = FALSE;
232 			}
233 		}
234 
235 		/* close defaults file */
236 		defopen(NULL);
237 	}
238 
239 	/*
240 	 * Conflict options error messages.
241 	 */
242 	if (opt_cnt > 1) {
243 		(void) fprintf(stderr, "\nConflicting options, only one of "
244 		    "the following options can be specified\n"
245 		    "in " NFSADMIN ":\n"
246 		    "\tNFSD_PROTOCOL=ALL\n"
247 		    "\tNFSD_PROTOCOL=protocol\n"
248 		    "\tNFSD_DEVICE=device\n\n");
249 		usage();
250 	}
251 	opt_cnt = 0;
252 
253 	while ((i = getopt(ac, av, "ac:p:s:t:l:")) != EOF) {
254 		switch (i) {
255 		case 'a':
256 			free(df_proto);
257 			df_proto = NULL;
258 			free(df_provider);
259 			df_provider = NULL;
260 
261 			allflag = 1;
262 			opt_cnt++;
263 			break;
264 
265 		case 'c':
266 			max_conns_allowed = atoi(optarg);
267 			break;
268 
269 		case 'p':
270 			proto = optarg;
271 			df_allflag = 0;
272 			opt_cnt++;
273 			break;
274 
275 		/*
276 		 * DSS: NFSv4 distributed stable storage.
277 		 *
278 		 * This is a Contracted Project Private interface, for
279 		 * the sole use of Sun Cluster HA-NFS. See PSARC/2006/313.
280 		 */
281 		case 's':
282 			if (strlen(optarg) < MAXPATHLEN) {
283 				/* first "-s" option encountered? */
284 				if (dss_pathnames == NULL) {
285 					/*
286 					 * Allocate maximum possible space
287 					 * required given cmdline arg count;
288 					 * "-s <path>" consumes two args.
289 					 */
290 					size_t sz = (ac / 2) * sizeof (char *);
291 					dss_pathnames = (char **)malloc(sz);
292 					if (dss_pathnames == NULL) {
293 						(void) fprintf(stderr, "%s: "
294 						    "dss paths malloc failed\n",
295 						    av[0]);
296 						exit(1);
297 					}
298 					(void) memset(dss_pathnames, 0, sz);
299 				}
300 				dss_pathnames[dss_npaths] = optarg;
301 				dss_npaths++;
302 			} else {
303 				(void) fprintf(stderr,
304 				    "%s: -s pathname too long.\n", av[0]);
305 			}
306 			break;
307 
308 		case 't':
309 			provider = optarg;
310 			df_allflag = 0;
311 			opt_cnt++;
312 			break;
313 
314 		case 'l':
315 			listen_backlog = atoi(optarg);
316 			break;
317 
318 		case '?':
319 			usage();
320 			/* NOTREACHED */
321 		}
322 	}
323 
324 	allflag = df_allflag;
325 	if (proto == NULL)
326 		proto = df_proto;
327 	if (provider == NULL)
328 		provider = df_provider;
329 
330 	/*
331 	 * Conflict options error messages.
332 	 */
333 	if (opt_cnt > 1) {
334 		(void) fprintf(stderr, "\nConflicting options, only one of "
335 		    "the following options can be specified\n"
336 		    "on the command line:\n"
337 		    "\t-a\n"
338 		    "\t-p protocol\n"
339 		    "\t-t transport\n\n");
340 		usage();
341 	}
342 
343 	if (proto != NULL &&
344 	    strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0) {
345 		if (nfs_server_vers_max == NFS_V4) {
346 			if (nfs_server_vers_min == NFS_V4) {
347 				syslog(LOG_ERR,
348 				    "NFS version 4 is not supported "
349 				    "with the UDP protocol.  Exiting\n");
350 				fprintf(stderr,
351 				    "NFS version 4 is not supported "
352 				    "with the UDP protocol.  Exiting\n");
353 				exit(3);
354 			} else {
355 				fprintf(stderr,
356 				    "NFS version 4 is not supported "
357 				    "with the UDP protocol.\n");
358 			}
359 		}
360 	}
361 
362 	/*
363 	 * If there is exactly one more argument, it is the number of
364 	 * servers.
365 	 */
366 	if (optind == ac - 1) {
367 		maxservers = atoi(av[optind]);
368 		maxservers_set = 1;
369 	}
370 	/*
371 	 * If there are two or more arguments, then this is a usage error.
372 	 */
373 	else if (optind < ac - 1)
374 		usage();
375 	/*
376 	 * Check the ranges for min/max version specified
377 	 */
378 	else if ((nfs_server_vers_min > nfs_server_vers_max) ||
379 	    (nfs_server_vers_min < NFS_VERSMIN) ||
380 	    (nfs_server_vers_max > NFS_VERSMAX))
381 		usage();
382 	/*
383 	 * There are no additional arguments, and we haven't set maxservers
384 	 * explicitly via the config file, we use a default number of
385 	 * servers.  We will log this.
386 	 */
387 	else if (maxservers_set == 0)
388 		logmaxservers = 1;
389 
390 	/*
391 	 * Basic Sanity checks on options
392 	 *
393 	 * max_conns_allowed must be positive, except for the special
394 	 * value of -1 which is used internally to mean unlimited, -1 isn't
395 	 * documented but we allow it anyway.
396 	 *
397 	 * maxservers must be positive
398 	 * listen_backlog must be positive or zero
399 	 */
400 	if (((max_conns_allowed != -1) && (max_conns_allowed <= 0)) ||
401 	    (listen_backlog < 0) || (maxservers <= 0)) {
402 		usage();
403 	}
404 
405 	/*
406 	 * Set current dir to server root
407 	 */
408 	if (chdir(dir) < 0) {
409 		(void) fprintf(stderr, "%s:  ", MyName);
410 		perror(dir);
411 		exit(1);
412 	}
413 
414 #ifndef DEBUG
415 	/*
416 	 * Background
417 	 */
418 	pid = fork();
419 	if (pid < 0) {
420 		perror("nfsd: fork");
421 		exit(1);
422 	}
423 	if (pid != 0)
424 		exit(0);
425 
426 	/*
427 	 * Close existing file descriptors, open "/dev/null" as
428 	 * standard input, output, and error, and detach from
429 	 * controlling terminal.
430 	 */
431 	closefrom(0);
432 	(void) open("/dev/null", O_RDONLY);
433 	(void) open("/dev/null", O_WRONLY);
434 	(void) dup(1);
435 	(void) setsid();
436 #endif
437 	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
438 
439 	/*
440 	 * establish our lock on the lock file and write our pid to it.
441 	 * exit if some other process holds the lock, or if there's any
442 	 * error in writing/locking the file.
443 	 */
444 	pid = _enter_daemon_lock(NFSD);
445 	switch (pid) {
446 	case 0:
447 		break;
448 	case -1:
449 		syslog(LOG_ERR, "error locking for %s: %s", NFSD,
450 		    strerror(errno));
451 		exit(2);
452 	default:
453 		/* daemon was already running */
454 		exit(0);
455 	}
456 
457 	/*
458 	 * If we've been given a list of paths to be used for distributed
459 	 * stable storage, and provided we're going to run a version
460 	 * that supports it, setup the DSS paths.
461 	 */
462 	if (dss_pathnames != NULL && nfs_server_vers_max >= DSS_VERSMIN) {
463 		if (dss_init(dss_npaths, dss_pathnames) != 0) {
464 			syslog(LOG_ERR, "dss_init failed. Exiting.");
465 			exit(1);
466 		}
467 	}
468 
469 	/*
470 	 * Block all signals till we spawn other
471 	 * threads.
472 	 */
473 	(void) sigfillset(&sgset);
474 	(void) thr_sigsetmask(SIG_BLOCK, &sgset, NULL);
475 
476 	if (logmaxservers) {
477 		(void) syslog(LOG_INFO,
478 		    "Number of servers not specified. Using default of %d.",
479 		    maxservers);
480 	}
481 
482 	/*
483 	 * Make sure to unregister any previous versions in case the
484 	 * user is reconfiguring the server in interesting ways.
485 	 */
486 	svc_unreg(NFS_PROGRAM, NFS_VERSION);
487 	svc_unreg(NFS_PROGRAM, NFS_V3);
488 	svc_unreg(NFS_PROGRAM, NFS_V4);
489 	svc_unreg(NFS_ACL_PROGRAM, NFS_ACL_V2);
490 	svc_unreg(NFS_ACL_PROGRAM, NFS_ACL_V3);
491 
492 	/*
493 	 * Set up kernel RPC thread pool for the NFS server.
494 	 */
495 	if (nfssvcpool(maxservers)) {
496 		(void) syslog(LOG_ERR,
497 		    "Can't set up kernel NFS service: %m. Exiting");
498 		exit(1);
499 	}
500 
501 
502 	/*
503 	 * Set up blocked thread to do LWP creation on behalf of the kernel.
504 	 */
505 	if (svcwait(NFS_SVCPOOL_ID)) {
506 		(void) syslog(LOG_ERR,
507 		    "Can't set up NFS pool creator: %m, Exiting");
508 		exit(1);
509 	}
510 
511 	/*
512 	 * RDMA start and stop thread.
513 	 * Per pool RDMA listener creation and
514 	 * destructor thread.
515 	 *
516 	 * start rdma services and block in the kernel.
517 	 */
518 	if (svcrdma(NFS_SVCPOOL_ID, nfs_server_vers_min, nfs_server_vers_max,
519 	    nfs_server_delegation)) {
520 		(void) syslog(LOG_ERR,
521 		    "Can't set up RDMA creator thread : %m.");
522 	}
523 
524 	/*
525 	 * Now open up for signal delivery
526 	 */
527 
528 	(void) thr_sigsetmask(SIG_UNBLOCK, &sgset, NULL);
529 	sigset(SIGTERM, sigflush);
530 	sigset(SIGUSR1, quiesce);
531 
532 	/*
533 	 * Build a protocol block list for registration.
534 	 */
535 	protobp0 = protobp = (struct protob *)malloc(sizeof (struct protob));
536 	protobp->serv = "NFS";
537 	protobp->versmin = nfs_server_vers_min;
538 	protobp->versmax = nfs_server_vers_max;
539 	protobp->program = NFS_PROGRAM;
540 
541 	protobp->next = (struct protob *)malloc(sizeof (struct protob));
542 	protobp = protobp->next;
543 	protobp->serv = "NFS_ACL";		/* not used */
544 	protobp->versmin = nfs_server_vers_min;
545 	/* XXX - this needs work to get the version just right */
546 	protobp->versmax = (nfs_server_vers_max > NFS_ACL_V3) ?
547 	    NFS_ACL_V3 : nfs_server_vers_max;
548 	protobp->program = NFS_ACL_PROGRAM;
549 	protobp->next = (struct protob *)NULL;
550 
551 	if (allflag) {
552 		if (do_all(protobp0, nfssvc, 0) == -1)
553 			exit(1);
554 	} else if (proto) {
555 		/* there's more than one match for the same protocol */
556 		struct netconfig *nconf;
557 		NCONF_HANDLE *nc;
558 		bool_t	protoFound = FALSE;
559 		if ((nc = setnetconfig()) == (NCONF_HANDLE *) NULL) {
560 			syslog(LOG_ERR, "setnetconfig failed: %m");
561 			goto done;
562 		}
563 		while (nconf = getnetconfig(nc)) {
564 			if (strcmp(nconf->nc_proto, proto) == 0) {
565 				protoFound = TRUE;
566 				do_one(nconf->nc_device, NULL,
567 				    protobp0, nfssvc, 0);
568 			}
569 		}
570 		(void) endnetconfig(nc);
571 		if (protoFound == FALSE)
572 			syslog(LOG_ERR, "couldn't find netconfig entry \
573 			    for protocol %s", proto);
574 
575 	} else if (provider)
576 		do_one(provider, proto, protobp0, nfssvc, 0);
577 	else {
578 		for (providerp = defaultproviders;
579 		    *providerp != NULL; providerp++) {
580 			provider = *providerp;
581 			do_one(provider, NULL, protobp0, nfssvc, 0);
582 		}
583 	}
584 done:
585 
586 	free(protobp);
587 	free(protobp0);
588 
589 
590 	if (num_fds == 0) {
591 		(void) syslog(LOG_ERR,
592 		"Could not start NFS service for any protocol. Exiting");
593 		exit(1);
594 	}
595 
596 	end_listen_fds = num_fds;
597 
598 	/*
599 	 * Get rid of unneeded privileges.
600 	 */
601 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
602 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
603 
604 	/*
605 	 * Poll for non-data control events on the transport descriptors.
606 	 */
607 	poll_for_action();
608 
609 	/*
610 	 * If we get here, something failed in poll_for_action().
611 	 */
612 	return (1);
613 }
614 
615 static int
616 nfssvcpool(int maxservers)
617 {
618 	struct svcpool_args npa;
619 
620 	npa.id = NFS_SVCPOOL_ID;
621 	npa.maxthreads = maxservers;
622 	npa.redline = 0;
623 	npa.qsize = 0;
624 	npa.timeout = 0;
625 	npa.stksize = 0;
626 	npa.max_same_xprt = 0;
627 	return (_nfssys(SVCPOOL_CREATE, &npa));
628 }
629 
630 /*
631  * Establish NFS service thread.
632  */
633 static int
634 nfssvc(int fd, struct netbuf addrmask, struct netconfig *nconf)
635 {
636 	struct nfs_svc_args nsa;
637 
638 	nsa.fd = fd;
639 	nsa.netid = nconf->nc_netid;
640 	nsa.addrmask = addrmask;
641 	if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) {
642 		nsa.versmax = (nfs_server_vers_max > NFS_V3) ?
643 		    NFS_V3 : nfs_server_vers_max;
644 		nsa.versmin = nfs_server_vers_min;
645 		/*
646 		 * If no version left, silently do nothing, previous
647 		 * checks will have assured at least TCP is available.
648 		 */
649 		if (nsa.versmin > nsa.versmax)
650 			return (0);
651 	} else {
652 		nsa.versmax = nfs_server_vers_max;
653 		nsa.versmin = nfs_server_vers_min;
654 	}
655 	nsa.delegation = nfs_server_delegation;
656 	return (_nfssys(NFS_SVC, &nsa));
657 }
658 
659 static void
660 usage(void)
661 {
662 	(void) fprintf(stderr,
663 "usage: %s [ -a ] [ -c max_conns ] [ -p protocol ] [ -t transport ] ", MyName);
664 	(void) fprintf(stderr, "\n[ -l listen_backlog ] [ nservers ]\n");
665 	(void) fprintf(stderr,
666 "\twhere -a causes <nservers> to be started on each appropriate transport,\n");
667 	(void) fprintf(stderr,
668 "\tmax_conns is the maximum number of concurrent connections allowed,\n");
669 	(void) fprintf(stderr, "\t\tand max_conns must be a decimal number");
670 	(void) fprintf(stderr, "> zero,\n");
671 	(void) fprintf(stderr, "\tprotocol is a protocol identifier,\n");
672 	(void) fprintf(stderr,
673 	    "\ttransport is a transport provider name (i.e. device),\n");
674 	(void) fprintf(stderr,
675 	    "\tlisten_backlog is the TCP listen backlog,\n");
676 	(void) fprintf(stderr,
677 	    "\tand <nservers> must be a decimal number > zero.\n");
678 	exit(1);
679 }
680 
681 /*
682  * Issue nfssys system call to flush all logging buffers asynchronously.
683  *
684  * NOTICE: It is extremely important to flush NFS logging buffers when
685  *	   nfsd exits. When the system is halted or rebooted nfslogd
686  *	   may not have an opportunity to flush the buffers.
687  */
688 static void
689 nfsl_flush()
690 {
691 	struct nfsl_flush_args nfa;
692 
693 	memset((void *)&nfa, 0, sizeof (nfa));
694 	nfa.version = NFSL_FLUSH_ARGS_VERS;
695 	nfa.directive = NFSL_ALL;	/* flush all asynchronously */
696 
697 	if (_nfssys(LOG_FLUSH, &nfa) < 0)
698 		syslog(LOG_ERR, "_nfssys(LOG_FLUSH) failed: %s\n",
699 		    strerror(errno));
700 }
701 
702 /*
703  * SIGTERM handler.
704  * Flush logging buffers and exit.
705  */
706 static void
707 sigflush(int sig)
708 {
709 	nfsl_flush();
710 	_exit(0);
711 }
712 
713 /*
714  * SIGUSR1 handler.
715  *
716  * Request that server quiesce, then (nfsd) exit. For subsequent warm start.
717  *
718  * This is a Contracted Project Private interface, for the sole use
719  * of Sun Cluster HA-NFS. See PSARC/2004/497.
720  *
721  * Equivalent to SIGTERM handler if nfs_server_vers_max < QUIESCE_VERSMIN.
722  */
723 static void
724 quiesce(int sig)
725 {
726 	int error;
727 	int id = NFS_SVCPOOL_ID;
728 
729 	if (nfs_server_vers_max >= QUIESCE_VERSMIN) {
730 		/* Request server quiesce at next shutdown */
731 		error = _nfssys(NFS4_SVC_REQUEST_QUIESCE, &id);
732 
733 		/*
734 		 * ENOENT is returned if there is no matching SVC pool
735 		 * for the id. Possibly because the pool is not yet setup.
736 		 * In this case, just exit as if no error. For all other errors,
737 		 * just return and allow caller to retry.
738 		 */
739 		if (error && errno != ENOENT) {
740 			syslog(LOG_ERR,
741 			    "_nfssys(NFS4_SVC_REQUEST_QUIESCE) failed: %s",
742 			    strerror(errno));
743 			return;
744 		}
745 	}
746 
747 	/* Flush logging buffers */
748 	nfsl_flush();
749 
750 	_exit(0);
751 }
752 
753 /*
754  * DSS: distributed stable storage.
755  * Create leaf directories as required, keeping an eye on path
756  * lengths. Calls exit(1) on failure.
757  * The pathnames passed in must already exist, and must be writeable by nfsd.
758  * Note: the leaf directories under NFS4_VAR_DIR are not created here;
759  * they're created at pkg install.
760  */
761 static void
762 dss_mkleafdirs(uint_t npaths, char **pathnames)
763 {
764 	int i;
765 	char *tmppath = NULL;
766 
767 	/*
768 	 * Create the temporary storage used by dss_mkleafdir() here,
769 	 * rather than in that function, so that it only needs to be
770 	 * done once, rather than once for each call. Too big to put
771 	 * on the function's stack.
772 	 */
773 	tmppath = (char *)malloc(MAXPATHLEN);
774 	if (tmppath == NULL) {
775 		syslog(LOG_ERR, "tmppath malloc failed. Exiting");
776 		exit(1);
777 	}
778 
779 	for (i = 0; i < npaths; i++) {
780 		char *p = pathnames[i];
781 
782 		dss_mkleafdir(p, NFS4_DSS_STATE_LEAF, tmppath);
783 		dss_mkleafdir(p, NFS4_DSS_OLDSTATE_LEAF, tmppath);
784 	}
785 
786 	free(tmppath);
787 }
788 
789 /*
790  * Create "leaf" in "dir" (which must already exist).
791  * leaf: should start with a '/'
792  */
793 static void
794 dss_mkleafdir(char *dir, char *leaf, char *tmppath)
795 {
796 	/* MAXPATHLEN includes the terminating NUL */
797 	if (strlen(dir) + strlen(leaf) > MAXPATHLEN - 1) {
798 		syslog(LOG_ERR, "stable storage path too long: %s%s. Exiting",
799 		    dir, leaf);
800 		exit(1);
801 	}
802 
803 	(void) snprintf(tmppath, MAXPATHLEN, "%s/%s", dir, leaf);
804 
805 	/* the directory may already exist: that's OK */
806 	if (mkdir(tmppath, NFS4_DSS_DIR_MODE) == -1 && errno != EEXIST) {
807 		syslog(LOG_ERR, "error creating stable storage directory: "
808 		    "%s: %s. Exiting", strerror(errno), tmppath);
809 		exit(1);
810 	}
811 }
812 
813 /*
814  * Create the storage dirs, and pass the path list to the kernel.
815  * This requires the nfssrv module to be loaded; the _nfssys() syscall
816  * will fail ENOTSUP if it is not.
817  * Use libnvpair(3LIB) to pass the data to the kernel.
818  */
819 static int
820 dss_init(uint_t npaths, char **pathnames)
821 {
822 	int i, j, nskipped, error;
823 	char *bufp;
824 	uint32_t bufsize;
825 	size_t buflen;
826 	nvlist_t *nvl;
827 
828 	if (npaths > 1) {
829 		/*
830 		 * We need to remove duplicate paths; this might be user error
831 		 * in the general case, but HA-NFSv4 can also cause this.
832 		 * Sort the pathnames array, and NULL out duplicates,
833 		 * then write the non-NULL entries to a new array.
834 		 * Sorting will also allow the kernel to optimise its searches.
835 		 */
836 
837 		qsort(pathnames, npaths, sizeof (char *), qstrcmp);
838 
839 		/* now NULL out any duplicates */
840 		i = 0; j = 1; nskipped = 0;
841 		while (j < npaths) {
842 			if (strcmp(pathnames[i], pathnames[j]) == NULL) {
843 				pathnames[j] = NULL;
844 				j++;
845 				nskipped++;
846 				continue;
847 			}
848 
849 			/* skip i over any of its NULLed duplicates */
850 			i = j++;
851 		}
852 
853 		/* finally, write the non-NULL entries to a new array */
854 		if (nskipped > 0) {
855 			int nreal;
856 			size_t sz;
857 			char **tmp_pathnames;
858 
859 			nreal = npaths - nskipped;
860 
861 			sz = nreal * sizeof (char *);
862 			tmp_pathnames = (char **)malloc(sz);
863 			if (tmp_pathnames == NULL) {
864 				syslog(LOG_ERR, "tmp_pathnames malloc failed");
865 				exit(1);
866 			}
867 
868 			for (i = 0, j = 0; i < npaths; i++)
869 				if (pathnames[i] != NULL)
870 					tmp_pathnames[j++] = pathnames[i];
871 			free(pathnames);
872 			pathnames = tmp_pathnames;
873 			npaths = nreal;
874 		}
875 
876 	}
877 
878 	/* Create directories to store the distributed state files */
879 	dss_mkleafdirs(npaths, pathnames);
880 
881 	/* Create the name-value pair list */
882 	error = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
883 	if (error) {
884 		syslog(LOG_ERR, "nvlist_alloc failed: %s.", strerror(errno));
885 		return (1);
886 	}
887 
888 	/* Add the pathnames array as a single name-value pair */
889 	error = nvlist_add_string_array(nvl, NFS4_DSS_NVPAIR_NAME,
890 	    pathnames, npaths);
891 	if (error) {
892 		syslog(LOG_ERR, "nvlist_add_string_array failed: %s.",
893 		    strerror(errno));
894 		nvlist_free(nvl);
895 		return (1);
896 	}
897 
898 	/*
899 	 * Pack list into contiguous memory, for passing to kernel.
900 	 * nvlist_pack() will allocate the memory for the buffer,
901 	 * which we should free() when no longer needed.
902 	 * NV_ENCODE_XDR for safety across ILP32/LP64 kernel boundary.
903 	 */
904 	bufp = NULL;
905 	error = nvlist_pack(nvl, &bufp, &buflen, NV_ENCODE_XDR, 0);
906 	if (error) {
907 		syslog(LOG_ERR, "nvlist_pack failed: %s.", strerror(errno));
908 		nvlist_free(nvl);
909 		return (1);
910 	}
911 
912 	/* Now we have the packed buffer, we no longer need the list */
913 	nvlist_free(nvl);
914 
915 	/*
916 	 * Let the kernel know in advance how big the buffer is.
917 	 * NOTE: we cannot just pass buflen, since size_t is a long, and
918 	 * thus a different size between ILP32 userland and LP64 kernel.
919 	 * Use an int for the transfer, since that should be big enough;
920 	 * this is a no-op at the moment, here, since nfsd is 32-bit, but
921 	 * that could change.
922 	 */
923 	bufsize = (uint32_t)buflen;
924 	error = _nfssys(NFS4_DSS_SETPATHS_SIZE, &bufsize);
925 	if (error) {
926 		syslog(LOG_ERR,
927 		    "_nfssys(NFS4_DSS_SETPATHS_SIZE) failed: %s. ",
928 		    strerror(errno));
929 		free(bufp);
930 		return (1);
931 	}
932 
933 	/* Pass the packed buffer to the kernel */
934 	error = _nfssys(NFS4_DSS_SETPATHS, bufp);
935 	if (error) {
936 		syslog(LOG_ERR,
937 		    "_nfssys(NFS4_DSS_SETPATHS) failed: %s. ", strerror(errno));
938 		free(bufp);
939 		return (1);
940 	}
941 
942 	/*
943 	 * The kernel has now unpacked the buffer and extracted the
944 	 * pathnames array, we no longer need the buffer.
945 	 */
946 	free(bufp);
947 
948 	return (0);
949 }
950 
951 /*
952  * Quick sort string compare routine, for qsort.
953  * Needed to make arg types correct.
954  */
955 int
956 qstrcmp(const void *p1, const void *p2)
957 {
958 	char *s1 = *((char **)p1);
959 	char *s2 = *((char **)p2);
960 
961 	return (strcmp(s1, s2));
962 }
963