13910Sdougm /*
23910Sdougm  * CDDL HEADER START
33910Sdougm  *
43910Sdougm  * The contents of this file are subject to the terms of the
53910Sdougm  * Common Development and Distribution License (the "License").
63910Sdougm  * You may not use this file except in compliance with the License.
73910Sdougm  *
83910Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93910Sdougm  * or http://www.opensolaris.org/os/licensing.
103910Sdougm  * See the License for the specific language governing permissions
113910Sdougm  * and limitations under the License.
123910Sdougm  *
133910Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
143910Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153910Sdougm  * If applicable, add the following below this CDDL HEADER, with the
163910Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
173910Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
183910Sdougm  *
193910Sdougm  * CDDL HEADER END
203910Sdougm  */
213910Sdougm 
223910Sdougm /*
233910Sdougm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
243910Sdougm  * Use is subject to license terms.
253910Sdougm  */
263910Sdougm 
273910Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
283910Sdougm 
293910Sdougm /*
303910Sdougm  * NFS specific functions
313910Sdougm  */
323910Sdougm #include <stdio.h>
333910Sdougm #include <string.h>
343910Sdougm #include <ctype.h>
353910Sdougm #include <stdlib.h>
363910Sdougm #include <unistd.h>
373910Sdougm #include <zone.h>
383910Sdougm #include <errno.h>
393910Sdougm #include <locale.h>
403910Sdougm #include <signal.h>
413910Sdougm #include "libshare.h"
423910Sdougm #include "libshare_impl.h"
433910Sdougm #include <nfs/export.h>
443910Sdougm #include <pwd.h>
453910Sdougm #include <limits.h>
463910Sdougm #include <libscf.h>
473910Sdougm #include "nfslog_config.h"
483910Sdougm #include "nfslogtab.h"
493910Sdougm #include "libshare_nfs.h"
503910Sdougm #include <rpcsvc/daemon_utils.h>
513910Sdougm #include <nfs/nfs.h>
523910Sdougm 
533910Sdougm /* should really be in some global place */
543910Sdougm #define	DEF_WIN	30000
553910Sdougm #define	OPT_CHUNK	1024
563910Sdougm 
573910Sdougm int debug = 0;
583910Sdougm 
593910Sdougm 
603910Sdougm /* internal functions */
613910Sdougm static int nfs_init();
623910Sdougm static void nfs_fini();
633910Sdougm static int nfs_enable_share(sa_share_t);
643910Sdougm static int nfs_disable_share(char *);
653910Sdougm static int nfs_validate_property(sa_property_t, sa_optionset_t);
663910Sdougm static int nfs_validate_security_mode(char *);
673910Sdougm static int nfs_is_security_opt(char *);
683910Sdougm static int nfs_parse_legacy_options(sa_group_t, char *);
693910Sdougm static char *nfs_format_options(sa_group_t, int);
703910Sdougm static int nfs_set_proto_prop(sa_property_t);
713910Sdougm static sa_protocol_properties_t nfs_get_proto_set();
723910Sdougm static char *nfs_get_status();
733910Sdougm static char *nfs_space_alias(char *);
743910Sdougm 
753910Sdougm /*
763910Sdougm  * ops vector that provides the protocol specific info and operations
773910Sdougm  * for share management.
783910Sdougm  */
793910Sdougm 
803910Sdougm struct sa_plugin_ops sa_plugin_ops = {
813910Sdougm 	SA_PLUGIN_VERSION,
823910Sdougm 	"nfs",
833910Sdougm 	nfs_init,
843910Sdougm 	nfs_fini,
853910Sdougm 	nfs_enable_share,
863910Sdougm 	nfs_disable_share,
873910Sdougm 	nfs_validate_property,
883910Sdougm 	nfs_validate_security_mode,
893910Sdougm 	nfs_is_security_opt,
903910Sdougm 	nfs_parse_legacy_options,
913910Sdougm 	nfs_format_options,
923910Sdougm 	nfs_set_proto_prop,
933910Sdougm 	nfs_get_proto_set,
943910Sdougm 	nfs_get_status,
953910Sdougm 	nfs_space_alias,
963910Sdougm 	NULL,
973910Sdougm 	NULL
983910Sdougm };
993910Sdougm 
1003910Sdougm /*
1013910Sdougm  * list of support services needed
1023910Sdougm  * defines should come from head/rpcsvc/daemon_utils.h
1033910Sdougm  */
1043910Sdougm 
1053910Sdougm static char *service_list_default[] =
1063910Sdougm 	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NULL };
1073910Sdougm static char *service_list_logging[] =
1083910Sdougm 	{ STATD, LOCKD, MOUNTD, NFSD, NFSMAPID, RQUOTAD, NFSLOGD, NULL };
1093910Sdougm 
1103910Sdougm /*
1113910Sdougm  * option definitions.  Make sure to keep the #define for the option
1123910Sdougm  * index just before the entry it is the index for. Changing the order
1133910Sdougm  * can cause breakage.  E.g OPT_RW is index 1 and must precede the
1143910Sdougm  * line that includes the SHOPT_RW and OPT_RW entries.
1153910Sdougm  */
1163910Sdougm 
1173910Sdougm struct option_defs optdefs[] = {
1183910Sdougm #define	OPT_RO		0
1193910Sdougm 	{SHOPT_RO, OPT_RO, OPT_TYPE_ACCLIST},
1203910Sdougm #define	OPT_RW		1
1213910Sdougm 	{SHOPT_RW, OPT_RW, OPT_TYPE_ACCLIST},
1223910Sdougm #define	OPT_ROOT	2
1233910Sdougm 	{SHOPT_ROOT, OPT_ROOT, OPT_TYPE_ACCLIST},
1243910Sdougm #define	OPT_SECURE	3
1253910Sdougm 	{SHOPT_SECURE, OPT_SECURE, OPT_TYPE_DEPRECATED},
1263910Sdougm #define	OPT_ANON	4
1273910Sdougm 	{SHOPT_ANON, OPT_ANON, OPT_TYPE_USER},
1283910Sdougm #define	OPT_WINDOW	5
1293910Sdougm 	{SHOPT_WINDOW, OPT_WINDOW, OPT_TYPE_NUMBER},
1303910Sdougm #define	OPT_NOSUID	6
1313910Sdougm 	{SHOPT_NOSUID, OPT_NOSUID, OPT_TYPE_BOOLEAN},
1323910Sdougm #define	OPT_ACLOK	7
1333910Sdougm 	{SHOPT_ACLOK, OPT_ACLOK, OPT_TYPE_BOOLEAN},
1343910Sdougm #define	OPT_NOSUB	8
1353910Sdougm 	{SHOPT_NOSUB, OPT_NOSUB, OPT_TYPE_BOOLEAN},
1363910Sdougm #define	OPT_SEC		9
1373910Sdougm 	{SHOPT_SEC, OPT_SEC, OPT_TYPE_SECURITY},
1383910Sdougm #define	OPT_PUBLIC	10
1393910Sdougm 	{SHOPT_PUBLIC, OPT_PUBLIC, OPT_TYPE_BOOLEAN, OPT_SHARE_ONLY},
1403910Sdougm #define	OPT_INDEX	11
1413910Sdougm 	{SHOPT_INDEX, OPT_INDEX, OPT_TYPE_FILE},
1423910Sdougm #define	OPT_LOG		12
1433910Sdougm 	{SHOPT_LOG, OPT_LOG, OPT_TYPE_LOGTAG},
1443910Sdougm #define	OPT_CKSUM	13
1453910Sdougm 	{SHOPT_CKSUM, OPT_CKSUM, OPT_TYPE_STRINGSET},
1463910Sdougm #ifdef VOLATILE_FH_TEST	/* XXX added for testing volatile fh's only */
1473910Sdougm #define	OPT_VOLFH	14
1483910Sdougm 	{SHOPT_VOLFH, OPT_VOLFH},
1493910Sdougm #endif /* VOLATILE_FH_TEST */
1503910Sdougm 	NULL
1513910Sdougm };
1523910Sdougm 
1533910Sdougm /*
1543910Sdougm  * list of properties that are related to security flavors.
1553910Sdougm  */
1563910Sdougm static char *seclist[] = {
1573910Sdougm 	SHOPT_RO,
1583910Sdougm 	SHOPT_RW,
1593910Sdougm 	SHOPT_ROOT,
1603910Sdougm 	SHOPT_WINDOW,
1613910Sdougm 	NULL
1623910Sdougm };
1633910Sdougm 
1643910Sdougm /* structure for list of securities */
1653910Sdougm struct securities {
1663910Sdougm 	sa_security_t security;
1673910Sdougm 	struct securities *next;
1683910Sdougm };
1693910Sdougm 
1703910Sdougm /*
1713910Sdougm  * findopt(name)
1723910Sdougm  *
1733910Sdougm  * Lookup option "name" in the option table and return the table
1743910Sdougm  * index.
1753910Sdougm  */
1763910Sdougm 
1773910Sdougm static int
1783910Sdougm findopt(char *name)
1793910Sdougm {
1803910Sdougm 	int i;
1813910Sdougm 	if (name != NULL) {
182*4345Sdougm 		for (i = 0; optdefs[i].tag != NULL; i++) {
183*4345Sdougm 			if (strcmp(optdefs[i].tag, name) == 0)
184*4345Sdougm 				return (i);
185*4345Sdougm 		}
1863910Sdougm 	}
1873910Sdougm 	return (-1);
1883910Sdougm }
1893910Sdougm 
1903910Sdougm /*
1913910Sdougm  * gettype(name)
1923910Sdougm  *
1933910Sdougm  * Return the type of option "name".
1943910Sdougm  */
1953910Sdougm 
1963910Sdougm static int
1973910Sdougm gettype(char *name)
1983910Sdougm {
1993910Sdougm 	int optdef;
2003910Sdougm 
2013910Sdougm 	optdef = findopt(name);
2023910Sdougm 	if (optdef != -1)
203*4345Sdougm 		return (optdefs[optdef].type);
2043910Sdougm 	return (OPT_TYPE_ANY);
2053910Sdougm }
2063910Sdougm 
2073910Sdougm /*
2083910Sdougm  * nfs_validate_security_mode(mode)
2093910Sdougm  *
2103910Sdougm  * is the specified mode string a valid one for use with NFS?
2113910Sdougm  */
2123910Sdougm 
2133910Sdougm static int
2143910Sdougm nfs_validate_security_mode(char *mode)
2153910Sdougm {
2163910Sdougm 	seconfig_t secinfo;
2173910Sdougm 	int err;
2183910Sdougm 
2193910Sdougm 	(void) memset(&secinfo, '\0', sizeof (secinfo));
2203910Sdougm 	err = nfs_getseconfig_byname(mode, &secinfo);
2213910Sdougm 	if (err == SC_NOERROR)
222*4345Sdougm 		return (1);
2233910Sdougm 	return (0);
2243910Sdougm }
2253910Sdougm 
2263910Sdougm /*
2273910Sdougm  * nfs_is_security_opt(tok)
2283910Sdougm  *
2293910Sdougm  * check to see if tok represents an option that is only valid in some
2303910Sdougm  * security flavor.
2313910Sdougm  */
2323910Sdougm 
2333910Sdougm static int
2343910Sdougm nfs_is_security_opt(char *tok)
2353910Sdougm {
2363910Sdougm 	int i;
2373910Sdougm 
2383910Sdougm 	for (i = 0; seclist[i] != NULL; i++) {
239*4345Sdougm 		if (strcmp(tok, seclist[i]) == 0)
240*4345Sdougm 			return (1);
2413910Sdougm 	}
2423910Sdougm 	return (0);
2433910Sdougm }
2443910Sdougm 
2453910Sdougm /*
2463910Sdougm  * find_security(seclist, sec)
2473910Sdougm  *
2483910Sdougm  * Walk the current list of security flavors and return true if it is
2493910Sdougm  * present, else return false.
2503910Sdougm  */
2513910Sdougm 
2523910Sdougm static int
2533910Sdougm find_security(struct securities *seclist, sa_security_t sec)
2543910Sdougm {
2553910Sdougm 	while (seclist != NULL) {
256*4345Sdougm 		if (seclist->security == sec)
257*4345Sdougm 			return (1);
258*4345Sdougm 		seclist = seclist->next;
2593910Sdougm 	}
2603910Sdougm 	return (0);
2613910Sdougm }
2623910Sdougm 
2633910Sdougm /*
2643910Sdougm  * make_security_list(group, securitymodes, proto)
2653910Sdougm  *	go through the list of securitymodes and add them to the
2663910Sdougm  *	group's list of security optionsets. We also keep a list of
2673910Sdougm  *	those optionsets so we don't have to find them later. All of
2683910Sdougm  *	these will get copies of the same properties.
2693910Sdougm  */
2703910Sdougm 
2713910Sdougm static struct securities *
2723910Sdougm make_security_list(sa_group_t group, char *securitymodes, char *proto)
2733910Sdougm {
2743910Sdougm 	char *tok, *next = NULL;
2753910Sdougm 	struct securities *curp, *headp = NULL, *prev;
2763910Sdougm 	sa_security_t check;
2773910Sdougm 	int freetok = 0;
2783910Sdougm 
2793910Sdougm 	for (tok = securitymodes; tok != NULL; tok = next) {
280*4345Sdougm 		next = strchr(tok, ':');
281*4345Sdougm 		if (next != NULL)
282*4345Sdougm 			*next++ = '\0';
283*4345Sdougm 		if (strcmp(tok, "default") == 0) {
284*4345Sdougm 			/* resolve default into the real type */
285*4345Sdougm 			tok = nfs_space_alias(tok);
286*4345Sdougm 			freetok = 1;
287*4345Sdougm 		}
288*4345Sdougm 		check = sa_get_security(group, tok, proto);
2893910Sdougm 
290*4345Sdougm 		/* add to the security list if it isn't there already */
291*4345Sdougm 		if (check == NULL || !find_security(headp, check)) {
292*4345Sdougm 			curp = (struct securities *)calloc(1,
293*4345Sdougm 			    sizeof (struct securities));
294*4345Sdougm 			if (curp != NULL) {
295*4345Sdougm 				if (check == NULL) {
296*4345Sdougm 					curp->security = sa_create_security(
297*4345Sdougm 					    group, tok, proto);
298*4345Sdougm 				} else {
299*4345Sdougm 					curp->security = check;
300*4345Sdougm 				}
301*4345Sdougm 				/*
302*4345Sdougm 				 * note that the first time through the loop,
303*4345Sdougm 				 * headp will be NULL and prev will be
304*4345Sdougm 				 * undefined.  Since headp is NULL, we set
305*4345Sdougm 				 * both it and prev to the curp (first
306*4345Sdougm 				 * structure to be allocated).
307*4345Sdougm 				 *
308*4345Sdougm 				 * later passes through the loop will have
309*4345Sdougm 				 * headp not being NULL and prev will be used
310*4345Sdougm 				 * to allocate at the end of the list.
311*4345Sdougm 				 */
312*4345Sdougm 				if (headp == NULL) {
313*4345Sdougm 					headp = curp;
314*4345Sdougm 					prev = curp;
315*4345Sdougm 				} else {
316*4345Sdougm 					prev->next = curp;
317*4345Sdougm 					prev = curp;
318*4345Sdougm 				}
319*4345Sdougm 			}
3203910Sdougm 		}
3213910Sdougm 
322*4345Sdougm 		if (freetok) {
323*4345Sdougm 			freetok = 0;
324*4345Sdougm 			sa_free_attr_string(tok);
325*4345Sdougm 		}
3263910Sdougm 	}
3273910Sdougm 	return (headp);
3283910Sdougm }
3293910Sdougm 
3303910Sdougm static void
3313910Sdougm free_security_list(struct securities *sec)
3323910Sdougm {
3333910Sdougm 	struct securities *next;
3343910Sdougm 	if (sec != NULL) {
335*4345Sdougm 		for (next = sec->next; sec != NULL; sec = next) {
336*4345Sdougm 			next = sec->next;
337*4345Sdougm 			free(sec);
338*4345Sdougm 		}
3393910Sdougm 	}
3403910Sdougm }
3413910Sdougm 
3423910Sdougm /*
3433910Sdougm  * nfs_alistcat(str1, str2, sep)
3443910Sdougm  *
3453910Sdougm  * concatenate str1 and str2 into a new string using sep as a separate
3463910Sdougm  * character. If memory allocation fails, return NULL;
3473910Sdougm  */
3483910Sdougm 
3493910Sdougm static char *
3503910Sdougm nfs_alistcat(char *str1, char *str2, char sep)
3513910Sdougm {
3523910Sdougm 	char *newstr;
3533910Sdougm 	size_t len;
3543910Sdougm 
3553910Sdougm 	len = strlen(str1) + strlen(str2) + 2;
3563910Sdougm 	newstr = (char *)malloc(len);
3573910Sdougm 	if (newstr != NULL)
358*4345Sdougm 		(void) snprintf(newstr, len, "%s%c%s", str1, sep, str2);
3593910Sdougm 	return (newstr);
3603910Sdougm }
3613910Sdougm 
3623910Sdougm /*
3633910Sdougm  * add_security_prop(sec, name, value, persist)
3643910Sdougm  *
3653910Sdougm  * Add the property to the securities structure. This accumulates
3663910Sdougm  * properties for as part of parsing legacy options.
3673910Sdougm  */
3683910Sdougm 
3693910Sdougm static int
3703910Sdougm add_security_prop(struct securities *sec, char *name, char *value,
3713910Sdougm 			int persist, int iszfs)
3723910Sdougm {
3733910Sdougm 	sa_property_t prop;
3743910Sdougm 	int ret = SA_OK;
3753910Sdougm 
3763910Sdougm 	for (; sec != NULL; sec = sec->next) {
377*4345Sdougm 		if (value == NULL) {
378*4345Sdougm 			if (strcmp(name, SHOPT_RW) == 0 ||
379*4345Sdougm 			    strcmp(name, SHOPT_RO) == 0)
380*4345Sdougm 				value = "*";
381*4345Sdougm 			else
382*4345Sdougm 				value = "true";
383*4345Sdougm 		}
3843910Sdougm 
3853910Sdougm 		/*
3863910Sdougm 		 * Get the existing property, if it exists, so we can
3873910Sdougm 		 * determine what to do with it. The ro/rw/root
3883910Sdougm 		 * properties can be merged if multiple instances of
3893910Sdougm 		 * these properies are given. For example, if "rw"
3903910Sdougm 		 * exists with a value "host1" and a later token of
3913910Sdougm 		 * rw="host2" is seen, the values are merged into a
3923910Sdougm 		 * single rw="host1:host2".
3933910Sdougm 		 */
394*4345Sdougm 		prop = sa_get_property(sec->security, name);
3953910Sdougm 
396*4345Sdougm 		if (prop != NULL) {
397*4345Sdougm 			char *oldvalue;
398*4345Sdougm 			char *newvalue;
3993910Sdougm 
4003910Sdougm 			/*
401*4345Sdougm 			 * The security options of ro/rw/root might appear
402*4345Sdougm 			 * multiple times. If they do, the values need to be
403*4345Sdougm 			 * merged into an access list. If it was previously
404*4345Sdougm 			 * empty, the new value alone is added.
4053910Sdougm 			 */
406*4345Sdougm 			oldvalue = sa_get_property_attr(prop, "value");
407*4345Sdougm 			if (oldvalue != NULL) {
408*4345Sdougm 				/*
409*4345Sdougm 				 * The general case is to concatenate the new
410*4345Sdougm 				 * value onto the old value for multiple
411*4345Sdougm 				 * rw(ro/root) properties. A special case
412*4345Sdougm 				 * exists when either the old or new is the
413*4345Sdougm 				 * "all" case. In the special case, if both
414*4345Sdougm 				 * are "all", then it is "all", else if one is
415*4345Sdougm 				 * an access-list, that replaces the "all".
416*4345Sdougm 				 */
417*4345Sdougm 				if (strcmp(oldvalue, "*") == 0) {
418*4345Sdougm 					/* Replace old value with new value. */
419*4345Sdougm 					newvalue = strdup(value);
420*4345Sdougm 				} else if (strcmp(value, "*") == 0) {
421*4345Sdougm 					/*
422*4345Sdougm 					 * Keep old value and ignore
423*4345Sdougm 					 * the new value.
424*4345Sdougm 					 */
425*4345Sdougm 					newvalue = NULL;
426*4345Sdougm 				} else {
427*4345Sdougm 					/*
428*4345Sdougm 					 * Make a new list of old plus new
429*4345Sdougm 					 * access-list.
430*4345Sdougm 					 */
431*4345Sdougm 					newvalue = nfs_alistcat(oldvalue,
432*4345Sdougm 					    value, ':');
433*4345Sdougm 				}
4343910Sdougm 
435*4345Sdougm 				if (newvalue != NULL) {
436*4345Sdougm 					(void) sa_remove_property(prop);
437*4345Sdougm 					prop = sa_create_property(name,
438*4345Sdougm 					    newvalue);
439*4345Sdougm 					ret = sa_add_property(sec->security,
440*4345Sdougm 					    prop);
441*4345Sdougm 					free(newvalue);
442*4345Sdougm 				}
443*4345Sdougm 				if (oldvalue != NULL)
444*4345Sdougm 					sa_free_attr_string(oldvalue);
445*4345Sdougm 			}
446*4345Sdougm 		} else {
447*4345Sdougm 			prop = sa_create_property(name, value);
4483910Sdougm 			ret = sa_add_property(sec->security, prop);
4493910Sdougm 		}
450*4345Sdougm 		if (ret == SA_OK && !iszfs) {
451*4345Sdougm 			ret = sa_commit_properties(sec->security, !persist);
452*4345Sdougm 		}
4533910Sdougm 	}
4543910Sdougm 	return (ret);
4553910Sdougm }
4563910Sdougm 
4573910Sdougm /*
4583910Sdougm  * check to see if group/share is persistent.
4593910Sdougm  */
4603910Sdougm static int
4613910Sdougm is_persistent(sa_group_t group)
4623910Sdougm {
4633910Sdougm 	char *type;
4643910Sdougm 	int persist = 1;
4653910Sdougm 
4663910Sdougm 	type = sa_get_group_attr(group, "type");
4673910Sdougm 	if (type != NULL && strcmp(type, "persist") != 0)
468*4345Sdougm 		persist = 0;
4693910Sdougm 	if (type != NULL)
470*4345Sdougm 		sa_free_attr_string(type);
4713910Sdougm 	return (persist);
4723910Sdougm }
4733910Sdougm 
4743910Sdougm /*
4753910Sdougm  * invalid_security(options)
4763910Sdougm  *
4773910Sdougm  * search option string for any invalid sec= type.
4783910Sdougm  * return true (1) if any are not valid else false (0)
4793910Sdougm  */
4803910Sdougm static int
4813910Sdougm invalid_security(char *options)
4823910Sdougm {
4833910Sdougm 	char *copy, *base, *token, *value;
4843910Sdougm 	int ret = 0;
4853910Sdougm 
4863910Sdougm 	copy = strdup(options);
4873910Sdougm 	token = base = copy;
4883910Sdougm 	while (token != NULL && ret == 0) {
489*4345Sdougm 		token = strtok(base, ",");
490*4345Sdougm 		base = NULL;
491*4345Sdougm 		if (token != NULL) {
492*4345Sdougm 			value = strchr(token, '=');
493*4345Sdougm 			if (value != NULL)
494*4345Sdougm 				*value++ = '\0';
495*4345Sdougm 			if (strcmp(token, "sec") == 0) {
496*4345Sdougm 				/* HAVE security flavors so check them */
497*4345Sdougm 				char *tok, *next;
498*4345Sdougm 				for (next = NULL, tok = value; tok != NULL;
499*4345Sdougm 				    tok = next) {
500*4345Sdougm 					next = strchr(tok, ':');
501*4345Sdougm 					if (next != NULL)
502*4345Sdougm 						*next++ = '\0';
503*4345Sdougm 					ret = !nfs_validate_security_mode(tok);
504*4345Sdougm 					if (ret)
505*4345Sdougm 						break;
506*4345Sdougm 				}
507*4345Sdougm 			}
5083910Sdougm 		}
5093910Sdougm 	}
5103910Sdougm 	if (copy != NULL)
511*4345Sdougm 		free(copy);
5123910Sdougm 	return (ret);
5133910Sdougm }
5143910Sdougm 
5153910Sdougm /*
5163910Sdougm  * nfs_parse_legacy_options(group, options)
5173910Sdougm  *
5183910Sdougm  * Parse the old style options into internal format and store on the
5193910Sdougm  * specified group.  Group could be a share for full legacy support.
5203910Sdougm  */
5213910Sdougm 
5223910Sdougm static int
5233910Sdougm nfs_parse_legacy_options(sa_group_t group, char *options)
5243910Sdougm {
5253910Sdougm 	char *dup = strdup(options);
5263910Sdougm 	char *base;
5273910Sdougm 	char *token;
5283910Sdougm 	sa_optionset_t optionset;
5293910Sdougm 	struct securities *security_list = NULL;
5303910Sdougm 	sa_property_t prop;
5313910Sdougm 	int ret = SA_OK;
5323910Sdougm 	int iszfs = 0;
5333910Sdougm 	sa_group_t parent;
5343910Sdougm 	int persist = 0;
5353910Sdougm 	char *lasts;
5363910Sdougm 
5373910Sdougm 	/* do we have an existing optionset? */
5383910Sdougm 	optionset = sa_get_optionset(group, "nfs");
5393910Sdougm 	if (optionset == NULL) {
540*4345Sdougm 		/* didn't find existing optionset so create one */
541*4345Sdougm 		optionset = sa_create_optionset(group, "nfs");
5423910Sdougm 	} else {
5433910Sdougm 		/*
5443910Sdougm 		 * have an existing optionset so we need to compare
5453910Sdougm 		 * options in order to detect errors. For now, we
5463910Sdougm 		 * assume that the first optionset is the correct one
5473910Sdougm 		 * and the others will be the same. This needs to be
5483910Sdougm 		 * fixed before the final code is ready.
5493910Sdougm 		 */
550*4345Sdougm 		return (ret);
5513910Sdougm 	}
5523910Sdougm 
5533910Sdougm 	if (strcmp(options, SHOPT_RW) == 0) {
5543910Sdougm 		/*
5553910Sdougm 		 * there is a special case of only the option "rw"
5563910Sdougm 		 * being the default option. We don't have to do
5573910Sdougm 		 * anything.
5583910Sdougm 		 */
559*4345Sdougm 		return (ret);
5603910Sdougm 	}
5613910Sdougm 
5623910Sdougm 	/*
5633910Sdougm 	 * check if security types are present and validate them. If
5643910Sdougm 	 * any are not legal, fail.
5653910Sdougm 	 */
5663910Sdougm 
5673910Sdougm 	if (invalid_security(options)) {
568*4345Sdougm 		return (SA_INVALID_SECURITY);
5693910Sdougm 	}
5703910Sdougm 
5713910Sdougm 	/*
5723910Sdougm 	 * in order to not attempt to change ZFS properties unless
5733910Sdougm 	 * absolutely necessary, we never do it in the legacy parsing.
5743910Sdougm 	 */
5753910Sdougm 	if (sa_is_share(group)) {
576*4345Sdougm 		char *zfs;
577*4345Sdougm 		parent = sa_get_parent_group(group);
578*4345Sdougm 		if (parent != NULL) {
579*4345Sdougm 			zfs = sa_get_group_attr(parent, "zfs");
580*4345Sdougm 			if (zfs != NULL) {
581*4345Sdougm 				sa_free_attr_string(zfs);
582*4345Sdougm 				iszfs++;
583*4345Sdougm 			}
5843910Sdougm 		}
5853910Sdougm 	} else {
586*4345Sdougm 		iszfs = sa_group_is_zfs(group);
5873910Sdougm 	}
5883910Sdougm 
5893910Sdougm 	/*
5903910Sdougm 	 * we need to step through each option in the string and then
5913910Sdougm 	 * add either the option or the security option as needed. If
5923910Sdougm 	 * this is not a persistent share, don't commit to the
5933910Sdougm 	 * repository. If there is an error, we also want to abort the
5943910Sdougm 	 * processing and report it.
5953910Sdougm 	 */
5963910Sdougm 	persist = is_persistent(group);
5973910Sdougm 	base = dup;
5983910Sdougm 	token = dup;
5993910Sdougm 	lasts = NULL;
6003910Sdougm 	while (token != NULL && ret == SA_OK) {
601*4345Sdougm 		ret = SA_OK;
602*4345Sdougm 		token = strtok_r(base, ",", &lasts);
603*4345Sdougm 		base = NULL;
604*4345Sdougm 		if (token != NULL) {
605*4345Sdougm 			char *value;
6063910Sdougm 			/*
607*4345Sdougm 			 * if the option has a value, it will have an '=' to
608*4345Sdougm 			 * separate the name from the value. The following
609*4345Sdougm 			 * code will result in value != NULL and token
610*4345Sdougm 			 * pointing to just the name if there is a value.
6113910Sdougm 			 */
612*4345Sdougm 			value = strchr(token, '=');
613*4345Sdougm 			if (value != NULL) {
614*4345Sdougm 				*value++ = '\0';
615*4345Sdougm 			}
616*4345Sdougm 			if (strcmp(token, "sec") == 0 ||
617*4345Sdougm 			    strcmp(token, "secure") == 0) {
6183910Sdougm 				/*
619*4345Sdougm 				 * Once in security parsing, we only
620*4345Sdougm 				 * do security. We do need to move
621*4345Sdougm 				 * between the security node and the
622*4345Sdougm 				 * toplevel. The security tag goes on
623*4345Sdougm 				 * the root while the following ones
624*4345Sdougm 				 * go on the security.
6253910Sdougm 				 */
626*4345Sdougm 				if (security_list != NULL) {
627*4345Sdougm 					/*
628*4345Sdougm 					 * have an old list so close it and
629*4345Sdougm 					 * start the new
630*4345Sdougm 					 */
631*4345Sdougm 					free_security_list(security_list);
632*4345Sdougm 				}
633*4345Sdougm 				if (strcmp(token, "secure") == 0) {
634*4345Sdougm 					value = "dh";
635*4345Sdougm 				} else {
636*4345Sdougm 					if (value == NULL) {
637*4345Sdougm 						ret = SA_SYNTAX_ERR;
638*4345Sdougm 						break;
639*4345Sdougm 					}
640*4345Sdougm 				}
641*4345Sdougm 				security_list = make_security_list(group,
642*4345Sdougm 				    value, "nfs");
6433910Sdougm 			} else {
644*4345Sdougm 				/*
645*4345Sdougm 				 * Note that the "old" syntax allowed a
646*4345Sdougm 				 * default security model This must be
647*4345Sdougm 				 * accounted for and internally converted to
648*4345Sdougm 				 * "standard" security structure.
649*4345Sdougm 				 */
650*4345Sdougm 				if (nfs_is_security_opt(token)) {
651*4345Sdougm 					if (security_list == NULL) {
652*4345Sdougm 						/*
653*4345Sdougm 						 * need to have a
654*4345Sdougm 						 * security
655*4345Sdougm 						 * option. This will
656*4345Sdougm 						 * be "closed" when a
657*4345Sdougm 						 * defined "sec="
658*4345Sdougm 						 * option is
659*4345Sdougm 						 * seen. This is
660*4345Sdougm 						 * technically an
661*4345Sdougm 						 * error but will be
662*4345Sdougm 						 * allowed with
663*4345Sdougm 						 * warning.
664*4345Sdougm 						 */
665*4345Sdougm 						security_list =
666*4345Sdougm 						    make_security_list(group,
667*4345Sdougm 						    "default",
668*4345Sdougm 						    "nfs");
669*4345Sdougm 					}
670*4345Sdougm 					if (security_list != NULL) {
671*4345Sdougm 						ret = add_security_prop(
672*4345Sdougm 						    security_list, token,
673*4345Sdougm 						    value, persist, iszfs);
674*4345Sdougm 					} else {
675*4345Sdougm 						ret = SA_NO_MEMORY;
676*4345Sdougm 					}
677*4345Sdougm 				} else {
678*4345Sdougm 					/* regular options */
679*4345Sdougm 					if (value == NULL) {
680*4345Sdougm 						if (strcmp(token, SHOPT_RW) ==
681*4345Sdougm 						    0 || strcmp(token,
682*4345Sdougm 						    SHOPT_RO) == 0) {
683*4345Sdougm 							value = "*";
684*4345Sdougm 						} else {
685*4345Sdougm 							value = "global";
686*4345Sdougm 							if (strcmp(token,
687*4345Sdougm 							    SHOPT_LOG) != 0) {
688*4345Sdougm 								value = "true";
689*4345Sdougm 							}
690*4345Sdougm 						}
691*4345Sdougm 						prop = sa_create_property(
692*4345Sdougm 						    token, value);
693*4345Sdougm 						ret =
694*4345Sdougm 						    sa_add_property(optionset,
695*4345Sdougm 						    prop);
696*4345Sdougm 						if (ret != SA_OK)
697*4345Sdougm 							break;
698*4345Sdougm 					}
699*4345Sdougm 					if (!iszfs) {
700*4345Sdougm 						ret = sa_commit_properties(
701*4345Sdougm 						    optionset, !persist);
702*4345Sdougm 					}
703*4345Sdougm 				}
7043910Sdougm 			}
7053910Sdougm 		}
7063910Sdougm 	}
7073910Sdougm 	if (security_list != NULL)
708*4345Sdougm 		free_security_list(security_list);
7093910Sdougm 	if (dup != NULL)
710*4345Sdougm 		free(dup);
7113910Sdougm 	return (ret);
7123910Sdougm }
7133910Sdougm 
7143910Sdougm /*
7153910Sdougm  * is_a_number(number)
7163910Sdougm  *
7173910Sdougm  * is the string a number in one of the forms we want to use?
7183910Sdougm  */
7193910Sdougm 
7203910Sdougm static int
7213910Sdougm is_a_number(char *number)
7223910Sdougm {
7233910Sdougm 	int ret = 1;
7243910Sdougm 	int hex = 0;
7253910Sdougm 
7263910Sdougm 	if (strncmp(number, "0x", 2) == 0) {
727*4345Sdougm 		number += 2;
728*4345Sdougm 		hex = 1;
729*4345Sdougm 	} else if (*number == '-') {
730*4345Sdougm 		number++; /* skip the minus */
731*4345Sdougm 	}
7323910Sdougm 	while (ret == 1 && *number != '\0') {
733*4345Sdougm 		if (hex) {
734*4345Sdougm 			ret = isxdigit(*number++);
735*4345Sdougm 		} else {
736*4345Sdougm 			ret = isdigit(*number++);
737*4345Sdougm 		}
7383910Sdougm 	}
7393910Sdougm 	return (ret);
7403910Sdougm }
7413910Sdougm 
7423910Sdougm /*
7433910Sdougm  * Look for the specified tag in the configuration file. If it is found,
7443910Sdougm  * enable logging and set the logging configuration information for exp.
7453910Sdougm  */
7463910Sdougm static void
7473910Sdougm configlog(struct exportdata *exp, char *tag)
7483910Sdougm {
7493910Sdougm 	nfsl_config_t *configlist = NULL, *configp;
7503910Sdougm 	int error = 0;
7513910Sdougm 	char globaltag[] = DEFAULTTAG;
7523910Sdougm 
7533910Sdougm 	/*
7543910Sdougm 	 * Sends config errors to stderr
7553910Sdougm 	 */
7563910Sdougm 	nfsl_errs_to_syslog = B_FALSE;
7573910Sdougm 
7583910Sdougm 	/*
7593910Sdougm 	 * get the list of configuration settings
7603910Sdougm 	 */
7613910Sdougm 	error = nfsl_getconfig_list(&configlist);
7623910Sdougm 	if (error) {
7633910Sdougm 		(void) fprintf(stderr,
764*4345Sdougm 		    dgettext(TEXT_DOMAIN, "Cannot get log configuration: %s\n"),
765*4345Sdougm 		    strerror(error));
7663910Sdougm 	}
7673910Sdougm 
7683910Sdougm 	if (tag == NULL)
7693910Sdougm 		tag = globaltag;
7703910Sdougm 	if ((configp = nfsl_findconfig(configlist, tag, &error)) == NULL) {
7713910Sdougm 		nfsl_freeconfig_list(&configlist);
7723910Sdougm 		(void) fprintf(stderr,
773*4345Sdougm 		    dgettext(TEXT_DOMAIN, "No tags matching \"%s\"\n"), tag);
7743910Sdougm 		/* bad configuration */
7753910Sdougm 		error = ENOENT;
7763910Sdougm 		goto err;
7773910Sdougm 	}
7783910Sdougm 
7793910Sdougm 	if ((exp->ex_tag = strdup(tag)) == NULL) {
7803910Sdougm 		error = ENOMEM;
7813910Sdougm 		goto out;
7823910Sdougm 	}
7833910Sdougm 	if ((exp->ex_log_buffer = strdup(configp->nc_bufferpath)) == NULL) {
7843910Sdougm 		error = ENOMEM;
7853910Sdougm 		goto out;
7863910Sdougm 	}
7873910Sdougm 	exp->ex_flags |= EX_LOG;
7883910Sdougm 	if (configp->nc_rpclogpath != NULL)
7893910Sdougm 		exp->ex_flags |= EX_LOG_ALLOPS;
7903910Sdougm out:
7913910Sdougm 	if (configlist != NULL)
792*4345Sdougm 		nfsl_freeconfig_list(&configlist);
7933910Sdougm 
7943910Sdougm err:
7953910Sdougm 	if (error != 0) {
7963910Sdougm 		if (exp->ex_flags != NULL)
7973910Sdougm 			free(exp->ex_tag);
7983910Sdougm 		if (exp->ex_log_buffer != NULL)
7993910Sdougm 			free(exp->ex_log_buffer);
8003910Sdougm 		(void) fprintf(stderr,
801*4345Sdougm 		    dgettext(TEXT_DOMAIN, "Cannot set log configuration: %s\n"),
802*4345Sdougm 		    strerror(error));
8033910Sdougm 	}
8043910Sdougm }
8053910Sdougm 
8063910Sdougm /*
8073910Sdougm  * fill_export_from_optionset(export, optionset)
8083910Sdougm  *
8093910Sdougm  * In order to share, we need to set all the possible general options
8103910Sdougm  * into the export structure. Share info will be filled in by the
8113910Sdougm  * caller. Various property values get turned into structure specific
8123910Sdougm  * values.
8133910Sdougm  */
8143910Sdougm 
8153910Sdougm static int
8163910Sdougm fill_export_from_optionset(struct exportdata *export, sa_optionset_t optionset)
8173910Sdougm {
8183910Sdougm 	sa_property_t option;
8193910Sdougm 	int ret = SA_OK;
8203910Sdougm 
8213910Sdougm 	for (option = sa_get_property(optionset, NULL);
822*4345Sdougm 	    option != NULL; option = sa_get_next_property(option)) {
823*4345Sdougm 		char *name;
824*4345Sdougm 		char *value;
825*4345Sdougm 		uint32_t val;
8263910Sdougm 
827*4345Sdougm 		/*
828*4345Sdougm 		 * since options may be set/reset multiple times, always do an
829*4345Sdougm 		 * explicit set or clear of the option. This allows defaults
830*4345Sdougm 		 * to be set and then the protocol specifici to override.
831*4345Sdougm 		 */
8323910Sdougm 
833*4345Sdougm 		name = sa_get_property_attr(option, "type");
834*4345Sdougm 		value = sa_get_property_attr(option, "value");
835*4345Sdougm 		switch (findopt(name)) {
836*4345Sdougm 		case OPT_ANON:
837*4345Sdougm 			if (value != NULL && is_a_number(value)) {
838*4345Sdougm 				val = strtoul(value, NULL, 0);
839*4345Sdougm 			} else {
840*4345Sdougm 				struct passwd *pw;
841*4345Sdougm 				pw = getpwnam(value != NULL ? value : "nobody");
842*4345Sdougm 				if (pw != NULL) {
843*4345Sdougm 					val = pw->pw_uid;
844*4345Sdougm 				} else {
845*4345Sdougm 					val = UID_NOBODY;
846*4345Sdougm 				}
847*4345Sdougm 				endpwent();
848*4345Sdougm 			}
849*4345Sdougm 			export->ex_anon = val;
850*4345Sdougm 			break;
851*4345Sdougm 		case OPT_NOSUID:
852*4345Sdougm 			if (value != NULL && (strcasecmp(value, "true") == 0 ||
853*4345Sdougm 			    strcmp(value, "1") == 0))
854*4345Sdougm 				export->ex_flags |= EX_NOSUID;
855*4345Sdougm 			else
856*4345Sdougm 				export->ex_flags &= ~EX_NOSUID;
857*4345Sdougm 			break;
858*4345Sdougm 		case OPT_ACLOK:
859*4345Sdougm 			if (value != NULL && (strcasecmp(value, "true") == 0 ||
860*4345Sdougm 			    strcmp(value, "1") == 0))
861*4345Sdougm 				export->ex_flags |= EX_ACLOK;
862*4345Sdougm 			else
863*4345Sdougm 				export->ex_flags &= ~EX_ACLOK;
864*4345Sdougm 			break;
865*4345Sdougm 		case OPT_NOSUB:
866*4345Sdougm 			if (value != NULL && (strcasecmp(value, "true") == 0 ||
867*4345Sdougm 			    strcmp(value, "1") == 0))
868*4345Sdougm 				export->ex_flags |= EX_NOSUB;
869*4345Sdougm 			else
870*4345Sdougm 				export->ex_flags &= ~EX_NOSUB;
871*4345Sdougm 			break;
872*4345Sdougm 		case OPT_PUBLIC:
873*4345Sdougm 			if (value != NULL && (strcasecmp(value, "true") == 0 ||
874*4345Sdougm 			    strcmp(value, "1") == 0))
875*4345Sdougm 				export->ex_flags |= EX_PUBLIC;
876*4345Sdougm 			else
877*4345Sdougm 				export->ex_flags &= ~EX_PUBLIC;
878*4345Sdougm 			break;
879*4345Sdougm 		case OPT_INDEX:
880*4345Sdougm 			if (value != NULL && (strcmp(value, "..") == 0 ||
881*4345Sdougm 			    strchr(value, '/') != NULL)) {
882*4345Sdougm 				/* this is an error */
883*4345Sdougm 				(void) printf(dgettext(TEXT_DOMAIN,
884*4345Sdougm 				    "NFS: index=\"%s\" not valid;"
885*4345Sdougm 				    "must be a filename.\n"),
886*4345Sdougm 				    value);
887*4345Sdougm 				break;
888*4345Sdougm 			}
889*4345Sdougm 			if (value != NULL && *value != '\0' &&
890*4345Sdougm 			    strcmp(value, ".") != 0) {
891*4345Sdougm 				/* valid index file string */
892*4345Sdougm 				if (export->ex_index != NULL) {
893*4345Sdougm 					/* left over from "default" */
894*4345Sdougm 					free(export->ex_index);
895*4345Sdougm 				}
896*4345Sdougm 				/* remember to free */
897*4345Sdougm 				export->ex_index = strdup(value);
898*4345Sdougm 				if (export->ex_index == NULL) {
899*4345Sdougm 					(void) printf(dgettext(TEXT_DOMAIN,
900*4345Sdougm 					    "NFS: out of memory setting "
901*4345Sdougm 					    "index property\n"));
902*4345Sdougm 					break;
903*4345Sdougm 				}
904*4345Sdougm 				export->ex_flags |= EX_INDEX;
905*4345Sdougm 			}
906*4345Sdougm 			break;
907*4345Sdougm 		case OPT_LOG:
908*4345Sdougm 			if (value == NULL)
909*4345Sdougm 				value = strdup("global");
910*4345Sdougm 			if (value != NULL)
911*4345Sdougm 				configlog(export,
912*4345Sdougm 				    strlen(value) ? value : "global");
913*4345Sdougm 			break;
914*4345Sdougm 		default:
915*4345Sdougm 			/* have a syntactic error */
916*4345Sdougm 			(void) printf(dgettext(TEXT_DOMAIN,
917*4345Sdougm 			    "NFS: unrecognized option %s=%s\n"),
918*4345Sdougm 			    name, value != NULL ? value : "");
919*4345Sdougm 			break;
9203910Sdougm 		}
921*4345Sdougm 		if (name != NULL)
922*4345Sdougm 			sa_free_attr_string(name);
9233910Sdougm 		if (value != NULL)
924*4345Sdougm 			sa_free_attr_string(value);
9253910Sdougm 	}
9263910Sdougm 	return (ret);
9273910Sdougm }
9283910Sdougm 
9293910Sdougm /*
9303910Sdougm  * cleanup_export(export)
9313910Sdougm  *
9323910Sdougm  * Cleanup the allocated areas so we don't leak memory
9333910Sdougm  */
9343910Sdougm 
9353910Sdougm static void
9363910Sdougm cleanup_export(struct exportdata *export)
9373910Sdougm {
9383910Sdougm 	int i;
9393910Sdougm 
9403910Sdougm 	if (export->ex_index != NULL)
941*4345Sdougm 		free(export->ex_index);
9423910Sdougm 	if (export->ex_secinfo != NULL) {
943*4345Sdougm 		for (i = 0; i < export->ex_seccnt; i++)
944*4345Sdougm 			if (export->ex_secinfo[i].s_rootnames != NULL)
945*4345Sdougm 				free(export->ex_secinfo[i].s_rootnames);
946*4345Sdougm 		free(export->ex_secinfo);
9473910Sdougm 	}
9483910Sdougm }
9493910Sdougm 
9503910Sdougm /*
9513910Sdougm  * Given a seconfig entry and a colon-separated
9523910Sdougm  * list of names, allocate an array big enough
9533910Sdougm  * to hold the root list, then convert each name to
9543910Sdougm  * a principal name according to the security
9553910Sdougm  * info and assign it to an array element.
9563910Sdougm  * Return the array and its size.
9573910Sdougm  */
9583910Sdougm static caddr_t *
9593910Sdougm get_rootnames(seconfig_t *sec, char *list, int *count)
9603910Sdougm {
9613910Sdougm 	caddr_t *a;
9623910Sdougm 	int c, i;
9633910Sdougm 	char *host, *p;
9643910Sdougm 
9653910Sdougm 	/*
9663910Sdougm 	 * Count the number of strings in the list.
9673910Sdougm 	 * This is the number of colon separators + 1.
9683910Sdougm 	 */
9693910Sdougm 	c = 1;
9703910Sdougm 	for (p = list; *p; p++)
9713910Sdougm 		if (*p == ':')
9723910Sdougm 			c++;
9733910Sdougm 	*count = c;
9743910Sdougm 
9753910Sdougm 	a = (caddr_t *)malloc(c * sizeof (char *));
9763910Sdougm 	if (a == NULL) {
9773910Sdougm 		(void) printf(dgettext(TEXT_DOMAIN,
978*4345Sdougm 		    "get_rootnames: no memory\n"));
9793910Sdougm 	} else {
980*4345Sdougm 		for (i = 0; i < c; i++) {
981*4345Sdougm 			host = strtok(list, ":");
982*4345Sdougm 			if (!nfs_get_root_principal(sec, host, &a[i])) {
983*4345Sdougm 				free(a);
984*4345Sdougm 				a = NULL;
985*4345Sdougm 				break;
986*4345Sdougm 			}
987*4345Sdougm 			list = NULL;
9883910Sdougm 		}
9893910Sdougm 	}
9903910Sdougm 
9913910Sdougm 	return (a);
9923910Sdougm }
9933910Sdougm 
9943910Sdougm /*
9953910Sdougm  * fill_security_from_secopts(sp, secopts)
9963910Sdougm  *
9973910Sdougm  * Fill the secinfo structure from the secopts optionset.
9983910Sdougm  */
9993910Sdougm 
10003910Sdougm static int
10013910Sdougm fill_security_from_secopts(struct secinfo *sp, sa_security_t secopts)
10023910Sdougm {
10033910Sdougm 	sa_property_t prop;
10043910Sdougm 	char *type;
10053910Sdougm 	int longform;
10063910Sdougm 	int err = SC_NOERROR;
10073910Sdougm 
10083910Sdougm 	type = sa_get_security_attr(secopts, "sectype");
10093910Sdougm 	if (type != NULL) {
1010*4345Sdougm 		/* named security type needs secinfo to be filled in */
1011*4345Sdougm 		err = nfs_getseconfig_byname(type, &sp->s_secinfo);
1012*4345Sdougm 		sa_free_attr_string(type);
1013*4345Sdougm 		if (err != SC_NOERROR)
1014*4345Sdougm 			return (err);
10153910Sdougm 	} else {
1016*4345Sdougm 		/* default case */
1017*4345Sdougm 		err = nfs_getseconfig_default(&sp->s_secinfo);
1018*4345Sdougm 		if (err != SC_NOERROR)
1019*4345Sdougm 			return (err);
10203910Sdougm 	}
10213910Sdougm 
10223910Sdougm 	err = SA_OK;
10233910Sdougm 	for (prop = sa_get_property(secopts, NULL);
1024*4345Sdougm 	    prop != NULL && err == SA_OK;
1025*4345Sdougm 	    prop = sa_get_next_property(prop)) {
1026*4345Sdougm 		char *name;
1027*4345Sdougm 		char *value;
10283910Sdougm 
1029*4345Sdougm 		name = sa_get_property_attr(prop, "type");
1030*4345Sdougm 		value = sa_get_property_attr(prop, "value");
10313910Sdougm 
1032*4345Sdougm 		longform = value != NULL && strcmp(value, "*") != 0;
10333910Sdougm 
1034*4345Sdougm 		switch (findopt(name)) {
1035*4345Sdougm 		case OPT_RO:
1036*4345Sdougm 			sp->s_flags |= longform ? M_ROL : M_RO;
1037*4345Sdougm 			break;
1038*4345Sdougm 		case OPT_RW:
1039*4345Sdougm 			sp->s_flags |= longform ? M_RWL : M_RW;
1040*4345Sdougm 			break;
1041*4345Sdougm 		case OPT_ROOT:
1042*4345Sdougm 			sp->s_flags |= M_ROOT;
1043*4345Sdougm 			/*
1044*4345Sdougm 			 * if we are using AUTH_UNIX, handle like other things
1045*4345Sdougm 			 * such as RO/RW
1046*4345Sdougm 			 */
1047*4345Sdougm 			if (sp->s_secinfo.sc_rpcnum == AUTH_UNIX)
1048*4345Sdougm 				continue;
1049*4345Sdougm 			/* not AUTH_UNIX */
1050*4345Sdougm 			if (value != NULL) {
1051*4345Sdougm 				sp->s_rootnames = get_rootnames(&sp->s_secinfo,
1052*4345Sdougm 				    value, &sp->s_rootcnt);
1053*4345Sdougm 				if (sp->s_rootnames == NULL) {
1054*4345Sdougm 					err = SA_BAD_VALUE;
1055*4345Sdougm 					(void) fprintf(stderr,
1056*4345Sdougm 					    dgettext(TEXT_DOMAIN,
1057*4345Sdougm 					    "Bad root list\n"));
1058*4345Sdougm 				}
1059*4345Sdougm 			}
1060*4345Sdougm 			break;
1061*4345Sdougm 		case OPT_WINDOW:
1062*4345Sdougm 			if (value != NULL) {
1063*4345Sdougm 				sp->s_window = atoi(value);
1064*4345Sdougm 				/* just in case */
1065*4345Sdougm 				if (sp->s_window < 0)
1066*4345Sdougm 					sp->s_window = DEF_WIN;
1067*4345Sdougm 			}
1068*4345Sdougm 			break;
1069*4345Sdougm 		default:
1070*4345Sdougm 			break;
10713910Sdougm 		}
1072*4345Sdougm 		if (name != NULL)
1073*4345Sdougm 			sa_free_attr_string(name);
1074*4345Sdougm 		if (value != NULL)
1075*4345Sdougm 			sa_free_attr_string(value);
10763910Sdougm 	}
10773910Sdougm 	/* if rw/ro options not set, use default of RW */
10783910Sdougm 	if ((sp->s_flags & NFS_RWMODES) == 0)
1079*4345Sdougm 		sp->s_flags |= M_RW;
10803910Sdougm 	return (err);
10813910Sdougm }
10823910Sdougm 
10833910Sdougm /*
10843910Sdougm  * This is for testing only
10853910Sdougm  * It displays the export structure that
10863910Sdougm  * goes into the kernel.
10873910Sdougm  */
10883910Sdougm static void
10893910Sdougm printarg(char *path, struct exportdata *ep)
10903910Sdougm {
10913910Sdougm 	int i, j;
10923910Sdougm 	struct secinfo *sp;
10933910Sdougm 
10943910Sdougm 	if (debug == 0)
1095*4345Sdougm 		return;
10963910Sdougm 
10973910Sdougm 	(void) printf("%s:\n", path);
10983910Sdougm 	(void) printf("\tex_version = %d\n", ep->ex_version);
10993910Sdougm 	(void) printf("\tex_path = %s\n", ep->ex_path);
11003910Sdougm 	(void) printf("\tex_pathlen = %ld\n", (ulong_t)ep->ex_pathlen);
11013910Sdougm 	(void) printf("\tex_flags: (0x%02x) ", ep->ex_flags);
11023910Sdougm 	if (ep->ex_flags & EX_NOSUID)
11033910Sdougm 		(void) printf("NOSUID ");
11043910Sdougm 	if (ep->ex_flags & EX_ACLOK)
11053910Sdougm 		(void) printf("ACLOK ");
11063910Sdougm 	if (ep->ex_flags & EX_PUBLIC)
11073910Sdougm 		(void) printf("PUBLIC ");
11083910Sdougm 	if (ep->ex_flags & EX_NOSUB)
11093910Sdougm 		(void) printf("NOSUB ");
11103910Sdougm 	if (ep->ex_flags & EX_LOG)
11113910Sdougm 		(void) printf("LOG ");
11123910Sdougm 	if (ep->ex_flags & EX_LOG_ALLOPS)
11133910Sdougm 		(void) printf("LOG_ALLOPS ");
11143910Sdougm 	if (ep->ex_flags == 0)
11153910Sdougm 		(void) printf("(none)");
11163910Sdougm 	(void) 	printf("\n");
11173910Sdougm 	if (ep->ex_flags & EX_LOG) {
11183910Sdougm 		(void) printf("\tex_log_buffer = %s\n",
1119*4345Sdougm 		    (ep->ex_log_buffer ? ep->ex_log_buffer : "(NULL)"));
11203910Sdougm 		(void) printf("\tex_tag = %s\n",
1121*4345Sdougm 		    (ep->ex_tag ? ep->ex_tag : "(NULL)"));
11223910Sdougm 	}
11233910Sdougm 	(void) printf("\tex_anon = %d\n", ep->ex_anon);
11243910Sdougm 	(void) printf("\tex_seccnt = %d\n", ep->ex_seccnt);
11253910Sdougm 	(void) printf("\n");
11263910Sdougm 	for (i = 0; i < ep->ex_seccnt; i++) {
11273910Sdougm 		sp = &ep->ex_secinfo[i];
11283910Sdougm 		(void) printf("\t\ts_secinfo = %s\n", sp->s_secinfo.sc_name);
11293910Sdougm 		(void) printf("\t\ts_flags: (0x%02x) ", sp->s_flags);
11303910Sdougm 		if (sp->s_flags & M_ROOT) (void) printf("M_ROOT ");
11313910Sdougm 		if (sp->s_flags & M_RO) (void) printf("M_RO ");
11323910Sdougm 		if (sp->s_flags & M_ROL) (void) printf("M_ROL ");
11333910Sdougm 		if (sp->s_flags & M_RW) (void) printf("M_RW ");
11343910Sdougm 		if (sp->s_flags & M_RWL) (void) printf("M_RWL ");
11353910Sdougm 		if (sp->s_flags == 0) (void) printf("(none)");
11363910Sdougm 		(void) printf("\n");
11373910Sdougm 		(void) printf("\t\ts_window = %d\n", sp->s_window);
11383910Sdougm 		(void) printf("\t\ts_rootcnt = %d ", sp->s_rootcnt);
11393910Sdougm 		(void) fflush(stdout);
11403910Sdougm 		for (j = 0; j < sp->s_rootcnt; j++)
11413910Sdougm 			(void) printf("%s ", sp->s_rootnames[j] ?
1142*4345Sdougm 			    sp->s_rootnames[j] : "<null>");
11433910Sdougm 		(void) printf("\n\n");
11443910Sdougm 	}
11453910Sdougm }
11463910Sdougm 
11473910Sdougm /*
11483910Sdougm  * count_security(opts)
11493910Sdougm  *
11503910Sdougm  * Count the number of security types (flavors). The optionset has
11513910Sdougm  * been populated with the security flavors as a holding mechanism.
11523910Sdougm  * We later use this number to allocate data structures.
11533910Sdougm  */
11543910Sdougm 
11553910Sdougm static int
11563910Sdougm count_security(sa_optionset_t opts)
11573910Sdougm {
11583910Sdougm 	int count = 0;
11593910Sdougm 	sa_property_t prop;
11603910Sdougm 	if (opts != NULL) {
1161*4345Sdougm 		for (prop = sa_get_property(opts, NULL); prop != NULL;
1162*4345Sdougm 		    prop = sa_get_next_property(prop)) {
1163*4345Sdougm 			count++;
1164*4345Sdougm 		}
11653910Sdougm 	}
11663910Sdougm 	return (count);
11673910Sdougm }
11683910Sdougm 
11693910Sdougm /*
11703910Sdougm  * nfs_sprint_option(rbuff, rbuffsize, incr, prop, sep)
11713910Sdougm  *
11723910Sdougm  * provides a mechanism to format NFS properties into legacy output
11733910Sdougm  * format. If the buffer would overflow, it is reallocated and grown
11743910Sdougm  * as appropriate. Special cases of converting internal form of values
11753910Sdougm  * to those used by "share" are done. this function does one property
11763910Sdougm  * at a time.
11773910Sdougm  */
11783910Sdougm 
11793910Sdougm static void
11803910Sdougm nfs_sprint_option(char **rbuff, size_t *rbuffsize, size_t incr,
11813910Sdougm 			sa_property_t prop, int sep)
11823910Sdougm {
11833910Sdougm 	char *name;
11843910Sdougm 	char *value;
11853910Sdougm 	int curlen;
11863910Sdougm 	char *buff = *rbuff;
11873910Sdougm 	size_t buffsize = *rbuffsize;
11883910Sdougm 
11893910Sdougm 	name = sa_get_property_attr(prop, "type");
11903910Sdougm 	value = sa_get_property_attr(prop, "value");
11913910Sdougm 	if (buff != NULL)
1192*4345Sdougm 		curlen = strlen(buff);
11933910Sdougm 	else
1194*4345Sdougm 		curlen = 0;
11953910Sdougm 	if (name != NULL) {
1196*4345Sdougm 		int len;
1197*4345Sdougm 		len = strlen(name) + sep;
11983910Sdougm 
11993910Sdougm 		/*
12003910Sdougm 		 * A future RFE would be to replace this with more
12013910Sdougm 		 * generic code and to possibly handle more types.
12023910Sdougm 		 */
1203*4345Sdougm 		switch (gettype(name)) {
1204*4345Sdougm 		case OPT_TYPE_BOOLEAN:
1205*4345Sdougm 			if (value != NULL && strcasecmp(value, "false") == 0)
1206*4345Sdougm 				*name = '\0';
1207*4345Sdougm 			if (value != NULL)
1208*4345Sdougm 				sa_free_attr_string(value);
1209*4345Sdougm 			value = NULL;
1210*4345Sdougm 			break;
1211*4345Sdougm 		case OPT_TYPE_ACCLIST:
1212*4345Sdougm 			if (value != NULL && strcmp(value, "*") == 0) {
1213*4345Sdougm 				sa_free_attr_string(value);
1214*4345Sdougm 				value = NULL;
1215*4345Sdougm 			} else {
1216*4345Sdougm 				if (value != NULL)
1217*4345Sdougm 					len += 1 + strlen(value);
1218*4345Sdougm 			}
1219*4345Sdougm 			break;
1220*4345Sdougm 		case OPT_TYPE_LOGTAG:
1221*4345Sdougm 			if (value != NULL && strlen(value) == 0) {
1222*4345Sdougm 				sa_free_attr_string(value);
1223*4345Sdougm 				value = NULL;
1224*4345Sdougm 			} else {
1225*4345Sdougm 				if (value != NULL)
1226*4345Sdougm 					len += 1 + strlen(value);
1227*4345Sdougm 			}
1228*4345Sdougm 			break;
1229*4345Sdougm 		default:
1230*4345Sdougm 			if (value != NULL)
1231*4345Sdougm 				len += 1 + strlen(value);
1232*4345Sdougm 			break;
12333910Sdougm 		}
1234*4345Sdougm 		while (buffsize <= (curlen + len)) {
1235*4345Sdougm 			/* need more room */
1236*4345Sdougm 			buffsize += incr;
1237*4345Sdougm 			buff = realloc(buff, buffsize);
1238*4345Sdougm 			if (buff == NULL) {
1239*4345Sdougm 				/* realloc failed so free everything */
1240*4345Sdougm 				if (*rbuff != NULL)
1241*4345Sdougm 					free(*rbuff);
1242*4345Sdougm 			}
1243*4345Sdougm 			*rbuff = buff;
1244*4345Sdougm 			*rbuffsize = buffsize;
1245*4345Sdougm 			if (buff == NULL) {
1246*4345Sdougm 				return;
1247*4345Sdougm 			}
12483910Sdougm 		}
1249*4345Sdougm 		if (buff == NULL)
1250*4345Sdougm 			return;
1251*4345Sdougm 		if (value == NULL) {
1252*4345Sdougm 			(void) snprintf(buff + curlen, buffsize - curlen,
1253*4345Sdougm 			    "%s%s", sep ? "," : "",
1254*4345Sdougm 			    name, value != NULL ? value : "");
1255*4345Sdougm 		} else {
1256*4345Sdougm 			(void) snprintf(buff + curlen, buffsize - curlen,
1257*4345Sdougm 			    "%s%s=%s", sep ? "," : "",
1258*4345Sdougm 			    name, value != NULL ? value : "");
12593910Sdougm 		}
12603910Sdougm 	}
12613910Sdougm 	if (name != NULL)
1262*4345Sdougm 		sa_free_attr_string(name);
12633910Sdougm 	if (value != NULL)
1264*4345Sdougm 		sa_free_attr_string(value);
12653910Sdougm }
12663910Sdougm 
12673910Sdougm /*
12683910Sdougm  * nfs_format_options(group, hier)
12693910Sdougm  *
12703910Sdougm  * format all the options on the group into an old-style option
12713910Sdougm  * string. If hier is non-zero, walk up the tree to get inherited
12723910Sdougm  * options.
12733910Sdougm  */
12743910Sdougm 
12753910Sdougm static char *
12763910Sdougm nfs_format_options(sa_group_t group, int hier)
12773910Sdougm {
12783910Sdougm 	sa_optionset_t options = NULL;
1279*4345Sdougm 	sa_optionset_t secoptions = NULL;
12803910Sdougm 	sa_property_t prop, secprop;
1281*4345Sdougm 	sa_security_t security = NULL;
12823910Sdougm 	char *buff;
12833910Sdougm 	size_t buffsize;
1284*4345Sdougm 	char *sectype = NULL;
1285*4345Sdougm 	int sep = 0;
1286*4345Sdougm 
1287*4345Sdougm 
1288*4345Sdougm 	buff = malloc(OPT_CHUNK);
1289*4345Sdougm 	if (buff == NULL) {
1290*4345Sdougm 		return (NULL);
1291*4345Sdougm 	}
1292*4345Sdougm 
1293*4345Sdougm 	buff[0] = '\0';
1294*4345Sdougm 	buffsize = OPT_CHUNK;
1295*4345Sdougm 
1296*4345Sdougm 	/*
1297*4345Sdougm 	 * We may have a an optionset relative to this item. format
1298*4345Sdougm 	 * these if we find them and then add any security definitions.
1299*4345Sdougm 	 */
13003910Sdougm 
13013910Sdougm 	options = sa_get_derived_optionset(group, "nfs", hier);
13023910Sdougm 
13033910Sdougm 	/*
1304*4345Sdougm 	 * do the default set first but skip any option that is also
1305*4345Sdougm 	 * in the protocol specific optionset.
13063910Sdougm 	 */
1307*4345Sdougm 	if (options != NULL) {
1308*4345Sdougm 		for (prop = sa_get_property(options, NULL);
1309*4345Sdougm 		    prop != NULL; prop = sa_get_next_property(prop)) {
13103910Sdougm 			/*
1311*4345Sdougm 			 * use this one since we skipped any
1312*4345Sdougm 			 * of these that were also in
1313*4345Sdougm 			 * optdefault
13143910Sdougm 			 */
1315*4345Sdougm 			nfs_sprint_option(&buff, &buffsize, OPT_CHUNK,
1316*4345Sdougm 			    prop, sep);
1317*4345Sdougm 			if (buff == NULL) {
1318*4345Sdougm 				/*
1319*4345Sdougm 				 * buff could become NULL if there
1320*4345Sdougm 				 * isn't enough memory for
1321*4345Sdougm 				 * nfs_sprint_option to realloc()
1322*4345Sdougm 				 * as necessary. We can't really
1323*4345Sdougm 				 * do anything about it at this
1324*4345Sdougm 				 * point so we return NULL.  The
1325*4345Sdougm 				 * caller should handle the
1326*4345Sdougm 				 * failure.
1327*4345Sdougm 				 */
1328*4345Sdougm 				if (options != NULL)
1329*4345Sdougm 					sa_free_derived_optionset(
1330*4345Sdougm 					    options);
1331*4345Sdougm 				return (buff);
1332*4345Sdougm 			}
1333*4345Sdougm 			sep = 1;
13343910Sdougm 		}
1335*4345Sdougm 	}
1336*4345Sdougm 	secoptions = (sa_optionset_t)sa_get_all_security_types(group,
1337*4345Sdougm 	    "nfs", hier);
1338*4345Sdougm 	if (secoptions != NULL) {
13393910Sdougm 		for (secprop = sa_get_property(secoptions, NULL);
1340*4345Sdougm 		    secprop != NULL;
1341*4345Sdougm 		    secprop = sa_get_next_property(secprop)) {
1342*4345Sdougm 			sectype = sa_get_property_attr(secprop, "type");
1343*4345Sdougm 			security =
1344*4345Sdougm 			    (sa_security_t)sa_get_derived_security(
1345*4345Sdougm 			    group, sectype, "nfs", hier);
1346*4345Sdougm 			if (security != NULL) {
1347*4345Sdougm 				if (sectype != NULL) {
1348*4345Sdougm 					prop = sa_create_property(
1349*4345Sdougm 					    "sec", sectype);
1350*4345Sdougm 					nfs_sprint_option(&buff,
1351*4345Sdougm 					    &buffsize, OPT_CHUNK,
1352*4345Sdougm 					    prop, sep);
1353*4345Sdougm 					(void) sa_remove_property(prop);
1354*4345Sdougm 					sep = 1;
1355*4345Sdougm 				}
1356*4345Sdougm 				for (prop = sa_get_property(security,
1357*4345Sdougm 				    NULL); prop != NULL;
1358*4345Sdougm 				    prop = sa_get_next_property(prop)) {
1359*4345Sdougm 					nfs_sprint_option(&buff,
1360*4345Sdougm 					    &buffsize, OPT_CHUNK, prop,
1361*4345Sdougm 					    sep);
1362*4345Sdougm 					if (buff == NULL)
1363*4345Sdougm 						goto err;
1364*4345Sdougm 					sep = 1;
1365*4345Sdougm 				}
1366*4345Sdougm 				sa_free_derived_optionset(security);
13673910Sdougm 			}
1368*4345Sdougm 			if (sectype != NULL)
1369*4345Sdougm 				sa_free_attr_string(sectype);
13703910Sdougm 		}
13713910Sdougm 		sa_free_derived_optionset(secoptions);
13723910Sdougm 	}
1373*4345Sdougm 
13743910Sdougm 	if (options != NULL)
1375*4345Sdougm 		sa_free_derived_optionset(options);
1376*4345Sdougm 	return (buff);
1377*4345Sdougm 
1378*4345Sdougm err:
1379*4345Sdougm 	/*
1380*4345Sdougm 	 * If we couldn't allocate memory for option printing, we need
1381*4345Sdougm 	 * to break out of the nested loops, cleanup and return NULL.
1382*4345Sdougm 	 */
1383*4345Sdougm 	if (secoptions != NULL)
1384*4345Sdougm 		sa_free_derived_optionset(secoptions);
1385*4345Sdougm 	if (security != NULL)
1386*4345Sdougm 		sa_free_derived_optionset(security);
1387*4345Sdougm 	if (sectype != NULL)
1388*4345Sdougm 		sa_free_attr_string(sectype);
1389*4345Sdougm 	if (options != NULL)
1390*4345Sdougm 		sa_free_derived_optionset(options);
13913910Sdougm 	return (buff);
13923910Sdougm }
1393*4345Sdougm 
13943910Sdougm /*
13953910Sdougm  * Append an entry to the nfslogtab file
13963910Sdougm  */
13973910Sdougm static int
13983910Sdougm nfslogtab_add(dir, buffer, tag)
13993910Sdougm 	char *dir, *buffer, *tag;
14003910Sdougm {
14013910Sdougm 	FILE *f;
14023910Sdougm 	struct logtab_ent lep;
14033910Sdougm 	int error = 0;
14043910Sdougm 
14053910Sdougm 	/*
14063910Sdougm 	 * Open the file for update and create it if necessary.
14073910Sdougm 	 * This may leave the I/O offset at the end of the file,
14083910Sdougm 	 * so rewind back to the beginning of the file.
14093910Sdougm 	 */
14103910Sdougm 	f = fopen(NFSLOGTAB, "a+");
14113910Sdougm 	if (f == NULL) {
14123910Sdougm 		error = errno;
14133910Sdougm 		goto out;
14143910Sdougm 	}
14153910Sdougm 	rewind(f);
14163910Sdougm 
14173910Sdougm 	if (lockf(fileno(f), F_LOCK, 0L) < 0) {
14183910Sdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1419*4345Sdougm 		    "share complete, however failed to lock %s "
1420*4345Sdougm 		    "for update: %s\n"), NFSLOGTAB, strerror(errno));
14213910Sdougm 		error = -1;
14223910Sdougm 		goto out;
14233910Sdougm 	}
14243910Sdougm 
14253910Sdougm 	if (logtab_deactivate_after_boot(f) == -1) {
14263910Sdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1427*4345Sdougm 		    "share complete, however could not deactivate "
1428*4345Sdougm 		    "entries in %s\n"), NFSLOGTAB);
14293910Sdougm 		error = -1;
14303910Sdougm 		goto out;
14313910Sdougm 	}
14323910Sdougm 
14333910Sdougm 	/*
14343910Sdougm 	 * Remove entries matching buffer and sharepoint since we're
14353910Sdougm 	 * going to replace it with perhaps an entry with a new tag.
14363910Sdougm 	 */
14373910Sdougm 	if (logtab_rement(f, buffer, dir, NULL, -1)) {
14383910Sdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1439*4345Sdougm 		    "share complete, however could not remove matching "
1440*4345Sdougm 		    "entries in %s\n"), NFSLOGTAB);
14413910Sdougm 		error = -1;
14423910Sdougm 		goto out;
14433910Sdougm 	}
14443910Sdougm 
14453910Sdougm 	/*
14463910Sdougm 	 * Deactivate all active entries matching this sharepoint
14473910Sdougm 	 */
14483910Sdougm 	if (logtab_deactivate(f, NULL, dir, NULL)) {
14493910Sdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1450*4345Sdougm 		    "share complete, however could not deactivate matching "
1451*4345Sdougm 		    "entries in %s\n"), NFSLOGTAB);
14523910Sdougm 		error = -1;
14533910Sdougm 		goto out;
14543910Sdougm 	}
14553910Sdougm 
14563910Sdougm 	lep.le_buffer = buffer;
14573910Sdougm 	lep.le_path = dir;
14583910Sdougm 	lep.le_tag = tag;
14593910Sdougm 	lep.le_state = LES_ACTIVE;
14603910Sdougm 
14613910Sdougm 	/*
14623910Sdougm 	 * Add new sharepoint / buffer location to nfslogtab
14633910Sdougm 	 */
14643910Sdougm 	if (logtab_putent(f, &lep) < 0) {
14653910Sdougm 		(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1466*4345Sdougm 		    "share complete, however could not add %s to %s\n"),
1467*4345Sdougm 		    dir, NFSLOGTAB);
14683910Sdougm 		error = -1;
14693910Sdougm 	}
14703910Sdougm 
14713910Sdougm out:
14723910Sdougm 	if (f != NULL)
14733910Sdougm 		(void) fclose(f);
14743910Sdougm 	return (error);
14753910Sdougm }
14763910Sdougm 
14773910Sdougm /*
14783910Sdougm  * Deactivate an entry from the nfslogtab file
14793910Sdougm  */
14803910Sdougm static int
14813910Sdougm nfslogtab_deactivate(path)
14823910Sdougm 	char *path;
14833910Sdougm {
14843910Sdougm 	FILE *f;
14853910Sdougm 	int error = 0;
14863910Sdougm 
14873910Sdougm 	f = fopen(NFSLOGTAB, "r+");
14883910Sdougm 	if (f == NULL) {
14893910Sdougm 		error = errno;
14903910Sdougm 		goto out;
14913910Sdougm 	}
14923910Sdougm 	if (lockf(fileno(f), F_LOCK, 0L) < 0) {
14933910Sdougm 		error = errno;
14943910Sdougm 		(void)  fprintf(stderr, dgettext(TEXT_DOMAIN,
1495*4345Sdougm 		    "share complete, however could not lock %s for "
1496*4345Sdougm 		    "update: %s\n"), NFSLOGTAB, strerror(error));
14973910Sdougm 		goto out;
14983910Sdougm 	}
14993910Sdougm 	if (logtab_deactivate(f, NULL, path, NULL) == -1) {
15003910Sdougm 		error = -1;
15013910Sdougm 		(void) fprintf(stderr,
1502*4345Sdougm 		    dgettext(TEXT_DOMAIN,
1503*4345Sdougm 		    "share complete, however could not "
1504*4345Sdougm 		    "deactivate %s in %s\n"), path, NFSLOGTAB);
15053910Sdougm 		goto out;
15063910Sdougm 	}
15073910Sdougm 
15083910Sdougm out:	if (f != NULL)
15093910Sdougm 		(void) fclose(f);
15103910Sdougm 
15113910Sdougm 	return (error);
15123910Sdougm }
15133910Sdougm 
15143910Sdougm /*
15153910Sdougm  * public_exists(share)
15163910Sdougm  *
15173910Sdougm  * check to see if public option is set on any other share than the
15183910Sdougm  * one specified.
15193910Sdougm  */
15203910Sdougm static int
15213910Sdougm public_exists(sa_share_t skipshare)
15223910Sdougm {
15233910Sdougm 	sa_share_t share;
15243910Sdougm 	sa_group_t group;
15253910Sdougm 	sa_optionset_t opt;
15263910Sdougm 	sa_property_t prop;
15273910Sdougm 	int exists = 0;
15283910Sdougm 	sa_handle_t handle;
15293910Sdougm 
15303910Sdougm 	group = sa_get_parent_group(skipshare);
15313910Sdougm 	if (group == NULL)
1532*4345Sdougm 		return (SA_NO_SUCH_GROUP);
15333910Sdougm 
15343910Sdougm 	handle = sa_find_group_handle(group);
15353910Sdougm 	if (handle == NULL)
1536*4345Sdougm 		return (SA_SYSTEM_ERR);
15373910Sdougm 
15383910Sdougm 	for (group = sa_get_group(handle, NULL); group != NULL;
15393910Sdougm 	    group = sa_get_next_group(group)) {
1540*4345Sdougm 		for (share = sa_get_share(group, NULL); share != NULL;
1541*4345Sdougm 		    share = sa_get_next_share(share)) {
1542*4345Sdougm 			if (share == skipshare)
1543*4345Sdougm 				continue;
1544*4345Sdougm 			opt = sa_get_optionset(share, "nfs");
1545*4345Sdougm 			if (opt != NULL) {
1546*4345Sdougm 				prop = sa_get_property(opt, "public");
1547*4345Sdougm 				if (prop != NULL) {
1548*4345Sdougm 					char *shared;
1549*4345Sdougm 					shared = sa_get_share_attr(share,
1550*4345Sdougm 					    "shared");
1551*4345Sdougm 					if (shared != NULL) {
1552*4345Sdougm 						exists = strcmp(shared,
1553*4345Sdougm 						    "true") == 0;
1554*4345Sdougm 						sa_free_attr_string(shared);
1555*4345Sdougm 						goto out;
1556*4345Sdougm 					}
1557*4345Sdougm 				}
15583910Sdougm 			}
15593910Sdougm 		}
15603910Sdougm 	}
15613910Sdougm out:
15623910Sdougm 	return (exists);
15633910Sdougm }
15643910Sdougm 
15653910Sdougm /*
15663910Sdougm  * sa_enable_share at the protocol level, enable_share must tell the
15673910Sdougm  * implementation that it is to enable the share. This entails
15683910Sdougm  * converting the path and options into the appropriate ioctl
15693910Sdougm  * calls. It is assumed that all error checking of paths, etc. were
15703910Sdougm  * done earlier.
15713910Sdougm  */
15723910Sdougm static int
15733910Sdougm nfs_enable_share(sa_share_t share)
15743910Sdougm {
15753910Sdougm 	struct exportdata export;
15763910Sdougm 	sa_optionset_t secoptlist;
15773910Sdougm 	struct secinfo *sp;
15783910Sdougm 	int num_secinfo;
15793910Sdougm 	sa_optionset_t opt;
15803910Sdougm 	sa_security_t sec;
15813910Sdougm 	sa_property_t prop;
15823910Sdougm 	char *path;
15833910Sdougm 	int err = SA_OK;
15843910Sdougm 
15853910Sdougm 	/* Don't drop core if the NFS module isn't loaded. */
15863910Sdougm 	(void) signal(SIGSYS, SIG_IGN);
15873910Sdougm 
15883910Sdougm 	/* get the path since it is important in several places */
15893910Sdougm 	path = sa_get_share_attr(share, "path");
15903910Sdougm 	if (path == NULL)
1591*4345Sdougm 		return (SA_NO_SUCH_PATH);
15923910Sdougm 
15933910Sdougm 	/*
15943910Sdougm 	 * find the optionsets and security sets.  There may not be
15953910Sdougm 	 * any or there could be one or two for each of optionset and
15963910Sdougm 	 * security may have multiple, one per security type per
15973910Sdougm 	 * protocol type.
15983910Sdougm 	 */
15993910Sdougm 	opt = sa_get_derived_optionset(share, "nfs", 1);
16003910Sdougm 	secoptlist = (sa_optionset_t)sa_get_all_security_types(share, "nfs", 1);
16013910Sdougm 	if (secoptlist != NULL)
1602*4345Sdougm 		num_secinfo = MAX(1, count_security(secoptlist));
16033910Sdougm 	else
1604*4345Sdougm 		num_secinfo = 1;
16053910Sdougm 
16063910Sdougm 	/*
16073910Sdougm 	 * walk through the options and fill in the structure
16083910Sdougm 	 * appropriately.
16093910Sdougm 	 */
16103910Sdougm 
16113910Sdougm 	(void) memset(&export, '\0', sizeof (export));
16123910Sdougm 
16133910Sdougm 	/*
16143910Sdougm 	 * do non-security options first since there is only one after
16153910Sdougm 	 * the derived group is constructed.
16163910Sdougm 	 */
16173910Sdougm 	export.ex_version = EX_CURRENT_VERSION;
16183910Sdougm 	export.ex_anon = UID_NOBODY; /* this is our default value */
16193910Sdougm 	export.ex_index = NULL;
16203910Sdougm 	export.ex_path = path;
16213910Sdougm 	export.ex_pathlen = strlen(path) + 1;
16223910Sdougm 
16233910Sdougm 	sp = calloc(num_secinfo, sizeof (struct secinfo));
16243910Sdougm 
16253910Sdougm 	if (opt != NULL)
1626*4345Sdougm 		err = fill_export_from_optionset(&export, opt);
16273910Sdougm 
16283910Sdougm 	/*
16293910Sdougm 	 * check to see if "public" is set. If it is, then make sure
16303910Sdougm 	 * no other share has it set. If it is already used, fail.
16313910Sdougm 	 */
16323910Sdougm 
16333910Sdougm 	if (export.ex_flags & EX_PUBLIC && public_exists(share)) {
1634*4345Sdougm 		(void) printf(dgettext(TEXT_DOMAIN,
1635*4345Sdougm 		    "NFS: Cannot share more than one file "
1636*4345Sdougm 		    "system with 'public' property\n"));
1637*4345Sdougm 		err = SA_NOT_ALLOWED;
1638*4345Sdougm 		goto out;
16393910Sdougm 	}
16403910Sdougm 
16413910Sdougm 	if (sp == NULL) {
16423910Sdougm 		/* failed to alloc memory */
1643*4345Sdougm 		(void) printf("NFS: no memory for security\n");
1644*4345Sdougm 		err = SA_NO_MEMORY;
16453910Sdougm 	} else {
1646*4345Sdougm 		int i;
1647*4345Sdougm 		export.ex_secinfo = sp;
1648*4345Sdougm 		/* get default secinfo */
1649*4345Sdougm 		export.ex_seccnt = num_secinfo;
16503910Sdougm 		/*
16513910Sdougm 		 * since we must have one security option defined, we
16523910Sdougm 		 * init to the default and then override as we find
16533910Sdougm 		 * defined security options. This handles the case
16543910Sdougm 		 * where we have no defined options but we need to set
16553910Sdougm 		 * up one.
16563910Sdougm 		 */
1657*4345Sdougm 		sp[0].s_window = DEF_WIN;
1658*4345Sdougm 		sp[0].s_rootnames = NULL;
1659*4345Sdougm 		/* setup a default in case no properties defined */
1660*4345Sdougm 		if (nfs_getseconfig_default(&sp[0].s_secinfo)) {
1661*4345Sdougm 			(void) printf(dgettext(TEXT_DOMAIN,
1662*4345Sdougm 			    "NFS: nfs_getseconfig_default: failed to "
1663*4345Sdougm 			    "get default security mode\n"));
1664*4345Sdougm 			err = SA_CONFIG_ERR;
1665*4345Sdougm 		}
1666*4345Sdougm 		if (secoptlist != NULL) {
1667*4345Sdougm 			for (i = 0, prop = sa_get_property(secoptlist, NULL);
1668*4345Sdougm 			    prop != NULL && i < num_secinfo;
1669*4345Sdougm 			    prop = sa_get_next_property(prop), i++) {
1670*4345Sdougm 				char *sectype;
16713910Sdougm 
1672*4345Sdougm 				sectype = sa_get_property_attr(prop, "type");
1673*4345Sdougm 				/*
1674*4345Sdougm 				 * if sectype is NULL, we probably
1675*4345Sdougm 				 * have a memory problem and can't get
1676*4345Sdougm 				 * the correct values. Rather than
1677*4345Sdougm 				 * exporting with incorrect security,
1678*4345Sdougm 				 * don't share it.
1679*4345Sdougm 				 */
1680*4345Sdougm 				if (sectype == NULL) {
1681*4345Sdougm 					err = SA_NO_MEMORY;
1682*4345Sdougm 					(void) printf(dgettext(TEXT_DOMAIN,
1683*4345Sdougm 					    "NFS: Cannot share %s: "
1684*4345Sdougm 					    "no memory\n"), path);
1685*4345Sdougm 					goto out;
1686*4345Sdougm 				}
1687*4345Sdougm 				sec = (sa_security_t)sa_get_derived_security(
1688*4345Sdougm 				    share, sectype, "nfs", 1);
1689*4345Sdougm 				sp[i].s_window = DEF_WIN;
1690*4345Sdougm 				sp[i].s_rootcnt = 0;
1691*4345Sdougm 				sp[i].s_rootnames = NULL;
16923910Sdougm 
1693*4345Sdougm 				(void) fill_security_from_secopts(&sp[i], sec);
1694*4345Sdougm 				if (sec != NULL)
1695*4345Sdougm 					sa_free_derived_security(sec);
1696*4345Sdougm 				if (sectype != NULL)
1697*4345Sdougm 					sa_free_attr_string(sectype);
1698*4345Sdougm 			}
16993910Sdougm 		}
17003910Sdougm 		/*
17013910Sdougm 		 * when we get here, we can do the exportfs system call and
17023910Sdougm 		 * initiate thinsg. We probably want to enable the nfs.server
17033910Sdougm 		 * service first if it isn't running within SMF.
17043910Sdougm 		 */
17053910Sdougm 		/* check nfs.server status and start if needed */
17063910Sdougm 
17073910Sdougm 		/* now add the share to the internal tables */
1708*4345Sdougm 		printarg(path, &export);
17093910Sdougm 		/*
17103910Sdougm 		 * call the exportfs system call which is implemented
17113910Sdougm 		 * via the nfssys() call as the EXPORTFS subfunction.
17123910Sdougm 		 */
1713*4345Sdougm 		if ((err = exportfs(path, &export)) < 0) {
1714*4345Sdougm 			err = SA_SYSTEM_ERR;
1715*4345Sdougm 			switch (errno) {
1716*4345Sdougm 			case EREMOTE:
1717*4345Sdougm 				(void) printf(dgettext(TEXT_DOMAIN,
1718*4345Sdougm 				    "NFS: Cannot share remote "
1719*4345Sdougm 				    "filesystem: %s\n"), path);
1720*4345Sdougm 				break;
1721*4345Sdougm 			case EPERM:
1722*4345Sdougm 				if (getzoneid() != GLOBAL_ZONEID) {
1723*4345Sdougm 					(void) printf(dgettext(TEXT_DOMAIN,
1724*4345Sdougm 					    "NFS: Cannot share filesystems "
1725*4345Sdougm 					    "in non-global zones: %s\n"), path);
1726*4345Sdougm 					err = SA_NOT_SUPPORTED;
1727*4345Sdougm 					break;
1728*4345Sdougm 				}
1729*4345Sdougm 				err = SA_NO_PERMISSION;
1730*4345Sdougm 				/* FALLTHROUGH */
1731*4345Sdougm 			default:
1732*4345Sdougm 				break;
1733*4345Sdougm 			}
1734*4345Sdougm 		} else {
1735*4345Sdougm 			/* update sharetab with an add/modify */
1736*4345Sdougm 			(void) sa_update_sharetab(share, "nfs");
17373910Sdougm 		}
17383910Sdougm 	}
17393910Sdougm 
17403910Sdougm 	if (err == SA_OK) {
17413910Sdougm 		/*
17423910Sdougm 		 * enable services as needed. This should probably be
17433910Sdougm 		 * done elsewhere in order to minimize the calls to
17443910Sdougm 		 * check services.
17453910Sdougm 		 */
17463910Sdougm 		/*
17473910Sdougm 		 * check to see if logging and other services need to
17483910Sdougm 		 * be triggered, but only if there wasn't an
17493910Sdougm 		 * error. This is probably where sharetab should be
17503910Sdougm 		 * updated with the NFS specific entry.
17513910Sdougm 		 */
1752*4345Sdougm 		if (export.ex_flags & EX_LOG) {
1753*4345Sdougm 			/* enable logging */
1754*4345Sdougm 			if (nfslogtab_add(path, export.ex_log_buffer,
1755*4345Sdougm 			    export.ex_tag) != 0) {
1756*4345Sdougm 				(void) fprintf(stderr, dgettext(TEXT_DOMAIN,
1757*4345Sdougm 				    "Could not enable logging for %s\n"),
1758*4345Sdougm 				    path);
1759*4345Sdougm 			}
1760*4345Sdougm 			_check_services(service_list_logging);
1761*4345Sdougm 		} else {
1762*4345Sdougm 			/*
1763*4345Sdougm 			 * don't have logging so remove it from file. It might
1764*4345Sdougm 			 * not be thre, but that doesn't matter.
1765*4345Sdougm 			 */
1766*4345Sdougm 			(void) nfslogtab_deactivate(path);
1767*4345Sdougm 			_check_services(service_list_default);
17683910Sdougm 		}
17693910Sdougm 	}
17703910Sdougm 
17713910Sdougm out:
17723910Sdougm 	if (path != NULL)
1773*4345Sdougm 		free(path);
17743910Sdougm 
17753910Sdougm 	cleanup_export(&export);
17763910Sdougm 	if (opt != NULL)
1777*4345Sdougm 		sa_free_derived_optionset(opt);
17783910Sdougm 	if (secoptlist != NULL)
1779*4345Sdougm 		(void) sa_destroy_optionset(secoptlist);
17803910Sdougm 	return (err);
17813910Sdougm }
17823910Sdougm 
17833910Sdougm /*
17843910Sdougm  * nfs_disable_share(share)
17853910Sdougm  *
17863910Sdougm  * Unshare the specified share.  How much error checking should be
17873910Sdougm  * done? We only do basic errors for now.
17883910Sdougm  */
17893910Sdougm static int
17903910Sdougm nfs_disable_share(char *share)
17913910Sdougm {
17923910Sdougm 	int err;
17933910Sdougm 	int ret = SA_OK;
17943910Sdougm 
17953910Sdougm 	if (share != NULL) {
1796*4345Sdougm 		err = exportfs(share, NULL);
1797*4345Sdougm 		if (err < 0) {
1798*4345Sdougm 			/*
1799*4345Sdougm 			 * TBD: only an error in some cases - need
1800*4345Sdougm 			 * better analysis
1801*4345Sdougm 			 */
1802*4345Sdougm 			switch (errno) {
1803*4345Sdougm 			case EPERM:
1804*4345Sdougm 			case EACCES:
1805*4345Sdougm 				ret = SA_NO_PERMISSION;
1806*4345Sdougm 				if (getzoneid() != GLOBAL_ZONEID)
1807*4345Sdougm 					ret = SA_NOT_SUPPORTED;
1808*4345Sdougm 				break;
1809*4345Sdougm 			case EINVAL:
1810*4345Sdougm 			case ENOENT:
1811*4345Sdougm 				ret = SA_NO_SUCH_PATH;
1812*4345Sdougm 				break;
1813*4345Sdougm 			default:
1814*4345Sdougm 				ret = SA_SYSTEM_ERR;
1815*4345Sdougm 				break;
1816*4345Sdougm 			}
18173910Sdougm 		}
1818*4345Sdougm 		if (ret == SA_OK || ret == SA_NO_SUCH_PATH) {
1819*4345Sdougm 			(void) sa_delete_sharetab(share, "nfs");
1820*4345Sdougm 			/* just in case it was logged */
1821*4345Sdougm 			(void) nfslogtab_deactivate(share);
1822*4345Sdougm 		}
18233910Sdougm 	}
18243910Sdougm 	return (ret);
18253910Sdougm }
18263910Sdougm 
18273910Sdougm /*
18283910Sdougm  * check ro vs rw values.  Over time this may get beefed up.
18293910Sdougm  * for now it just does simple checks.
18303910Sdougm  */
18313910Sdougm 
18323910Sdougm static int
18333910Sdougm check_rorw(char *v1, char *v2)
18343910Sdougm {
18353910Sdougm 	int ret = SA_OK;
18363910Sdougm 	if (strcmp(v1, v2) == 0)
1837*4345Sdougm 		ret = SA_VALUE_CONFLICT;
18383910Sdougm 	return (ret);
18393910Sdougm }
18403910Sdougm 
18413910Sdougm /*
18423910Sdougm  * nfs_validate_property(property, parent)
18433910Sdougm  *
18443910Sdougm  * Check that the property has a legitimate value for its type.
18453910Sdougm  */
18463910Sdougm 
18473910Sdougm static int
18483910Sdougm nfs_validate_property(sa_property_t property, sa_optionset_t parent)
18493910Sdougm {
18503910Sdougm 	int ret = SA_OK;
18513910Sdougm 	char *propname;
18523910Sdougm 	char *other;
18533910Sdougm 	int optindex;
18543910Sdougm 	nfsl_config_t *configlist;
18553910Sdougm 	sa_group_t parent_group;
18563910Sdougm 	char *value;
18573910Sdougm 
18583910Sdougm 	propname = sa_get_property_attr(property, "type");
18593910Sdougm 
18603910Sdougm 	if ((optindex = findopt(propname)) < 0)
1861*4345Sdougm 		ret = SA_NO_SUCH_PROP;
18623910Sdougm 
18633910Sdougm 	/* need to validate value range here as well */
18643910Sdougm 
18653910Sdougm 	if (ret == SA_OK) {
1866*4345Sdougm 		parent_group = sa_get_parent_group((sa_share_t)parent);
1867*4345Sdougm 		if (optdefs[optindex].share && !sa_is_share(parent_group))
1868*4345Sdougm 			ret = SA_PROP_SHARE_ONLY;
18693910Sdougm 	}
18703910Sdougm 	if (ret == SA_OK) {
1871*4345Sdougm 		value = sa_get_property_attr(property, "value");
1872*4345Sdougm 		if (value != NULL) {
1873*4345Sdougm 			/* first basic type checking */
1874*4345Sdougm 			switch (optdefs[optindex].type) {
1875*4345Sdougm 			case OPT_TYPE_NUMBER:
1876*4345Sdougm 				/* check that the value is all digits */
1877*4345Sdougm 				if (!is_a_number(value))
1878*4345Sdougm 					ret = SA_BAD_VALUE;
1879*4345Sdougm 				break;
1880*4345Sdougm 			case OPT_TYPE_BOOLEAN:
1881*4345Sdougm 				if (strlen(value) == 0 ||
1882*4345Sdougm 				    strcasecmp(value, "true") == 0 ||
1883*4345Sdougm 				    strcmp(value, "1") == 0 ||
1884*4345Sdougm 				    strcasecmp(value, "false") == 0 ||
1885*4345Sdougm 				    strcmp(value, "0") == 0) {
1886*4345Sdougm 					ret = SA_OK;
1887*4345Sdougm 				} else {
1888*4345Sdougm 					ret = SA_BAD_VALUE;
1889*4345Sdougm 				}
1890*4345Sdougm 				break;
1891*4345Sdougm 			case OPT_TYPE_USER:
1892*4345Sdougm 				if (!is_a_number(value)) {
1893*4345Sdougm 					struct passwd *pw;
1894*4345Sdougm 					/*
1895*4345Sdougm 					 * in this case it would have to be a
1896*4345Sdougm 					 * user name
1897*4345Sdougm 					 */
1898*4345Sdougm 					pw = getpwnam(value);
1899*4345Sdougm 					if (pw == NULL)
1900*4345Sdougm 						ret = SA_BAD_VALUE;
1901*4345Sdougm 					endpwent();
1902*4345Sdougm 				} else {
1903*4345Sdougm 					uint64_t intval;
1904*4345Sdougm 					intval = strtoull(value, NULL, 0);
1905*4345Sdougm 					if (intval > UID_MAX && intval != ~0)
1906*4345Sdougm 						ret = SA_BAD_VALUE;
1907*4345Sdougm 				}
1908*4345Sdougm 				break;
1909*4345Sdougm 			case OPT_TYPE_FILE:
1910*4345Sdougm 				if (strcmp(value, "..") == 0 ||
1911*4345Sdougm 				    strchr(value, '/') != NULL) {
1912*4345Sdougm 					ret = SA_BAD_VALUE;
1913*4345Sdougm 				}
1914*4345Sdougm 				break;
1915*4345Sdougm 			case OPT_TYPE_ACCLIST:
1916*4345Sdougm 				/*
1917*4345Sdougm 				 * access list handling. Should eventually
1918*4345Sdougm 				 * validate that all the values make sense.
1919*4345Sdougm 				 * Also, ro and rw may have cross value
1920*4345Sdougm 				 * conflicts.
1921*4345Sdougm 				 */
1922*4345Sdougm 				if (strcmp(propname, SHOPT_RO) == 0)
1923*4345Sdougm 					other = SHOPT_RW;
1924*4345Sdougm 				else if (strcmp(propname, SHOPT_RW) == 0)
1925*4345Sdougm 					other = SHOPT_RO;
1926*4345Sdougm 				else
1927*4345Sdougm 					other = NULL;
1928*4345Sdougm 
1929*4345Sdougm 				if (other != NULL && parent != NULL) {
1930*4345Sdougm 					/* compare rw(ro) with ro(rw) */
1931*4345Sdougm 					sa_property_t oprop;
1932*4345Sdougm 					oprop = sa_get_property(parent, other);
1933*4345Sdougm 					if (oprop != NULL) {
1934*4345Sdougm 						/*
1935*4345Sdougm 						 * only potential
1936*4345Sdougm 						 * confusion if other
1937*4345Sdougm 						 * exists
1938*4345Sdougm 						 */
1939*4345Sdougm 						char *ovalue;
1940*4345Sdougm 						ovalue = sa_get_property_attr(
1941*4345Sdougm 						    oprop, "value");
1942*4345Sdougm 						if (ovalue != NULL) {
1943*4345Sdougm 							ret = check_rorw(value,
1944*4345Sdougm 							    ovalue);
1945*4345Sdougm 							sa_free_attr_string(
1946*4345Sdougm 							    ovalue);
1947*4345Sdougm 						}
1948*4345Sdougm 					}
1949*4345Sdougm 				}
1950*4345Sdougm 				break;
1951*4345Sdougm 			case OPT_TYPE_LOGTAG:
1952*4345Sdougm 				if (nfsl_getconfig_list(&configlist) == 0) {
1953*4345Sdougm 					int error;
1954*4345Sdougm 					if (value == NULL ||
1955*4345Sdougm 					    strlen(value) == 0) {
1956*4345Sdougm 						if (value != NULL)
1957*4345Sdougm 							sa_free_attr_string(
1958*4345Sdougm 							    value);
1959*4345Sdougm 						value = strdup("global");
1960*4345Sdougm 					}
1961*4345Sdougm 					if (value != NULL &&
1962*4345Sdougm 					    nfsl_findconfig(configlist, value,
1963*4345Sdougm 					    &error) == NULL) {
1964*4345Sdougm 						ret = SA_BAD_VALUE;
1965*4345Sdougm 					} else {
1966*4345Sdougm 						nfsl_freeconfig_list(
1967*4345Sdougm 						    &configlist);
1968*4345Sdougm 					}
1969*4345Sdougm 				} else {
1970*4345Sdougm 					ret = SA_CONFIG_ERR;
1971*4345Sdougm 				}
1972*4345Sdougm 				break;
1973*4345Sdougm 			case OPT_TYPE_STRING:
1974*4345Sdougm 				/* whatever is here should be ok */
1975*4345Sdougm 				break;
1976*4345Sdougm 			case OPT_TYPE_SECURITY:
1977*4345Sdougm 				/*
1978*4345Sdougm 				 * The "sec" property isn't used in the
1979*4345Sdougm 				 * non-legacy parts of sharemgr. We need to
1980*4345Sdougm 				 * reject it here. For legacy, it is pulled
1981*4345Sdougm 				 * out well before we get here.
1982*4345Sdougm 				 */
1983*4345Sdougm 				ret = SA_NO_SUCH_PROP;
1984*4345Sdougm 				break;
1985*4345Sdougm 			default:
1986*4345Sdougm 				break;
19873910Sdougm 			}
1988*4345Sdougm 			sa_free_attr_string(value);
1989*4345Sdougm 			if (ret == SA_OK && optdefs[optindex].check != NULL) {
1990*4345Sdougm 				/* do the property specific check */
1991*4345Sdougm 				ret = optdefs[optindex].check(property);
19923910Sdougm 			}
19933910Sdougm 		}
19943910Sdougm 	}
19953910Sdougm 
19963910Sdougm 	if (propname != NULL)
1997*4345Sdougm 		sa_free_attr_string(propname);
19983910Sdougm 	return (ret);
19993910Sdougm }
20003910Sdougm 
20013910Sdougm /*
20023910Sdougm  * Protocol management functions
20033910Sdougm  *
20043910Sdougm  * Properties defined in the default files are defined in
20053910Sdougm  * proto_option_defs for parsing and validation. If "other" and
20063910Sdougm  * "compare" are set, then the value for this property should be
20073910Sdougm  * compared against the property specified in "other" using the
20083910Sdougm  * "compare" check (either <= or >=) in order to ensure that the
20093910Sdougm  * values are in the correct range.  E.g. setting server_versmin
20103910Sdougm  * higher than server_versmax should not be allowed.
20113910Sdougm  */
20123910Sdougm 
20133910Sdougm struct proto_option_defs {
20143910Sdougm 	char *tag;
20153910Sdougm 	char *name;	/* display name -- remove protocol identifier */
20163910Sdougm 	int index;
20173910Sdougm 	int type;
20183910Sdougm 	union {
20193910Sdougm 	    int intval;
20203910Sdougm 	    char *string;
20213910Sdougm 	} defvalue;
20223910Sdougm 	uint32_t svcs;
20233910Sdougm 	int32_t minval;
20243910Sdougm 	int32_t maxval;
20253910Sdougm 	char *file;
20263910Sdougm 	char *other;
20273910Sdougm 	int compare;
20283910Sdougm #define	OPT_CMP_GE	0
20293910Sdougm #define	OPT_CMP_LE	1
20303910Sdougm 	int (*check)(char *);
20313910Sdougm } proto_options[] = {
20323910Sdougm #define	PROTO_OPT_NFSD_SERVERS			0
20333910Sdougm 	{"nfsd_servers",
20343910Sdougm 	    "servers", PROTO_OPT_NFSD_SERVERS, OPT_TYPE_NUMBER, 16, SVC_NFSD,
20353910Sdougm 	    1, INT32_MAX, NFSADMIN},
20363910Sdougm #define	PROTO_OPT_LOCKD_LISTEN_BACKLOG		1
20373910Sdougm 	{"lockd_listen_backlog",
20383910Sdougm 	    "lockd_listen_backlog", PROTO_OPT_LOCKD_LISTEN_BACKLOG,
20393910Sdougm 	    OPT_TYPE_NUMBER, 32, SVC_LOCKD, 32, INT32_MAX, NFSADMIN},
20403910Sdougm #define	PROTO_OPT_LOCKD_SERVERS			2
20413910Sdougm 	{"lockd_servers",
20423910Sdougm 	    "lockd_servers", PROTO_OPT_LOCKD_SERVERS, OPT_TYPE_NUMBER, 20,
20433910Sdougm 	    SVC_LOCKD, 1, INT32_MAX, NFSADMIN},
20443910Sdougm #define	PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT	3
20453910Sdougm 	{"lockd_retransmit_timeout",
20463910Sdougm 	    "lockd_retransmit_timeout", PROTO_OPT_LOCKD_RETRANSMIT_TIMEOUT,
20473910Sdougm 	    OPT_TYPE_NUMBER, 5, SVC_LOCKD, 0, INT32_MAX, NFSADMIN},
20483910Sdougm #define	PROTO_OPT_GRACE_PERIOD			4
20493910Sdougm 	{"grace_period",
20503910Sdougm 	    "grace_period", PROTO_OPT_GRACE_PERIOD, OPT_TYPE_NUMBER, 90,
20513910Sdougm 	    SVC_LOCKD, 0, INT32_MAX, NFSADMIN},
20523910Sdougm #define	PROTO_OPT_NFS_SERVER_VERSMIN		5
20533910Sdougm 	{"nfs_server_versmin",
20543910Sdougm 	    "server_versmin", PROTO_OPT_NFS_SERVER_VERSMIN, OPT_TYPE_NUMBER,
20553910Sdougm 	    (int)NFS_VERSMIN_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
20563910Sdougm 	    NFS_VERSMAX, NFSADMIN, "server_versmax", OPT_CMP_LE},
20573910Sdougm #define	PROTO_OPT_NFS_SERVER_VERSMAX		6
20583910Sdougm 	{"nfs_server_versmax",
20593910Sdougm 	    "server_versmax", PROTO_OPT_NFS_SERVER_VERSMAX, OPT_TYPE_NUMBER,
20603910Sdougm 	    (int)NFS_VERSMAX_DEFAULT, SVC_NFSD|SVC_MOUNTD, NFS_VERSMIN,
20613910Sdougm 	    NFS_VERSMAX, NFSADMIN, "server_versmin", OPT_CMP_GE},
20623910Sdougm #define	PROTO_OPT_NFS_CLIENT_VERSMIN		7
20633910Sdougm 	{"nfs_client_versmin",
20643910Sdougm 	    "client_versmin", PROTO_OPT_NFS_CLIENT_VERSMIN, OPT_TYPE_NUMBER,
20653910Sdougm 	    (int)NFS_VERSMIN_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX,
20663910Sdougm 	    NFSADMIN, "client_versmax", OPT_CMP_LE},
20673910Sdougm #define	PROTO_OPT_NFS_CLIENT_VERSMAX		8
20683910Sdougm 	{"nfs_client_versmax",
20693910Sdougm 	    "client_versmax", PROTO_OPT_NFS_CLIENT_VERSMAX, OPT_TYPE_NUMBER,
20703910Sdougm 	    (int)NFS_VERSMAX_DEFAULT, NULL, NFS_VERSMIN, NFS_VERSMAX,
20713910Sdougm 	    NFSADMIN, "client_versmin", OPT_CMP_GE},
20723910Sdougm #define	PROTO_OPT_NFS_SERVER_DELEGATION		9
20733910Sdougm 	{"nfs_server_delegation",
20743910Sdougm 	    "server_delegation", PROTO_OPT_NFS_SERVER_DELEGATION,
20753910Sdougm 	    OPT_TYPE_ONOFF, NFS_SERVER_DELEGATION_DEFAULT, SVC_NFSD, 0, 0,
20763910Sdougm 	    NFSADMIN},
20773910Sdougm #define	PROTO_OPT_NFSMAPID_DOMAIN		10
20783910Sdougm 	{"nfsmapid_domain",
20793910Sdougm 	    "nfsmapid_domain", PROTO_OPT_NFSMAPID_DOMAIN, OPT_TYPE_DOMAIN,
20803910Sdougm 	    NULL, SVC_NFSMAPID, 0, 0, NFSADMIN},
20813910Sdougm #define	PROTO_OPT_NFSD_MAX_CONNECTIONS		11
20823910Sdougm 	{"nfsd_max_connections",
20833910Sdougm 	    "max_connections", PROTO_OPT_NFSD_MAX_CONNECTIONS,
20843910Sdougm 	    OPT_TYPE_NUMBER, -1, SVC_NFSD, -1, INT32_MAX, NFSADMIN},
20853910Sdougm #define	PROTO_OPT_NFSD_PROTOCOL			12
20863910Sdougm 	{"nfsd_protocol",
20873910Sdougm 	    "protocol", PROTO_OPT_NFSD_PROTOCOL, OPT_TYPE_PROTOCOL, 0,
20883910Sdougm 	    SVC_NFSD, 0, 0, NFSADMIN},
20893910Sdougm #define	PROTO_OPT_NFSD_LISTEN_BACKLOG		13
20903910Sdougm 	{"nfsd_listen_backlog",
20913910Sdougm 	    "listen_backlog", PROTO_OPT_NFSD_LISTEN_BACKLOG,
20923910Sdougm 	    OPT_TYPE_NUMBER, 0,
20933910Sdougm 	    SVC_LOCKD, 0, INT32_MAX, NFSADMIN},
20943910Sdougm 	{NULL}
20953910Sdougm };
20963910Sdougm 
20973910Sdougm /*
20983910Sdougm  * the protoset holds the defined options so we don't have to read
20993910Sdougm  * them multiple times
21003910Sdougm  */
21013910Sdougm sa_protocol_properties_t protoset;
21023910Sdougm 
21033910Sdougm static int
21043910Sdougm findprotoopt(char *name, int whichname)
21053910Sdougm {
21063910Sdougm 	int i;
21073910Sdougm 	for (i = 0; proto_options[i].tag != NULL; i++) {
2108*4345Sdougm 		if (whichname == 1) {
2109*4345Sdougm 			if (strcasecmp(proto_options[i].name, name) == 0)
21103910Sdougm 			return (i);
2111*4345Sdougm 		} else {
2112*4345Sdougm 			if (strcasecmp(proto_options[i].tag, name) == 0)
2113*4345Sdougm 				return (i);
2114*4345Sdougm 		}
21153910Sdougm 	}
21163910Sdougm 	return (-1);
21173910Sdougm }
21183910Sdougm 
21193910Sdougm /*
21203910Sdougm  * fixcaselower(str)
21213910Sdougm  *
21223910Sdougm  * convert a string to lower case (inplace).
21233910Sdougm  */
21243910Sdougm 
21253910Sdougm static void
21263910Sdougm fixcaselower(char *str)
21273910Sdougm {
21283910Sdougm 	while (*str) {
2129*4345Sdougm 		*str = tolower(*str);
2130*4345Sdougm 		str++;
21313910Sdougm 	}
21323910Sdougm }
21333910Sdougm 
21343910Sdougm /*
21353910Sdougm  * fixcaseupper(str)
21363910Sdougm  *
21373910Sdougm  * convert a string to upper case (inplace).
21383910Sdougm  */
21393910Sdougm 
21403910Sdougm static void
21413910Sdougm fixcaseupper(char *str)
21423910Sdougm {
21433910Sdougm 	while (*str) {
2144*4345Sdougm 		*str = toupper(*str);
2145*4345Sdougm 		str++;
21463910Sdougm 	}
21473910Sdougm }
21483910Sdougm 
21493910Sdougm /*
21504241Sdougm  * skipwhitespace(str)
21514241Sdougm  *
21524241Sdougm  * Skip leading white space. It is assumed that it is called with a
21534241Sdougm  * valid pointer.
21544241Sdougm  */
21554241Sdougm 
21564241Sdougm static char *
21574241Sdougm skipwhitespace(char *str)
21584241Sdougm {
21594241Sdougm 	while (*str && isspace(*str))
21604241Sdougm 		str++;
21614241Sdougm 
21624241Sdougm 	return (str);
21634241Sdougm }
21644241Sdougm 
21654241Sdougm /*
2166*4345Sdougm  * extractprop()
2167*4345Sdougm  *
2168*4345Sdougm  * Extract the property and value out of the line and create the
2169*4345Sdougm  * property in the optionset.
2170*4345Sdougm  */
2171*4345Sdougm static void
2172*4345Sdougm extractprop(char *name, char *value)
2173*4345Sdougm {
2174*4345Sdougm 	sa_property_t prop;
2175*4345Sdougm 	int index;
2176*4345Sdougm 	/*
2177*4345Sdougm 	 * Remove any leading
2178*4345Sdougm 	 * white space.
2179*4345Sdougm 	 */
2180*4345Sdougm 	name = skipwhitespace(name);
2181*4345Sdougm 
2182*4345Sdougm 	index = findprotoopt(name, 0);
2183*4345Sdougm 	if (index >= 0) {
2184*4345Sdougm 		fixcaselower(name);
2185*4345Sdougm 		prop = sa_create_property(proto_options[index].name, value);
2186*4345Sdougm 		if (prop != NULL)
2187*4345Sdougm 			(void) sa_add_protocol_property(protoset, prop);
2188*4345Sdougm 	}
2189*4345Sdougm }
2190*4345Sdougm 
2191*4345Sdougm /*
21923910Sdougm  * initprotofromdefault()
21933910Sdougm  *
21943910Sdougm  * read the default file(s) and add the defined values to the
21953910Sdougm  * protoset.  Note that default values are known from the built in
21963910Sdougm  * table in case the file doesn't have a definition.
21973910Sdougm  */
21983910Sdougm 
21993910Sdougm static int
22003910Sdougm initprotofromdefault()
22013910Sdougm {
22023910Sdougm 	FILE *nfs;
22033910Sdougm 	char buff[BUFSIZ];
22043910Sdougm 	char *name;
22053910Sdougm 	char *value;
22063910Sdougm 
22073910Sdougm 	protoset = sa_create_protocol_properties("nfs");
22083910Sdougm 
22093910Sdougm 	if (protoset != NULL) {
2210*4345Sdougm 		nfs = fopen(NFSADMIN, "r");
2211*4345Sdougm 		if (nfs != NULL) {
2212*4345Sdougm 			while (fgets(buff, sizeof (buff), nfs) != NULL) {
2213*4345Sdougm 				switch (buff[0]) {
2214*4345Sdougm 				case '\n':
2215*4345Sdougm 				case '#':
2216*4345Sdougm 					/* skip */
2217*4345Sdougm 					break;
2218*4345Sdougm 				default:
2219*4345Sdougm 					name = buff;
2220*4345Sdougm 					buff[strlen(buff) - 1] = '\0';
2221*4345Sdougm 					value = strchr(name, '=');
2222*4345Sdougm 					if (value != NULL) {
2223*4345Sdougm 						*value++ = '\0';
2224*4345Sdougm 						extractprop(name, value);
2225*4345Sdougm 					}
2226*4345Sdougm 				}
22273910Sdougm 			}
2228*4345Sdougm 			if (nfs != NULL)
2229*4345Sdougm 				(void) fclose(nfs);
22303910Sdougm 		}
22313910Sdougm 	}
22323910Sdougm 	if (protoset == NULL)
2233*4345Sdougm 		return (SA_NO_MEMORY);
22343910Sdougm 	return (SA_OK);
22353910Sdougm }
22363910Sdougm 
22373910Sdougm /*
2238*4345Sdougm  * add_defaults()
22393910Sdougm  *
22403910Sdougm  * Add the default values for any property not defined in the parsing
22413910Sdougm  * of the default files. Values are set according to their defined
22423910Sdougm  * types.
22433910Sdougm  */
22443910Sdougm 
22453910Sdougm static void
22463910Sdougm add_defaults()
22473910Sdougm {
22483910Sdougm 	int i;
22493910Sdougm 	char number[MAXDIGITS];
22503910Sdougm 
22513910Sdougm 	for (i = 0; proto_options[i].tag != NULL; i++) {
2252*4345Sdougm 		sa_property_t prop;
2253*4345Sdougm 		prop = sa_get_protocol_property(protoset,
2254*4345Sdougm 		    proto_options[i].name);
2255*4345Sdougm 		if (prop == NULL) {
2256*4345Sdougm 			/* add the default value */
2257*4345Sdougm 			switch (proto_options[i].type) {
2258*4345Sdougm 			case OPT_TYPE_NUMBER:
2259*4345Sdougm 				(void) snprintf(number, sizeof (number), "%d",
2260*4345Sdougm 				    proto_options[i].defvalue.intval);
2261*4345Sdougm 				prop = sa_create_property(proto_options[i].name,
2262*4345Sdougm 				    number);
2263*4345Sdougm 				break;
22643910Sdougm 
2265*4345Sdougm 			case OPT_TYPE_BOOLEAN:
2266*4345Sdougm 				prop = sa_create_property(proto_options[i].name,
2267*4345Sdougm 				    proto_options[i].defvalue.intval ?
2268*4345Sdougm 				    "true" : "false");
2269*4345Sdougm 				break;
22703910Sdougm 
2271*4345Sdougm 			case OPT_TYPE_ONOFF:
2272*4345Sdougm 				prop = sa_create_property(proto_options[i].name,
2273*4345Sdougm 				    proto_options[i].defvalue.intval ?
2274*4345Sdougm 				    "on" : "off");
2275*4345Sdougm 				break;
22763910Sdougm 
2277*4345Sdougm 			default:
2278*4345Sdougm 				/* treat as strings of zero length */
2279*4345Sdougm 				prop = sa_create_property(proto_options[i].name,
2280*4345Sdougm 				    "");
2281*4345Sdougm 				break;
2282*4345Sdougm 			}
2283*4345Sdougm 			if (prop != NULL)
2284*4345Sdougm 				(void) sa_add_protocol_property(protoset, prop);
22853910Sdougm 		}
22863910Sdougm 	}
22873910Sdougm }
22883910Sdougm 
22893910Sdougm static void
22903910Sdougm free_protoprops()
22913910Sdougm {
22923910Sdougm 	xmlFreeNode(protoset);
22933910Sdougm }
22943910Sdougm 
22953910Sdougm /*
22963910Sdougm  * nfs_init()
22973910Sdougm  *
22983910Sdougm  * Initialize the NFS plugin.
22993910Sdougm  */
23003910Sdougm 
23013910Sdougm static int
23023910Sdougm nfs_init()
23033910Sdougm {
23043910Sdougm 	int ret = SA_OK;
23053910Sdougm 
23063910Sdougm 	if (sa_plugin_ops.sa_init != nfs_init)
2307*4345Sdougm 		(void) printf(dgettext(TEXT_DOMAIN,
2308*4345Sdougm 		    "NFS plugin not properly initialized\n"));
23093910Sdougm 
23103910Sdougm 	ret = initprotofromdefault();
2311*4345Sdougm 	if (ret == SA_OK)
2312*4345Sdougm 		add_defaults();
23133910Sdougm 
23143910Sdougm 	return (ret);
23153910Sdougm }
23163910Sdougm 
23173910Sdougm /*
23183910Sdougm  * nfs_fini()
23193910Sdougm  *
23203910Sdougm  * uninitialize the NFS plugin. Want to avoid memory leaks.
23213910Sdougm  */
23223910Sdougm 
23233910Sdougm static void
23243910Sdougm nfs_fini()
23253910Sdougm {
23263910Sdougm 	free_protoprops();
23273910Sdougm }
23283910Sdougm 
23293910Sdougm /*
23303910Sdougm  * nfs_get_proto_set()
23313910Sdougm  *
23323910Sdougm  * Return an optionset with all the protocol specific properties in
23333910Sdougm  * it.
23343910Sdougm  */
23353910Sdougm 
23363910Sdougm static sa_protocol_properties_t
23373910Sdougm nfs_get_proto_set()
23383910Sdougm {
23393910Sdougm 	return (protoset);
23403910Sdougm }
23413910Sdougm 
23423910Sdougm struct deffile {
23433910Sdougm 	struct deffile *next;
23443910Sdougm 	char *line;
23453910Sdougm };
23463910Sdougm 
23473910Sdougm /*
23483910Sdougm  * read_default_file(fname)
23493910Sdougm  *
23503910Sdougm  * Read the specified default file. We return a list of entries. This
23513910Sdougm  * get used for adding or removing values.
23523910Sdougm  */
23533910Sdougm 
23543910Sdougm static struct deffile *
23553910Sdougm read_default_file(char *fname)
23563910Sdougm {
23573910Sdougm 	FILE *file;
23583910Sdougm 	struct deffile *defs = NULL;
23593910Sdougm 	struct deffile *newdef;
23603910Sdougm 	struct deffile *prevdef = NULL;
23613910Sdougm 	char buff[BUFSIZ * 2];
23623910Sdougm 
23633910Sdougm 	file = fopen(fname, "r");
23643910Sdougm 	if (file != NULL) {
2365*4345Sdougm 		while (fgets(buff, sizeof (buff), file) != NULL) {
2366*4345Sdougm 			newdef = (struct deffile *)calloc(1,
2367*4345Sdougm 			    sizeof (struct deffile));
2368*4345Sdougm 			if (newdef != NULL) {
2369*4345Sdougm 				/* Make sure we skip any leading whitespace. */
2370*4345Sdougm 				newdef->line = strdup(skipwhitespace(buff));
2371*4345Sdougm 				if (defs == NULL) {
2372*4345Sdougm 					prevdef = defs = newdef;
2373*4345Sdougm 				} else {
2374*4345Sdougm 					prevdef->next = newdef;
2375*4345Sdougm 					prevdef = newdef;
2376*4345Sdougm 				}
2377*4345Sdougm 			}
23783910Sdougm 		}
23793910Sdougm 	}
23803910Sdougm 	(void) fclose(file);
23813910Sdougm 	return (defs);
23823910Sdougm }
23833910Sdougm 
23843910Sdougm static void
23853910Sdougm free_default_file(struct deffile *defs)
23863910Sdougm {
23873910Sdougm 	struct deffile *curdefs = NULL;
23883910Sdougm 
23893910Sdougm 	while (defs != NULL) {
2390*4345Sdougm 		curdefs = defs;
2391*4345Sdougm 		defs = defs->next;
2392*4345Sdougm 		if (curdefs->line != NULL)
2393*4345Sdougm 			free(curdefs->line);
2394*4345Sdougm 		free(curdefs);
23953910Sdougm 	}
23963910Sdougm }
23973910Sdougm 
23983910Sdougm /*
23993910Sdougm  * write_default_file(fname, defs)
24003910Sdougm  *
24013910Sdougm  * Write the default file back.
24023910Sdougm  */
24033910Sdougm 
24043910Sdougm static int
24053910Sdougm write_default_file(char *fname, struct deffile *defs)
24063910Sdougm {
24073910Sdougm 	FILE *file;
24083910Sdougm 	int ret = SA_OK;
24093910Sdougm 	sigset_t old, new;
24103910Sdougm 
24113910Sdougm 	file = fopen(fname, "w+");
24123910Sdougm 	if (file != NULL) {
2413*4345Sdougm 		(void) sigprocmask(SIG_BLOCK, NULL, &new);
2414*4345Sdougm 		(void) sigaddset(&new, SIGHUP);
2415*4345Sdougm 		(void) sigaddset(&new, SIGINT);
2416*4345Sdougm 		(void) sigaddset(&new, SIGQUIT);
2417*4345Sdougm 		(void) sigaddset(&new, SIGTSTP);
2418*4345Sdougm 		(void) sigprocmask(SIG_SETMASK, &new, &old);
2419*4345Sdougm 		while (defs != NULL) {
2420*4345Sdougm 			(void) fputs(defs->line, file);
2421*4345Sdougm 			defs = defs->next;
2422*4345Sdougm 		}
2423*4345Sdougm 		(void) fsync(fileno(file));
2424*4345Sdougm 		(void) sigprocmask(SIG_SETMASK, &old, NULL);
2425*4345Sdougm 		(void) fclose(file);
24263910Sdougm 	} else {
2427*4345Sdougm 		switch (errno) {
2428*4345Sdougm 		case EPERM:
2429*4345Sdougm 		case EACCES:
2430*4345Sdougm 			ret = SA_NO_PERMISSION;
2431*4345Sdougm 			break;
2432*4345Sdougm 		default:
2433*4345Sdougm 			ret = SA_SYSTEM_ERR;
2434*4345Sdougm 		}
24353910Sdougm 	}
24363910Sdougm 	return (ret);
24373910Sdougm }
24383910Sdougm 
24393910Sdougm 
24403910Sdougm /*
24413910Sdougm  * set_default_file_value(tag, value)
24423910Sdougm  *
24433910Sdougm  * Set the default file value for tag to value. Then rewrite the file.
24443910Sdougm  * tag and value are always set.  The caller must ensure this.
24453910Sdougm  */
24463910Sdougm 
24473910Sdougm #define	MAX_STRING_LENGTH	256
24483910Sdougm static int
24493910Sdougm set_default_file_value(char *tag, char *value)
24503910Sdougm {
24513910Sdougm 	int ret = SA_OK;
24523910Sdougm 	struct deffile *root;
24533910Sdougm 	struct deffile *defs;
24543910Sdougm 	struct deffile *prev;
24553910Sdougm 	char string[MAX_STRING_LENGTH];
24563910Sdougm 	int len;
24573910Sdougm 	int update = 0;
24583910Sdougm 
24593910Sdougm 	(void) snprintf(string, MAX_STRING_LENGTH, "%s=", tag);
24603910Sdougm 	len = strlen(string);
24613910Sdougm 
24623910Sdougm 	root = defs = read_default_file(NFSADMIN);
24633910Sdougm 	if (root == NULL) {
2464*4345Sdougm 		if (errno == EPERM || errno == EACCES)
2465*4345Sdougm 			ret = SA_NO_PERMISSION;
2466*4345Sdougm 		else
2467*4345Sdougm 			ret = SA_SYSTEM_ERR;
24683910Sdougm 	} else {
24693910Sdougm 		while (defs != NULL) {
2470*4345Sdougm 			if (defs->line != NULL &&
2471*4345Sdougm 			    strncasecmp(defs->line, string, len) == 0) {
2472*4345Sdougm 				/* replace with the new value */
2473*4345Sdougm 				free(defs->line);
2474*4345Sdougm 				fixcaseupper(tag);
2475*4345Sdougm 				(void) snprintf(string, sizeof (string),
24763910Sdougm 				    "%s=%s\n", tag, value);
2477*4345Sdougm 				string[MAX_STRING_LENGTH - 1] = '\0';
2478*4345Sdougm 				defs->line = strdup(string);
2479*4345Sdougm 				update = 1;
2480*4345Sdougm 				break;
2481*4345Sdougm 			}
2482*4345Sdougm 			defs = defs->next;
24833910Sdougm 		}
2484*4345Sdougm 		if (!update) {
2485*4345Sdougm 			defs = root;
2486*4345Sdougm 			/* didn't find, so see if it is a comment */
2487*4345Sdougm 			(void) snprintf(string, MAX_STRING_LENGTH, "#%s=", tag);
2488*4345Sdougm 			len = strlen(string);
2489*4345Sdougm 			while (defs != NULL) {
2490*4345Sdougm 				if (strncasecmp(defs->line, string, len) == 0) {
2491*4345Sdougm 					/* replace with the new value */
2492*4345Sdougm 					free(defs->line);
2493*4345Sdougm 					fixcaseupper(tag);
2494*4345Sdougm 					(void) snprintf(string, sizeof (string),
2495*4345Sdougm 					    "%s=%s\n", tag, value);
2496*4345Sdougm 					string[MAX_STRING_LENGTH - 1] = '\0';
2497*4345Sdougm 					defs->line = strdup(string);
2498*4345Sdougm 					update = 1;
2499*4345Sdougm 					break;
2500*4345Sdougm 				}
2501*4345Sdougm 				defs = defs->next;
2502*4345Sdougm 			}
25033910Sdougm 		}
2504*4345Sdougm 		if (!update) {
2505*4345Sdougm 			fixcaseupper(tag);
2506*4345Sdougm 			(void) snprintf(string, sizeof (string), "%s=%s\n",
2507*4345Sdougm 			    tag, value);
2508*4345Sdougm 			prev = root;
2509*4345Sdougm 			while (prev->next != NULL)
2510*4345Sdougm 				prev = prev->next;
2511*4345Sdougm 			defs = malloc(sizeof (struct deffile));
2512*4345Sdougm 			prev->next = defs;
2513*4345Sdougm 			if (defs != NULL) {
2514*4345Sdougm 				defs->next = NULL;
2515*4345Sdougm 				defs->line = strdup(string);
2516*4345Sdougm 			}
2517*4345Sdougm 		}
2518*4345Sdougm 		if (update) {
2519*4345Sdougm 			ret = write_default_file(NFSADMIN, root);
2520*4345Sdougm 		}
2521*4345Sdougm 		free_default_file(root);
25223910Sdougm 	}
25233910Sdougm 	return (ret);
25243910Sdougm }
25253910Sdougm 
25263910Sdougm /*
25273910Sdougm  * service_in_state(service, chkstate)
25283910Sdougm  *
25293910Sdougm  * Want to know if the specified service is in the desired state
25303910Sdougm  * (chkstate) or not. Return true (1) if it is and false (0) if it
25313910Sdougm  * isn't.
25323910Sdougm  */
25333910Sdougm static int
25343910Sdougm service_in_state(char *service, const char *chkstate)
25353910Sdougm {
25363910Sdougm 	char *state;
25373910Sdougm 	int ret = B_FALSE;
25383910Sdougm 
25393910Sdougm 	state = smf_get_state(service);
25403910Sdougm 	if (state != NULL) {
2541*4345Sdougm 		/* got the state so get the equality for the return value */
2542*4345Sdougm 		ret = strcmp(state, chkstate) == 0 ? B_TRUE : B_FALSE;
2543*4345Sdougm 		free(state);
25443910Sdougm 	}
25453910Sdougm 	return (ret);
25463910Sdougm }
25473910Sdougm 
25483910Sdougm /*
25493910Sdougm  * restart_service(svcs)
25503910Sdougm  *
25513910Sdougm  * Walk through the bit mask of services that need to be restarted in
25523910Sdougm  * order to use the new property values. Some properties affect
25533910Sdougm  * multiple daemons. Should only restart a service if it is currently
25543910Sdougm  * enabled (online).
25553910Sdougm  */
25563910Sdougm 
25573910Sdougm static void
25583910Sdougm restart_service(uint32_t svcs)
25593910Sdougm {
25603910Sdougm 	uint32_t mask;
25613910Sdougm 	int ret;
25623910Sdougm 	char *service;
25633910Sdougm 
25643910Sdougm 	for (mask = 1; svcs != 0; mask <<= 1) {
2565*4345Sdougm 		switch (svcs & mask) {
2566*4345Sdougm 		case SVC_LOCKD:
2567*4345Sdougm 			service = LOCKD;
2568*4345Sdougm 			break;
2569*4345Sdougm 		case SVC_STATD:
2570*4345Sdougm 			service = STATD;
2571*4345Sdougm 			break;
2572*4345Sdougm 		case SVC_NFSD:
2573*4345Sdougm 			service = NFSD;
2574*4345Sdougm 			break;
2575*4345Sdougm 		case SVC_MOUNTD:
2576*4345Sdougm 			service = MOUNTD;
2577*4345Sdougm 			break;
2578*4345Sdougm 		case SVC_NFS4CBD:
2579*4345Sdougm 			service = NFS4CBD;
2580*4345Sdougm 			break;
2581*4345Sdougm 		case SVC_NFSMAPID:
2582*4345Sdougm 			service = NFSMAPID;
2583*4345Sdougm 			break;
2584*4345Sdougm 		case SVC_RQUOTAD:
2585*4345Sdougm 			service = RQUOTAD;
2586*4345Sdougm 			break;
2587*4345Sdougm 		case SVC_NFSLOGD:
2588*4345Sdougm 			service = NFSLOGD;
2589*4345Sdougm 			break;
2590*4345Sdougm 		default:
2591*4345Sdougm 			continue;
2592*4345Sdougm 		}
25933910Sdougm 
25943910Sdougm 		/*
25953910Sdougm 		 * Only attempt to restart the service if it is
25963910Sdougm 		 * currently running. In the future, it may be
25973910Sdougm 		 * desirable to use smf_refresh_instance if the NFS
25983910Sdougm 		 * services ever implement the refresh method.
25993910Sdougm 		 */
2600*4345Sdougm 		if (service_in_state(service, SCF_STATE_STRING_ONLINE)) {
2601*4345Sdougm 			ret = smf_restart_instance(service);
26023910Sdougm 			/*
2603*4345Sdougm 			 * There are only a few SMF errors at this point, but
2604*4345Sdougm 			 * it is also possible that a bad value may have put
2605*4345Sdougm 			 * the service into maintenance if there wasn't an
2606*4345Sdougm 			 * SMF level error.
26073910Sdougm 			 */
2608*4345Sdougm 			if (ret != 0) {
2609*4345Sdougm 				(void) fprintf(stderr,
2610*4345Sdougm 				    dgettext(TEXT_DOMAIN,
2611*4345Sdougm 				    "%s failed to restart: %s\n"),
2612*4345Sdougm 				    scf_strerror(scf_error()));
2613*4345Sdougm 			} else {
2614*4345Sdougm 				/*
2615*4345Sdougm 				 * Check whether it has gone to "maintenance"
2616*4345Sdougm 				 * mode or not. Maintenance implies something
2617*4345Sdougm 				 * went wrong.
2618*4345Sdougm 				 */
2619*4345Sdougm 				if (service_in_state(service,
2620*4345Sdougm 				    SCF_STATE_STRING_MAINT)) {
2621*4345Sdougm 					(void) fprintf(stderr,
2622*4345Sdougm 					    dgettext(TEXT_DOMAIN,
2623*4345Sdougm 					    "%s failed to restart\n"),
2624*4345Sdougm 					    service);
2625*4345Sdougm 				}
2626*4345Sdougm 			}
26273910Sdougm 		}
2628*4345Sdougm 		svcs &= ~mask;
26293910Sdougm 	}
26303910Sdougm }
26313910Sdougm 
26323910Sdougm /*
26333910Sdougm  * nfs_minmax_check(name, value)
26343910Sdougm  *
26353910Sdougm  * Verify that the value for the property specified by index is valid
26363910Sdougm  * relative to the opposite value in the case of a min/max variable.
26373910Sdougm  * Currently, server_minvers/server_maxvers and
26383910Sdougm  * client_minvers/client_maxvers are the only ones to check.
26393910Sdougm  */
26403910Sdougm 
26413910Sdougm static int
26423910Sdougm nfs_minmax_check(int index, int value)
26433910Sdougm {
26443910Sdougm 	int val;
26453910Sdougm 	char *pval;
26463910Sdougm 	sa_property_t prop;
26473910Sdougm 	sa_optionset_t opts;
26483910Sdougm 	int ret = B_TRUE;
26493910Sdougm 
26503910Sdougm 	if (proto_options[index].other != NULL) {
2651*4345Sdougm 		/* have a property to compare against */
2652*4345Sdougm 		opts = nfs_get_proto_set();
2653*4345Sdougm 		prop = sa_get_property(opts, proto_options[index].other);
26543910Sdougm 		/*
26553910Sdougm 		 * If we don't find the property, assume default
26563910Sdougm 		 * values which will work since the max will be at the
26573910Sdougm 		 * max and the min at the min.
26583910Sdougm 		 */
2659*4345Sdougm 		if (prop != NULL) {
2660*4345Sdougm 			pval = sa_get_property_attr(prop, "value");
2661*4345Sdougm 			if (pval != NULL) {
2662*4345Sdougm 				val = strtoul(pval, NULL, 0);
2663*4345Sdougm 				if (proto_options[index].compare ==
2664*4345Sdougm 				    OPT_CMP_LE) {
2665*4345Sdougm 					ret = value <= val ? B_TRUE : B_FALSE;
2666*4345Sdougm 				} else if (proto_options[index].compare ==
2667*4345Sdougm 				    OPT_CMP_GE) {
2668*4345Sdougm 					ret = value >= val ? B_TRUE : B_FALSE;
2669*4345Sdougm 				}
2670*4345Sdougm 			}
26713910Sdougm 		}
26723910Sdougm 	}
26733910Sdougm 	return (ret);
26743910Sdougm }
26753910Sdougm 
26763910Sdougm /*
26773910Sdougm  * nfs_validate_proto_prop(index, name, value)
26783910Sdougm  *
26793910Sdougm  * Verify that the property specifed by name can take the new
26803910Sdougm  * value. This is a sanity check to prevent bad values getting into
26813910Sdougm  * the default files. All values need to be checked against what is
26823910Sdougm  * allowed by their defined type. If a type isn't explicitly defined
26833910Sdougm  * here, it is treated as a string.
26843910Sdougm  *
26853910Sdougm  * Note that OPT_TYPE_NUMBER will additionally check that the value is
26863910Sdougm  * within the range specified and potentially against another property
26873910Sdougm  * value as well as specified in the proto_options members other and
26883910Sdougm  * compare.
26893910Sdougm  */
26903910Sdougm 
26913910Sdougm static int
26923910Sdougm nfs_validate_proto_prop(int index, char *name, char *value)
26933910Sdougm {
26943910Sdougm 	int ret = SA_OK;
26953910Sdougm 	char *cp;
26963910Sdougm #ifdef lint
26973910Sdougm 	name = name;
26983910Sdougm #endif
26993910Sdougm 
27003910Sdougm 	switch (proto_options[index].type) {
27013910Sdougm 	case OPT_TYPE_NUMBER:
2702*4345Sdougm 		if (!is_a_number(value))
2703*4345Sdougm 			ret = SA_BAD_VALUE;
2704*4345Sdougm 		else {
2705*4345Sdougm 			int val;
2706*4345Sdougm 			val = strtoul(value, NULL, 0);
2707*4345Sdougm 			if (val < proto_options[index].minval ||
2708*4345Sdougm 			    val > proto_options[index].maxval)
2709*4345Sdougm 				ret = SA_BAD_VALUE;
2710*4345Sdougm 			/*
2711*4345Sdougm 			 * For server_versmin/server_versmax and
2712*4345Sdougm 			 * client_versmin/client_versmax, the value of the
2713*4345Sdougm 			 * min(max) should be checked to be correct relative
2714*4345Sdougm 			 * to the current max(min).
2715*4345Sdougm 			 */
2716*4345Sdougm 			if (!nfs_minmax_check(index, val)) {
2717*4345Sdougm 				ret = SA_BAD_VALUE;
2718*4345Sdougm 			}
27193910Sdougm 		}
2720*4345Sdougm 		break;
27213910Sdougm 
27223910Sdougm 	case OPT_TYPE_DOMAIN:
27233910Sdougm 		/*
27243910Sdougm 		 * needs to be a qualified domain so will have at
27253910Sdougm 		 * least one period and other characters on either
27263910Sdougm 		 * side of it.  A zero length string is also allowed
27273910Sdougm 		 * and is the way to turn off the override.
27283910Sdougm 		 */
2729*4345Sdougm 		if (strlen(value) == 0)
2730*4345Sdougm 			break;
2731*4345Sdougm 		cp = strchr(value, '.');
2732*4345Sdougm 		if (cp == NULL || cp == value || strchr(value, '@') != NULL)
2733*4345Sdougm 			ret = SA_BAD_VALUE;
27343910Sdougm 		break;
27353910Sdougm 
27363910Sdougm 	case OPT_TYPE_BOOLEAN:
2737*4345Sdougm 		if (strlen(value) == 0 ||
2738*4345Sdougm 		    strcasecmp(value, "true") == 0 ||
2739*4345Sdougm 		    strcmp(value, "1") == 0 ||
2740*4345Sdougm 		    strcasecmp(value, "false") == 0 ||
2741*4345Sdougm 		    strcmp(value, "0") == 0) {
2742*4345Sdougm 			ret = SA_OK;
2743*4345Sdougm 		} else {
2744*4345Sdougm 			ret = SA_BAD_VALUE;
2745*4345Sdougm 		}
2746*4345Sdougm 		break;
27473910Sdougm 
27483910Sdougm 	case OPT_TYPE_ONOFF:
2749*4345Sdougm 		if (strcasecmp(value, "on") != 0 &&
2750*4345Sdougm 		    strcasecmp(value, "off") != 0) {
2751*4345Sdougm 			ret = SA_BAD_VALUE;
2752*4345Sdougm 		}
2753*4345Sdougm 		break;
27543910Sdougm 
27553910Sdougm 	case OPT_TYPE_PROTOCOL:
2756*4345Sdougm 		if (strcasecmp(value, "all") != 0 &&
2757*4345Sdougm 		    strcasecmp(value, "tcp") != 0 &&
2758*4345Sdougm 		    strcasecmp(value, "udp") != 0)
2759*4345Sdougm 			ret = SA_BAD_VALUE;
2760*4345Sdougm 		break;
27613910Sdougm 
27623910Sdougm 	default:
2763*4345Sdougm 		/* treat as a string */
2764*4345Sdougm 		break;
27653910Sdougm 	}
27663910Sdougm 	return (ret);
27673910Sdougm }
27683910Sdougm 
27693910Sdougm /*
27703910Sdougm  * nfs_set_proto_prop(prop)
27713910Sdougm  *
27723910Sdougm  * check that prop is valid.
27733910Sdougm  */
27743910Sdougm 
27753910Sdougm static int
27763910Sdougm nfs_set_proto_prop(sa_property_t prop)
27773910Sdougm {
27783910Sdougm 	int ret = SA_OK;
27793910Sdougm 	char *name;
27803910Sdougm 	char *value;
27813910Sdougm 
27823910Sdougm 	name = sa_get_property_attr(prop, "type");
27833910Sdougm 	value = sa_get_property_attr(prop, "value");
27843910Sdougm 	if (name != NULL && value != NULL) {
2785*4345Sdougm 		int index = findprotoopt(name, 1);
2786*4345Sdougm 		if (index >= 0) {
2787*4345Sdougm 			/* should test for valid value */
2788*4345Sdougm 			ret = nfs_validate_proto_prop(index, name, value);
2789*4345Sdougm 			if (ret == SA_OK)
2790*4345Sdougm 				ret = set_default_file_value(
2791*4345Sdougm 				    proto_options[index].tag, value);
2792*4345Sdougm 			if (ret == SA_OK)
2793*4345Sdougm 				restart_service(proto_options[index].svcs);
2794*4345Sdougm 		}
27953910Sdougm 	}
27963910Sdougm 	if (name != NULL)
2797*4345Sdougm 		sa_free_attr_string(name);
27983910Sdougm 	if (value != NULL)
2799*4345Sdougm 		sa_free_attr_string(value);
28003910Sdougm 	return (ret);
28013910Sdougm }
28023910Sdougm 
28033910Sdougm /*
28043910Sdougm  * nfs_get_status()
28053910Sdougm  *
28063910Sdougm  * What is the current status of the nfsd? We use the SMF state here.
28073910Sdougm  * Caller must free the returned value.
28083910Sdougm  */
28093910Sdougm 
28103910Sdougm static char *
28113910Sdougm nfs_get_status()
28123910Sdougm {
28133910Sdougm 	char *state;
28143910Sdougm 	state = smf_get_state(NFSD);
28153910Sdougm 	return (state != NULL ? state : strdup("-"));
28163910Sdougm }
28173910Sdougm 
28183910Sdougm /*
28193910Sdougm  * nfs_space_alias(alias)
28203910Sdougm  *
28213910Sdougm  * Lookup the space (security) name. If it is default, convert to the
28223910Sdougm  * real name.
28233910Sdougm  */
28243910Sdougm 
28253910Sdougm static char *
28263910Sdougm nfs_space_alias(char *space)
28273910Sdougm {
28283910Sdougm 	char *name = space;
28293910Sdougm 	seconfig_t secconf;
28303910Sdougm 
28313910Sdougm 	/*
28323910Sdougm 	 * Only the space named "default" is special. If it is used,
28333910Sdougm 	 * the default needs to be looked up and the real name used.
28343910Sdougm 	 * This is normally "sys" but could be changed.  We always
28353910Sdougm 	 * change defautl to the real name.
28363910Sdougm 	 */
28373910Sdougm 	if (strcmp(space, "default") == 0 &&
28383910Sdougm 	    nfs_getseconfig_default(&secconf) == 0) {
2839*4345Sdougm 		if (nfs_getseconfig_bynumber(secconf.sc_nfsnum, &secconf) == 0)
2840*4345Sdougm 			name = secconf.sc_name;
28413910Sdougm 	}
28423910Sdougm 	return (strdup(name));
28433910Sdougm }
2844