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