dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / fs.d / nfs / nfsd / nfsd.c
bloba10a17dca0b4f9f8615f9671da0222909c2d96d3
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
29 * University Copyright- Copyright (c) 1982, 1986, 1988
30 * The Regents of the University of California
31 * All Rights Reserved
33 * University Acknowledgment- Portions of this document are derived from
34 * software developed by the University of California, Berkeley, and its
35 * contributors.
38 /* LINTLIBRARY */
39 /* PROTOLIB1 */
41 /* NFS server */
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 <limits.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 <libscf.h>
78 #include <libshare.h>
79 #include "nfs_tbind.h"
80 #include "thrpool.h"
81 #include "smfcfg.h"
83 /* quiesce requests will be ignored if nfs_server_vers_max < QUIESCE_VERSMIN */
84 #define QUIESCE_VERSMIN 4
85 /* DSS: distributed stable storage */
86 #define DSS_VERSMIN 4
88 static int nfssvc(int, struct netbuf, struct netconfig *);
89 static int nfssvcpool(int maxservers);
90 static int dss_init(uint_t npaths, char **pathnames);
91 static void dss_mkleafdirs(uint_t npaths, char **pathnames);
92 static void dss_mkleafdir(char *dir, char *leaf, char *path);
93 static void usage(void);
94 int qstrcmp(const void *s1, const void *s2);
96 extern int _nfssys(int, void *);
98 extern int daemonize_init(void);
99 extern void daemonize_fini(int fd);
101 /* signal handlers */
102 static void sigflush(int);
103 static void quiesce(int);
105 static char *MyName;
106 static NETSELDECL(defaultproviders)[] = { "/dev/tcp6", "/dev/tcp", "/dev/udp",
107 "/dev/udp6", NULL };
108 /* static NETSELDECL(defaultprotos)[] = { NC_UDP, NC_TCP, NULL }; */
110 * The following are all globals used by routines in nfs_tbind.c.
112 size_t end_listen_fds; /* used by conn_close_oldest() */
113 size_t num_fds = 0; /* used by multiple routines */
114 int listen_backlog = 32; /* used by bind_to_{provider,proto}() */
115 int num_servers; /* used by cots_listen_event() */
116 int (*Mysvc)(int, struct netbuf, struct netconfig *) = nfssvc;
117 /* used by cots_listen_event() */
118 int max_conns_allowed = -1; /* used by cots_listen_event() */
121 * Keep track of min/max versions of NFS protocol to be started.
122 * Start with the defaults (min == 2, max == 3). We have the
123 * capability of starting vers=4 but only if the user requests it.
125 int nfs_server_vers_min = NFS_VERSMIN_DEFAULT;
126 int nfs_server_vers_max = NFS_VERSMAX_DEFAULT;
129 * Set the default for server delegation enablement and set per
130 * /etc/default/nfs configuration (if present).
132 int nfs_server_delegation = NFS_SERVER_DELEGATION_DEFAULT;
135 main(int ac, char *av[])
137 char *dir = "/";
138 int allflag = 0;
139 int df_allflag = 0;
140 int opt_cnt = 0;
141 int maxservers = 1024; /* zero allows inifinte number of threads */
142 int maxservers_set = 0;
143 int logmaxservers = 0;
144 int pid;
145 int i;
146 char *provider = NULL;
147 char *df_provider = NULL;
148 struct protob *protobp0, *protobp;
149 NETSELDECL(proto) = NULL;
150 NETSELDECL(df_proto) = NULL;
151 NETSELPDECL(providerp);
152 char *defval;
153 uint_t dss_npaths = 0;
154 char **dss_pathnames = NULL;
155 sigset_t sgset;
156 char name[PATH_MAX], value[PATH_MAX];
157 int ret, bufsz;
159 int pipe_fd = -1;
161 MyName = *av;
164 * Initializations that require more privileges than we need to run.
166 (void) _create_daemon_lock(NFSD, DAEMON_UID, DAEMON_GID);
167 svcsetprio();
169 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
170 DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, NULL) == -1) {
171 (void) fprintf(stderr, "%s should be run with"
172 " sufficient privileges\n", av[0]);
173 exit(1);
176 (void) enable_extended_FILE_stdio(-1, -1);
179 * Read in the values from SMF first before we check
180 * command line options so the options override SMF values.
182 bufsz = PATH_MAX;
183 ret = nfs_smf_get_prop("max_connections", value, DEFAULT_INSTANCE,
184 SCF_TYPE_INTEGER, NFSD, &bufsz);
185 if (ret == SA_OK) {
186 errno = 0;
187 max_conns_allowed = strtol(value, (char **)NULL, 10);
188 if (errno != 0)
189 max_conns_allowed = -1;
192 bufsz = PATH_MAX;
193 ret = nfs_smf_get_prop("listen_backlog", value, DEFAULT_INSTANCE,
194 SCF_TYPE_INTEGER, NFSD, &bufsz);
195 if (ret == SA_OK) {
196 errno = 0;
197 listen_backlog = strtol(value, (char **)NULL, 10);
198 if (errno != 0) {
199 listen_backlog = 32;
203 bufsz = PATH_MAX;
204 ret = nfs_smf_get_prop("protocol", value, DEFAULT_INSTANCE,
205 SCF_TYPE_ASTRING, NFSD, &bufsz);
206 if ((ret == SA_OK) && strlen(value) > 0) {
207 df_proto = strdup(value);
208 opt_cnt++;
209 if (strncasecmp("ALL", value, 3) == 0) {
210 free(df_proto);
211 df_proto = NULL;
212 df_allflag = 1;
216 bufsz = PATH_MAX;
217 ret = nfs_smf_get_prop("device", value, DEFAULT_INSTANCE,
218 SCF_TYPE_ASTRING, NFSD, &bufsz);
219 if ((ret == SA_OK) && strlen(value) > 0) {
220 df_provider = strdup(value);
221 opt_cnt++;
224 bufsz = PATH_MAX;
225 ret = nfs_smf_get_prop("servers", value, DEFAULT_INSTANCE,
226 SCF_TYPE_INTEGER, NFSD, &bufsz);
227 if (ret == SA_OK) {
228 errno = 0;
229 maxservers = strtol(value, (char **)NULL, 10);
230 if (errno != 0)
231 maxservers = 1024;
232 else
233 maxservers_set = 1;
236 bufsz = 4;
237 ret = nfs_smf_get_prop("server_versmin", value, DEFAULT_INSTANCE,
238 SCF_TYPE_INTEGER, NFSD, &bufsz);
239 if (ret == SA_OK)
240 nfs_server_vers_min = strtol(value, (char **)NULL, 10);
242 bufsz = 4;
243 ret = nfs_smf_get_prop("server_versmax", value, DEFAULT_INSTANCE,
244 SCF_TYPE_INTEGER, NFSD, &bufsz);
245 if (ret == SA_OK)
246 nfs_server_vers_max = strtol(value, (char **)NULL, 10);
248 bufsz = PATH_MAX;
249 ret = nfs_smf_get_prop("server_delegation", value, DEFAULT_INSTANCE,
250 SCF_TYPE_ASTRING, NFSD, &bufsz);
251 if (ret == SA_OK)
252 if (strncasecmp(value, "off", 3) == 0)
253 nfs_server_delegation = FALSE;
256 * Conflict options error messages.
258 if (opt_cnt > 1) {
259 (void) fprintf(stderr, "\nConflicting options, only one of "
260 "the following options can be specified\n"
261 "in SMF:\n"
262 "\tprotocol=ALL\n"
263 "\tprotocol=protocol\n"
264 "\tdevice=devicename\n\n");
265 usage();
267 opt_cnt = 0;
269 while ((i = getopt(ac, av, "ac:p:s:t:l:")) != EOF) {
270 switch (i) {
271 case 'a':
272 free(df_proto);
273 df_proto = NULL;
274 free(df_provider);
275 df_provider = NULL;
277 allflag = 1;
278 opt_cnt++;
279 break;
281 case 'c':
282 max_conns_allowed = atoi(optarg);
283 break;
285 case 'p':
286 proto = optarg;
287 df_allflag = 0;
288 opt_cnt++;
289 break;
292 * DSS: NFSv4 distributed stable storage.
294 * This is a Contracted Project Private interface, for
295 * the sole use of Sun Cluster HA-NFS. See PSARC/2006/313.
297 case 's':
298 if (strlen(optarg) < MAXPATHLEN) {
299 /* first "-s" option encountered? */
300 if (dss_pathnames == NULL) {
302 * Allocate maximum possible space
303 * required given cmdline arg count;
304 * "-s <path>" consumes two args.
306 size_t sz = (ac / 2) * sizeof (char *);
307 dss_pathnames = (char **)malloc(sz);
308 if (dss_pathnames == NULL) {
309 (void) fprintf(stderr, "%s: "
310 "dss paths malloc failed\n",
311 av[0]);
312 exit(1);
314 (void) memset(dss_pathnames, 0, sz);
316 dss_pathnames[dss_npaths] = optarg;
317 dss_npaths++;
318 } else {
319 (void) fprintf(stderr,
320 "%s: -s pathname too long.\n", av[0]);
322 break;
324 case 't':
325 provider = optarg;
326 df_allflag = 0;
327 opt_cnt++;
328 break;
330 case 'l':
331 listen_backlog = atoi(optarg);
332 break;
334 case '?':
335 usage();
336 /* NOTREACHED */
340 allflag = df_allflag;
341 if (proto == NULL)
342 proto = df_proto;
343 if (provider == NULL)
344 provider = df_provider;
347 * Conflict options error messages.
349 if (opt_cnt > 1) {
350 (void) fprintf(stderr, "\nConflicting options, only one of "
351 "the following options can be specified\n"
352 "on the command line:\n"
353 "\t-a\n"
354 "\t-p protocol\n"
355 "\t-t transport\n\n");
356 usage();
359 if (proto != NULL &&
360 strncasecmp(proto, NC_UDP, strlen(NC_UDP)) == 0) {
361 if (nfs_server_vers_max == NFS_V4) {
362 if (nfs_server_vers_min == NFS_V4) {
363 fprintf(stderr,
364 "NFS version 4 is not supported "
365 "with the UDP protocol. Exiting\n");
366 exit(3);
367 } else {
368 fprintf(stderr,
369 "NFS version 4 is not supported "
370 "with the UDP protocol.\n");
376 * If there is exactly one more argument, it is the number of
377 * servers.
379 if (optind == ac - 1) {
380 maxservers = atoi(av[optind]);
381 maxservers_set = 1;
384 * If there are two or more arguments, then this is a usage error.
386 else if (optind < ac - 1)
387 usage();
389 * Check the ranges for min/max version specified
391 else if ((nfs_server_vers_min > nfs_server_vers_max) ||
392 (nfs_server_vers_min < NFS_VERSMIN) ||
393 (nfs_server_vers_max > NFS_VERSMAX))
394 usage();
396 * There are no additional arguments, and we haven't set maxservers
397 * explicitly via the config file, we use a default number of
398 * servers. We will log this.
400 else if (maxservers_set == 0)
401 logmaxservers = 1;
404 * Basic Sanity checks on options
406 * max_conns_allowed must be positive, except for the special
407 * value of -1 which is used internally to mean unlimited, -1 isn't
408 * documented but we allow it anyway.
410 * maxservers must be positive
411 * listen_backlog must be positive or zero
413 if (((max_conns_allowed != -1) && (max_conns_allowed <= 0)) ||
414 (listen_backlog < 0) || (maxservers <= 0)) {
415 usage();
419 * Set current dir to server root
421 if (chdir(dir) < 0) {
422 (void) fprintf(stderr, "%s: ", MyName);
423 perror(dir);
424 exit(1);
427 #ifndef DEBUG
428 pipe_fd = daemonize_init();
429 #endif
431 openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
434 * establish our lock on the lock file and write our pid to it.
435 * exit if some other process holds the lock, or if there's any
436 * error in writing/locking the file.
438 pid = _enter_daemon_lock(NFSD);
439 switch (pid) {
440 case 0:
441 break;
442 case -1:
443 fprintf(stderr, "error locking for %s: %s\n", NFSD,
444 strerror(errno));
445 exit(2);
446 default:
447 /* daemon was already running */
448 exit(0);
452 * If we've been given a list of paths to be used for distributed
453 * stable storage, and provided we're going to run a version
454 * that supports it, setup the DSS paths.
456 if (dss_pathnames != NULL && nfs_server_vers_max >= DSS_VERSMIN) {
457 if (dss_init(dss_npaths, dss_pathnames) != 0) {
458 fprintf(stderr, "%s", "dss_init failed. Exiting.\n");
459 exit(1);
464 * Block all signals till we spawn other
465 * threads.
467 (void) sigfillset(&sgset);
468 (void) thr_sigsetmask(SIG_BLOCK, &sgset, NULL);
470 if (logmaxservers) {
471 fprintf(stderr,
472 "Number of servers not specified. Using default of %d.\n",
473 maxservers);
477 * Make sure to unregister any previous versions in case the
478 * user is reconfiguring the server in interesting ways.
480 svc_unreg(NFS_PROGRAM, NFS_VERSION);
481 svc_unreg(NFS_PROGRAM, NFS_V3);
482 svc_unreg(NFS_PROGRAM, NFS_V4);
483 svc_unreg(NFS_ACL_PROGRAM, NFS_ACL_V2);
484 svc_unreg(NFS_ACL_PROGRAM, NFS_ACL_V3);
487 * Set up kernel RPC thread pool for the NFS server.
489 if (nfssvcpool(maxservers)) {
490 fprintf(stderr, "Can't set up kernel NFS service: %s. "
491 "Exiting.\n", strerror(errno));
492 exit(1);
496 * Set up blocked thread to do LWP creation on behalf of the kernel.
498 if (svcwait(NFS_SVCPOOL_ID)) {
499 fprintf(stderr, "Can't set up NFS pool creator: %s. Exiting.\n",
500 strerror(errno));
501 exit(1);
505 * RDMA start and stop thread.
506 * Per pool RDMA listener creation and
507 * destructor thread.
509 * start rdma services and block in the kernel.
510 * (only if proto or provider is not set to TCP or UDP)
512 if ((proto == NULL) && (provider == NULL)) {
513 if (svcrdma(NFS_SVCPOOL_ID, nfs_server_vers_min,
514 nfs_server_vers_max, nfs_server_delegation)) {
515 fprintf(stderr,
516 "Can't set up RDMA creator thread : %s\n",
517 strerror(errno));
522 * Now open up for signal delivery
525 (void) thr_sigsetmask(SIG_UNBLOCK, &sgset, NULL);
526 sigset(SIGTERM, sigflush);
527 sigset(SIGUSR1, quiesce);
530 * Build a protocol block list for registration.
532 protobp0 = protobp = (struct protob *)malloc(sizeof (struct protob));
533 protobp->serv = "NFS";
534 protobp->versmin = nfs_server_vers_min;
535 protobp->versmax = nfs_server_vers_max;
536 protobp->program = NFS_PROGRAM;
538 protobp->next = (struct protob *)malloc(sizeof (struct protob));
539 protobp = protobp->next;
540 protobp->serv = "NFS_ACL"; /* not used */
541 protobp->versmin = nfs_server_vers_min;
542 /* XXX - this needs work to get the version just right */
543 protobp->versmax = (nfs_server_vers_max > NFS_ACL_V3) ?
544 NFS_ACL_V3 : nfs_server_vers_max;
545 protobp->program = NFS_ACL_PROGRAM;
546 protobp->next = NULL;
548 if (allflag) {
549 if (do_all(protobp0, nfssvc) == -1) {
550 fprintf(stderr, "setnetconfig failed : %s\n",
551 strerror(errno));
552 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 fprintf(stderr, "setnetconfig failed : %s\n",
561 strerror(errno));
562 goto done;
564 while (nconf = getnetconfig(nc)) {
565 if (strcmp(nconf->nc_proto, proto) == 0) {
566 protoFound = TRUE;
567 do_one(nconf->nc_device, NULL,
568 protobp0, nfssvc);
571 (void) endnetconfig(nc);
572 if (protoFound == FALSE) {
573 fprintf(stderr,
574 "couldn't find netconfig entry for protocol %s\n",
575 proto);
577 } else if (provider)
578 do_one(provider, proto, protobp0, nfssvc);
579 else {
580 for (providerp = defaultproviders;
581 *providerp != NULL; providerp++) {
582 provider = *providerp;
583 do_one(provider, NULL, protobp0, nfssvc);
586 done:
588 free(protobp);
589 free(protobp0);
591 if (num_fds == 0) {
592 fprintf(stderr, "Could not start NFS service for any protocol."
593 " Exiting.\n");
594 exit(1);
597 end_listen_fds = num_fds;
600 * nfsd is up and running as far as we are concerned.
602 daemonize_fini(pipe_fd);
605 * Get rid of unneeded privileges.
607 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
608 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, NULL);
611 * Poll for non-data control events on the transport descriptors.
613 poll_for_action();
616 * If we get here, something failed in poll_for_action().
618 return (1);
621 static int
622 nfssvcpool(int maxservers)
624 struct svcpool_args npa;
626 npa.id = NFS_SVCPOOL_ID;
627 npa.maxthreads = maxservers;
628 npa.redline = 0;
629 npa.qsize = 0;
630 npa.timeout = 0;
631 npa.stksize = 0;
632 npa.max_same_xprt = 0;
633 return (_nfssys(SVCPOOL_CREATE, &npa));
637 * Establish NFS service thread.
639 static int
640 nfssvc(int fd, struct netbuf addrmask, struct netconfig *nconf)
642 struct nfs_svc_args nsa;
644 nsa.fd = fd;
645 nsa.netid = nconf->nc_netid;
646 nsa.addrmask = addrmask;
647 if (strncasecmp(nconf->nc_proto, NC_UDP, strlen(NC_UDP)) == 0) {
648 nsa.versmax = (nfs_server_vers_max > NFS_V3) ?
649 NFS_V3 : nfs_server_vers_max;
650 nsa.versmin = nfs_server_vers_min;
652 * If no version left, silently do nothing, previous
653 * checks will have assured at least TCP is available.
655 if (nsa.versmin > nsa.versmax)
656 return (0);
657 } else {
658 nsa.versmax = nfs_server_vers_max;
659 nsa.versmin = nfs_server_vers_min;
661 nsa.delegation = nfs_server_delegation;
662 return (_nfssys(NFS_SVC, &nsa));
665 static void
666 usage(void)
668 (void) fprintf(stderr,
669 "usage: %s [ -a ] [ -c max_conns ] [ -p protocol ] [ -t transport ] ", MyName);
670 (void) fprintf(stderr, "\n[ -l listen_backlog ] [ nservers ]\n");
671 (void) fprintf(stderr,
672 "\twhere -a causes <nservers> to be started on each appropriate transport,\n");
673 (void) fprintf(stderr,
674 "\tmax_conns is the maximum number of concurrent connections allowed,\n");
675 (void) fprintf(stderr, "\t\tand max_conns must be a decimal number");
676 (void) fprintf(stderr, "> zero,\n");
677 (void) fprintf(stderr, "\tprotocol is a protocol identifier,\n");
678 (void) fprintf(stderr,
679 "\ttransport is a transport provider name (i.e. device),\n");
680 (void) fprintf(stderr,
681 "\tlisten_backlog is the TCP listen backlog,\n");
682 (void) fprintf(stderr,
683 "\tand <nservers> must be a decimal number > zero.\n");
684 exit(1);
688 * Issue nfssys system call to flush all logging buffers asynchronously.
690 * NOTICE: It is extremely important to flush NFS logging buffers when
691 * nfsd exits. When the system is halted or rebooted nfslogd
692 * may not have an opportunity to flush the buffers.
694 static void
695 nfsl_flush()
697 struct nfsl_flush_args nfa;
699 memset(&nfa, 0, sizeof (nfa));
700 nfa.version = NFSL_FLUSH_ARGS_VERS;
701 nfa.directive = NFSL_ALL; /* flush all asynchronously */
703 if (_nfssys(LOG_FLUSH, &nfa) < 0)
704 syslog(LOG_ERR, "_nfssys(LOG_FLUSH) failed: %s\n",
705 strerror(errno));
709 * SIGTERM handler.
710 * Flush logging buffers and exit.
712 static void
713 sigflush(int sig)
715 nfsl_flush();
716 _exit(0);
720 * SIGUSR1 handler.
722 * Request that server quiesce, then (nfsd) exit. For subsequent warm start.
724 * This is a Contracted Project Private interface, for the sole use
725 * of Sun Cluster HA-NFS. See PSARC/2004/497.
727 * Equivalent to SIGTERM handler if nfs_server_vers_max < QUIESCE_VERSMIN.
729 static void
730 quiesce(int sig)
732 int error;
733 int id = NFS_SVCPOOL_ID;
735 if (nfs_server_vers_max >= QUIESCE_VERSMIN) {
736 /* Request server quiesce at next shutdown */
737 error = _nfssys(NFS4_SVC_REQUEST_QUIESCE, &id);
740 * ENOENT is returned if there is no matching SVC pool
741 * for the id. Possibly because the pool is not yet setup.
742 * In this case, just exit as if no error. For all other errors,
743 * just return and allow caller to retry.
745 if (error && errno != ENOENT) {
746 syslog(LOG_ERR,
747 "_nfssys(NFS4_SVC_REQUEST_QUIESCE) failed: %s",
748 strerror(errno));
749 return;
753 /* Flush logging buffers */
754 nfsl_flush();
756 _exit(0);
760 * DSS: distributed stable storage.
761 * Create leaf directories as required, keeping an eye on path
762 * lengths. Calls exit(1) on failure.
763 * The pathnames passed in must already exist, and must be writeable by nfsd.
764 * Note: the leaf directories under NFS4_VAR_DIR are not created here;
765 * they're created at pkg install.
767 static void
768 dss_mkleafdirs(uint_t npaths, char **pathnames)
770 int i;
771 char *tmppath = NULL;
774 * Create the temporary storage used by dss_mkleafdir() here,
775 * rather than in that function, so that it only needs to be
776 * done once, rather than once for each call. Too big to put
777 * on the function's stack.
779 tmppath = (char *)malloc(MAXPATHLEN);
780 if (tmppath == NULL) {
781 syslog(LOG_ERR, "tmppath malloc failed. Exiting");
782 exit(1);
785 for (i = 0; i < npaths; i++) {
786 char *p = pathnames[i];
788 dss_mkleafdir(p, NFS4_DSS_STATE_LEAF, tmppath);
789 dss_mkleafdir(p, NFS4_DSS_OLDSTATE_LEAF, tmppath);
792 free(tmppath);
796 * Create "leaf" in "dir" (which must already exist).
797 * leaf: should start with a '/'
799 static void
800 dss_mkleafdir(char *dir, char *leaf, char *tmppath)
802 /* MAXPATHLEN includes the terminating NUL */
803 if (strlen(dir) + strlen(leaf) > MAXPATHLEN - 1) {
804 fprintf(stderr, "stable storage path too long: %s%s. "
805 "Exiting.\n", dir, leaf);
806 exit(1);
809 (void) snprintf(tmppath, MAXPATHLEN, "%s/%s", dir, leaf);
811 /* the directory may already exist: that's OK */
812 if (mkdir(tmppath, NFS4_DSS_DIR_MODE) == -1 && errno != EEXIST) {
813 fprintf(stderr, "error creating stable storage directory: "
814 "%s: %s. Exiting.\n", strerror(errno), tmppath);
815 exit(1);
820 * Create the storage dirs, and pass the path list to the kernel.
821 * This requires the nfssrv module to be loaded; the _nfssys() syscall
822 * will fail ENOTSUP if it is not.
823 * Use libnvpair(3LIB) to pass the data to the kernel.
825 static int
826 dss_init(uint_t npaths, char **pathnames)
828 int i, j, nskipped, error;
829 char *bufp;
830 uint32_t bufsize;
831 size_t buflen;
832 nvlist_t *nvl;
834 if (npaths > 1) {
836 * We need to remove duplicate paths; this might be user error
837 * in the general case, but HA-NFSv4 can also cause this.
838 * Sort the pathnames array, and NULL out duplicates,
839 * then write the non-NULL entries to a new array.
840 * Sorting will also allow the kernel to optimise its searches.
843 qsort(pathnames, npaths, sizeof (char *), qstrcmp);
845 /* now NULL out any duplicates */
846 i = 0; j = 1; nskipped = 0;
847 while (j < npaths) {
848 if (strcmp(pathnames[i], pathnames[j]) == 0) {
849 pathnames[j] = NULL;
850 j++;
851 nskipped++;
852 continue;
855 /* skip i over any of its NULLed duplicates */
856 i = j++;
859 /* finally, write the non-NULL entries to a new array */
860 if (nskipped > 0) {
861 int nreal;
862 size_t sz;
863 char **tmp_pathnames;
865 nreal = npaths - nskipped;
867 sz = nreal * sizeof (char *);
868 tmp_pathnames = (char **)malloc(sz);
869 if (tmp_pathnames == NULL) {
870 fprintf(stderr, "tmp_pathnames malloc "
871 "failed\n");
872 exit(1);
875 for (i = 0, j = 0; i < npaths; i++)
876 if (pathnames[i] != NULL)
877 tmp_pathnames[j++] = pathnames[i];
878 free(pathnames);
879 pathnames = tmp_pathnames;
880 npaths = nreal;
885 /* Create directories to store the distributed state files */
886 dss_mkleafdirs(npaths, pathnames);
888 /* Create the name-value pair list */
889 error = nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0);
890 if (error) {
891 fprintf(stderr, "nvlist_alloc failed: %s\n", strerror(errno));
892 return (1);
895 /* Add the pathnames array as a single name-value pair */
896 error = nvlist_add_string_array(nvl, NFS4_DSS_NVPAIR_NAME,
897 pathnames, npaths);
898 if (error) {
899 fprintf(stderr, "nvlist_add_string_array failed: %s\n",
900 strerror(errno));
901 nvlist_free(nvl);
902 return (1);
906 * Pack list into contiguous memory, for passing to kernel.
907 * nvlist_pack() will allocate the memory for the buffer,
908 * which we should free() when no longer needed.
909 * NV_ENCODE_XDR for safety across ILP32/LP64 kernel boundary.
911 bufp = NULL;
912 error = nvlist_pack(nvl, &bufp, &buflen, NV_ENCODE_XDR, 0);
913 if (error) {
914 fprintf(stderr, "nvlist_pack failed: %s\n", strerror(errno));
915 nvlist_free(nvl);
916 return (1);
919 /* Now we have the packed buffer, we no longer need the list */
920 nvlist_free(nvl);
923 * Let the kernel know in advance how big the buffer is.
924 * NOTE: we cannot just pass buflen, since size_t is a long, and
925 * thus a different size between ILP32 userland and LP64 kernel.
926 * Use an int for the transfer, since that should be big enough;
927 * this is a no-op at the moment, here, since nfsd is 32-bit, but
928 * that could change.
930 bufsize = (uint32_t)buflen;
931 error = _nfssys(NFS4_DSS_SETPATHS_SIZE, &bufsize);
932 if (error) {
933 fprintf(stderr,
934 "_nfssys(NFS4_DSS_SETPATHS_SIZE) failed: %s\n",
935 strerror(errno));
936 free(bufp);
937 return (1);
940 /* Pass the packed buffer to the kernel */
941 error = _nfssys(NFS4_DSS_SETPATHS, bufp);
942 if (error) {
943 fprintf(stderr,
944 "_nfssys(NFS4_DSS_SETPATHS) failed: %s\n", strerror(errno));
945 free(bufp);
946 return (1);
950 * The kernel has now unpacked the buffer and extracted the
951 * pathnames array, we no longer need the buffer.
953 free(bufp);
955 return (0);
959 * Quick sort string compare routine, for qsort.
960 * Needed to make arg types correct.
963 qstrcmp(const void *p1, const void *p2)
965 char *s1 = *((char **)p1);
966 char *s2 = *((char **)p2);
968 return (strcmp(s1, s2));