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