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