13034Sdougm /*
23034Sdougm  * CDDL HEADER START
33034Sdougm  *
43034Sdougm  * The contents of this file are subject to the terms of the
53034Sdougm  * Common Development and Distribution License (the "License").
63034Sdougm  * You may not use this file except in compliance with the License.
73034Sdougm  *
83034Sdougm  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93034Sdougm  * or http://www.opensolaris.org/os/licensing.
103034Sdougm  * See the License for the specific language governing permissions
113034Sdougm  * and limitations under the License.
123034Sdougm  *
133034Sdougm  * When distributing Covered Code, include this CDDL HEADER in each
143034Sdougm  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153034Sdougm  * If applicable, add the following below this CDDL HEADER, with the
163034Sdougm  * fields enclosed by brackets "[]" replaced with your own identifying
173034Sdougm  * information: Portions Copyright [yyyy] [name of copyright owner]
183034Sdougm  *
193034Sdougm  * CDDL HEADER END
203034Sdougm  */
213034Sdougm 
223034Sdougm /*
233348Sdougm  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
243034Sdougm  * Use is subject to license terms.
253034Sdougm  */
263034Sdougm 
273034Sdougm #pragma ident	"%Z%%M%	%I%	%E% SMI"
283034Sdougm 
293034Sdougm #include <sys/types.h>
303034Sdougm #include <sys/stat.h>
313034Sdougm #include <fcntl.h>
323034Sdougm #include <stdlib.h>
333034Sdougm #include <stdio.h>
343034Sdougm #include <string.h>
353034Sdougm #include <ctype.h>
363034Sdougm #include <unistd.h>
373034Sdougm #include <getopt.h>
383034Sdougm #include <utmpx.h>
393034Sdougm #include <pwd.h>
403034Sdougm #include <auth_attr.h>
413034Sdougm #include <secdb.h>
423034Sdougm #include <sys/param.h>
433034Sdougm #include <sys/stat.h>
443034Sdougm #include <errno.h>
453034Sdougm 
463034Sdougm #include <libshare.h>
473034Sdougm #include "sharemgr.h"
483034Sdougm #include <libscf.h>
493034Sdougm #include <libxml/tree.h>
503034Sdougm #include <libintl.h>
513034Sdougm 
523034Sdougm static char *sa_get_usage(sa_usage_t);
533034Sdougm 
543034Sdougm /*
553034Sdougm  * Implementation of the common sub-commands supported by sharemgr.
563034Sdougm  * A number of helper functions are also included.
573034Sdougm  */
583034Sdougm 
593034Sdougm /*
603034Sdougm  * has_protocol(group, proto)
613034Sdougm  *	If the group has an optionset with the specified protocol,
623034Sdougm  *	return true (1) otherwise false (0).
633034Sdougm  */
643034Sdougm static int
653034Sdougm has_protocol(sa_group_t group, char *protocol)
663034Sdougm {
673034Sdougm 	sa_optionset_t optionset;
683034Sdougm 	int result = 0;
693034Sdougm 
703034Sdougm 	optionset = sa_get_optionset(group, protocol);
713034Sdougm 	if (optionset != NULL) {
724653Sdougm 		result++;
733034Sdougm 	}
743034Sdougm 	return (result);
753034Sdougm }
763034Sdougm 
773034Sdougm /*
783034Sdougm  * add_list(list, item)
793034Sdougm  *	Adds a new list member that points to item to the list.
803034Sdougm  *	If list is NULL, it starts a new list.  The function returns
813034Sdougm  *	the first member of the list.
823034Sdougm  */
833034Sdougm struct list *
843034Sdougm add_list(struct list *listp, void *item, void *data)
853034Sdougm {
863034Sdougm 	struct list *new, *tmp;
873034Sdougm 
883034Sdougm 	new = malloc(sizeof (struct list));
893034Sdougm 	if (new != NULL) {
904653Sdougm 		new->next = NULL;
914653Sdougm 		new->item = item;
924653Sdougm 		new->itemdata = data;
933034Sdougm 	} else {
944653Sdougm 		return (listp);
953034Sdougm 	}
963034Sdougm 
973034Sdougm 	if (listp == NULL)
984653Sdougm 		return (new);
993034Sdougm 
1003034Sdougm 	for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
1013034Sdougm 		/* get to end of list */
1023034Sdougm 	}
1033034Sdougm 	tmp->next = new;
1043034Sdougm 	return (listp);
1053034Sdougm }
1063034Sdougm 
1073034Sdougm /*
1083034Sdougm  * free_list(list)
1093034Sdougm  *	Given a list, free all the members of the list;
1103034Sdougm  */
1113034Sdougm static void
1123034Sdougm free_list(struct list *listp)
1133034Sdougm {
1143034Sdougm 	struct list *tmp;
1153034Sdougm 	while (listp != NULL) {
1164653Sdougm 		tmp = listp;
1174653Sdougm 		listp = listp->next;
1184653Sdougm 		free(tmp);
1193034Sdougm 	}
1203034Sdougm }
1213034Sdougm 
1223034Sdougm /*
1233034Sdougm  * check_authorization(instname, which)
1243034Sdougm  *
1253034Sdougm  * Checks to see if the specific type of authorization in which is
1263034Sdougm  * enabled for the user in this SMF service instance.
1273034Sdougm  */
1283034Sdougm 
1293034Sdougm static int
1303034Sdougm check_authorization(char *instname, int which)
1313034Sdougm {
1323034Sdougm 	scf_handle_t *handle = NULL;
1333034Sdougm 	scf_simple_prop_t *prop = NULL;
1343034Sdougm 	char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
1353034Sdougm 	char *authstr = NULL;
1363034Sdougm 	ssize_t numauths;
1374653Sdougm 	int ret = B_TRUE;
1383034Sdougm 	uid_t uid;
1393034Sdougm 	struct passwd *pw = NULL;
1403034Sdougm 
1413034Sdougm 	uid = getuid();
1423034Sdougm 	pw = getpwuid(uid);
1434653Sdougm 	if (pw == NULL) {
1444653Sdougm 		ret = B_FALSE;
1454653Sdougm 	} else {
1464653Sdougm 		/*
1474653Sdougm 		 * Since names are restricted to SA_MAX_NAME_LEN won't
1484653Sdougm 		 * overflow.
1494653Sdougm 		 */
1504653Sdougm 		(void) snprintf(svcstring, sizeof (svcstring), "%s:%s",
1514653Sdougm 		    SA_SVC_FMRI_BASE, instname);
1524653Sdougm 		handle = scf_handle_create(SCF_VERSION);
1534653Sdougm 		if (handle != NULL) {
1544653Sdougm 			if (scf_handle_bind(handle) == 0) {
1554653Sdougm 				switch (which) {
1564653Sdougm 				case SVC_SET:
1574653Sdougm 					prop = scf_simple_prop_get(handle,
1584653Sdougm 					    svcstring, "general",
1594653Sdougm 					    SVC_AUTH_VALUE);
1604653Sdougm 					break;
1614653Sdougm 				case SVC_ACTION:
1624653Sdougm 					prop = scf_simple_prop_get(handle,
1634653Sdougm 					    svcstring, "general",
1644653Sdougm 					    SVC_AUTH_ACTION);
1654653Sdougm 					break;
1664653Sdougm 				}
1674653Sdougm 			}
1683034Sdougm 		}
1693034Sdougm 	}
1703034Sdougm 	/* make sure we have an authorization string property */
1713034Sdougm 	if (prop != NULL) {
1724653Sdougm 		int i;
1734653Sdougm 		numauths = scf_simple_prop_numvalues(prop);
1744653Sdougm 		for (ret = 0, i = 0; i < numauths; i++) {
1754653Sdougm 			authstr = scf_simple_prop_next_astring(prop);
1764653Sdougm 			if (authstr != NULL) {
1774653Sdougm 				/* check if this user has one of the strings */
1784653Sdougm 				if (chkauthattr(authstr, pw->pw_name)) {
1794653Sdougm 					ret = 1;
1804653Sdougm 					break;
1814653Sdougm 				}
1824653Sdougm 			}
1833034Sdougm 		}
1844653Sdougm 		endauthattr();
1854653Sdougm 		scf_simple_prop_free(prop);
1863034Sdougm 	} else {
1874653Sdougm 		/* no authorization string defined */
1884653Sdougm 		ret = 0;
1893034Sdougm 	}
1903034Sdougm 	if (handle != NULL)
1914653Sdougm 		scf_handle_destroy(handle);
1923034Sdougm 	return (ret);
1933034Sdougm }
1943034Sdougm 
1953034Sdougm /*
1963034Sdougm  * check_authorizations(instname, flags)
1973034Sdougm  *
1983034Sdougm  * check all the needed authorizations for the user in this service
1993034Sdougm  * instance. Return value of 1(true) or 0(false) indicates whether
2003034Sdougm  * there are authorizations for the user or not.
2013034Sdougm  */
2023034Sdougm 
2033034Sdougm static int
2043034Sdougm check_authorizations(char *instname, int flags)
2053034Sdougm {
2063034Sdougm 	int ret1 = 0;
2073034Sdougm 	int ret2 = 0;
2083034Sdougm 	int ret;
2093034Sdougm 
2103034Sdougm 	if (flags & SVC_SET)
2114653Sdougm 		ret1 = check_authorization(instname, SVC_SET);
2123034Sdougm 	if (flags & SVC_ACTION)
2134653Sdougm 		ret2 = check_authorization(instname, SVC_ACTION);
2143034Sdougm 	switch (flags) {
2153034Sdougm 	case SVC_ACTION:
2164653Sdougm 		ret = ret2;
2174653Sdougm 		break;
2183034Sdougm 	case SVC_SET:
2194653Sdougm 		ret = ret1;
2204653Sdougm 		break;
2213034Sdougm 	case SVC_ACTION|SVC_SET:
2224653Sdougm 		ret = ret1 & ret2;
2234653Sdougm 		break;
2243034Sdougm 	default:
2254653Sdougm 		/* if not flags set, we assume we don't need authorizations */
2264653Sdougm 		ret = 1;
2273034Sdougm 	}
2283034Sdougm 	return (ret);
2293034Sdougm }
2303034Sdougm 
2313034Sdougm /*
2323082Sdougm  * enable_group(group, updateproto)
2333082Sdougm  *
2343082Sdougm  * enable all the shares in the specified group. This is a helper for
2353082Sdougm  * enable_all_groups in order to simplify regular and subgroup (zfs)
2363082Sdougm  * disabling. Group has already been checked for non-NULL.
2373082Sdougm  */
2383082Sdougm 
2393082Sdougm static void
2403082Sdougm enable_group(sa_group_t group, char *updateproto)
2413082Sdougm {
2423082Sdougm 	sa_share_t share;
2433082Sdougm 
2443082Sdougm 	for (share = sa_get_share(group, NULL);
2453082Sdougm 	    share != NULL;
2463082Sdougm 	    share = sa_get_next_share(share)) {
2474653Sdougm 		if (updateproto != NULL)
2484653Sdougm 			(void) sa_update_legacy(share, updateproto);
2494653Sdougm 		(void) sa_enable_share(share, NULL);
2503082Sdougm 	}
2513082Sdougm }
2523082Sdougm 
2533082Sdougm /*
2544241Sdougm  * isenabled(group)
2554241Sdougm  *
2564241Sdougm  * Returns B_TRUE if the group is enabled or B_FALSE if it isn't.
2574241Sdougm  * Moved to separate function to reduce clutter in the code.
2584241Sdougm  */
2594241Sdougm 
2604241Sdougm static int
2614241Sdougm isenabled(sa_group_t group)
2624241Sdougm {
2634241Sdougm 	char *state;
2644241Sdougm 	int ret = B_FALSE;
2654241Sdougm 
2664241Sdougm 	if (group != NULL) {
2674653Sdougm 		state = sa_get_group_attr(group, "state");
2684653Sdougm 		if (state != NULL) {
2694653Sdougm 			if (strcmp(state, "enabled") == 0)
2704653Sdougm 				ret = B_TRUE;
2714653Sdougm 			sa_free_attr_string(state);
2724653Sdougm 		}
2734241Sdougm 	}
2744241Sdougm 	return (ret);
2754241Sdougm }
2764241Sdougm 
2774241Sdougm /*
2783082Sdougm  * enable_all_groups(list, setstate, online, updateproto)
2793082Sdougm  *	Given a list of groups, enable each one found.  If updateproto
2803082Sdougm  *	is not NULL, then update all the shares for the protocol that
2813082Sdougm  *	was passed in.
2823034Sdougm  */
2833034Sdougm static int
2843910Sdougm enable_all_groups(sa_handle_t handle, struct list *work, int setstate,
2853910Sdougm 	int online, char *updateproto)
2863034Sdougm {
2874241Sdougm 	int ret;
2883034Sdougm 	char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
2893034Sdougm 	char *state;
2903034Sdougm 	char *name;
2913034Sdougm 	char *zfs = NULL;
2923034Sdougm 	sa_group_t group;
2933082Sdougm 	sa_group_t subgroup;
2943034Sdougm 
2954241Sdougm 	for (ret = SA_OK; work != NULL;	work = work->next) {
2964653Sdougm 		group = (sa_group_t)work->item;
2974241Sdougm 
2984241Sdougm 		/*
2994241Sdougm 		 * If setstate == TRUE, then make sure to set
3004241Sdougm 		 * enabled. This needs to be done here in order for
3014241Sdougm 		 * the isenabled check to succeed on a newly enabled
3024241Sdougm 		 * group.
3034241Sdougm 		 */
3044653Sdougm 		if (setstate == B_TRUE) {
3054653Sdougm 			ret = sa_set_group_attr(group, "state",	"enabled");
3064653Sdougm 			if (ret != SA_OK)
3074653Sdougm 				break;
3084653Sdougm 		}
3094241Sdougm 
3104241Sdougm 		/*
3114241Sdougm 		 * Check to see if group is enabled. If it isn't, skip
3124241Sdougm 		 * the rest.  We don't want shares starting if the
3134241Sdougm 		 * group is disabled. The properties may have been
3144241Sdougm 		 * updated, but there won't be a change until the
3154241Sdougm 		 * group is enabled.
3164241Sdougm 		 */
3174653Sdougm 		if (!isenabled(group))
3184653Sdougm 			continue;
3194653Sdougm 
3204653Sdougm 		/* if itemdata != NULL then a single share */
3214653Sdougm 		if (work->itemdata != NULL) {
3224653Sdougm 			ret = sa_enable_share((sa_share_t)work->itemdata, NULL);
3233034Sdougm 		}
3244653Sdougm 		if (ret != SA_OK)
3254653Sdougm 			break;
3264653Sdougm 
3274653Sdougm 		/* if itemdata == NULL then the whole group */
3284653Sdougm 		if (work->itemdata == NULL) {
3294653Sdougm 			zfs = sa_get_group_attr(group, "zfs");
3304653Sdougm 			/*
3314653Sdougm 			 * if the share is managed by ZFS, don't
3324653Sdougm 			 * update any of the protocols since ZFS is
3334653Sdougm 			 * handling this.  updateproto will contain
3344653Sdougm 			 * the name of the protocol that we want to
3354653Sdougm 			 * update legacy files for.
3364653Sdougm 			 */
3374653Sdougm 			enable_group(group, zfs == NULL ? updateproto : NULL);
3384653Sdougm 			for (subgroup = sa_get_sub_group(group);
3394653Sdougm 			    subgroup != NULL;
3404653Sdougm 			    subgroup = sa_get_next_group(subgroup)) {
3414653Sdougm 				/* never update legacy for ZFS subgroups */
3424653Sdougm 				enable_group(subgroup, NULL);
3433034Sdougm 			}
3443034Sdougm 		}
3454653Sdougm 		if (online) {
3464653Sdougm 			zfs = sa_get_group_attr(group, "zfs");
3474653Sdougm 			name = sa_get_group_attr(group, "name");
3484653Sdougm 			if (name != NULL) {
3494653Sdougm 				if (zfs == NULL) {
3504653Sdougm 					(void) snprintf(instance,
3514653Sdougm 					    sizeof (instance), "%s:%s",
3524653Sdougm 					    SA_SVC_FMRI_BASE, name);
3534653Sdougm 					state = smf_get_state(instance);
3544653Sdougm 					if (state == NULL ||
3554653Sdougm 					    strcmp(state, "online") != 0) {
3564653Sdougm 						(void) smf_enable_instance(
3574653Sdougm 						    instance, 0);
3584653Sdougm 						free(state);
3594653Sdougm 					}
3604653Sdougm 				} else {
3614653Sdougm 					sa_free_attr_string(zfs);
3624653Sdougm 					zfs = NULL;
3634653Sdougm 				}
3644653Sdougm 				if (name != NULL)
3654653Sdougm 					sa_free_attr_string(name);
3664653Sdougm 			}
3674653Sdougm 		}
3683034Sdougm 	}
3693034Sdougm 	if (ret == SA_OK) {
3704653Sdougm 		ret = sa_update_config(handle);
3713034Sdougm 	}
3723034Sdougm 	return (ret);
3733034Sdougm }
3743034Sdougm 
3753034Sdougm /*
3763034Sdougm  * chk_opt(optlistp, security, proto)
3773034Sdougm  *
3783034Sdougm  * Do a sanity check on the optlist provided for the protocol.  This
3793034Sdougm  * is a syntax check and verification that the property is either a
3803034Sdougm  * general or specific to a names optionset.
3813034Sdougm  */
3823034Sdougm 
3833034Sdougm static int
3843034Sdougm chk_opt(struct options *optlistp, int security, char *proto)
3853034Sdougm {
3863034Sdougm 	struct options *optlist;
3873034Sdougm 	char *sep = "";
3883034Sdougm 	int notfirst = 0;
3893034Sdougm 	int ret;
3903034Sdougm 
3913034Sdougm 	for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
3924653Sdougm 		char *optname;
3934653Sdougm 
3944653Sdougm 		optname = optlist->optname;
3954653Sdougm 		ret = OPT_ADD_OK;
3964653Sdougm 		/* extract property/value pair */
3974653Sdougm 		if (sa_is_security(optname, proto)) {
3984653Sdougm 			if (!security)
3994653Sdougm 				ret = OPT_ADD_SECURITY;
4004653Sdougm 		} else {
4014653Sdougm 			if (security)
4024653Sdougm 				ret = OPT_ADD_PROPERTY;
4034653Sdougm 		}
4044653Sdougm 		if (ret != OPT_ADD_OK) {
4054653Sdougm 			if (notfirst == 0)
4064653Sdougm 				(void) printf(
4074653Sdougm 				    gettext("Property syntax error: "));
4084653Sdougm 			switch (ret) {
4094653Sdougm 			case OPT_ADD_SYNTAX:
4104653Sdougm 				(void) printf(gettext("%ssyntax error: %s"),
4113034Sdougm 				    sep, optname);
4124653Sdougm 				sep = ", ";
4134653Sdougm 				break;
4144653Sdougm 			case OPT_ADD_SECURITY:
4154653Sdougm 				(void) printf(gettext("%s%s requires -S"),
4163034Sdougm 				    optname, sep);
4174653Sdougm 				sep = ", ";
4184653Sdougm 				break;
4194653Sdougm 			case OPT_ADD_PROPERTY:
4204653Sdougm 				(void) printf(
4214653Sdougm 				    gettext("%s%s not supported with -S"),
4223034Sdougm 				    optname, sep);
4234653Sdougm 				sep = ", ";
4244653Sdougm 				break;
4254653Sdougm 			}
4264653Sdougm 			notfirst++;
4273034Sdougm 		}
4283034Sdougm 	}
4293034Sdougm 	if (notfirst) {
4304653Sdougm 		(void) printf("\n");
4314653Sdougm 		ret = SA_SYNTAX_ERR;
4323034Sdougm 	}
4333034Sdougm 	return (ret);
4343034Sdougm }
4353034Sdougm 
4363034Sdougm /*
4373034Sdougm  * free_opt(optlist)
4383034Sdougm  *	Free the specified option list.
4393034Sdougm  */
4403034Sdougm static void
4413034Sdougm free_opt(struct options *optlist)
4423034Sdougm {
4433034Sdougm 	struct options *nextopt;
4443034Sdougm 	while (optlist != NULL) {
4453034Sdougm 		nextopt = optlist->next;
4463034Sdougm 		free(optlist);
4473034Sdougm 		optlist = nextopt;
4483034Sdougm 	}
4493034Sdougm }
4503034Sdougm 
4513034Sdougm /*
4523034Sdougm  * check property list for valid properties
4533034Sdougm  * A null value is a remove which is always valid.
4543034Sdougm  */
4553034Sdougm static int
4563034Sdougm valid_options(struct options *optlist, char *proto, void *object, char *sec)
4573034Sdougm {
4583034Sdougm 	int ret = SA_OK;
4593034Sdougm 	struct options *cur;
4603034Sdougm 	sa_property_t prop;
4613034Sdougm 	sa_optionset_t parent = NULL;
4623034Sdougm 
4633034Sdougm 	if (object != NULL) {
4644653Sdougm 		if (sec == NULL)
4654653Sdougm 			parent = sa_get_optionset(object, proto);
4664653Sdougm 		else
4674653Sdougm 			parent = sa_get_security(object, sec, proto);
4683034Sdougm 	}
4693034Sdougm 
4703034Sdougm 	for (cur = optlist; cur != NULL; cur = cur->next) {
4714653Sdougm 		if (cur->optvalue == NULL)
4724653Sdougm 			continue;
4733034Sdougm 		prop = sa_create_property(cur->optname, cur->optvalue);
4743034Sdougm 		if (prop == NULL)
4754653Sdougm 			ret = SA_NO_MEMORY;
4763034Sdougm 		if (ret != SA_OK ||
4773034Sdougm 		    (ret = sa_valid_property(parent, proto, prop)) != SA_OK) {
4784653Sdougm 			(void) printf(
4794653Sdougm 			    gettext("Could not add property %s: %s\n"),
4804653Sdougm 			    cur->optname, sa_errorstr(ret));
4813034Sdougm 		}
4823034Sdougm 		(void) sa_remove_property(prop);
4833034Sdougm 	}
4843034Sdougm 	return (ret);
4853034Sdougm }
4863034Sdougm 
4873034Sdougm /*
4883034Sdougm  * add_optionset(group, optlist, protocol, *err)
4893034Sdougm  *	Add the options in optlist to an optionset and then add the optionset
4903034Sdougm  *	to the group.
4913034Sdougm  *
4923034Sdougm  *	The return value indicates if there was a "change" while errors are
4933034Sdougm  *	returned via the *err parameters.
4943034Sdougm  */
4953034Sdougm static int
4963034Sdougm add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
4973034Sdougm {
4983034Sdougm 	sa_optionset_t optionset;
4993034Sdougm 	int ret = SA_OK;
5003034Sdougm 	int result = 0;
5013034Sdougm 
5023034Sdougm 	optionset = sa_get_optionset(group, proto);
5033034Sdougm 	if (optionset == NULL) {
5044653Sdougm 		optionset = sa_create_optionset(group, proto);
5054653Sdougm 		result = 1; /* adding a protocol is a change */
5063034Sdougm 	}
5074653Sdougm 	if (optionset == NULL) {
5084653Sdougm 		ret = SA_NO_MEMORY;
5094653Sdougm 		goto out;
5104653Sdougm 	}
5114653Sdougm 	while (optlist != NULL) {
5123034Sdougm 		sa_property_t prop;
5133034Sdougm 		prop = sa_get_property(optionset, optlist->optname);
5143034Sdougm 		if (prop == NULL) {
5153034Sdougm 			/*
5163034Sdougm 			 * add the property, but only if it is
5173034Sdougm 			 * a non-NULL or non-zero length value
5183034Sdougm 			 */
5194653Sdougm 			if (optlist->optvalue != NULL) {
5204653Sdougm 				prop = sa_create_property(optlist->optname,
5214653Sdougm 				    optlist->optvalue);
5224653Sdougm 				if (prop != NULL) {
5234653Sdougm 					ret = sa_valid_property(optionset,
5244653Sdougm 					    proto, prop);
5254653Sdougm 					if (ret != SA_OK) {
5264653Sdougm 						(void) sa_remove_property(prop);
5274653Sdougm 						(void) printf(gettext("Could "
5284653Sdougm 						    "not add property "
5294653Sdougm 						    "%s: %s\n"),
5304653Sdougm 						    optlist->optname,
5314653Sdougm 						    sa_errorstr(ret));
5324653Sdougm 					}
5334653Sdougm 				}
5344653Sdougm 				if (ret == SA_OK) {
5354653Sdougm 					ret = sa_add_property(optionset, prop);
5364653Sdougm 					if (ret != SA_OK) {
5374653Sdougm 						(void) printf(gettext(
5384653Sdougm 						    "Could not add property "
5394653Sdougm 						    "%s: %s\n"),
5404653Sdougm 						    optlist->optname,
5414653Sdougm 						    sa_errorstr(ret));
5424653Sdougm 					} else {
5434653Sdougm 						/* there was a change */
5444653Sdougm 						result = 1;
5454653Sdougm 					}
5464653Sdougm 				}
5473034Sdougm 			}
5484653Sdougm 		} else {
5494653Sdougm 			ret = sa_update_property(prop, optlist->optvalue);
5504653Sdougm 			/* should check to see if value changed */
5514653Sdougm 			if (ret != SA_OK) {
5524653Sdougm 				(void) printf(gettext("Could not update "
5534653Sdougm 				    "property %s: %s\n"), optlist->optname,
5544653Sdougm 				    sa_errorstr(ret));
5554653Sdougm 			} else {
5563034Sdougm 				result = 1;
5573034Sdougm 			}
5583034Sdougm 		}
5593034Sdougm 		optlist = optlist->next;
5603034Sdougm 	}
5614653Sdougm 	ret = sa_commit_properties(optionset, 0);
5624653Sdougm 
5634653Sdougm out:
5643034Sdougm 	if (err != NULL)
5654653Sdougm 		*err = ret;
5663034Sdougm 	return (result);
5673034Sdougm }
5683034Sdougm 
5693034Sdougm /*
5703034Sdougm  * sa_create(flags, argc, argv)
5713034Sdougm  *	create a new group
5723034Sdougm  *	this may or may not have a protocol associated with it.
5733034Sdougm  *	No protocol means "all" protocols in this case.
5743034Sdougm  */
5753034Sdougm static int
5763910Sdougm sa_create(sa_handle_t handle, int flags, int argc, char *argv[])
5773034Sdougm {
5783034Sdougm 	char *groupname;
5793034Sdougm 
5803034Sdougm 	sa_group_t group;
5813034Sdougm 	int verbose = 0;
5823034Sdougm 	int dryrun = 0;
5833034Sdougm 	int c;
5843034Sdougm 	char *protocol = NULL;
5853034Sdougm 	int ret = SA_OK;
5863034Sdougm 	struct options *optlist = NULL;
5873034Sdougm 	int err = 0;
5883034Sdougm 	int auth;
5893034Sdougm 
5903034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:p:")) != EOF) {
5914653Sdougm 		switch (c) {
5924653Sdougm 		case 'v':
5934653Sdougm 			verbose++;
5944653Sdougm 			break;
5954653Sdougm 		case 'n':
5964653Sdougm 			dryrun++;
5974653Sdougm 			break;
5984653Sdougm 		case 'P':
5994653Sdougm 			protocol = optarg;
6004653Sdougm 			if (sa_valid_protocol(protocol))
6014653Sdougm 				break;
6024653Sdougm 			(void) printf(gettext(
6034653Sdougm 			    "Invalid protocol specified: %s\n"), protocol);
6044653Sdougm 			return (SA_INVALID_PROTOCOL);
6054653Sdougm 			break;
6064653Sdougm 		case 'p':
6074653Sdougm 			ret = add_opt(&optlist, optarg, 0);
6084653Sdougm 			switch (ret) {
6094653Sdougm 			case OPT_ADD_SYNTAX:
6104653Sdougm 				(void) printf(gettext(
6114653Sdougm 				    "Property syntax error for property: %s\n"),
6124653Sdougm 				    optarg);
6134653Sdougm 				return (SA_SYNTAX_ERR);
6144653Sdougm 			case OPT_ADD_SECURITY:
6154653Sdougm 				(void) printf(gettext(
6164653Sdougm 				    "Security properties need "
6174653Sdougm 				    "to be set with set-security: %s\n"),
6184653Sdougm 				    optarg);
6194653Sdougm 				return (SA_SYNTAX_ERR);
6204653Sdougm 			default:
6214653Sdougm 				break;
6224653Sdougm 			}
6234653Sdougm 			break;
6244653Sdougm 		default:
6254653Sdougm 		case 'h':
6264653Sdougm 		case '?':
6274653Sdougm 			(void) printf(gettext("usage: %s\n"),
6284653Sdougm 			    sa_get_usage(USAGE_CREATE));
6294653Sdougm 			return (0);
6303034Sdougm 		}
6313034Sdougm 	}
6323034Sdougm 
6333034Sdougm 	if (optind >= argc) {
6344653Sdougm 		(void) printf(gettext("usage: %s\n"),
6354653Sdougm 		    sa_get_usage(USAGE_CREATE));
6364653Sdougm 		(void) printf(gettext("\tgroup must be specified.\n"));
6374653Sdougm 		return (SA_BAD_PATH);
6383034Sdougm 	}
6393034Sdougm 
6403034Sdougm 	if ((optind + 1) < argc) {
6414653Sdougm 		(void) printf(gettext("usage: %s\n"),
6424653Sdougm 		    sa_get_usage(USAGE_CREATE));
6434653Sdougm 		(void) printf(gettext("\textraneous group(s) at end\n"));
6444653Sdougm 		return (SA_SYNTAX_ERR);
6453034Sdougm 	}
6463034Sdougm 
6473034Sdougm 	if (protocol == NULL && optlist != NULL) {
6484653Sdougm 		/* lookup default protocol */
6494653Sdougm 		(void) printf(gettext("usage: %s\n"),
6504653Sdougm 		    sa_get_usage(USAGE_CREATE));
6514653Sdougm 		(void) printf(gettext("\tprotocol must be specified "
6524653Sdougm 		    "with properties\n"));
6534653Sdougm 		return (SA_INVALID_PROTOCOL);
6543034Sdougm 	}
6553034Sdougm 
6563034Sdougm 	if (optlist != NULL)
6574653Sdougm 		ret = chk_opt(optlist, 0, protocol);
6583034Sdougm 	if (ret == OPT_ADD_SECURITY) {
6594653Sdougm 		(void) printf(gettext("Security properties not "
6604653Sdougm 		    "supported with create\n"));
6614653Sdougm 		return (SA_SYNTAX_ERR);
6623034Sdougm 	}
6633034Sdougm 
6643034Sdougm 	/*
6654653Sdougm 	 * If a group already exists, we can only add a new protocol
6663034Sdougm 	 * to it and not create a new one or add the same protocol
6673034Sdougm 	 * again.
6683034Sdougm 	 */
6693034Sdougm 
6703034Sdougm 	groupname = argv[optind];
6713034Sdougm 
6723034Sdougm 	auth = check_authorizations(groupname, flags);
6733034Sdougm 
6743910Sdougm 	group = sa_get_group(handle, groupname);
6753034Sdougm 	if (group != NULL) {
6764653Sdougm 		/* group exists so must be a protocol add */
6774653Sdougm 		if (protocol != NULL) {
6784653Sdougm 			if (has_protocol(group, protocol)) {
6794653Sdougm 				(void) printf(gettext(
6804653Sdougm 				    "Group \"%s\" already exists"
6814653Sdougm 				    " with protocol %s\n"), groupname,
6824653Sdougm 				    protocol);
6834653Sdougm 				ret = SA_DUPLICATE_NAME;
6844653Sdougm 			}
6854653Sdougm 		} else {
6864653Sdougm 			/* must add new protocol */
6874653Sdougm 			(void) printf(gettext(
6884653Sdougm 			    "Group already exists and no protocol "
6894653Sdougm 			    "specified.\n"));
6904653Sdougm 			ret = SA_DUPLICATE_NAME;
6913034Sdougm 		}
6923034Sdougm 	} else {
6933034Sdougm 		/*
6943034Sdougm 		 * is it a valid name? Must comply with SMF instance
6953034Sdougm 		 * name restrictions.
6963034Sdougm 		 */
6974653Sdougm 		if (!sa_valid_group_name(groupname)) {
6984653Sdougm 			ret = SA_INVALID_NAME;
6994653Sdougm 			(void) printf(gettext("Invalid group name: %s\n"),
7004653Sdougm 			    groupname);
7014653Sdougm 		}
7023034Sdougm 	}
7033034Sdougm 	if (ret == SA_OK) {
7044653Sdougm 		/* check protocol vs optlist */
7054653Sdougm 		if (optlist != NULL) {
7064653Sdougm 			/* check options, if any, for validity */
7074653Sdougm 			ret = valid_options(optlist, protocol, group, NULL);
7084653Sdougm 		}
7093034Sdougm 	}
7103034Sdougm 	if (ret == SA_OK && !dryrun) {
7114653Sdougm 		if (group == NULL) {
7124653Sdougm 			group = sa_create_group(handle, (char *)groupname,
7134653Sdougm 			    &err);
7143034Sdougm 		}
7154653Sdougm 		if (group != NULL) {
7164653Sdougm 			sa_optionset_t optionset;
7174653Sdougm 			if (optlist != NULL) {
7184653Sdougm 				(void) add_optionset(group, optlist, protocol,
7194653Sdougm 				    &ret);
7204653Sdougm 			} else if (protocol != NULL) {
7214653Sdougm 				optionset = sa_create_optionset(group,
7224653Sdougm 				    protocol);
7234653Sdougm 				if (optionset == NULL)
7244653Sdougm 					ret = SA_NO_MEMORY;
7254653Sdougm 			} else if (protocol == NULL) {
7264653Sdougm 				char **protolist;
7274653Sdougm 				int numprotos, i;
7284653Sdougm 				numprotos = sa_get_protocols(&protolist);
7294653Sdougm 				for (i = 0; i < numprotos; i++) {
7304653Sdougm 					optionset = sa_create_optionset(group,
7314653Sdougm 					    protolist[i]);
7324653Sdougm 				}
7334653Sdougm 				if (protolist != NULL)
7344653Sdougm 					free(protolist);
7354653Sdougm 			}
7363034Sdougm 			/*
7374653Sdougm 			 * We have a group and legal additions
7383034Sdougm 			 */
7394653Sdougm 			if (ret == SA_OK) {
7404653Sdougm 				/*
7414653Sdougm 				 * Commit to configuration for protocols that
7424653Sdougm 				 * need to do block updates. For NFS, this
7434653Sdougm 				 * doesn't do anything but it will be run for
7444653Sdougm 				 * all protocols that implement the
7454653Sdougm 				 * appropriate plugin.
7464653Sdougm 				 */
7474653Sdougm 				ret = sa_update_config(handle);
7484653Sdougm 			} else {
7494653Sdougm 				if (group != NULL)
7504653Sdougm 					(void) sa_remove_group(group);
7514653Sdougm 			}
7523034Sdougm 		} else {
7534653Sdougm 			ret = err;
7544653Sdougm 			(void) printf(gettext("Could not create group: %s\n"),
7554653Sdougm 			    sa_errorstr(ret));
7563034Sdougm 		}
7573034Sdougm 	}
7583034Sdougm 	if (dryrun && ret == SA_OK && !auth && verbose) {
7594653Sdougm 		(void) printf(gettext("Command would fail: %s\n"),
7604653Sdougm 		    sa_errorstr(SA_NO_PERMISSION));
7614653Sdougm 		ret = SA_NO_PERMISSION;
7623034Sdougm 	}
7633034Sdougm 	free_opt(optlist);
7643034Sdougm 	return (ret);
7653034Sdougm }
7663034Sdougm 
7673034Sdougm /*
7683034Sdougm  * group_status(group)
7693034Sdougm  *
7703034Sdougm  * return the current status (enabled/disabled) of the group.
7713034Sdougm  */
7723034Sdougm 
7733034Sdougm static char *
7743034Sdougm group_status(sa_group_t group)
7753034Sdougm {
7763034Sdougm 	char *state;
7773034Sdougm 	int enabled = 0;
7783034Sdougm 
7793034Sdougm 	state = sa_get_group_attr(group, "state");
7803034Sdougm 	if (state != NULL) {
7814653Sdougm 		if (strcmp(state, "enabled") == 0) {
7824653Sdougm 			enabled = 1;
7834653Sdougm 		}
7844653Sdougm 		sa_free_attr_string(state);
7853034Sdougm 	}
7864255Sdougm 	return (enabled ? "enabled" : "disabled");
7873034Sdougm }
7883034Sdougm 
7893034Sdougm /*
7903034Sdougm  * sa_delete(flags, argc, argv)
7913034Sdougm  *
7923034Sdougm  *	Delete a group.
7933034Sdougm  */
7943034Sdougm 
7953034Sdougm static int
7963910Sdougm sa_delete(sa_handle_t handle, int flags, int argc, char *argv[])
7973034Sdougm {
7983034Sdougm 	char *groupname;
7993034Sdougm 	sa_group_t group;
8003034Sdougm 	sa_share_t share;
8013034Sdougm 	int verbose = 0;
8023034Sdougm 	int dryrun = 0;
8033034Sdougm 	int force = 0;
8043034Sdougm 	int c;
8053034Sdougm 	char *protocol = NULL;
8063034Sdougm 	char *sectype = NULL;
8073034Sdougm 	int ret = SA_OK;
8083034Sdougm 	int auth;
8093034Sdougm 
8103034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
8114653Sdougm 		switch (c) {
8124653Sdougm 		case 'v':
8134653Sdougm 			verbose++;
8144653Sdougm 			break;
8154653Sdougm 		case 'n':
8164653Sdougm 			dryrun++;
8174653Sdougm 			break;
8184653Sdougm 		case 'P':
8194653Sdougm 			protocol = optarg;
8204653Sdougm 			if (!sa_valid_protocol(protocol)) {
8214653Sdougm 				(void) printf(gettext("Invalid protocol "
8224653Sdougm 				    "specified: %s\n"),   protocol);
8234653Sdougm 				return (SA_INVALID_PROTOCOL);
8244653Sdougm 			}
8254653Sdougm 			break;
8264653Sdougm 		case 'S':
8274653Sdougm 			sectype = optarg;
8284653Sdougm 			break;
8294653Sdougm 		case 'f':
8304653Sdougm 			force++;
8314653Sdougm 			break;
8324653Sdougm 		default:
8334653Sdougm 		case 'h':
8344653Sdougm 		case '?':
8354653Sdougm 			(void) printf(gettext("usage: %s\n"),
8364653Sdougm 			    sa_get_usage(USAGE_DELETE));
8374653Sdougm 			return (0);
8383034Sdougm 		}
8393034Sdougm 	}
8403034Sdougm 
8413034Sdougm 	if (optind >= argc) {
8424653Sdougm 		(void) printf(gettext("usage: %s\n"),
8434653Sdougm 		    sa_get_usage(USAGE_DELETE));
8444653Sdougm 		(void) printf(gettext("\tgroup must be specified.\n"));
8454653Sdougm 		return (SA_SYNTAX_ERR);
8463034Sdougm 	}
8473034Sdougm 
8483034Sdougm 	if ((optind + 1) < argc) {
8494653Sdougm 		(void) printf(gettext("usage: %s\n"),
8504653Sdougm 		    sa_get_usage(USAGE_DELETE));
8514653Sdougm 		(void) printf(gettext("\textraneous group(s) at end\n"));
8524653Sdougm 		return (SA_SYNTAX_ERR);
8533034Sdougm 	}
8543034Sdougm 
8553034Sdougm 	if (sectype != NULL && protocol == NULL) {
8564653Sdougm 		(void) printf(gettext("usage: %s\n"),
8574653Sdougm 		    sa_get_usage(USAGE_DELETE));
8584653Sdougm 		(void) printf(gettext("\tsecurity requires protocol to be "
8594653Sdougm 		    "specified.\n"));
8604653Sdougm 		return (SA_SYNTAX_ERR);
8613034Sdougm 	}
8623034Sdougm 
8633034Sdougm 	/*
8643034Sdougm 	 * Determine if the group already exists since it must in
8653034Sdougm 	 * order to be removed.
8663034Sdougm 	 *
8673034Sdougm 	 * We can delete when:
8683034Sdougm 	 *
8693034Sdougm 	 *	- group is empty
8703034Sdougm 	 *	- force flag is set
8713034Sdougm 	 *	- if protocol specified, only delete the protocol
8723034Sdougm 	 */
8733034Sdougm 
8743034Sdougm 	groupname = argv[optind];
8753910Sdougm 	group = sa_get_group(handle, groupname);
8763034Sdougm 	if (group == NULL) {
8773034Sdougm 		ret = SA_NO_SUCH_GROUP;
8784653Sdougm 		goto done;
8794653Sdougm 	}
8804653Sdougm 	auth = check_authorizations(groupname, flags);
8814653Sdougm 	if (protocol == NULL) {
8823034Sdougm 		share = sa_get_share(group, NULL);
8833034Sdougm 		if (share != NULL)
8844653Sdougm 			ret = SA_BUSY;
8853034Sdougm 		if (share == NULL || (share != NULL && force == 1)) {
8864653Sdougm 			ret = SA_OK;
8874653Sdougm 			if (!dryrun) {
8884653Sdougm 				while (share != NULL) {
8894653Sdougm 					sa_share_t next_share;
8904653Sdougm 					next_share = sa_get_next_share(share);
8914653Sdougm 					/*
8924653Sdougm 					 * need to do the disable of
8934653Sdougm 					 * each share, but don't
8944653Sdougm 					 * actually do anything on a
8954653Sdougm 					 * dryrun.
8964653Sdougm 					 */
8974653Sdougm 					ret = sa_disable_share(share, NULL);
8984653Sdougm 					ret = sa_remove_share(share);
8994653Sdougm 					share = next_share;
9004653Sdougm 				}
9014653Sdougm 				ret = sa_remove_group(group);
9023034Sdougm 			}
9033034Sdougm 		}
9044653Sdougm 		/* Commit to configuration if not a dryrun */
9053034Sdougm 		if (!dryrun && ret == SA_OK) {
9064653Sdougm 			ret = sa_update_config(handle);
9073034Sdougm 		}
9084653Sdougm 	} else {
9093034Sdougm 		/* a protocol delete */
9103034Sdougm 		sa_optionset_t optionset;
9113034Sdougm 		sa_security_t security;
9124653Sdougm 			if (sectype != NULL) {
9134653Sdougm 			/* only delete specified security */
9144653Sdougm 			security = sa_get_security(group, sectype, protocol);
9154653Sdougm 			if (security != NULL && !dryrun)
9164653Sdougm 				ret = sa_destroy_security(security);
9174653Sdougm 			else
9184653Sdougm 				ret = SA_INVALID_PROTOCOL;
9193034Sdougm 		} else {
9204653Sdougm 			optionset = sa_get_optionset(group, protocol);
9214653Sdougm 			if (optionset != NULL && !dryrun) {
9224653Sdougm 				/*
9234653Sdougm 				 * have an optionset with
9244653Sdougm 				 * protocol to delete
9254653Sdougm 				 */
9264653Sdougm 				ret = sa_destroy_optionset(optionset);
9274653Sdougm 				/*
9284653Sdougm 				 * Now find all security sets
9294653Sdougm 				 * for the protocol and remove
9304653Sdougm 				 * them. Don't remove other
9314653Sdougm 				 * protocols.
9324653Sdougm 				 */
9334653Sdougm 				for (security =
9344653Sdougm 				    sa_get_security(group, NULL, NULL);
9354653Sdougm 				    ret == SA_OK && security != NULL;
9364653Sdougm 				    security = sa_get_next_security(security)) {
9374653Sdougm 					char *secprot;
9384653Sdougm 					secprot = sa_get_security_attr(security,
9394653Sdougm 					    "type");
9404653Sdougm 					if (secprot != NULL &&
9414653Sdougm 					    strcmp(secprot, protocol) == 0)
9424653Sdougm 						ret = sa_destroy_security(
9434653Sdougm 						    security);
9444653Sdougm 					if (secprot != NULL)
9454653Sdougm 						sa_free_attr_string(secprot);
9464653Sdougm 				}
9474653Sdougm 			} else {
9484653Sdougm 				if (!dryrun)
9494653Sdougm 					ret = SA_INVALID_PROTOCOL;
9503034Sdougm 			}
9513034Sdougm 		}
9523034Sdougm 	}
9534653Sdougm 
9544653Sdougm done:
9553034Sdougm 	if (ret != SA_OK) {
9564653Sdougm 		(void) printf(gettext("Could not delete group: %s\n"),
9574653Sdougm 		    sa_errorstr(ret));
9583034Sdougm 	} else if (dryrun && !auth && verbose) {
9594653Sdougm 		(void) printf(gettext("Command would fail: %s\n"),
9604653Sdougm 		    sa_errorstr(SA_NO_PERMISSION));
9613034Sdougm 	}
9623034Sdougm 	return (ret);
9633034Sdougm }
9643034Sdougm 
9653034Sdougm /*
9663034Sdougm  * strndupr(*buff, str, buffsize)
9673034Sdougm  *
9683034Sdougm  * used with small strings to duplicate and possibly increase the
9693034Sdougm  * buffer size of a string.
9703034Sdougm  */
9713034Sdougm static char *
9723034Sdougm strndupr(char *buff, char *str, int *buffsize)
9733034Sdougm {
9743034Sdougm 	int limit;
9753034Sdougm 	char *orig_buff = buff;
9763034Sdougm 
9773034Sdougm 	if (buff == NULL) {
9784653Sdougm 		buff = (char *)malloc(64);
9794653Sdougm 		if (buff == NULL)
9804653Sdougm 			return (NULL);
9814653Sdougm 		*buffsize = 64;
9824653Sdougm 		buff[0] = '\0';
9833034Sdougm 	}
9843034Sdougm 	limit = strlen(buff) + strlen(str) + 1;
9853034Sdougm 	if (limit > *buffsize) {
9864653Sdougm 		limit = *buffsize = *buffsize + ((limit / 64) + 64);
9874653Sdougm 		buff = realloc(buff, limit);
9883034Sdougm 	}
9893034Sdougm 	if (buff != NULL) {
9904653Sdougm 		(void) strcat(buff, str);
9913034Sdougm 	} else {
9924653Sdougm 		/* if it fails, fail it hard */
9934653Sdougm 		if (orig_buff != NULL)
9944653Sdougm 			free(orig_buff);
9953034Sdougm 	}
9963034Sdougm 	return (buff);
9973034Sdougm }
9983034Sdougm 
9993034Sdougm /*
10003034Sdougm  * group_proto(group)
10013034Sdougm  *
10023034Sdougm  * return a string of all the protocols (space separated) associated
10033034Sdougm  * with this group.
10043034Sdougm  */
10053034Sdougm 
10063034Sdougm static char *
10073034Sdougm group_proto(sa_group_t group)
10083034Sdougm {
10093034Sdougm 	sa_optionset_t optionset;
10103034Sdougm 	char *proto;
10113034Sdougm 	char *buff = NULL;
10123034Sdougm 	int buffsize = 0;
10133034Sdougm 	int addspace = 0;
10143034Sdougm 	/*
10153034Sdougm 	 * get the protocol list by finding the optionsets on this
10163034Sdougm 	 * group and extracting the type value. The initial call to
10173034Sdougm 	 * strndupr() initailizes buff.
10183034Sdougm 	 */
10193034Sdougm 	buff = strndupr(buff, "", &buffsize);
10203034Sdougm 	if (buff != NULL) {
10214653Sdougm 		for (optionset = sa_get_optionset(group, NULL);
10224653Sdougm 		    optionset != NULL && buff != NULL;
10234653Sdougm 		    optionset = sa_get_next_optionset(optionset)) {
10244653Sdougm 			/*
10254653Sdougm 			 * extract out the protocol type from this optionset
10264653Sdougm 			 * and append it to the buffer "buff". strndupr() will
10274653Sdougm 			 * reallocate space as necessay.
10284653Sdougm 			 */
10294653Sdougm 			proto = sa_get_optionset_attr(optionset, "type");
10304653Sdougm 			if (proto != NULL) {
10314653Sdougm 				if (addspace++)
10324653Sdougm 					buff = strndupr(buff, " ", &buffsize);
10334653Sdougm 				buff = strndupr(buff, proto, &buffsize);
10344653Sdougm 				sa_free_attr_string(proto);
10354653Sdougm 			}
10363034Sdougm 		}
10373034Sdougm 	}
10383034Sdougm 	return (buff);
10393034Sdougm }
10403034Sdougm 
10413034Sdougm /*
10423034Sdougm  * sa_list(flags, argc, argv)
10433034Sdougm  *
10443034Sdougm  * implements the "list" subcommand to list groups and optionally
10453034Sdougm  * their state and protocols.
10463034Sdougm  */
10473034Sdougm 
10484653Sdougm /*ARGSUSED*/
10493034Sdougm static int
10503910Sdougm sa_list(sa_handle_t handle, int flags, int argc, char *argv[])
10513034Sdougm {
10523034Sdougm 	sa_group_t group;
10533034Sdougm 	int verbose = 0;
10543034Sdougm 	int c;
10553034Sdougm 	char *protocol = NULL;
10563034Sdougm 
10573034Sdougm 	while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
10584653Sdougm 		switch (c) {
10594653Sdougm 		case 'v':
10604653Sdougm 			verbose++;
10614653Sdougm 			break;
10624653Sdougm 		case 'P':
10634653Sdougm 			protocol = optarg;
10644653Sdougm 			if (!sa_valid_protocol(protocol)) {
10654653Sdougm 				(void) printf(gettext(
10664653Sdougm 				    "Invalid protocol specified: %s\n"),
10674653Sdougm 				    protocol);
10684653Sdougm 				return (SA_INVALID_PROTOCOL);
10694653Sdougm 			}
10704653Sdougm 			break;
10714653Sdougm 		default:
10724653Sdougm 		case 'h':
10734653Sdougm 		case '?':
10744653Sdougm 			(void) printf(gettext("usage: %s\n"),
10754653Sdougm 			    sa_get_usage(USAGE_LIST));
10764653Sdougm 			return (0);
10773034Sdougm 		}
10783034Sdougm 	}
10793034Sdougm 
10804653Sdougm 	for (group = sa_get_group(handle, NULL);
10814653Sdougm 	    group != NULL;
10823034Sdougm 	    group = sa_get_next_group(group)) {
10834653Sdougm 		char *name;
10844653Sdougm 		char *proto;
10854653Sdougm 		if (protocol == NULL || has_protocol(group, protocol)) {
10864653Sdougm 			name = sa_get_group_attr(group, "name");
10874653Sdougm 			if (name != NULL && (verbose > 1 || name[0] != '#')) {
10884653Sdougm 				(void) printf("%s", (char *)name);
10894653Sdougm 				if (verbose) {
10904653Sdougm 					/*
10914653Sdougm 					 * Need the list of protocols
10924653Sdougm 					 * and current status once
10934653Sdougm 					 * available. We do want to
10944653Sdougm 					 * translate the
10954653Sdougm 					 * enabled/disabled text here.
10964653Sdougm 					 */
10974653Sdougm 					(void) printf("\t%s", isenabled(group) ?
10984653Sdougm 					    gettext("enabled") :
10994653Sdougm 					    gettext("disabled"));
11004653Sdougm 					proto = group_proto(group);
11014653Sdougm 					if (proto != NULL) {
11024653Sdougm 						(void) printf("\t%s",
11034653Sdougm 						    (char *)proto);
11044653Sdougm 						free(proto);
11054653Sdougm 					}
11064653Sdougm 				}
11074653Sdougm 				(void) printf("\n");
11083034Sdougm 			}
11094653Sdougm 			if (name != NULL)
11104653Sdougm 				sa_free_attr_string(name);
11113034Sdougm 		}
11123034Sdougm 	}
11133034Sdougm 	return (0);
11143034Sdougm }
11153034Sdougm 
11163034Sdougm /*
11173034Sdougm  * out_properties(optionset, proto, sec)
11183034Sdougm  *
11193034Sdougm  * Format the properties and encode the protocol and optional named
11203034Sdougm  * optionset into the string.
11213034Sdougm  *
11223034Sdougm  * format is protocol[:name]=(property-list)
11233034Sdougm  */
11243034Sdougm 
11253034Sdougm static void
11263034Sdougm out_properties(sa_optionset_t optionset, char *proto, char *sec)
11273034Sdougm {
11283034Sdougm 	char *type;
11293034Sdougm 	char *value;
11303034Sdougm 	int spacer;
11313034Sdougm 	sa_property_t prop;
11323034Sdougm 
11334653Sdougm 	if (sec == NULL)
11344653Sdougm 		(void) printf(" %s=(", proto ? proto : gettext("all"));
11354653Sdougm 	else
11364653Sdougm 		(void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
11373034Sdougm 
11383034Sdougm 	for (spacer = 0, prop = sa_get_property(optionset, NULL);
11394653Sdougm 	    prop != NULL;
11404653Sdougm 	    prop = sa_get_next_property(prop)) {
11413034Sdougm 
11423034Sdougm 		/*
11433034Sdougm 		 * extract the property name/value and output with
11443034Sdougm 		 * appropriate spacing. I.e. no prefixed space the
11453034Sdougm 		 * first time through but a space on subsequent
11463034Sdougm 		 * properties.
11473034Sdougm 		 */
11484653Sdougm 		type = sa_get_property_attr(prop, "type");
11494653Sdougm 		value = sa_get_property_attr(prop, "value");
11504653Sdougm 		if (type != NULL) {
11514653Sdougm 			(void) printf("%s%s=", spacer ? " " : "",	type);
11524653Sdougm 			spacer = 1;
11534653Sdougm 			if (value != NULL)
11544653Sdougm 				(void) printf("\"%s\"", value);
11554653Sdougm 			else
11564653Sdougm 				(void) printf("\"\"");
11574653Sdougm 		}
11584653Sdougm 		if (type != NULL)
11594653Sdougm 			sa_free_attr_string(type);
11603034Sdougm 		if (value != NULL)
11614653Sdougm 			sa_free_attr_string(value);
11623034Sdougm 	}
11633034Sdougm 	(void) printf(")");
11643034Sdougm }
11653034Sdougm 
11663034Sdougm /*
11673034Sdougm  * show_properties(group, protocol, prefix)
11683034Sdougm  *
11693034Sdougm  * print the properties for a group. If protocol is NULL, do all
11703034Sdougm  * protocols otherwise only the specified protocol. All security
11713034Sdougm  * (named groups specific to the protocol) are included.
11723034Sdougm  *
11733034Sdougm  * The "prefix" is always applied. The caller knows whether it wants
11743034Sdougm  * some type of prefix string (white space) or not.  Once the prefix
11753034Sdougm  * has been output, it is reduced to the zero length string for the
11763034Sdougm  * remainder of the property output.
11773034Sdougm  */
11783034Sdougm 
11793034Sdougm static void
11803034Sdougm show_properties(sa_group_t group, char *protocol, char *prefix)
11813034Sdougm {
11823034Sdougm 	sa_optionset_t optionset;
11833034Sdougm 	sa_security_t security;
11843034Sdougm 	char *value;
11853034Sdougm 	char *secvalue;
11863034Sdougm 
11873034Sdougm 	if (protocol != NULL) {
11884653Sdougm 		optionset = sa_get_optionset(group, protocol);
11894653Sdougm 		if (optionset != NULL) {
11904653Sdougm 			(void) printf("%s", prefix);
11914653Sdougm 			prefix = "";
11924653Sdougm 			out_properties(optionset, protocol, NULL);
11934653Sdougm 		}
11944653Sdougm 		security = sa_get_security(group, protocol, NULL);
11954653Sdougm 		if (security != NULL) {
11964653Sdougm 			(void) printf("%s", prefix);
11974653Sdougm 			prefix = "";
11984653Sdougm 			out_properties(security, protocol, NULL);
11994653Sdougm 		}
12003034Sdougm 	} else {
12014653Sdougm 		for (optionset = sa_get_optionset(group, protocol);
12024653Sdougm 		    optionset != NULL;
12034653Sdougm 		    optionset = sa_get_next_optionset(optionset)) {
12044653Sdougm 
12054653Sdougm 			value = sa_get_optionset_attr(optionset, "type");
12064653Sdougm 			(void) printf("%s", prefix);
12074653Sdougm 			prefix = "";
12084653Sdougm 			out_properties(optionset, value, 0);
12094653Sdougm 			if (value != NULL)
12104653Sdougm 				sa_free_attr_string(value);
12114653Sdougm 		}
12124653Sdougm 		for (security = sa_get_security(group, NULL, protocol);
12134653Sdougm 		    security != NULL;
12144653Sdougm 		    security = sa_get_next_security(security)) {
12154653Sdougm 
12164653Sdougm 			value = sa_get_security_attr(security, "type");
12174653Sdougm 			secvalue = sa_get_security_attr(security, "sectype");
12184653Sdougm 			(void) printf("%s", prefix);
12194653Sdougm 			prefix = "";
12204653Sdougm 			out_properties(security, value, secvalue);
12214653Sdougm 			if (value != NULL)
12224653Sdougm 				sa_free_attr_string(value);
12234653Sdougm 			if (secvalue != NULL)
12244653Sdougm 				sa_free_attr_string(secvalue);
12254653Sdougm 		}
12263034Sdougm 	}
12273034Sdougm }
12283034Sdougm 
12293034Sdougm /*
12303034Sdougm  * show_group(group, verbose, properties, proto, subgroup)
12313034Sdougm  *
12323034Sdougm  * helper function to show the contents of a group.
12333034Sdougm  */
12343034Sdougm 
12353034Sdougm static void
12363034Sdougm show_group(sa_group_t group, int verbose, int properties, char *proto,
12373034Sdougm 		char *subgroup)
12383034Sdougm {
12393034Sdougm 	sa_share_t share;
12403034Sdougm 	char *groupname;
12413034Sdougm 	char *sharepath;
12423034Sdougm 	char *resource;
12433034Sdougm 	char *description;
12443034Sdougm 	char *type;
12453034Sdougm 	char *zfs = NULL;
12463034Sdougm 	int iszfs = 0;
12473034Sdougm 
12483034Sdougm 	groupname = sa_get_group_attr(group, "name");
12493034Sdougm 	if (groupname != NULL) {
12504653Sdougm 		if (proto != NULL && !has_protocol(group, proto)) {
12514653Sdougm 			sa_free_attr_string(groupname);
12524653Sdougm 			return;
12534653Sdougm 		}
12543034Sdougm 		/*
12553034Sdougm 		 * check to see if the group is managed by ZFS. If
12563034Sdougm 		 * there is an attribute, then it is. A non-NULL zfs
12573034Sdougm 		 * variable will trigger the different way to display
12583034Sdougm 		 * and will remove the transient property indicator
12593034Sdougm 		 * from the output.
12603034Sdougm 		 */
12614653Sdougm 		zfs = sa_get_group_attr(group, "zfs");
12624653Sdougm 		if (zfs != NULL) {
12634653Sdougm 			iszfs = 1;
12644653Sdougm 			sa_free_attr_string(zfs);
12653034Sdougm 		}
12664653Sdougm 		share = sa_get_share(group, NULL);
12674653Sdougm 		if (subgroup == NULL)
12684653Sdougm 			(void) printf("%s", groupname);
12694653Sdougm 		else
12704653Sdougm 			(void) printf("    %s/%s", subgroup, groupname);
12714653Sdougm 		if (properties)
12724653Sdougm 			show_properties(group, proto, "");
12734653Sdougm 		(void) printf("\n");
12744653Sdougm 		if (strcmp(groupname, "zfs") == 0) {
12754653Sdougm 			sa_group_t zgroup;
12764653Sdougm 
12774653Sdougm 			for (zgroup = sa_get_sub_group(group);
12784653Sdougm 			    zgroup != NULL;
12794653Sdougm 			    zgroup = sa_get_next_group(zgroup)) {
12804653Sdougm 				show_group(zgroup, verbose, properties, proto,
12814653Sdougm 				    "zfs");
12824653Sdougm 			}
12834653Sdougm 			sa_free_attr_string(groupname);
12844653Sdougm 			return;
12854653Sdougm 		}
12863034Sdougm 		/*
12874653Sdougm 		 * Have a group, so list the contents. Resource and
12883034Sdougm 		 * description are only listed if verbose is set.
12893034Sdougm 		 */
12904653Sdougm 		for (share = sa_get_share(group, NULL);
12914653Sdougm 		    share != NULL;
12924653Sdougm 		    share = sa_get_next_share(share)) {
12934653Sdougm 			sharepath = sa_get_share_attr(share, "path");
12944653Sdougm 			if (sharepath != NULL) {
12954653Sdougm 				if (verbose) {
12964653Sdougm 					resource = sa_get_share_attr(share,
12974653Sdougm 					    "resource");
12984653Sdougm 					description =
12994653Sdougm 					    sa_get_share_description(share);
13004653Sdougm 					type = sa_get_share_attr(share,
13014653Sdougm 					    "type");
13024653Sdougm 					if (type != NULL && !iszfs &&
13034653Sdougm 					    strcmp(type, "transient") == 0)
13044653Sdougm 						(void) printf("\t* ");
13054653Sdougm 					else
13064653Sdougm 						(void) printf("\t  ");
13074653Sdougm 					if (resource != NULL &&
13084653Sdougm 					    strlen(resource) > 0) {
13094653Sdougm 						(void) printf("%s=%s",
13104653Sdougm 						    resource, sharepath);
13114653Sdougm 					} else {
13124653Sdougm 						(void) printf("%s", sharepath);
13134653Sdougm 					}
13144653Sdougm 					if (resource != NULL)
13154653Sdougm 						sa_free_attr_string(resource);
13164653Sdougm 					if (properties)
13174653Sdougm 						show_properties(share, NULL,
13184653Sdougm 						    "\t");
13194653Sdougm 					if (description != NULL) {
13204653Sdougm 						if (strlen(description) > 0) {
13214653Sdougm 							(void) printf(
13224653Sdougm 							    "\t\"%s\"",
13234653Sdougm 							    description);
13244653Sdougm 						}
13254653Sdougm 						sa_free_share_description(
13264653Sdougm 						    description);
13274653Sdougm 					}
13284653Sdougm 					if (type != NULL)
13294653Sdougm 						sa_free_attr_string(type);
13304653Sdougm 				} else {
13314653Sdougm 					(void) printf("\t%s", sharepath);
13324653Sdougm 					if (properties)
13334653Sdougm 						show_properties(share, NULL,
13344653Sdougm 						    "\t");
13354653Sdougm 				}
13364653Sdougm 				(void) printf("\n");
13374653Sdougm 				sa_free_attr_string(sharepath);
13383034Sdougm 			}
13393034Sdougm 		}
13403034Sdougm 	}
13413034Sdougm 	if (groupname != NULL) {
13423034Sdougm 		sa_free_attr_string(groupname);
13433034Sdougm 	}
13443034Sdougm }
13453034Sdougm 
13463034Sdougm /*
13473034Sdougm  * show_group_xml_init()
13483034Sdougm  *
13493034Sdougm  * Create an XML document that will be used to display config info via
13503034Sdougm  * XML format.
13513034Sdougm  */
13523034Sdougm 
13533034Sdougm xmlDocPtr
13543034Sdougm show_group_xml_init()
13553034Sdougm {
13563034Sdougm 	xmlDocPtr doc;
13573034Sdougm 	xmlNodePtr root;
13583034Sdougm 
13593034Sdougm 	doc = xmlNewDoc((xmlChar *)"1.0");
13603034Sdougm 	if (doc != NULL) {
13614653Sdougm 		root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
13624653Sdougm 		if (root != NULL)
13634653Sdougm 			xmlDocSetRootElement(doc, root);
13643034Sdougm 	}
13653034Sdougm 	return (doc);
13663034Sdougm }
13673034Sdougm 
13683034Sdougm /*
13693034Sdougm  * show_group_xml(doc, group)
13703034Sdougm  *
13713034Sdougm  * Copy the group info into the XML doc.
13723034Sdougm  */
13733034Sdougm 
13743034Sdougm static void
13753034Sdougm show_group_xml(xmlDocPtr doc, sa_group_t group)
13763034Sdougm {
13773034Sdougm 	xmlNodePtr node;
13783034Sdougm 	xmlNodePtr root;
13793034Sdougm 
13803034Sdougm 	root = xmlDocGetRootElement(doc);
13813034Sdougm 	node = xmlCopyNode((xmlNodePtr)group, 1);
13823034Sdougm 	if (node != NULL && root != NULL) {
13834653Sdougm 		xmlAddChild(root, node);
13843034Sdougm 		/*
13853034Sdougm 		 * In the future, we may have interally used tags that
13863034Sdougm 		 * should not appear in the XML output. Remove
13873034Sdougm 		 * anything we don't want to show here.
13883034Sdougm 		 */
13893034Sdougm 	}
13903034Sdougm }
13913034Sdougm 
13923034Sdougm /*
13933034Sdougm  * sa_show(flags, argc, argv)
13943034Sdougm  *
13953034Sdougm  * Implements the show subcommand.
13963034Sdougm  */
13973034Sdougm 
13984653Sdougm /*ARGSUSED*/
13993034Sdougm int
14003910Sdougm sa_show(sa_handle_t handle, int flags, int argc, char *argv[])
14013034Sdougm {
14023034Sdougm 	sa_group_t group;
14033034Sdougm 	int verbose = 0;
14043034Sdougm 	int properties = 0;
14053034Sdougm 	int c;
14063034Sdougm 	int ret = SA_OK;
14073034Sdougm 	char *protocol = NULL;
14083034Sdougm 	int xml = 0;
14093034Sdougm 	xmlDocPtr doc;
14103034Sdougm 
14113034Sdougm 	while ((c = getopt(argc, argv, "?hvP:px")) !=	EOF) {
14124653Sdougm 		switch (c) {
14134653Sdougm 		case 'v':
14144653Sdougm 			verbose++;
14154653Sdougm 			break;
14164653Sdougm 		case 'p':
14174653Sdougm 			properties++;
14184653Sdougm 			break;
14194653Sdougm 		case 'P':
14204653Sdougm 			protocol = optarg;
14214653Sdougm 			if (!sa_valid_protocol(protocol)) {
14224653Sdougm 				(void) printf(gettext(
14234653Sdougm 				    "Invalid protocol specified: %s\n"),
14244653Sdougm 				    protocol);
14254653Sdougm 				return (SA_INVALID_PROTOCOL);
14264653Sdougm 			}
14274653Sdougm 			break;
14284653Sdougm 		case 'x':
14294653Sdougm 			xml++;
14304653Sdougm 			break;
14314653Sdougm 		default:
14324653Sdougm 		case 'h':
14334653Sdougm 		case '?':
14344653Sdougm 			(void) printf(gettext("usage: %s\n"),
14354653Sdougm 			    sa_get_usage(USAGE_SHOW));
14364653Sdougm 			return (0);
14373034Sdougm 		}
14383034Sdougm 	}
14393034Sdougm 
14403034Sdougm 	if (xml) {
14414653Sdougm 		doc = show_group_xml_init();
14424653Sdougm 		if (doc == NULL)
14434653Sdougm 			ret = SA_NO_MEMORY;
14443034Sdougm 	}
14453034Sdougm 
14463034Sdougm 	if (optind == argc) {
14474653Sdougm 		/* No group specified so go through them all */
14484653Sdougm 		for (group = sa_get_group(handle, NULL);
14494653Sdougm 		    group != NULL;
14504653Sdougm 		    group = sa_get_next_group(group)) {
14514653Sdougm 			/*
14524653Sdougm 			 * Have a group so check if one we want and then list
14534653Sdougm 			 * contents with appropriate options.
14544653Sdougm 			 */
14554653Sdougm 			if (xml)
14564653Sdougm 				show_group_xml(doc, group);
14574653Sdougm 			else
14584653Sdougm 				show_group(group, verbose, properties, protocol,
14594653Sdougm 				    NULL);
14604653Sdougm 		}
14613034Sdougm 	} else {
14624653Sdougm 		/* Have a specified list of groups */
14634653Sdougm 		for (; optind < argc; optind++) {
14644653Sdougm 			group = sa_get_group(handle, argv[optind]);
14654653Sdougm 			if (group != NULL) {
14664653Sdougm 				if (xml)
14674653Sdougm 					show_group_xml(doc, group);
14684653Sdougm 				else
14694653Sdougm 					show_group(group, verbose, properties,
14704653Sdougm 					    protocol, NULL);
14714653Sdougm 			} else {
14724653Sdougm 				(void) printf(gettext("%s: not found\n"),
14734653Sdougm 				    argv[optind]);
14744653Sdougm 				ret = SA_NO_SUCH_GROUP;
14754653Sdougm 			}
14763034Sdougm 		}
14773034Sdougm 	}
14783034Sdougm 	if (xml && ret == SA_OK) {
14794653Sdougm 		xmlDocFormatDump(stdout, doc, 1);
14804653Sdougm 		xmlFreeDoc(doc);
14813034Sdougm 	}
14823034Sdougm 	return (ret);
14833034Sdougm 
14843034Sdougm }
14853034Sdougm 
14863034Sdougm /*
14873034Sdougm  * enable_share(group, share, update_legacy)
14883034Sdougm  *
14893034Sdougm  * helper function to enable a share if the group is enabled.
14903034Sdougm  */
14913034Sdougm 
14923034Sdougm static int
14933910Sdougm enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
14943910Sdougm 		int update_legacy)
14953034Sdougm {
14963034Sdougm 	char *value;
14973034Sdougm 	int enabled;
14983034Sdougm 	sa_optionset_t optionset;
14993034Sdougm 	int ret = SA_OK;
15003034Sdougm 	char *zfs = NULL;
15013034Sdougm 	int iszfs = 0;
15023034Sdougm 
15033034Sdougm 	/*
15043034Sdougm 	 * need to enable this share if the group is enabled but not
15053034Sdougm 	 * otherwise. The enable is also done on each protocol
15063034Sdougm 	 * represented in the group.
15073034Sdougm 	 */
15083034Sdougm 	value = sa_get_group_attr(group, "state");
15093034Sdougm 	enabled = value != NULL && strcmp(value, "enabled") == 0;
15103034Sdougm 	if (value != NULL)
15114653Sdougm 		sa_free_attr_string(value);
15123034Sdougm 	/* remove legacy config if necessary */
15133034Sdougm 	if (update_legacy)
15144653Sdougm 		ret = sa_delete_legacy(share);
15153034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
15163034Sdougm 	if (zfs != NULL) {
15174653Sdougm 		iszfs++;
15184653Sdougm 		sa_free_attr_string(zfs);
15193034Sdougm 	}
15203034Sdougm 
15213034Sdougm 	/*
15223034Sdougm 	 * Step through each optionset at the group level and
15233034Sdougm 	 * enable the share based on the protocol type. This
15243034Sdougm 	 * works because protocols must be set on the group
15253034Sdougm 	 * for the protocol to be enabled.
15263034Sdougm 	 */
15273034Sdougm 	for (optionset = sa_get_optionset(group, NULL);
15283034Sdougm 	    optionset != NULL && ret == SA_OK;
15293034Sdougm 	    optionset = sa_get_next_optionset(optionset)) {
15304653Sdougm 		value = sa_get_optionset_attr(optionset, "type");
15314653Sdougm 		if (value != NULL) {
15324653Sdougm 			if (enabled)
15334653Sdougm 				ret = sa_enable_share(share, value);
15344653Sdougm 			if (update_legacy && !iszfs)
15354653Sdougm 				(void) sa_update_legacy(share, value);
15364653Sdougm 			sa_free_attr_string(value);
15374653Sdougm 		}
15383034Sdougm 	}
15393034Sdougm 	if (ret == SA_OK)
15404653Sdougm 		(void) sa_update_config(handle);
15413034Sdougm 	return (ret);
15423034Sdougm }
15433034Sdougm 
15443034Sdougm /*
15453034Sdougm  * sa_addshare(flags, argc, argv)
15463034Sdougm  *
15473034Sdougm  * implements add-share subcommand.
15483034Sdougm  */
15493034Sdougm 
15503034Sdougm int
15513910Sdougm sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[])
15523034Sdougm {
15533034Sdougm 	int verbose = 0;
15543034Sdougm 	int dryrun = 0;
15553034Sdougm 	int c;
15563034Sdougm 	int ret = SA_OK;
15573034Sdougm 	sa_group_t group;
15583034Sdougm 	sa_share_t share;
15593034Sdougm 	char *sharepath = NULL;
15603034Sdougm 	char *description = NULL;
15613034Sdougm 	char *resource = NULL;
15623034Sdougm 	int persist = SA_SHARE_PERMANENT; /* default to persist */
15633034Sdougm 	int auth;
15643034Sdougm 	char dir[MAXPATHLEN];
15653034Sdougm 
15663034Sdougm 	while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
15674653Sdougm 		switch (c) {
15684653Sdougm 		case 'n':
15694653Sdougm 			dryrun++;
15704653Sdougm 			break;
15714653Sdougm 		case 'v':
15724653Sdougm 			verbose++;
15734653Sdougm 			break;
15744653Sdougm 		case 'd':
15754653Sdougm 			description = optarg;
15764653Sdougm 			break;
15774653Sdougm 		case 'r':
15784653Sdougm 			resource = optarg;
15794653Sdougm 			break;
15804653Sdougm 		case 's':
15814653Sdougm 			/*
15824653Sdougm 			 * Save share path into group. Currently limit
15834653Sdougm 			 * to one share per command.
15844653Sdougm 			 */
15854653Sdougm 			if (sharepath != NULL) {
15864653Sdougm 				(void) printf(gettext(
15874653Sdougm 				    "Adding multiple shares not supported\n"));
15884653Sdougm 				return (1);
15894653Sdougm 			}
15904653Sdougm 			sharepath = optarg;
15914653Sdougm 			break;
15924653Sdougm 		case 't':
15934653Sdougm 			persist = SA_SHARE_TRANSIENT;
15944653Sdougm 			break;
15954653Sdougm 		default:
15964653Sdougm 		case 'h':
15974653Sdougm 		case '?':
15984653Sdougm 			(void) printf(gettext("usage: %s\n"),
15994653Sdougm 			    sa_get_usage(USAGE_ADD_SHARE));
16004653Sdougm 			return (0);
16013034Sdougm 		}
16023034Sdougm 	}
16033034Sdougm 
16043034Sdougm 	if (optind >= argc) {
16054653Sdougm 		(void) printf(gettext("usage: %s\n"),
16064653Sdougm 		    sa_get_usage(USAGE_ADD_SHARE));
16074653Sdougm 		if (dryrun || sharepath != NULL || description != NULL ||
16084653Sdougm 		    resource != NULL || verbose || persist) {
16094653Sdougm 			(void) printf(gettext("\tgroup must be specified\n"));
16104653Sdougm 			ret = SA_NO_SUCH_GROUP;
16114653Sdougm 		} else {
16124653Sdougm 			ret = SA_OK;
16134653Sdougm 		}
16143034Sdougm 	} else {
16154653Sdougm 		if (sharepath == NULL) {
16164653Sdougm 			(void) printf(gettext("usage: %s\n"),
16174653Sdougm 			    sa_get_usage(USAGE_ADD_SHARE));
16184653Sdougm 			(void) printf(gettext(
16194653Sdougm 			    "\t-s sharepath must be specified\n"));
16204653Sdougm 			return (SA_BAD_PATH);
16214653Sdougm 		}
16223034Sdougm 		if (realpath(sharepath, dir) == NULL) {
16234653Sdougm 			(void) printf(gettext(
16244653Sdougm 			    "Path is not valid: %s\n"), sharepath);
16254653Sdougm 			return (SA_BAD_PATH);
16263034Sdougm 		} else {
16274653Sdougm 			sharepath = dir;
16283034Sdougm 		}
16294653Sdougm 
16304653Sdougm 		/* Check for valid syntax */
16314653Sdougm 		if (resource != NULL && strpbrk(resource, " \t/") != NULL) {
16324653Sdougm 			(void) printf(gettext("usage: %s\n"),
16334653Sdougm 			    sa_get_usage(USAGE_ADD_SHARE));
16344653Sdougm 			(void) printf(gettext(
16354653Sdougm 			    "\tresource must not contain white"
16364653Sdougm 			    "space or '/' characters\n"));
16374653Sdougm 			return (SA_BAD_PATH);
16383034Sdougm 		}
16393910Sdougm 		group = sa_get_group(handle, argv[optind]);
16404653Sdougm 		if (group == NULL) {
16414653Sdougm 			(void) printf(gettext("Group \"%s\" not found\n"),
16424653Sdougm 			    argv[optind]);
16434653Sdougm 			return (SA_NO_SUCH_GROUP);
16444653Sdougm 		}
16454653Sdougm 		auth = check_authorizations(argv[optind],  flags);
16464653Sdougm 		share = sa_find_share(handle, sharepath);
16474653Sdougm 		if (share != NULL) {
16483034Sdougm 			group = sa_get_parent_group(share);
16493034Sdougm 			if (group != NULL) {
16504653Sdougm 				char *groupname;
16514653Sdougm 				groupname = sa_get_group_attr(
16524653Sdougm 				    group, "name");
16534653Sdougm 				if (groupname != NULL) {
16544653Sdougm 					(void) printf(gettext(
16554653Sdougm 					    "Share path already "
16564653Sdougm 					    "shared in group "
16574653Sdougm 					    "\"%s\": %s\n"),
16584653Sdougm 					    groupname, sharepath);
16594653Sdougm 					sa_free_attr_string(groupname);
16604653Sdougm 				} else {
16614653Sdougm 					(void) printf(gettext(
16624653Sdougm 					    "Share path already"
16634653Sdougm 					    "shared: %s\n"),
16644653Sdougm 					    groupname, sharepath);
16654653Sdougm 				}
16663034Sdougm 			} else {
16674653Sdougm 				(void) printf(gettext(
16684653Sdougm 				    "Share path %s already shared\n"),
16693034Sdougm 				    sharepath);
16703034Sdougm 			}
16714653Sdougm 			return (SA_DUPLICATE_NAME);
16724653Sdougm 		} else {
16733034Sdougm 			/*
16744653Sdougm 			 * Need to check that resource name is
16754653Sdougm 			 * unique at some point. Path checking
16764653Sdougm 			 * should use the "normal" rules which
16774653Sdougm 			 * don't check the repository.
16783034Sdougm 			 */
16793034Sdougm 			if (dryrun)
16804653Sdougm 				ret = sa_check_path(group, sharepath,
16814653Sdougm 				    SA_CHECK_NORMAL);
16823034Sdougm 			else
16834653Sdougm 				share = sa_add_share(group, sharepath,
16844653Sdougm 				    persist, &ret);
16853034Sdougm 			if (!dryrun && share == NULL) {
16864653Sdougm 				(void) printf(gettext(
16874653Sdougm 				    "Could not add share: %s\n"),
16884653Sdougm 				    sa_errorstr(ret));
16893034Sdougm 			} else {
16904653Sdougm 				if (!dryrun && ret == SA_OK) {
16914653Sdougm 					if (resource != NULL &&
16924653Sdougm 					    strpbrk(resource, " \t/") == NULL) {
16934653Sdougm 						ret = sa_set_share_attr(share,
16944653Sdougm 						    "resource", resource);
16954653Sdougm 					}
16964653Sdougm 					if (ret == SA_OK &&
16974653Sdougm 					    description != NULL) {
16984653Sdougm 						ret = sa_set_share_description(
16994653Sdougm 						    share, description);
17004653Sdougm 					}
17014653Sdougm 					if (ret == SA_OK) {
17024653Sdougm 						/* Now enable the share(s) */
17034653Sdougm 						ret = enable_share(handle,
17044653Sdougm 						    group, share, 1);
17054653Sdougm 						ret = sa_update_config(handle);
17064653Sdougm 					}
17074653Sdougm 					switch (ret) {
17084653Sdougm 					case SA_DUPLICATE_NAME:
17094653Sdougm 						(void) printf(gettext(
17104653Sdougm 						    "Resource name in"
17114653Sdougm 						    "use: %s\n"), resource);
17124653Sdougm 						break;
17134653Sdougm 					default:
17144653Sdougm 						(void) printf(
17154653Sdougm 						    gettext("Could not set "
17164653Sdougm 						    "attribute: %s\n"),
17174653Sdougm 						    sa_errorstr(ret));
17184653Sdougm 						break;
17194653Sdougm 					case SA_OK:
17204653Sdougm 						break;
17214653Sdougm 					}
17224653Sdougm 				} else if (dryrun && ret == SA_OK && !auth &&
17234653Sdougm 				    verbose) {
17244653Sdougm 					(void) printf(gettext(
17254653Sdougm 					    "Command would fail: %s\n"),
17264653Sdougm 					    sa_errorstr(SA_NO_PERMISSION));
17274653Sdougm 					ret = SA_NO_PERMISSION;
17283034Sdougm 				}
17293034Sdougm 			}
17303034Sdougm 		}
17313034Sdougm 	}
17323034Sdougm 	return (ret);
17333034Sdougm }
17343034Sdougm 
17353034Sdougm /*
17363034Sdougm  * sa_moveshare(flags, argc, argv)
17373034Sdougm  *
17383034Sdougm  * implements move-share subcommand.
17393034Sdougm  */
17403034Sdougm 
17413034Sdougm int
17423910Sdougm sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[])
17433034Sdougm {
17443034Sdougm 	int verbose = 0;
17453034Sdougm 	int dryrun = 0;
17463034Sdougm 	int c;
17473034Sdougm 	int ret = SA_OK;
17483034Sdougm 	sa_group_t group;
17493034Sdougm 	sa_share_t share;
17503034Sdougm 	char *sharepath = NULL;
17513034Sdougm 	int authsrc = 0, authdst = 0;
17523034Sdougm 
17533034Sdougm 	while ((c = getopt(argc, argv, "?hvns:")) != EOF) {
17544653Sdougm 		switch (c) {
17554653Sdougm 		case 'n':
17564653Sdougm 			dryrun++;
17574653Sdougm 			break;
17584653Sdougm 		case 'v':
17594653Sdougm 			verbose++;
17604653Sdougm 			break;
17614653Sdougm 		case 's':
17624653Sdougm 			/*
17634653Sdougm 			 * Remove share path from group. Currently limit
17644653Sdougm 			 * to one share per command.
17654653Sdougm 			 */
17664653Sdougm 			if (sharepath != NULL) {
17674653Sdougm 				(void) printf(gettext("Moving multiple shares"
17684653Sdougm 				    "not supported\n"));
17694653Sdougm 				return (SA_BAD_PATH);
17704653Sdougm 			}
17714653Sdougm 			sharepath = optarg;
17724653Sdougm 			break;
17734653Sdougm 		default:
17744653Sdougm 		case 'h':
17754653Sdougm 		case '?':
17764653Sdougm 			(void) printf(gettext("usage: %s\n"),
17774653Sdougm 			    sa_get_usage(USAGE_MOVE_SHARE));
17784653Sdougm 			return (0);
17793034Sdougm 		}
17803034Sdougm 	}
17813034Sdougm 
17823034Sdougm 	if (optind >= argc || sharepath == NULL) {
17833034Sdougm 			(void) printf(gettext("usage: %s\n"),
17844653Sdougm 			    sa_get_usage(USAGE_MOVE_SHARE));
17854653Sdougm 			if (dryrun || verbose || sharepath != NULL) {
17864653Sdougm 				(void) printf(gettext(
17874653Sdougm 				    "\tgroup must be specified\n"));
17884653Sdougm 				ret = SA_NO_SUCH_GROUP;
17894653Sdougm 			} else {
17904653Sdougm 				if (sharepath == NULL) {
17914653Sdougm 					ret = SA_SYNTAX_ERR;
17924653Sdougm 					(void) printf(gettext(
17934653Sdougm 					    "\tsharepath must be specified\n"));
17944653Sdougm 				} else {
17954653Sdougm 					ret = SA_OK;
17964653Sdougm 				}
17974653Sdougm 			}
17984653Sdougm 	} else {
17994653Sdougm 		sa_group_t parent;
18004653Sdougm 		char *zfsold;
18014653Sdougm 		char *zfsnew;
18024653Sdougm 
18033034Sdougm 		if (sharepath == NULL) {
18044653Sdougm 			(void) printf(gettext(
18054653Sdougm 			    "sharepath must be specified with the -s "
18064653Sdougm 			    "option\n"));
18074653Sdougm 			return (SA_BAD_PATH);
18084653Sdougm 		}
18093910Sdougm 		group = sa_get_group(handle, argv[optind]);
18104653Sdougm 		if (group == NULL) {
18114653Sdougm 			(void) printf(gettext("Group \"%s\" not found\n"),
18124653Sdougm 			    argv[optind]);
18134653Sdougm 			return (SA_NO_SUCH_GROUP);
18144653Sdougm 		}
18154653Sdougm 		share = sa_find_share(handle, sharepath);
18164653Sdougm 		authdst = check_authorizations(argv[optind], flags);
18174653Sdougm 		if (share == NULL) {
18183034Sdougm 			(void) printf(gettext("Share not found: %s\n"),
18194653Sdougm 			    sharepath);
18204653Sdougm 			return (SA_NO_SUCH_PATH);
18214653Sdougm 		}
18224653Sdougm 
18234653Sdougm 		parent = sa_get_parent_group(share);
18244653Sdougm 		if (parent != NULL) {
18254653Sdougm 			char *pname;
18264653Sdougm 			pname = sa_get_group_attr(parent, "name");
18274653Sdougm 			if (pname != NULL) {
18283034Sdougm 				authsrc = check_authorizations(pname, flags);
18293034Sdougm 				sa_free_attr_string(pname);
18304653Sdougm 			}
18314653Sdougm 			zfsold = sa_get_group_attr(parent, "zfs");
18324653Sdougm 			zfsnew = sa_get_group_attr(group, "zfs");
18334653Sdougm 			if ((zfsold != NULL && zfsnew == NULL) ||
18344653Sdougm 			    (zfsold == NULL && zfsnew != NULL)) {
18353034Sdougm 				ret = SA_NOT_ALLOWED;
18363034Sdougm 			}
18374653Sdougm 			if (zfsold != NULL)
18384653Sdougm 				sa_free_attr_string(zfsold);
18394653Sdougm 			if (zfsnew != NULL)
18404653Sdougm 				sa_free_attr_string(zfsnew);
18414653Sdougm 		}
18424653Sdougm 		if (!dryrun && ret == SA_OK)
18434653Sdougm 			ret = sa_move_share(group, share);
18444653Sdougm 
18454653Sdougm 		if (ret == SA_OK && parent != group && !dryrun) {
18464653Sdougm 			char *oldstate;
18474653Sdougm 			ret = sa_update_config(handle);
18484653Sdougm 			/*
18494653Sdougm 			 * Note that the share may need to be
18504653Sdougm 			 * "unshared" if the new group is
18514653Sdougm 			 * disabled and the old was enabled or
18524653Sdougm 			 * it may need to be share to update
18534653Sdougm 			 * if the new group is enabled.
18544653Sdougm 			 */
18554653Sdougm 			oldstate = sa_get_group_attr(parent, "state");
18564653Sdougm 
18574653Sdougm 			/* enable_share determines what to do */
18584653Sdougm 			if (strcmp(oldstate, "enabled") == 0) {
18593034Sdougm 				(void) sa_disable_share(share, NULL);
18604653Sdougm 			}
18614653Sdougm 			(void) enable_share(handle, group, share, 1);
18624653Sdougm 			if (oldstate != NULL)
18633034Sdougm 				sa_free_attr_string(oldstate);
18643034Sdougm 		}
18654653Sdougm 
18664653Sdougm 		if (ret != SA_OK)
18674653Sdougm 			(void) printf(gettext("Could not move share: %s\n"),
18684653Sdougm 			    sa_errorstr(ret));
18694653Sdougm 
18704653Sdougm 		if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
18714653Sdougm 		    verbose) {
18724653Sdougm 			(void) printf(gettext("Command would fail: %s\n"),
18734653Sdougm 			    sa_errorstr(SA_NO_PERMISSION));
18744653Sdougm 		}
18753034Sdougm 	}
18763034Sdougm 	return (ret);
18773034Sdougm }
18783034Sdougm 
18793034Sdougm /*
18803034Sdougm  * sa_removeshare(flags, argc, argv)
18813034Sdougm  *
18823034Sdougm  * implements remove-share subcommand.
18833034Sdougm  */
18843034Sdougm 
18853034Sdougm int
18863910Sdougm sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[])
18873034Sdougm {
18883034Sdougm 	int verbose = 0;
18893034Sdougm 	int dryrun = 0;
18903034Sdougm 	int force = 0;
18913034Sdougm 	int c;
18923034Sdougm 	int ret = SA_OK;
18933034Sdougm 	sa_group_t group;
18943034Sdougm 	sa_share_t share;
18953034Sdougm 	char *sharepath = NULL;
18963034Sdougm 	char dir[MAXPATHLEN];
18973034Sdougm 	int auth;
18983034Sdougm 
18993034Sdougm 	while ((c = getopt(argc, argv, "?hfns:v")) != EOF) {
19004653Sdougm 		switch (c) {
19014653Sdougm 		case 'n':
19024653Sdougm 			dryrun++;
19034653Sdougm 			break;
19044653Sdougm 		case 'v':
19054653Sdougm 			verbose++;
19064653Sdougm 			break;
19074653Sdougm 		case 'f':
19084653Sdougm 			force++;
19094653Sdougm 			break;
19104653Sdougm 		case 's':
19114653Sdougm 			/*
19124653Sdougm 			 * Remove share path from group. Currently limit
19134653Sdougm 			 * to one share per command.
19144653Sdougm 			 */
19154653Sdougm 			if (sharepath != NULL) {
19164653Sdougm 				(void) printf(gettext(
19174653Sdougm 				    "Removing multiple shares not "
19183034Sdougm 				    "supported\n"));
19194653Sdougm 				return (SA_SYNTAX_ERR);
19204653Sdougm 			}
19214653Sdougm 			sharepath = optarg;
19224653Sdougm 			break;
19234653Sdougm 		default:
19244653Sdougm 		case 'h':
19254653Sdougm 		case '?':
19264653Sdougm 			(void) printf(gettext("usage: %s\n"),
19274653Sdougm 			    sa_get_usage(USAGE_REMOVE_SHARE));
19284653Sdougm 			return (0);
19293034Sdougm 		}
19303034Sdougm 	}
19313034Sdougm 
19323034Sdougm 	if (optind >= argc || sharepath == NULL) {
19334653Sdougm 		if (sharepath == NULL) {
19343034Sdougm 			(void) printf(gettext("usage: %s\n"),
19354653Sdougm 			    sa_get_usage(USAGE_REMOVE_SHARE));
19364653Sdougm 			(void) printf(gettext(
19374653Sdougm 			    "\t-s sharepath must be specified\n"));
19384653Sdougm 			ret = SA_BAD_PATH;
19394653Sdougm 		} else {
19404653Sdougm 			ret = SA_OK;
19414653Sdougm 		}
19423034Sdougm 	}
19434653Sdougm 	if (ret != SA_OK) {
19444653Sdougm 		return (ret);
19454653Sdougm 	}
19464653Sdougm 
19474653Sdougm 	if (optind < argc) {
19483034Sdougm 		if ((optind + 1) < argc) {
19494653Sdougm 			(void) printf(gettext("Extraneous group(s) at end of "
19504653Sdougm 			    "command\n"));
19514653Sdougm 			ret = SA_SYNTAX_ERR;
19523034Sdougm 		} else {
19534653Sdougm 			group = sa_get_group(handle, argv[optind]);
19544653Sdougm 			if (group == NULL) {
19554653Sdougm 				(void) printf(gettext(
19564653Sdougm 				    "Group \"%s\" not found\n"), argv[optind]);
19574653Sdougm 				ret = SA_NO_SUCH_GROUP;
19584653Sdougm 			}
19593034Sdougm 		}
19604653Sdougm 	} else {
19613034Sdougm 		group = NULL;
19624653Sdougm 	}
19634653Sdougm 
19644653Sdougm 	/*
19654653Sdougm 	 * Lookup the path in the internal configuration. Care
19664653Sdougm 	 * must be taken to handle the case where the
19674653Sdougm 	 * underlying path has been removed since we need to
19684653Sdougm 	 * be able to deal with that as well.
19694653Sdougm 	 */
19704653Sdougm 	if (ret == SA_OK) {
19713034Sdougm 		if (group != NULL)
19724653Sdougm 			share = sa_get_share(group, sharepath);
19733034Sdougm 		else
19744653Sdougm 			share = sa_find_share(handle, sharepath);
19753663Sdougm 		/*
19763663Sdougm 		 * If we didn't find the share with the provided path,
19773663Sdougm 		 * it may be a symlink so attempt to resolve it using
19783663Sdougm 		 * realpath and try again. Realpath will resolve any
19793663Sdougm 		 * symlinks and place them in "dir". Note that
19803663Sdougm 		 * sharepath is only used for the lookup the first
19813663Sdougm 		 * time and later for error messages. dir will be used
19823663Sdougm 		 * on the second attempt. Once a share is found, all
19833663Sdougm 		 * operations are based off of the share variable.
19843663Sdougm 		 */
19853663Sdougm 		if (share == NULL) {
19864653Sdougm 			if (realpath(sharepath, dir) == NULL) {
19874653Sdougm 				ret = SA_BAD_PATH;
19884653Sdougm 				(void) printf(gettext(
19894653Sdougm 				    "Path is not valid: %s\n"), sharepath);
19904653Sdougm 			} else {
19914653Sdougm 				if (group != NULL)
19924653Sdougm 					share = sa_get_share(group, dir);
19934653Sdougm 				else
19944653Sdougm 					share = sa_find_share(handle, dir);
19954653Sdougm 			}
19963663Sdougm 		}
19974653Sdougm 	}
19984653Sdougm 
19994653Sdougm 	/*
20004653Sdougm 	 * If there hasn't been an error, there was likely a
20014653Sdougm 	 * path found. If not, give the appropriate error
20024653Sdougm 	 * message and set the return error. If it was found,
20034653Sdougm 	 * then disable the share and then remove it from the
20044653Sdougm 	 * configuration.
20054653Sdougm 	 */
20064653Sdougm 	if (ret != SA_OK) {
20074653Sdougm 		return (ret);
20084653Sdougm 	}
20094653Sdougm 	if (share == NULL) {
20104653Sdougm 		if (group != NULL)
20113034Sdougm 			(void) printf(gettext("Share not found in group %s:"
20124653Sdougm 			    " %s\n"), argv[optind], sharepath);
20134653Sdougm 		else
20143034Sdougm 			(void) printf(gettext("Share not found: %s\n"),
20154653Sdougm 			    sharepath);
20164653Sdougm 			ret = SA_NO_SUCH_PATH;
20174653Sdougm 	} else {
20184653Sdougm 		if (group == NULL)
20193034Sdougm 			group = sa_get_parent_group(share);
20204653Sdougm 		if (!dryrun) {
20213034Sdougm 			if (ret == SA_OK) {
20224653Sdougm 				ret = sa_disable_share(share, NULL);
20233034Sdougm 				/*
20244653Sdougm 				 * We don't care if it fails since it
20253663Sdougm 				 * could be disabled already. Some
20263663Sdougm 				 * unexpected errors could occur that
20273663Sdougm 				 * prevent removal, so also check for
20283663Sdougm 				 * force being set.
20293034Sdougm 				 */
20304653Sdougm 				if (ret == SA_OK || ret == SA_NO_SUCH_PATH ||
20314653Sdougm 				    ret == SA_NOT_SUPPORTED ||
20324653Sdougm 				    ret == SA_SYSTEM_ERR || force) {
20334653Sdougm 					ret = sa_remove_share(share);
20344653Sdougm 				}
20354653Sdougm 				if (ret == SA_OK)
20364653Sdougm 					ret = sa_update_config(handle);
20373034Sdougm 			}
20384653Sdougm 			if (ret != SA_OK)
20394653Sdougm 				(void) printf(gettext(
20404653Sdougm 				    "Could not remove share: %s\n"),
20414653Sdougm 				    sa_errorstr(ret));
20424653Sdougm 
20434653Sdougm 		} else if (ret == SA_OK) {
20443034Sdougm 			char *pname;
20453034Sdougm 			pname = sa_get_group_attr(group, "name");
20463034Sdougm 			if (pname != NULL) {
20474653Sdougm 				auth = check_authorizations(pname, flags);
20484653Sdougm 				sa_free_attr_string(pname);
20493034Sdougm 			}
20503034Sdougm 			if (!auth && verbose) {
20514653Sdougm 				(void) printf(gettext(
20524653Sdougm 				    "Command would fail: %s\n"),
20534653Sdougm 				    sa_errorstr(SA_NO_PERMISSION));
20543034Sdougm 			}
20553034Sdougm 		}
20563034Sdougm 	}
20573034Sdougm 	return (ret);
20583034Sdougm }
20593034Sdougm 
20603034Sdougm /*
20613034Sdougm  * sa_set_share(flags, argc, argv)
20623034Sdougm  *
20633034Sdougm  * implements set-share subcommand.
20643034Sdougm  */
20653034Sdougm 
20663034Sdougm int
20673910Sdougm sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[])
20683034Sdougm {
20693034Sdougm 	int dryrun = 0;
20703034Sdougm 	int c;
20713034Sdougm 	int ret = SA_OK;
20723034Sdougm 	sa_group_t group, sharegroup;
20733034Sdougm 	sa_share_t share;
20743034Sdougm 	char *sharepath = NULL;
20753034Sdougm 	char *description = NULL;
20763034Sdougm 	char *resource = NULL;
20773034Sdougm 	int auth;
20783034Sdougm 	int verbose = 0;
20794653Sdougm 	char *groupname;
20803034Sdougm 
20813034Sdougm 	while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
20824653Sdougm 		switch (c) {
20834653Sdougm 		case 'n':
20844653Sdougm 			dryrun++;
20854653Sdougm 			break;
20864653Sdougm 		case 'd':
20874653Sdougm 			description = optarg;
20884653Sdougm 			break;
20894653Sdougm 		case 'r':
20904653Sdougm 			resource = optarg;
20914653Sdougm 			break;
20924653Sdougm 		case 'v':
20934653Sdougm 			verbose++;
20944653Sdougm 			break;
20954653Sdougm 		case 's':
20964653Sdougm 			/*
20974653Sdougm 			 * Save share path into group. Currently limit
20984653Sdougm 			 * to one share per command.
20994653Sdougm 			 */
21004653Sdougm 			if (sharepath != NULL) {
21014653Sdougm 				(void) printf(gettext(
21024653Sdougm 				    "Updating multiple shares not "
21033034Sdougm 				    "supported\n"));
21044653Sdougm 				return (SA_BAD_PATH);
21054653Sdougm 			}
21064653Sdougm 			sharepath = optarg;
21074653Sdougm 			break;
21084653Sdougm 		default:
21094653Sdougm 		case 'h':
21104653Sdougm 		case '?':
21114653Sdougm 			(void) printf(gettext("usage: %s\n"),
21124653Sdougm 			    sa_get_usage(USAGE_SET_SHARE));
21134653Sdougm 			return (SA_OK);
21143034Sdougm 		}
21153034Sdougm 	}
21164653Sdougm 
21173034Sdougm 	if (optind >= argc || sharepath == NULL) {
21184653Sdougm 		if (sharepath == NULL) {
21194653Sdougm 			(void) printf(gettext("usage: %s\n"),
21204653Sdougm 			    sa_get_usage(USAGE_SET_SHARE));
21214653Sdougm 			(void) printf(gettext("\tgroup must be specified\n"));
21224653Sdougm 			ret = SA_BAD_PATH;
21234653Sdougm 		} else {
21244653Sdougm 			ret = SA_OK;
21254653Sdougm 		}
21263034Sdougm 	}
21273034Sdougm 	if ((optind + 1) < argc) {
21284653Sdougm 		(void) printf(gettext("usage: %s\n"),
21294653Sdougm 		    sa_get_usage(USAGE_SET_SHARE));
21304653Sdougm 		(void) printf(gettext("\tExtraneous group(s) at end\n"));
21314653Sdougm 		ret = SA_SYNTAX_ERR;
21323034Sdougm 	}
21334653Sdougm 
21344653Sdougm 	if (ret != SA_OK)
21354653Sdougm 		return (ret);
21364653Sdougm 
21374653Sdougm 	if (optind < argc) {
21383034Sdougm 		groupname = argv[optind];
21393910Sdougm 		group = sa_get_group(handle, groupname);
21404653Sdougm 	} else {
21413034Sdougm 		group = NULL;
21423034Sdougm 		groupname = NULL;
21434653Sdougm 	}
21444653Sdougm 	share = sa_find_share(handle, sharepath);
21454653Sdougm 	if (share == NULL) {
21464653Sdougm 		(void) printf(gettext("Share path \"%s\" not found\n"),
21474653Sdougm 		    sharepath);
21484653Sdougm 		return (SA_NO_SUCH_PATH);
21494653Sdougm 	}
21504653Sdougm 	sharegroup = sa_get_parent_group(share);
21514653Sdougm 	if (group != NULL && group != sharegroup) {
21524653Sdougm 		(void) printf(gettext("Group \"%s\" does not contain "
21534653Sdougm 		    "share %s\n"), argv[optind], sharepath);
21544653Sdougm 		ret = SA_BAD_PATH;
21554653Sdougm 	} else {
21564653Sdougm 		int delgroupname = 0;
21574653Sdougm 		if (groupname == NULL) {
21583034Sdougm 			groupname = sa_get_group_attr(sharegroup, "name");
21593034Sdougm 			delgroupname = 1;
21604653Sdougm 		}
21614653Sdougm 		if (groupname != NULL) {
21623034Sdougm 			auth = check_authorizations(groupname, flags);
21633034Sdougm 			if (delgroupname) {
21644653Sdougm 				sa_free_attr_string(groupname);
21654653Sdougm 				groupname = NULL;
21663034Sdougm 			}
21674653Sdougm 		} else {
21683034Sdougm 			ret = SA_NO_MEMORY;
21694653Sdougm 		}
21704653Sdougm 		if (resource != NULL) {
21713034Sdougm 			if (strpbrk(resource, " \t/") == NULL) {
21724653Sdougm 				if (!dryrun) {
21734653Sdougm 					ret = sa_set_share_attr(share,
21744653Sdougm 					    "resource", resource);
21754653Sdougm 				} else {
21764653Sdougm 					sa_share_t resshare;
21774653Sdougm 					resshare = sa_get_resource(sharegroup,
21784653Sdougm 					    resource);
21794653Sdougm 					if (resshare != NULL &&
21804653Sdougm 					    resshare != share)
21814653Sdougm 						ret = SA_DUPLICATE_NAME;
21824653Sdougm 				}
21833034Sdougm 			} else {
21844653Sdougm 				ret = SA_BAD_PATH;
21854653Sdougm 				(void) printf(gettext("Resource must not "
21864653Sdougm 				    "contain white space or '/'\n"));
21873034Sdougm 			}
21884653Sdougm 		}
21894653Sdougm 		if (ret == SA_OK && description != NULL)
21903034Sdougm 			ret = sa_set_share_description(share, description);
21914653Sdougm 	}
21924653Sdougm 	if (!dryrun && ret == SA_OK)
21934653Sdougm 		ret = sa_update_config(handle);
21944653Sdougm 
21954653Sdougm 	switch (ret) {
21964653Sdougm 	case SA_DUPLICATE_NAME:
21974653Sdougm 		(void) printf(gettext("Resource name in use: %s\n"), resource);
21984653Sdougm 		break;
21994653Sdougm 	default:
22004653Sdougm 		(void) printf(gettext("Could not set attribute: %s\n"),
22014653Sdougm 		    sa_errorstr(ret));
22024653Sdougm 		break;
22034653Sdougm 	case SA_OK:
22044653Sdougm 		if (dryrun && !auth && verbose)
22053034Sdougm 			(void) printf(gettext("Command would fail: %s\n"),
22064653Sdougm 			    sa_errorstr(SA_NO_PERMISSION));
22074653Sdougm 		break;
22083034Sdougm 	}
22094653Sdougm 
22103034Sdougm 	return (ret);
22113034Sdougm }
22123034Sdougm 
22133034Sdougm /*
22143034Sdougm  * add_security(group, sectype, optlist, proto, *err)
22153034Sdougm  *
22163034Sdougm  * Helper function to add a security option (named optionset) to the
22173034Sdougm  * group.
22183034Sdougm  */
22193034Sdougm 
22203034Sdougm static int
22213034Sdougm add_security(sa_group_t group, char *sectype,
22223034Sdougm 		struct options *optlist, char *proto, int *err)
22233034Sdougm {
22243034Sdougm 	sa_security_t security;
22253034Sdougm 	int ret = SA_OK;
22263034Sdougm 	int result = 0;
22273034Sdougm 
22283034Sdougm 	sectype = sa_proto_space_alias(proto, sectype);
22293034Sdougm 	security = sa_get_security(group, sectype, proto);
22304653Sdougm 	if (security == NULL)
22314653Sdougm 		security = sa_create_security(group, sectype, proto);
22324653Sdougm 
22333034Sdougm 	if (sectype != NULL)
22344653Sdougm 		sa_free_attr_string(sectype);
22354653Sdougm 
22364653Sdougm 	if (security == NULL)
22374653Sdougm 		return (ret);
22384653Sdougm 
22394653Sdougm 	while (optlist != NULL) {
22403034Sdougm 		sa_property_t prop;
22413034Sdougm 		prop = sa_get_property(security, optlist->optname);
22423034Sdougm 		if (prop == NULL) {
22433034Sdougm 			/*
22444653Sdougm 			 * Add the property, but only if it is
22453034Sdougm 			 * a non-NULL or non-zero length value
22463034Sdougm 			 */
22474653Sdougm 			if (optlist->optvalue != NULL) {
22484653Sdougm 				prop = sa_create_property(optlist->optname,
22494653Sdougm 				    optlist->optvalue);
22504653Sdougm 				if (prop != NULL) {
22514653Sdougm 					ret = sa_valid_property(security, proto,
22524653Sdougm 					    prop);
22534653Sdougm 					if (ret != SA_OK) {
22544653Sdougm 						(void) sa_remove_property(prop);
22554653Sdougm 						(void) printf(gettext(
22564653Sdougm 						    "Could not add "
22574653Sdougm 						    "property %s: %s\n"),
22584653Sdougm 						    optlist->optname,
22594653Sdougm 						    sa_errorstr(ret));
22604653Sdougm 					}
22614653Sdougm 					if (ret == SA_OK) {
22624653Sdougm 						ret = sa_add_property(security,
22634653Sdougm 						    prop);
22644653Sdougm 						if (ret != SA_OK) {
22654653Sdougm 							(void) printf(gettext(
22664653Sdougm 							    "Could not add "
22674653Sdougm 							    "property (%s=%s): "
22684653Sdougm 							    "%s\n"),
22694653Sdougm 							    optlist->optname,
22704653Sdougm 							    optlist->optvalue,
22714653Sdougm 							    sa_errorstr(ret));
22724653Sdougm 						} else {
22734653Sdougm 							result = 1;
22744653Sdougm 						}
22754653Sdougm 					}
22763034Sdougm 				}
22773034Sdougm 			}
22783034Sdougm 		} else {
22794653Sdougm 			ret = sa_update_property(prop, optlist->optvalue);
22804653Sdougm 			result = 1; /* should check if really changed */
22813034Sdougm 		}
22823034Sdougm 		optlist = optlist->next;
22834653Sdougm 	}
22844653Sdougm 	/*
22854653Sdougm 	 * When done, properties may have all been removed but
22864653Sdougm 	 * we need to keep the security type itself until
22874653Sdougm 	 * explicitly removed.
22884653Sdougm 	 */
22894653Sdougm 	if (result)
22903034Sdougm 		ret = sa_commit_properties(security, 0);
22913034Sdougm 	*err = ret;
22923034Sdougm 	return (result);
22933034Sdougm }
22943034Sdougm 
22953034Sdougm /*
2296*5089Sdougm  * zfscheck(group, share)
2297*5089Sdougm  *
2298*5089Sdougm  * For the special case where a share was provided, make sure it is a
2299*5089Sdougm  * compatible path for a ZFS property change.  The only path
2300*5089Sdougm  * acceptable is the path that defines the zfs sub-group (dataset with
2301*5089Sdougm  * the sharenfs property set) and not one of the paths that inherited
2302*5089Sdougm  * the NFS properties. Returns SA_OK if it is usable and
2303*5089Sdougm  * SA_NOT_ALLOWED if it isn't.
2304*5089Sdougm  *
2305*5089Sdougm  * If group is not a ZFS group/subgroup, we assume OK since the check
2306*5089Sdougm  * on return will catch errors for those cases.  What we are looking
2307*5089Sdougm  * for here is that the group is ZFS and the share is not the defining
2308*5089Sdougm  * share.  All else is SA_OK.
2309*5089Sdougm  */
2310*5089Sdougm 
2311*5089Sdougm static int
2312*5089Sdougm zfscheck(sa_group_t group, sa_share_t share)
2313*5089Sdougm {
2314*5089Sdougm 	int ret = SA_OK;
2315*5089Sdougm 	char *attr;
2316*5089Sdougm 
2317*5089Sdougm 	if (sa_group_is_zfs(group)) {
2318*5089Sdougm 		/*
2319*5089Sdougm 		 * The group is a ZFS group.  Does the share represent
2320*5089Sdougm 		 * the dataset that defined the group? It is only OK
2321*5089Sdougm 		 * if the attribute "subgroup" exists on the share and
2322*5089Sdougm 		 * has a value of "true".
2323*5089Sdougm 		 */
2324*5089Sdougm 
2325*5089Sdougm 		ret = SA_NOT_ALLOWED;
2326*5089Sdougm 		attr = sa_get_share_attr(share, "subgroup");
2327*5089Sdougm 		if (attr != NULL) {
2328*5089Sdougm 			if (strcmp(attr, "true") == 0)
2329*5089Sdougm 				ret = SA_OK;
2330*5089Sdougm 			sa_free_attr_string(attr);
2331*5089Sdougm 		}
2332*5089Sdougm 	}
2333*5089Sdougm 	return (ret);
2334*5089Sdougm }
2335*5089Sdougm 
2336*5089Sdougm /*
23373034Sdougm  * basic_set(groupname, optlist, protocol, sharepath, dryrun)
23383034Sdougm  *
23393034Sdougm  * This function implements "set" when a name space (-S) is not
23403034Sdougm  * specified. It is a basic set. Options and other CLI parsing has
23413034Sdougm  * already been done.
23423034Sdougm  */
23433034Sdougm 
23443034Sdougm static int
23453910Sdougm basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
23463910Sdougm 		char *protocol,	char *sharepath, int dryrun)
23473034Sdougm {
23483034Sdougm 	sa_group_t group;
23493034Sdougm 	int ret = SA_OK;
23503034Sdougm 	int change = 0;
23513034Sdougm 	struct list *worklist = NULL;
23523034Sdougm 
23533910Sdougm 	group = sa_get_group(handle, groupname);
23543034Sdougm 	if (group != NULL) {
23554653Sdougm 		sa_share_t share = NULL;
23564653Sdougm 		if (sharepath != NULL) {
23574653Sdougm 			share = sa_get_share(group, sharepath);
23584653Sdougm 			if (share == NULL) {
23594653Sdougm 				(void) printf(gettext(
23604653Sdougm 				    "Share does not exist in group %s\n"),
23614653Sdougm 				    groupname, sharepath);
23624653Sdougm 				ret = SA_NO_SUCH_PATH;
2363*5089Sdougm 			} else {
2364*5089Sdougm 				/* if ZFS and OK, then only group */
2365*5089Sdougm 				ret = zfscheck(group, share);
2366*5089Sdougm 				if (ret == SA_OK &&
2367*5089Sdougm 				    sa_group_is_zfs(group))
2368*5089Sdougm 					share = NULL;
2369*5089Sdougm 				if (ret == SA_NOT_ALLOWED)
2370*5089Sdougm 					(void) printf(gettext(
2371*5089Sdougm 					    "Properties on ZFS group shares "
2372*5089Sdougm 					    "not supported: %s\n"), sharepath);
23734653Sdougm 			}
23743034Sdougm 		}
23754653Sdougm 		if (ret == SA_OK) {
23764653Sdougm 			/* group must exist */
23774653Sdougm 			ret = valid_options(optlist, protocol,
23784653Sdougm 			    share == NULL ? group : share, NULL);
23794653Sdougm 			if (ret == SA_OK && !dryrun) {
23804653Sdougm 				if (share != NULL)
23814653Sdougm 					change |= add_optionset(share, optlist,
23824653Sdougm 					    protocol, &ret);
23834653Sdougm 				else
23844653Sdougm 					change |= add_optionset(group, optlist,
23854653Sdougm 					    protocol, &ret);
23864653Sdougm 				if (ret == SA_OK && change)
23874653Sdougm 					worklist = add_list(worklist, group,
23884653Sdougm 					    share);
23894653Sdougm 			}
23903034Sdougm 		}
23914653Sdougm 		free_opt(optlist);
23923034Sdougm 	} else {
23933034Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
23943034Sdougm 		ret = SA_NO_SUCH_GROUP;
23953034Sdougm 	}
23963034Sdougm 	/*
23973034Sdougm 	 * we have a group and potentially legal additions
23983034Sdougm 	 */
23993034Sdougm 
24004653Sdougm 	/*
24014653Sdougm 	 * Commit to configuration if not a dryrunp and properties
24024653Sdougm 	 * have changed.
24034653Sdougm 	 */
24044653Sdougm 	if (!dryrun && ret == SA_OK && change && worklist != NULL)
24053034Sdougm 		/* properties changed, so update all shares */
24063910Sdougm 		(void) enable_all_groups(handle, worklist, 0, 0, protocol);
24074653Sdougm 
24083034Sdougm 	if (worklist != NULL)
24094653Sdougm 		free_list(worklist);
24103034Sdougm 	return (ret);
24113034Sdougm }
24123034Sdougm 
24133034Sdougm /*
24143034Sdougm  * space_set(groupname, optlist, protocol, sharepath, dryrun)
24153034Sdougm  *
24163034Sdougm  * This function implements "set" when a name space (-S) is
24173034Sdougm  * specified. It is a namespace set. Options and other CLI parsing has
24183034Sdougm  * already been done.
24193034Sdougm  */
24203034Sdougm 
24213034Sdougm static int
24223910Sdougm space_set(sa_handle_t handle, char *groupname, struct options *optlist,
24233910Sdougm 		char *protocol,	char *sharepath, int dryrun, char *sectype)
24243034Sdougm {
24253034Sdougm 	sa_group_t group;
24263034Sdougm 	int ret = SA_OK;
24273034Sdougm 	int change = 0;
24283034Sdougm 	struct list *worklist = NULL;
24293034Sdougm 
24303034Sdougm 	/*
24313034Sdougm 	 * make sure protcol and sectype are valid
24323034Sdougm 	 */
24333034Sdougm 
24343034Sdougm 	if (sa_proto_valid_space(protocol, sectype) == 0) {
24354653Sdougm 		(void) printf(gettext("Option space \"%s\" not valid "
24364653Sdougm 		    "for protocol.\n"), sectype);
24374653Sdougm 		return (SA_INVALID_SECURITY);
24383034Sdougm 	}
24393034Sdougm 
24403910Sdougm 	group = sa_get_group(handle, groupname);
24413034Sdougm 	if (group != NULL) {
24424653Sdougm 		sa_share_t share = NULL;
24434653Sdougm 		if (sharepath != NULL) {
24444653Sdougm 			share = sa_get_share(group, sharepath);
24454653Sdougm 			if (share == NULL) {
24464653Sdougm 				(void) printf(gettext(
24474653Sdougm 				    "Share does not exist in group %s\n"),
24484653Sdougm 				    groupname, sharepath);
24494653Sdougm 				ret = SA_NO_SUCH_PATH;
2450*5089Sdougm 			} else {
2451*5089Sdougm 				/* if ZFS and OK, then only group */
2452*5089Sdougm 				ret = zfscheck(group, share);
2453*5089Sdougm 				if (ret == SA_OK &&
2454*5089Sdougm 				    sa_group_is_zfs(group))
2455*5089Sdougm 					share = NULL;
2456*5089Sdougm 				if (ret == SA_NOT_ALLOWED)
2457*5089Sdougm 					(void) printf(gettext(
2458*5089Sdougm 					    "Properties on ZFS group shares "
2459*5089Sdougm 					    "not supported: %s\n"), sharepath);
24604653Sdougm 			}
24613034Sdougm 		}
24624653Sdougm 		if (ret == SA_OK) {
24634653Sdougm 			/* group must exist */
24644653Sdougm 			ret = valid_options(optlist, protocol,
24654653Sdougm 			    share == NULL ? group : share, sectype);
24664653Sdougm 			if (ret == SA_OK && !dryrun) {
24674653Sdougm 				if (share != NULL)
24684653Sdougm 					change = add_security(share, sectype,
24694653Sdougm 					    optlist, protocol, &ret);
24704653Sdougm 				else
24714653Sdougm 					change = add_security(group, sectype,
24724653Sdougm 					    optlist, protocol, &ret);
24734653Sdougm 				if (ret != SA_OK)
24744653Sdougm 					(void) printf(gettext(
24754653Sdougm 					    "Could not set property: %s\n"),
24764653Sdougm 					    sa_errorstr(ret));
24774653Sdougm 			}
24784653Sdougm 			if (ret == SA_OK && change)
24794653Sdougm 				worklist = add_list(worklist, group, share);
24803034Sdougm 		}
24814653Sdougm 		free_opt(optlist);
24823034Sdougm 	} else {
24833034Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
24843034Sdougm 		ret = SA_NO_SUCH_GROUP;
24853034Sdougm 	}
24863034Sdougm 	/*
24873034Sdougm 	 * we have a group and potentially legal additions
24883034Sdougm 	 */
24893034Sdougm 
24904653Sdougm 	/* Commit to configuration if not a dryrun */
24913034Sdougm 	if (!dryrun && ret == 0) {
24924653Sdougm 		if (change && worklist != NULL) {
24934653Sdougm 			/* properties changed, so update all shares */
24944653Sdougm 			(void) enable_all_groups(handle, worklist, 0, 0,
24954653Sdougm 			    protocol);
24964653Sdougm 		}
24974653Sdougm 		ret = sa_update_config(handle);
24983034Sdougm 	}
24993034Sdougm 	if (worklist != NULL)
25004653Sdougm 		free_list(worklist);
25013034Sdougm 	return (ret);
25023034Sdougm }
25033034Sdougm 
25043034Sdougm /*
25053034Sdougm  * sa_set(flags, argc, argv)
25063034Sdougm  *
25073034Sdougm  * Implements the set subcommand. It keys off of -S to determine which
25083034Sdougm  * set of operations to actually do.
25093034Sdougm  */
25103034Sdougm 
25113034Sdougm int
25123910Sdougm sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
25133034Sdougm {
25143034Sdougm 	char *groupname;
25153034Sdougm 	int verbose = 0;
25163034Sdougm 	int dryrun = 0;
25173034Sdougm 	int c;
25183034Sdougm 	char *protocol = NULL;
25193034Sdougm 	int ret = SA_OK;
25203034Sdougm 	struct options *optlist = NULL;
25213034Sdougm 	char *sharepath = NULL;
25223034Sdougm 	char *optset = NULL;
25233034Sdougm 	int auth;
25243034Sdougm 
25253034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
25264653Sdougm 		switch (c) {
25274653Sdougm 		case 'v':
25284653Sdougm 			verbose++;
25294653Sdougm 			break;
25304653Sdougm 		case 'n':
25314653Sdougm 			dryrun++;
25324653Sdougm 			break;
25334653Sdougm 		case 'P':
25344653Sdougm 			protocol = optarg;
25354653Sdougm 			if (!sa_valid_protocol(protocol)) {
25364653Sdougm 				(void) printf(gettext(
25374653Sdougm 				    "Invalid protocol specified: %s\n"),
25384653Sdougm 				    protocol);
25394653Sdougm 				return (SA_INVALID_PROTOCOL);
25404653Sdougm 			}
25414653Sdougm 			break;
25424653Sdougm 		case 'p':
25434653Sdougm 			ret = add_opt(&optlist, optarg, 0);
25444653Sdougm 			switch (ret) {
25454653Sdougm 			case OPT_ADD_SYNTAX:
25464653Sdougm 				(void) printf(gettext("Property syntax error:"
25474653Sdougm 				    " %s\n"), optarg);
25484653Sdougm 				return (SA_SYNTAX_ERR);
25494653Sdougm 			case OPT_ADD_MEMORY:
25504653Sdougm 				(void) printf(gettext("No memory to set "
25514653Sdougm 				    "property: %s\n"), optarg);
25524653Sdougm 				return (SA_NO_MEMORY);
25534653Sdougm 			default:
25544653Sdougm 				break;
25554653Sdougm 			}
25564653Sdougm 			break;
25574653Sdougm 		case 's':
25584653Sdougm 			sharepath = optarg;
25594653Sdougm 			break;
25604653Sdougm 		case 'S':
25614653Sdougm 			optset = optarg;
25624653Sdougm 			break;
25634653Sdougm 		default:
25644653Sdougm 		case 'h':
25654653Sdougm 		case '?':
25664653Sdougm 			(void) printf(gettext("usage: %s\n"),
25674653Sdougm 			    sa_get_usage(USAGE_SET));
25684653Sdougm 			return (SA_OK);
25693034Sdougm 		}
25703034Sdougm 	}
25713034Sdougm 
25723034Sdougm 	if (optlist != NULL)
25734653Sdougm 		ret = chk_opt(optlist, optset != NULL, protocol);
25743034Sdougm 
25753034Sdougm 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
25764653Sdougm 	    protocol == NULL || ret != OPT_ADD_OK) {
25774653Sdougm 		char *sep = "\t";
25784653Sdougm 
25794653Sdougm 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
25804653Sdougm 		if (optind >= argc) {
25814653Sdougm 			(void) printf(gettext("%sgroup must be specified"),
25824653Sdougm 			    sep);
25834653Sdougm 			sep = ", ";
25844653Sdougm 		}
25854653Sdougm 		if (optlist == NULL) {
25864653Sdougm 			(void) printf(gettext("%sat least one property must be"
25874653Sdougm 			    " specified"), sep);
25884653Sdougm 			sep = ", ";
25894653Sdougm 		}
25904653Sdougm 		if (protocol == NULL) {
25914653Sdougm 			(void) printf(gettext("%sprotocol must be specified"),
25924653Sdougm 			    sep);
25934653Sdougm 			sep = ", ";
25944653Sdougm 		}
25954653Sdougm 		(void) printf("\n");
25964653Sdougm 		ret = SA_SYNTAX_ERR;
25973034Sdougm 	} else {
25983034Sdougm 		/*
2599*5089Sdougm 		 * Group already exists so we can proceed after a few
2600*5089Sdougm 		 * additional checks related to ZFS handling.
26013034Sdougm 		 */
26023034Sdougm 
26034653Sdougm 		groupname = argv[optind];
2604*5089Sdougm 		if (strcmp(groupname, "zfs") == 0) {
2605*5089Sdougm 			(void) printf(gettext("Changing properties for group "
2606*5089Sdougm 			    "\"zfs\" not allowed\n"));
2607*5089Sdougm 			return (SA_NOT_ALLOWED);
2608*5089Sdougm 		}
2609*5089Sdougm 
26104653Sdougm 		auth = check_authorizations(groupname, flags);
26114653Sdougm 		if (optset == NULL)
26124653Sdougm 			ret = basic_set(handle, groupname, optlist, protocol,
26134653Sdougm 			    sharepath, dryrun);
26144653Sdougm 		else
26154653Sdougm 			ret = space_set(handle, groupname, optlist, protocol,
26164653Sdougm 			    sharepath, dryrun, optset);
26174653Sdougm 		if (dryrun && ret == SA_OK && !auth && verbose) {
26184653Sdougm 			(void) printf(gettext("Command would fail: %s\n"),
26194653Sdougm 			    sa_errorstr(SA_NO_PERMISSION));
26204653Sdougm 		}
26213034Sdougm 	}
26223034Sdougm 	return (ret);
26233034Sdougm }
26243034Sdougm 
26253034Sdougm /*
26263034Sdougm  * remove_options(group, optlist, proto, *err)
26273034Sdougm  *
26284653Sdougm  * Helper function to actually remove options from a group after all
26293034Sdougm  * preprocessing is done.
26303034Sdougm  */
26313034Sdougm 
26323034Sdougm static int
26333034Sdougm remove_options(sa_group_t group, struct options *optlist,
26343034Sdougm 		char *proto, int *err)
26353034Sdougm {
26363034Sdougm 	struct options *cur;
26373034Sdougm 	sa_optionset_t optionset;
26383034Sdougm 	sa_property_t prop;
26393034Sdougm 	int change = 0;
26403034Sdougm 	int ret = SA_OK;
26413034Sdougm 
26423034Sdougm 	optionset = sa_get_optionset(group, proto);
26433034Sdougm 	if (optionset != NULL) {
26444653Sdougm 		for (cur = optlist; cur != NULL; cur = cur->next) {
26454653Sdougm 			prop = sa_get_property(optionset, cur->optname);
26464653Sdougm 			if (prop != NULL) {
26474653Sdougm 				ret = sa_remove_property(prop);
26484653Sdougm 				if (ret != SA_OK)
26494653Sdougm 					break;
26504653Sdougm 				change = 1;
26514653Sdougm 			}
26523034Sdougm 		}
26533034Sdougm 	}
26543034Sdougm 	if (ret == SA_OK && change)
26554653Sdougm 		ret = sa_commit_properties(optionset, 0);
26563034Sdougm 
26573034Sdougm 	if (err != NULL)
26584653Sdougm 		*err = ret;
26593034Sdougm 	return (change);
26603034Sdougm }
26613034Sdougm 
26623034Sdougm /*
26633034Sdougm  * valid_unset(group, optlist, proto)
26643034Sdougm  *
26653034Sdougm  * Sanity check the optlist to make sure they can be removed. Issue an
26663034Sdougm  * error if a property doesn't exist.
26673034Sdougm  */
26683034Sdougm 
26693034Sdougm static int
26703034Sdougm valid_unset(sa_group_t group, struct options *optlist, char *proto)
26713034Sdougm {
26723034Sdougm 	struct options *cur;
26733034Sdougm 	sa_optionset_t optionset;
26743034Sdougm 	sa_property_t prop;
26753034Sdougm 	int ret = SA_OK;
26763034Sdougm 
26773034Sdougm 	optionset = sa_get_optionset(group, proto);
26783034Sdougm 	if (optionset != NULL) {
26794653Sdougm 		for (cur = optlist; cur != NULL; cur = cur->next) {
26804653Sdougm 			prop = sa_get_property(optionset, cur->optname);
26814653Sdougm 			if (prop == NULL) {
26824653Sdougm 				(void) printf(gettext(
26834653Sdougm 				    "Could not unset property %s: not set\n"),
26844653Sdougm 				    cur->optname);
26854653Sdougm 				ret = SA_NO_SUCH_PROP;
26864653Sdougm 			}
26873034Sdougm 		}
26883034Sdougm 	}
26893034Sdougm 	return (ret);
26903034Sdougm }
26913034Sdougm 
26923034Sdougm /*
26933034Sdougm  * valid_unset_security(group, optlist, proto)
26943034Sdougm  *
26953034Sdougm  * Sanity check the optlist to make sure they can be removed. Issue an
26963034Sdougm  * error if a property doesn't exist.
26973034Sdougm  */
26983034Sdougm 
26993034Sdougm static int
27003034Sdougm valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
27013034Sdougm 	    char *sectype)
27023034Sdougm {
27033034Sdougm 	struct options *cur;
27043034Sdougm 	sa_security_t security;
27053034Sdougm 	sa_property_t prop;
27063034Sdougm 	int ret = SA_OK;
27073034Sdougm 	char *sec;
27083034Sdougm 
27093034Sdougm 	sec = sa_proto_space_alias(proto, sectype);
27103034Sdougm 	security = sa_get_security(group, sec, proto);
27113034Sdougm 	if (security != NULL) {
27124653Sdougm 		for (cur = optlist; cur != NULL; cur = cur->next) {
27134653Sdougm 			prop = sa_get_property(security, cur->optname);
27144653Sdougm 			if (prop == NULL) {
27154653Sdougm 				(void) printf(gettext(
27164653Sdougm 				    "Could not unset property %s: not set\n"),
27174653Sdougm 				    cur->optname);
27184653Sdougm 				ret = SA_NO_SUCH_PROP;
27194653Sdougm 			}
27203034Sdougm 		}
27213034Sdougm 	} else {
27224653Sdougm 		(void) printf(gettext(
27234653Sdougm 		    "Could not unset %s: space not defined\n"), sectype);
27244653Sdougm 		ret = SA_NO_SUCH_SECURITY;
27253034Sdougm 	}
27263034Sdougm 	if (sec != NULL)
27274653Sdougm 		sa_free_attr_string(sec);
27283034Sdougm 	return (ret);
27293034Sdougm }
27303034Sdougm 
27313034Sdougm /*
27323034Sdougm  * remove_security(group, optlist, proto)
27333034Sdougm  *
27343034Sdougm  * Remove the properties since they were checked as valid.
27353034Sdougm  */
27363034Sdougm 
27373034Sdougm static int
27383034Sdougm remove_security(sa_group_t group, char *sectype,
27393034Sdougm 		struct options *optlist, char *proto, int *err)
27403034Sdougm {
27413034Sdougm 	sa_security_t security;
27423034Sdougm 	int ret = SA_OK;
27433034Sdougm 	int change = 0;
27443034Sdougm 
27453034Sdougm 	sectype = sa_proto_space_alias(proto, sectype);
27463034Sdougm 	security = sa_get_security(group, sectype, proto);
27473034Sdougm 	if (sectype != NULL)
27484653Sdougm 		sa_free_attr_string(sectype);
27493034Sdougm 
27503034Sdougm 	if (security != NULL) {
27514653Sdougm 		while (optlist != NULL) {
27524653Sdougm 			sa_property_t prop;
27534653Sdougm 			prop = sa_get_property(security, optlist->optname);
27544653Sdougm 			if (prop != NULL) {
27554653Sdougm 				ret = sa_remove_property(prop);
27564653Sdougm 				if (ret != SA_OK)
27574653Sdougm 					break;
27584653Sdougm 				change = 1;
27594653Sdougm 			}
27604653Sdougm 			optlist = optlist->next;
27613034Sdougm 		}
27623034Sdougm 		/*
27633034Sdougm 		 * when done, properties may have all been removed but
27643034Sdougm 		 * we need to keep the security type itself until
27653034Sdougm 		 * explicitly removed.
27663034Sdougm 		 */
27674653Sdougm 		if (ret == SA_OK && change)
27684653Sdougm 			ret = sa_commit_properties(security, 0);
27693034Sdougm 	} else {
27704653Sdougm 		ret = SA_NO_SUCH_PROP;
27713034Sdougm 	}
27723034Sdougm 	if (err != NULL)
27734653Sdougm 		*err = ret;
27743034Sdougm 	return (change);
27753034Sdougm }
27763034Sdougm 
27773034Sdougm /*
27783034Sdougm  * basic_unset(groupname, optlist, protocol, sharepath, dryrun)
27793034Sdougm  *
27804653Sdougm  * Unset non-named optionset properties.
27813034Sdougm  */
27823034Sdougm 
27833034Sdougm static int
27843910Sdougm basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
27853910Sdougm 		char *protocol,	char *sharepath, int dryrun)
27863034Sdougm {
27873034Sdougm 	sa_group_t group;
27883034Sdougm 	int ret = SA_OK;
27893034Sdougm 	int change = 0;
27903034Sdougm 	struct list *worklist = NULL;
27914653Sdougm 	sa_share_t share = NULL;
27923034Sdougm 
27933910Sdougm 	group = sa_get_group(handle, groupname);
27944653Sdougm 	if (group == NULL)
27954653Sdougm 		return (ret);
27964653Sdougm 
27974653Sdougm 	if (sharepath != NULL) {
27983034Sdougm 		share = sa_get_share(group, sharepath);
27993034Sdougm 		if (share == NULL) {
28004653Sdougm 			(void) printf(gettext(
28014653Sdougm 			    "Share does not exist in group %s\n"),
28024653Sdougm 			    groupname, sharepath);
28034653Sdougm 			ret = SA_NO_SUCH_PATH;
28043034Sdougm 		}
28054653Sdougm 	}
28064653Sdougm 	if (ret == SA_OK) {
28073034Sdougm 		/* group must exist */
28083034Sdougm 		ret = valid_unset(share != NULL ? share : group,
28094653Sdougm 		    optlist, protocol);
28103034Sdougm 		if (ret == SA_OK && !dryrun) {
28114653Sdougm 			if (share != NULL) {
28124653Sdougm 				sa_optionset_t optionset;
28134653Sdougm 				sa_property_t prop;
28144653Sdougm 				change |= remove_options(share, optlist,
28154653Sdougm 				    protocol, &ret);
28164653Sdougm 				/*
28174653Sdougm 				 * If a share optionset is
28184653Sdougm 				 * empty, remove it.
28194653Sdougm 				 */
28204653Sdougm 				optionset = sa_get_optionset((sa_share_t)share,
28214653Sdougm 				    protocol);
28224653Sdougm 				if (optionset != NULL) {
28234653Sdougm 					prop = sa_get_property(optionset, NULL);
28244653Sdougm 					if (prop == NULL)
28254653Sdougm 						(void) sa_destroy_optionset(
28264653Sdougm 						    optionset);
28274653Sdougm 				}
28284653Sdougm 			} else {
28294653Sdougm 				change |= remove_options(group,
28304653Sdougm 				    optlist, protocol, &ret);
28313034Sdougm 			}
28324653Sdougm 			if (ret == SA_OK && change)
28334653Sdougm 				worklist = add_list(worklist, group,
28344653Sdougm 				    share);
28354653Sdougm 			if (ret != SA_OK)
28364653Sdougm 				(void) printf(gettext(
28374653Sdougm 				    "Could not remove properties: "
28384653Sdougm 				    "%s\n"), sa_errorstr(ret));
28393034Sdougm 		}
28404653Sdougm 	} else {
28414653Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"),
28424653Sdougm 		    groupname);
28433034Sdougm 		ret = SA_NO_SUCH_GROUP;
28443034Sdougm 	}
28454653Sdougm 	free_opt(optlist);
28463034Sdougm 
28473034Sdougm 	/*
28484653Sdougm 	 * We have a group and potentially legal additions
28494653Sdougm 	 *
28504653Sdougm 	 * Commit to configuration if not a dryrun
28513034Sdougm 	 */
28523034Sdougm 	if (!dryrun && ret == SA_OK) {
28534653Sdougm 		if (change && worklist != NULL) {
28544653Sdougm 			/* properties changed, so update all shares */
28554653Sdougm 			(void) enable_all_groups(handle, worklist, 0, 0,
28564653Sdougm 			    protocol);
28574653Sdougm 		}
28583034Sdougm 	}
28593034Sdougm 	if (worklist != NULL)
28604653Sdougm 		free_list(worklist);
28613034Sdougm 	return (ret);
28623034Sdougm }
28633034Sdougm 
28643034Sdougm /*
28653034Sdougm  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
28663034Sdougm  *
28674653Sdougm  * Unset named optionset properties.
28683034Sdougm  */
28693034Sdougm static int
28703910Sdougm space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
28713910Sdougm 		char *protocol, char *sharepath, int dryrun, char *sectype)
28723034Sdougm {
28733034Sdougm 	sa_group_t group;
28743034Sdougm 	int ret = SA_OK;
28753034Sdougm 	int change = 0;
28763034Sdougm 	struct list *worklist = NULL;
28774653Sdougm 	sa_share_t share = NULL;
28783034Sdougm 
28793910Sdougm 	group = sa_get_group(handle, groupname);
28804653Sdougm 	if (group == NULL) {
28814653Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
28824653Sdougm 		return (SA_NO_SUCH_GROUP);
28834653Sdougm 	}
28844653Sdougm 	if (sharepath != NULL) {
28853034Sdougm 		share = sa_get_share(group, sharepath);
28863034Sdougm 		if (share == NULL) {
28874653Sdougm 			(void) printf(gettext(
28884653Sdougm 			    "Share does not exist in group %s\n"),
28894653Sdougm 			    groupname, sharepath);
28904653Sdougm 			return (SA_NO_SUCH_PATH);
28913034Sdougm 		}
28924653Sdougm 	}
28934653Sdougm 	ret = valid_unset_security(share != NULL ? share : group, optlist,
28944653Sdougm 	    protocol, sectype);
28954653Sdougm 
28964653Sdougm 	if (ret == SA_OK && !dryrun) {
28974653Sdougm 		if (optlist != NULL) {
28983034Sdougm 			if (share != NULL) {
28994653Sdougm 				sa_security_t optionset;
29004653Sdougm 				sa_property_t prop;
29014653Sdougm 				change = remove_security(share,
29024653Sdougm 				    sectype, optlist, protocol, &ret);
29034653Sdougm 
29044653Sdougm 				/* If a share security is empty, remove it */
29054653Sdougm 				optionset = sa_get_security((sa_group_t)share,
29064653Sdougm 				    sectype, protocol);
29074653Sdougm 				if (optionset != NULL) {
29084653Sdougm 					prop = sa_get_property(optionset,
29094653Sdougm 					    NULL);
29104653Sdougm 					if (prop == NULL)
29114653Sdougm 						ret = sa_destroy_security(
29124653Sdougm 						    optionset);
29134653Sdougm 				}
29143034Sdougm 			} else {
29154653Sdougm 				change = remove_security(group, sectype,
29164653Sdougm 				    optlist, protocol, &ret);
29173034Sdougm 			}
29184653Sdougm 		} else {
29193034Sdougm 			sa_security_t security;
29203034Sdougm 			char *sec;
29213034Sdougm 			sec = sa_proto_space_alias(protocol, sectype);
29223034Sdougm 			security = sa_get_security(group, sec, protocol);
29233034Sdougm 			if (sec != NULL)
29244653Sdougm 				sa_free_attr_string(sec);
29253034Sdougm 			if (security != NULL) {
29264653Sdougm 				ret = sa_destroy_security(security);
29274653Sdougm 				if (ret == SA_OK)
29284653Sdougm 					change = 1;
29293034Sdougm 			} else {
29304653Sdougm 				ret = SA_NO_SUCH_PROP;
29313034Sdougm 			}
29324653Sdougm 		}
29334653Sdougm 		if (ret != SA_OK)
29343034Sdougm 			(void) printf(gettext("Could not unset property: %s\n"),
29354653Sdougm 			    sa_errorstr(ret));
29363034Sdougm 	}
29374653Sdougm 
29384653Sdougm 	if (ret == SA_OK && change)
29394653Sdougm 		worklist = add_list(worklist, group, 0);
29404653Sdougm 
29413034Sdougm 	free_opt(optlist);
29423034Sdougm 	/*
29434653Sdougm 	 * We have a group and potentially legal additions
29443034Sdougm 	 */
29453034Sdougm 
29464653Sdougm 	/* Commit to configuration if not a dryrun */
29473034Sdougm 	if (!dryrun && ret == 0) {
29483034Sdougm 		/* properties changed, so update all shares */
29494653Sdougm 		if (change && worklist != NULL)
29504653Sdougm 			(void) enable_all_groups(handle, worklist, 0, 0,
29514653Sdougm 			    protocol);
29524653Sdougm 		ret = sa_update_config(handle);
29533034Sdougm 	}
29543034Sdougm 	if (worklist != NULL)
29554653Sdougm 		free_list(worklist);
29563034Sdougm 	return (ret);
29573034Sdougm }
29583034Sdougm 
29593034Sdougm /*
29603034Sdougm  * sa_unset(flags, argc, argv)
29613034Sdougm  *
29624653Sdougm  * Implements the unset subcommand. Parsing done here and then basic
29633034Sdougm  * or space versions of the real code are called.
29643034Sdougm  */
29653034Sdougm 
29663034Sdougm int
29673910Sdougm sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
29683034Sdougm {
29693034Sdougm 	char *groupname;
29703034Sdougm 	int verbose = 0;
29713034Sdougm 	int dryrun = 0;
29723034Sdougm 	int c;
29733034Sdougm 	char *protocol = NULL;
29743034Sdougm 	int ret = SA_OK;
29753034Sdougm 	struct options *optlist = NULL;
29763034Sdougm 	char *sharepath = NULL;
29773034Sdougm 	char *optset = NULL;
29783034Sdougm 	int auth;
29793034Sdougm 
29803034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
29814653Sdougm 		switch (c) {
29824653Sdougm 		case 'v':
29834653Sdougm 			verbose++;
29844653Sdougm 			break;
29854653Sdougm 		case 'n':
29864653Sdougm 			dryrun++;
29874653Sdougm 			break;
29884653Sdougm 		case 'P':
29894653Sdougm 			protocol = optarg;
29904653Sdougm 			if (!sa_valid_protocol(protocol)) {
29914653Sdougm 				(void) printf(gettext(
29924653Sdougm 				    "Invalid protocol specified: %s\n"),
29934653Sdougm 				    protocol);
29944653Sdougm 				return (SA_INVALID_PROTOCOL);
29954653Sdougm 			}
29964653Sdougm 			break;
29974653Sdougm 		case 'p':
29984653Sdougm 			ret = add_opt(&optlist, optarg, 1);
29994653Sdougm 			switch (ret) {
30004653Sdougm 			case OPT_ADD_SYNTAX:
30014653Sdougm 				(void) printf(gettext("Property syntax error "
30024653Sdougm 				    "for property %s\n"), optarg);
30034653Sdougm 				return (SA_SYNTAX_ERR);
30044653Sdougm 
30054653Sdougm 			case OPT_ADD_PROPERTY:
30064653Sdougm 				(void) printf(gettext("Properties need to be "
30074653Sdougm 				    "set with set command: %s\n"), optarg);
30084653Sdougm 				return (SA_SYNTAX_ERR);
30094653Sdougm 
30104653Sdougm 			default:
30114653Sdougm 				break;
30124653Sdougm 			}
30134653Sdougm 			break;
30144653Sdougm 		case 's':
30154653Sdougm 			sharepath = optarg;
30164653Sdougm 			break;
30174653Sdougm 		case 'S':
30184653Sdougm 			optset = optarg;
30194653Sdougm 			break;
30204653Sdougm 		default:
30214653Sdougm 		case 'h':
30224653Sdougm 		case '?':
30234653Sdougm 			(void) printf(gettext("usage: %s\n"),
30244653Sdougm 			    sa_get_usage(USAGE_UNSET));
30254653Sdougm 			return (SA_OK);
30263034Sdougm 		}
30273034Sdougm 	}
30283034Sdougm 
30293034Sdougm 	if (optlist != NULL)
30304653Sdougm 		ret = chk_opt(optlist, optset != NULL, protocol);
30313034Sdougm 
30323034Sdougm 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
30333034Sdougm 	    protocol == NULL) {
30344653Sdougm 		char *sep = "\t";
30354653Sdougm 		(void) printf(gettext("usage: %s\n"),
30364653Sdougm 		    sa_get_usage(USAGE_UNSET));
30374653Sdougm 		if (optind >= argc) {
30384653Sdougm 			(void) printf(gettext("%sgroup must be specified"),
30394653Sdougm 			    sep);
30404653Sdougm 			sep = ", ";
30414653Sdougm 		}
30424653Sdougm 		if (optlist == NULL) {
30434653Sdougm 			(void) printf(gettext("%sat least one property must "
30444653Sdougm 			    "be specified"), sep);
30454653Sdougm 			sep = ", ";
30464653Sdougm 		}
30474653Sdougm 		if (protocol == NULL) {
30484653Sdougm 			(void) printf(gettext("%sprotocol must be specified"),
30494653Sdougm 			    sep);
30504653Sdougm 			sep = ", ";
30514653Sdougm 		}
30524653Sdougm 		(void) printf("\n");
30534653Sdougm 		ret = SA_SYNTAX_ERR;
30543034Sdougm 	} else {
30553034Sdougm 
30563034Sdougm 		/*
30574653Sdougm 		 * If a group already exists, we can only add a new
30583034Sdougm 		 * protocol to it and not create a new one or add the
30593034Sdougm 		 * same protocol again.
30603034Sdougm 		 */
30613034Sdougm 
30624653Sdougm 		groupname = argv[optind];
30634653Sdougm 		auth = check_authorizations(groupname, flags);
30644653Sdougm 		if (optset == NULL)
30654653Sdougm 			ret = basic_unset(handle, groupname, optlist, protocol,
30664653Sdougm 			    sharepath, dryrun);
30674653Sdougm 		else
30684653Sdougm 			ret = space_unset(handle, groupname, optlist, protocol,
30694653Sdougm 			    sharepath, dryrun, optset);
30704653Sdougm 
30714653Sdougm 		if (dryrun && ret == SA_OK && !auth && verbose)
30724653Sdougm 			(void) printf(gettext("Command would fail: %s\n"),
30734653Sdougm 			    sa_errorstr(SA_NO_PERMISSION));
30743034Sdougm 	}
30753034Sdougm 	return (ret);
30763034Sdougm }
30773034Sdougm 
30783034Sdougm /*
30793034Sdougm  * sa_enable_group(flags, argc, argv)
30803034Sdougm  *
30813034Sdougm  * Implements the enable subcommand
30823034Sdougm  */
30833034Sdougm 
30843034Sdougm int
30853910Sdougm sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
30863034Sdougm {
30873034Sdougm 	int verbose = 0;
30883034Sdougm 	int dryrun = 0;
30893034Sdougm 	int all = 0;
30903034Sdougm 	int c;
30913034Sdougm 	int ret = SA_OK;
30923034Sdougm 	char *protocol = NULL;
30933034Sdougm 	char *state;
30943034Sdougm 	struct list *worklist = NULL;
30953034Sdougm 	int auth = 1;
30964653Sdougm 	sa_group_t group;
30973034Sdougm 
30983034Sdougm 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
30994653Sdougm 		switch (c) {
31004653Sdougm 		case 'a':
31014653Sdougm 			all = 1;
31024653Sdougm 			break;
31034653Sdougm 		case 'n':
31044653Sdougm 			dryrun++;
31054653Sdougm 			break;
31064653Sdougm 		case 'P':
31074653Sdougm 			protocol = optarg;
31084653Sdougm 			if (!sa_valid_protocol(protocol)) {
31094653Sdougm 				(void) printf(gettext(
31104653Sdougm 				    "Invalid protocol specified: %s\n"),
31113034Sdougm 				    protocol);
31124653Sdougm 				return (SA_INVALID_PROTOCOL);
31134653Sdougm 			}
31144653Sdougm 			break;
31154653Sdougm 		case 'v':
31164653Sdougm 			verbose++;
31174653Sdougm 			break;
31184653Sdougm 		default:
31194653Sdougm 		case 'h':
31204653Sdougm 		case '?':
31214653Sdougm 			(void) printf(gettext("usage: %s\n"),
31224653Sdougm 			    sa_get_usage(USAGE_ENABLE));
31234653Sdougm 			return (0);
31243034Sdougm 		}
31253034Sdougm 	}
31263034Sdougm 
31273034Sdougm 	if (optind == argc && !all) {
31284653Sdougm 		(void) printf(gettext("usage: %s\n"),
31294653Sdougm 		    sa_get_usage(USAGE_ENABLE));
31304653Sdougm 		(void) printf(gettext("\tmust specify group\n"));
31314653Sdougm 		return (SA_NO_SUCH_PATH);
31324653Sdougm 	}
31334653Sdougm 	if (!all) {
31343034Sdougm 		while (optind < argc) {
31354653Sdougm 			group = sa_get_group(handle, argv[optind]);
31364653Sdougm 			if (group != NULL) {
31374653Sdougm 				auth &= check_authorizations(argv[optind],
31384653Sdougm 				    flags);
31394653Sdougm 				state = sa_get_group_attr(group, "state");
31404653Sdougm 				if (state != NULL &&
31414653Sdougm 				    strcmp(state, "enabled") == 0) {
31424653Sdougm 					/* already enabled */
31434653Sdougm 					if (verbose)
31444653Sdougm 						(void) printf(gettext(
31454653Sdougm 						    "Group \"%s\" is already "
31464653Sdougm 						    "enabled\n"),
31474653Sdougm 						    argv[optind]);
31484653Sdougm 					ret = SA_BUSY; /* already enabled */
31494653Sdougm 				} else {
31504653Sdougm 					worklist = add_list(worklist, group,
31514653Sdougm 					    0);
31524653Sdougm 					if (verbose)
31534653Sdougm 						(void) printf(gettext(
31544653Sdougm 						    "Enabling group \"%s\"\n"),
31554653Sdougm 						    argv[optind]);
31564653Sdougm 				}
31574653Sdougm 				if (state != NULL)
31584653Sdougm 					sa_free_attr_string(state);
31593034Sdougm 			} else {
31604653Sdougm 				ret = SA_NO_SUCH_GROUP;
31613034Sdougm 			}
31624653Sdougm 			optind++;
31633034Sdougm 		}
31644653Sdougm 	} else {
31654653Sdougm 		for (group = sa_get_group(handle, NULL);
31664653Sdougm 		    group != NULL;
31673034Sdougm 		    group = sa_get_next_group(group)) {
31684653Sdougm 			worklist = add_list(worklist, group, 0);
31693034Sdougm 		}
31704653Sdougm 	}
31714653Sdougm 	if (!dryrun && ret == SA_OK)
31723910Sdougm 		ret = enable_all_groups(handle, worklist, 1, 0, NULL);
31734653Sdougm 
31744653Sdougm 	if (ret != SA_OK && ret != SA_BUSY)
31753034Sdougm 		(void) printf(gettext("Could not enable group: %s\n"),
31764653Sdougm 		    sa_errorstr(ret));
31774653Sdougm 	if (ret == SA_BUSY)
31783034Sdougm 		ret = SA_OK;
31794653Sdougm 
31803034Sdougm 	if (worklist != NULL)
31814653Sdougm 		free_list(worklist);
31823034Sdougm 	if (dryrun && ret == SA_OK && !auth && verbose) {
31834653Sdougm 		(void) printf(gettext("Command would fail: %s\n"),
31844653Sdougm 		    sa_errorstr(SA_NO_PERMISSION));
31853034Sdougm 	}
31863034Sdougm 	return (ret);
31873034Sdougm }
31883034Sdougm 
31893034Sdougm /*
31903034Sdougm  * disable_group(group, setstate)
31913034Sdougm  *
31924653Sdougm  * Disable all the shares in the specified group honoring the setstate
31933034Sdougm  * argument. This is a helper for disable_all_groups in order to
31943034Sdougm  * simplify regular and subgroup (zfs) disabling. Group has already
31953034Sdougm  * been checked for non-NULL.
31963034Sdougm  */
31973034Sdougm 
31983034Sdougm static int
31993034Sdougm disable_group(sa_group_t group)
32003034Sdougm {
32013034Sdougm 	sa_share_t share;
32023034Sdougm 	int ret = SA_OK;
32033034Sdougm 
32043034Sdougm 	for (share = sa_get_share(group, NULL);
32053034Sdougm 	    share != NULL && ret == SA_OK;
32063034Sdougm 	    share = sa_get_next_share(share)) {
32074653Sdougm 		ret = sa_disable_share(share, NULL);
32084653Sdougm 		if (ret == SA_NO_SUCH_PATH) {
32094653Sdougm 			/*
32104653Sdougm 			 * this is OK since the path is gone. we can't
32114653Sdougm 			 * re-share it anyway so no error.
32124653Sdougm 			 */
32134653Sdougm 			ret = SA_OK;
32144653Sdougm 		}
32153034Sdougm 	}
32163034Sdougm 	return (ret);
32173034Sdougm }
32183034Sdougm 
32193034Sdougm 
32203034Sdougm /*
32213034Sdougm  * disable_all_groups(work, setstate)
32223034Sdougm  *
32233034Sdougm  * helper function that disables the shares in the list of groups
32243034Sdougm  * provided. It optionally marks the group as disabled. Used by both
32253034Sdougm  * enable and start subcommands.
32263034Sdougm  */
32273034Sdougm 
32283034Sdougm static int
32293910Sdougm disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
32303034Sdougm {
32313034Sdougm 	int ret = SA_OK;
32323034Sdougm 	sa_group_t subgroup, group;
32333034Sdougm 
32343034Sdougm 	while (work != NULL && ret == SA_OK) {
32354653Sdougm 		group = (sa_group_t)work->item;
32364653Sdougm 		if (setstate)
32374653Sdougm 			ret = sa_set_group_attr(group, "state", "disabled");
32384653Sdougm 		if (ret == SA_OK) {
32394653Sdougm 			char *name;
32404653Sdougm 			name = sa_get_group_attr(group, "name");
32414653Sdougm 			if (name != NULL && strcmp(name, "zfs") == 0) {
32424653Sdougm 				/* need to get the sub-groups for stopping */
32434653Sdougm 				for (subgroup = sa_get_sub_group(group);
32444653Sdougm 				    subgroup != NULL;
32454653Sdougm 				    subgroup = sa_get_next_group(subgroup)) {
32464653Sdougm 					ret = disable_group(subgroup);
32474653Sdougm 				}
32484653Sdougm 			} else {
32494653Sdougm 				ret = disable_group(group);
32504653Sdougm 			}
32514653Sdougm 			/*
32524653Sdougm 			 * We don't want to "disable" since it won't come
32534653Sdougm 			 * up after a reboot.  The SMF framework should do
32544653Sdougm 			 * the right thing. On enable we do want to do
32554653Sdougm 			 * something.
32564653Sdougm 			 */
32573034Sdougm 		}
32584653Sdougm 		work = work->next;
32593034Sdougm 	}
32603034Sdougm 	if (ret == SA_OK)
32614653Sdougm 		ret = sa_update_config(handle);
32623034Sdougm 	return (ret);
32633034Sdougm }
32643034Sdougm 
32653034Sdougm /*
32663034Sdougm  * sa_disable_group(flags, argc, argv)
32673034Sdougm  *
32683034Sdougm  * Implements the disable subcommand
32693034Sdougm  */
32703034Sdougm 
32713034Sdougm int
32723910Sdougm sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
32733034Sdougm {
32743034Sdougm 	int verbose = 0;
32753034Sdougm 	int dryrun = 0;
32763034Sdougm 	int all = 0;
32773034Sdougm 	int c;
32783034Sdougm 	int ret = SA_OK;
32793034Sdougm 	char *protocol;
32803034Sdougm 	char *state;
32813034Sdougm 	struct list *worklist = NULL;
32824653Sdougm 	sa_group_t group;
32833034Sdougm 	int auth = 1;
32843034Sdougm 
32853034Sdougm 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
32864653Sdougm 		switch (c) {
32874653Sdougm 		case 'a':
32884653Sdougm 			all = 1;
32894653Sdougm 			break;
32904653Sdougm 		case 'n':
32914653Sdougm 			dryrun++;
32924653Sdougm 			break;
32934653Sdougm 		case 'P':
32944653Sdougm 			protocol = optarg;
32954653Sdougm 			if (!sa_valid_protocol(protocol)) {
32964653Sdougm 				(void) printf(gettext(
32974653Sdougm 				    "Invalid protocol specified: %s\n"),
32984653Sdougm 				    protocol);
32994653Sdougm 				return (SA_INVALID_PROTOCOL);
33004653Sdougm 			}
33014653Sdougm 			break;
33024653Sdougm 		case 'v':
33034653Sdougm 			verbose++;
33044653Sdougm 			break;
33054653Sdougm 		default:
33064653Sdougm 		case 'h':
33074653Sdougm 		case '?':
33084653Sdougm 			(void) printf(gettext("usage: %s\n"),
33094653Sdougm 			    sa_get_usage(USAGE_DISABLE));
33104653Sdougm 			return (0);
33113034Sdougm 		}
33123034Sdougm 	}
33133034Sdougm 
33143034Sdougm 	if (optind == argc && !all) {
33153034Sdougm 		(void) printf(gettext("usage: %s\n"),
33164653Sdougm 		    sa_get_usage(USAGE_DISABLE));
33173034Sdougm 		(void) printf(gettext("\tmust specify group\n"));
33184653Sdougm 		return (SA_NO_SUCH_PATH);
33194653Sdougm 	}
33204653Sdougm 	if (!all) {
33214653Sdougm 		while (optind < argc) {
33223910Sdougm 			group = sa_get_group(handle, argv[optind]);
33233034Sdougm 			if (group != NULL) {
33244653Sdougm 				auth &= check_authorizations(argv[optind],
33254653Sdougm 				    flags);
33264653Sdougm 				state = sa_get_group_attr(group, "state");
33274653Sdougm 				if (state == NULL ||
33284653Sdougm 				    strcmp(state, "disabled") == 0) {
33294653Sdougm 					/* already disabled */
33304653Sdougm 					if (verbose)
33314653Sdougm 						(void) printf(gettext(
33324653Sdougm 						    "Group \"%s\" is "
33334653Sdougm 						    "already disabled\n"),
33344653Sdougm 						    argv[optind]);
33354653Sdougm 					ret = SA_BUSY; /* already disable */
33364653Sdougm 				} else {
33374653Sdougm 					worklist = add_list(worklist, group, 0);
33384653Sdougm 					if (verbose)
33394653Sdougm 						(void) printf(gettext(
33404653Sdougm 						    "Disabling group "
33414653Sdougm 						    "\"%s\"\n"), argv[optind]);
33424653Sdougm 				}
33434653Sdougm 				if (state != NULL)
33444653Sdougm 					sa_free_attr_string(state);
33453034Sdougm 			} else {
33464653Sdougm 				ret = SA_NO_SUCH_GROUP;
33473034Sdougm 			}
33483034Sdougm 			optind++;
33494653Sdougm 		}
33504653Sdougm 	} else {
33514653Sdougm 		for (group = sa_get_group(handle, NULL);
33524653Sdougm 		    group != NULL;
33534653Sdougm 		    group = sa_get_next_group(group))
33543034Sdougm 			worklist = add_list(worklist, group, 0);
33553034Sdougm 	}
33564653Sdougm 
33574653Sdougm 	if (ret == SA_OK && !dryrun)
33584653Sdougm 		ret = disable_all_groups(handle, worklist, 1);
33594653Sdougm 	if (ret != SA_OK && ret != SA_BUSY)
33604653Sdougm 		(void) printf(gettext("Could not disable group: %s\n"),
33614653Sdougm 		    sa_errorstr(ret));
33624653Sdougm 	if (ret == SA_BUSY)
33634653Sdougm 		ret = SA_OK;
33643034Sdougm 	if (worklist != NULL)
33654653Sdougm 		free_list(worklist);
33664653Sdougm 	if (dryrun && ret == SA_OK && !auth && verbose)
33674653Sdougm 		(void) printf(gettext("Command would fail: %s\n"),
33684653Sdougm 		    sa_errorstr(SA_NO_PERMISSION));
33693034Sdougm 	return (ret);
33703034Sdougm }
33713034Sdougm 
33723034Sdougm /*
33733034Sdougm  * sa_start_group(flags, argc, argv)
33743034Sdougm  *
33753034Sdougm  * Implements the start command.
33763034Sdougm  * This is similar to enable except it doesn't change the state
33773034Sdougm  * of the group(s) and only enables shares if the group is already
33783034Sdougm  * enabled.
33793034Sdougm  */
33804653Sdougm /*ARGSUSED*/
33813034Sdougm int
33823910Sdougm sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
33833034Sdougm {
33843034Sdougm 	int verbose = 0;
33853034Sdougm 	int all = 0;
33863034Sdougm 	int c;
33873034Sdougm 	int ret = SMF_EXIT_OK;
33883034Sdougm 	char *protocol = NULL;
33893034Sdougm 	char *state;
33903034Sdougm 	struct list *worklist = NULL;
33914653Sdougm 	sa_group_t group;
33923034Sdougm 
33933034Sdougm 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
33944653Sdougm 		switch (c) {
33954653Sdougm 		case 'a':
33964653Sdougm 			all = 1;
33974653Sdougm 			break;
33984653Sdougm 		case 'P':
33994653Sdougm 			protocol = optarg;
34004653Sdougm 			if (!sa_valid_protocol(protocol)) {
34014653Sdougm 				(void) printf(gettext(
34024653Sdougm 				    "Invalid protocol specified: %s\n"),
34033034Sdougm 				    protocol);
34044653Sdougm 				return (SA_INVALID_PROTOCOL);
34054653Sdougm 			}
34064653Sdougm 			break;
34074653Sdougm 		case 'v':
34084653Sdougm 			verbose++;
34094653Sdougm 			break;
34104653Sdougm 		default:
34114653Sdougm 		case 'h':
34124653Sdougm 		case '?':
34134653Sdougm 			(void) printf(gettext("usage: %s\n"),
34144653Sdougm 			    sa_get_usage(USAGE_START));
34154653Sdougm 			return (SA_OK);
34163034Sdougm 		}
34173034Sdougm 	}
34183034Sdougm 
34193034Sdougm 	if (optind == argc && !all) {
34203034Sdougm 		(void) printf(gettext("usage: %s\n"),
34214653Sdougm 		    sa_get_usage(USAGE_START));
34224653Sdougm 		return (SMF_EXIT_ERR_FATAL);
34234653Sdougm 	}
34244653Sdougm 
34254653Sdougm 	if (!all) {
34264653Sdougm 		while (optind < argc) {
34273910Sdougm 			group = sa_get_group(handle, argv[optind]);
34283034Sdougm 			if (group != NULL) {
34294653Sdougm 				state = sa_get_group_attr(group, "state");
34304653Sdougm 				if (state == NULL ||
34314653Sdougm 				    strcmp(state, "enabled") == 0) {
34324653Sdougm 					worklist = add_list(worklist, group, 0);
34334653Sdougm 					if (verbose)
34344653Sdougm 						(void) printf(gettext(
34354653Sdougm 						    "Starting group \"%s\"\n"),
34364653Sdougm 						    argv[optind]);
34374653Sdougm 				} else {
34384653Sdougm 					/*
34394653Sdougm 					 * Determine if there are any
34404653Sdougm 					 * protocols.  if there aren't any,
34414653Sdougm 					 * then there isn't anything to do in
34424653Sdougm 					 * any case so no error.
34434653Sdougm 					 */
34444653Sdougm 					if (sa_get_optionset(group,
34454653Sdougm 					    protocol) != NULL) {
34464653Sdougm 						ret = SMF_EXIT_OK;
34474653Sdougm 					}
34483034Sdougm 				}
34494653Sdougm 				if (state != NULL)
34504653Sdougm 					sa_free_attr_string(state);
34513034Sdougm 			}
34523034Sdougm 			optind++;
34534653Sdougm 		}
34544653Sdougm 	} else {
34554653Sdougm 		for (group = sa_get_group(handle, NULL); group != NULL;
34564653Sdougm 		    group = sa_get_next_group(group)) {
34573034Sdougm 			state = sa_get_group_attr(group, "state");
34583034Sdougm 			if (state == NULL || strcmp(state, "enabled") == 0)
34594653Sdougm 				worklist = add_list(worklist, group, 0);
34603034Sdougm 			if (state != NULL)
34614653Sdougm 				sa_free_attr_string(state);
34623034Sdougm 		}
34633034Sdougm 	}
34644653Sdougm 
34654653Sdougm 	(void) enable_all_groups(handle, worklist, 0, 1, NULL);
34664653Sdougm 
34673034Sdougm 	if (worklist != NULL)
34684653Sdougm 		free_list(worklist);
34693034Sdougm 	return (ret);
34703034Sdougm }
34713034Sdougm 
34723034Sdougm /*
34733034Sdougm  * sa_stop_group(flags, argc, argv)
34743034Sdougm  *
34753034Sdougm  * Implements the stop command.
34763034Sdougm  * This is similar to disable except it doesn't change the state
34773034Sdougm  * of the group(s) and only disables shares if the group is already
34783034Sdougm  * enabled.
34793034Sdougm  */
34804653Sdougm /*ARGSUSED*/
34813034Sdougm int
34823910Sdougm sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
34833034Sdougm {
34843034Sdougm 	int verbose = 0;
34853034Sdougm 	int all = 0;
34863034Sdougm 	int c;
34873034Sdougm 	int ret = SMF_EXIT_OK;
34883034Sdougm 	char *protocol = NULL;
34893034Sdougm 	char *state;
34903034Sdougm 	struct list *worklist = NULL;
34914653Sdougm 	sa_group_t group;
34923034Sdougm 
34933034Sdougm 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
34944653Sdougm 		switch (c) {
34954653Sdougm 		case 'a':
34964653Sdougm 			all = 1;
34974653Sdougm 			break;
34984653Sdougm 		case 'P':
34994653Sdougm 			protocol = optarg;
35004653Sdougm 			if (!sa_valid_protocol(protocol)) {
35014653Sdougm 				(void) printf(gettext(
35024653Sdougm 				    "Invalid protocol specified: %s\n"),
35034653Sdougm 				    protocol);
35044653Sdougm 				return (SA_INVALID_PROTOCOL);
35054653Sdougm 			}
35064653Sdougm 			break;
35074653Sdougm 		case 'v':
35084653Sdougm 			verbose++;
35094653Sdougm 			break;
35104653Sdougm 		default:
35114653Sdougm 		case 'h':
35124653Sdougm 		case '?':
35134653Sdougm 			(void) printf(gettext("usage: %s\n"),
35144653Sdougm 			    sa_get_usage(USAGE_STOP));
35154653Sdougm 			return (0);
35163034Sdougm 		}
35173034Sdougm 	}
35183034Sdougm 
35193034Sdougm 	if (optind == argc && !all) {
35204653Sdougm 		(void) printf(gettext("usage: %s\n"),
35214653Sdougm 		    sa_get_usage(USAGE_STOP));
35224653Sdougm 		return (SMF_EXIT_ERR_FATAL);
35234653Sdougm 	} else if (!all) {
35244653Sdougm 		while (optind < argc) {
35253910Sdougm 			group = sa_get_group(handle, argv[optind]);
35263034Sdougm 			if (group != NULL) {
35274653Sdougm 				state = sa_get_group_attr(group, "state");
35284653Sdougm 				if (state == NULL ||
35294653Sdougm 				    strcmp(state, "enabled") == 0) {
35304653Sdougm 					worklist = add_list(worklist, group, 0);
35314653Sdougm 					if (verbose)
35324653Sdougm 						(void) printf(gettext(
35334653Sdougm 						    "Stopping group \"%s\"\n"),
35344653Sdougm 						    argv[optind]);
35354653Sdougm 				} else {
35364653Sdougm 					ret = SMF_EXIT_OK;
35374653Sdougm 				}
35384653Sdougm 				if (state != NULL)
35394653Sdougm 					sa_free_attr_string(state);
35403034Sdougm 			}
35413034Sdougm 			optind++;
35424653Sdougm 		}
35434653Sdougm 	} else {
35444653Sdougm 		for (group = sa_get_group(handle, NULL); group != NULL;
35454653Sdougm 		    group = sa_get_next_group(group)) {
35463034Sdougm 			state = sa_get_group_attr(group, "state");
35473034Sdougm 			if (state == NULL || strcmp(state, "enabled") == 0)
35484653Sdougm 				worklist = add_list(worklist, group, 0);
35493034Sdougm 			if (state != NULL)
35504653Sdougm 				sa_free_attr_string(state);
35513034Sdougm 		}
35523034Sdougm 	}
35534653Sdougm 
35544653Sdougm 	(void) disable_all_groups(handle, worklist, 0);
35554653Sdougm 	ret = sa_update_config(handle);
35564653Sdougm 
35573034Sdougm 	if (worklist != NULL)
35584653Sdougm 		free_list(worklist);
35593034Sdougm 	return (ret);
35603034Sdougm }
35613034Sdougm 
35623034Sdougm /*
35633034Sdougm  * remove_all_options(share, proto)
35643034Sdougm  *
35653034Sdougm  * Removes all options on a share.
35663034Sdougm  */
35673034Sdougm 
35683034Sdougm static void
35693034Sdougm remove_all_options(sa_share_t share, char *proto)
35703034Sdougm {
35713034Sdougm 	sa_optionset_t optionset;
35723034Sdougm 	sa_security_t security;
35733034Sdougm 	sa_security_t prevsec = NULL;
35743034Sdougm 
35753034Sdougm 	optionset = sa_get_optionset(share, proto);
35763034Sdougm 	if (optionset != NULL)
35774653Sdougm 		(void) sa_destroy_optionset(optionset);
35783034Sdougm 	for (security = sa_get_security(share, NULL, NULL);
35793034Sdougm 	    security != NULL;
35803034Sdougm 	    security = sa_get_next_security(security)) {
35814653Sdougm 		char *type;
35823034Sdougm 		/*
35834653Sdougm 		 * We walk through the list.  prevsec keeps the
35843034Sdougm 		 * previous security so we can delete it without
35853034Sdougm 		 * destroying the list.
35863034Sdougm 		 */
35874653Sdougm 		if (prevsec != NULL) {
35884653Sdougm 			/* remove the previously seen security */
35894653Sdougm 			(void) sa_destroy_security(prevsec);
35904653Sdougm 			/* set to NULL so we don't try multiple times */
35914653Sdougm 			prevsec = NULL;
35924653Sdougm 		}
35934653Sdougm 		type = sa_get_security_attr(security, "type");
35944653Sdougm 		if (type != NULL) {
35954653Sdougm 			/*
35964653Sdougm 			 * if the security matches the specified protocol, we
35974653Sdougm 			 * want to remove it. prevsec holds it until either
35984653Sdougm 			 * the next pass or we fall out of the loop.
35994653Sdougm 			 */
36004653Sdougm 			if (strcmp(type, proto) == 0)
36014653Sdougm 				prevsec = security;
36024653Sdougm 			sa_free_attr_string(type);
36034653Sdougm 		}
36043034Sdougm 	}
36053034Sdougm 	/* in case there is one left */
36063034Sdougm 	if (prevsec != NULL)
36074653Sdougm 		(void) sa_destroy_security(prevsec);
36083034Sdougm }
36093034Sdougm 
36103034Sdougm 
36113034Sdougm /*
36123034Sdougm  * for legacy support, we need to handle the old syntax. This is what
36133034Sdougm  * we get if sharemgr is called with the name "share" rather than
36143034Sdougm  * sharemgr.
36153034Sdougm  */
36163034Sdougm 
36173034Sdougm static int
36183034Sdougm format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
36193034Sdougm {
36203034Sdougm 	int err;
36213034Sdougm 
36223034Sdougm 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
36233034Sdougm 	if (err > buffsize)
36244653Sdougm 		return (-1);
36253034Sdougm 	return (0);
36263034Sdougm }
36273034Sdougm 
36283034Sdougm 
36293034Sdougm /*
36303034Sdougm  * check_legacy_cmd(proto, cmd)
36313034Sdougm  *
36323034Sdougm  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
36333034Sdougm  * executable.
36343034Sdougm  */
36353034Sdougm 
36363034Sdougm static int
36373034Sdougm check_legacy_cmd(char *path)
36383034Sdougm {
36393034Sdougm 	struct stat st;
36403034Sdougm 	int ret = 0;
36413034Sdougm 
36423034Sdougm 	if (stat(path, &st) == 0) {
36434653Sdougm 		if (S_ISREG(st.st_mode) &&
36444653Sdougm 		    st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
36454653Sdougm 			ret = 1;
36463034Sdougm 	}
36473034Sdougm 	return (ret);
36483034Sdougm }
36493034Sdougm 
36503034Sdougm /*
36513034Sdougm  * run_legacy_command(proto, cmd, argv)
36523034Sdougm  *
36534653Sdougm  * We know the command exists, so attempt to execute it with all the
36543034Sdougm  * arguments. This implements full legacy share support for those
36553034Sdougm  * protocols that don't have plugin providers.
36563034Sdougm  */
36573034Sdougm 
36583034Sdougm static int
36593034Sdougm run_legacy_command(char *path, char *argv[])
36603034Sdougm {
36613034Sdougm 	int ret;
36623034Sdougm 
36633034Sdougm 	ret = execv(path, argv);
36643034Sdougm 	if (ret < 0) {
36654653Sdougm 		switch (errno) {
36664653Sdougm 		case EACCES:
36674653Sdougm 			ret = SA_NO_PERMISSION;
36684653Sdougm 			break;
36694653Sdougm 		default:
36704653Sdougm 			ret = SA_SYSTEM_ERR;
36714653Sdougm 			break;
36724653Sdougm 		}
36733034Sdougm 	}
36743034Sdougm 	return (ret);
36753034Sdougm }
36763034Sdougm 
36773034Sdougm /*
36783348Sdougm  * out_share(out, group, proto)
36793034Sdougm  *
36803034Sdougm  * Display the share information in the format that the "share"
36813034Sdougm  * command has traditionally used.
36823034Sdougm  */
36833034Sdougm 
36843034Sdougm static void
36853348Sdougm out_share(FILE *out, sa_group_t group, char *proto)
36863034Sdougm {
36873034Sdougm 	sa_share_t share;
36883034Sdougm 	char resfmt[128];
36893034Sdougm 
36904653Sdougm 	for (share = sa_get_share(group, NULL);
36914653Sdougm 	    share != NULL;
36924653Sdougm 	    share = sa_get_next_share(share)) {
36934653Sdougm 		char *path;
36944653Sdougm 		char *type;
36954653Sdougm 		char *resource;
36964653Sdougm 		char *description;
36974653Sdougm 		char *groupname;
36984653Sdougm 		char *sharedstate;
36994653Sdougm 		int shared = 1;
37004653Sdougm 		char *soptions;
37014653Sdougm 
37024653Sdougm 		sharedstate = sa_get_share_attr(share, "shared");
37034653Sdougm 		path = sa_get_share_attr(share, "path");
37044653Sdougm 		type = sa_get_share_attr(share, "type");
37054653Sdougm 		resource = sa_get_share_attr(share, "resource");
37064653Sdougm 		groupname = sa_get_group_attr(group, "name");
37074653Sdougm 
37084653Sdougm 		if (groupname != NULL && strcmp(groupname, "default") == 0) {
37094653Sdougm 			sa_free_attr_string(groupname);
37104653Sdougm 			groupname = NULL;
37114653Sdougm 		}
37124653Sdougm 		description = sa_get_share_description(share);
37134653Sdougm 
37144653Sdougm 		/* Want the sharetab version if it exists */
37154653Sdougm 		soptions = sa_get_share_attr(share, "shareopts");
37164653Sdougm 
37174653Sdougm 		if (sharedstate == NULL)
37184653Sdougm 			shared = 0;
37194653Sdougm 
37204653Sdougm 		if (soptions == NULL)
37214653Sdougm 			soptions = sa_proto_legacy_format(proto, share, 1);
37224653Sdougm 
37234653Sdougm 		if (shared) {
37244653Sdougm 			/* only active shares go here */
37254653Sdougm 			(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
37264653Sdougm 			    resource != NULL ? resource : "-",
37274653Sdougm 			    groupname != NULL ? "@" : "",
37284653Sdougm 			    groupname != NULL ? groupname : "");
37294653Sdougm 			(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
37304653Sdougm 			    resfmt, path,
37314653Sdougm 			    (soptions != NULL && strlen(soptions) > 0) ?
37324653Sdougm 			    soptions : "rw",
37334653Sdougm 			    (description != NULL) ? description : "");
37344653Sdougm 		}
37354653Sdougm 
37364653Sdougm 		if (path != NULL)
37374653Sdougm 			sa_free_attr_string(path);
37384653Sdougm 		if (type != NULL)
37394653Sdougm 			sa_free_attr_string(type);
37404653Sdougm 		if (resource != NULL)
37414653Sdougm 			sa_free_attr_string(resource);
37424653Sdougm 		if (groupname != NULL)
37434653Sdougm 			sa_free_attr_string(groupname);
37444653Sdougm 		if (description != NULL)
37454653Sdougm 			sa_free_share_description(description);
37464653Sdougm 		if (sharedstate != NULL)
37474653Sdougm 			sa_free_attr_string(sharedstate);
37484653Sdougm 		if (soptions != NULL)
37494653Sdougm 			sa_format_free(soptions);
37503034Sdougm 	}
37513034Sdougm }
37523034Sdougm 
37533034Sdougm /*
37543034Sdougm  * output_legacy_file(out, proto)
37553034Sdougm  *
37563034Sdougm  * Walk all of the groups for the specified protocol and call
37573034Sdougm  * out_share() to format and write in the format displayed by the
37583034Sdougm  * "share" command with no arguments.
37593034Sdougm  */
37603034Sdougm 
37613034Sdougm static void
37623910Sdougm output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
37633034Sdougm {
37643034Sdougm 	sa_group_t group;
37653034Sdougm 
37663910Sdougm 	for (group = sa_get_group(handle, NULL); group != NULL;
37674653Sdougm 	    group = sa_get_next_group(group)) {
37684653Sdougm 		char *options;
37694653Sdougm 		char *zfs;
37703034Sdougm 
37713034Sdougm 		/*
37724653Sdougm 		 * Get default options preformated, being careful to
37733034Sdougm 		 * handle legacy shares differently from new style
37743034Sdougm 		 * shares. Legacy share have options on the share.
37753034Sdougm 		 */
37763034Sdougm 
37774653Sdougm 		zfs = sa_get_group_attr(group, "zfs");
37784653Sdougm 		if (zfs != NULL) {
37794653Sdougm 			sa_group_t zgroup;
37804653Sdougm 			sa_free_attr_string(zfs);
37814653Sdougm 			options = sa_proto_legacy_format(proto, group, 1);
37824653Sdougm 			for (zgroup = sa_get_sub_group(group);
37834653Sdougm 			    zgroup != NULL;
37844653Sdougm 			    zgroup = sa_get_next_group(zgroup)) {
37854653Sdougm 
37864653Sdougm 				/* got a group, so display it */
37874653Sdougm 				out_share(out, zgroup, proto);
37884653Sdougm 			}
37894653Sdougm 		} else {
37904653Sdougm 			options = sa_proto_legacy_format(proto, group, 1);
37914653Sdougm 			out_share(out, group, proto);
37923034Sdougm 		}
37934653Sdougm 		if (options != NULL)
37944653Sdougm 			free(options);
37953034Sdougm 	}
37963034Sdougm }
37973034Sdougm 
37984653Sdougm /*ARGSUSED*/
37993034Sdougm int
38003910Sdougm sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
38013034Sdougm {
38023034Sdougm 	char *protocol = "nfs";
38033034Sdougm 	char *options = NULL;
38043034Sdougm 	char *description = NULL;
38053034Sdougm 	char *groupname = NULL;
38063034Sdougm 	char *sharepath = NULL;
38073034Sdougm 	char *resource = NULL;
38083034Sdougm 	char *groupstatus = NULL;
38093034Sdougm 	int persist = SA_SHARE_TRANSIENT;
38103034Sdougm 	int argsused = 0;
38113034Sdougm 	int c;
38123034Sdougm 	int ret = SA_OK;
38133034Sdougm 	int zfs = 0;
38143034Sdougm 	int true_legacy = 0;
38153034Sdougm 	int curtype = SA_SHARE_TRANSIENT;
38163034Sdougm 	char cmd[MAXPATHLEN];
38174653Sdougm 	sa_group_t group = NULL;
38184653Sdougm 	sa_share_t share;
38194653Sdougm 	char dir[MAXPATHLEN];
38203034Sdougm 
38213034Sdougm 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
38224653Sdougm 		switch (c) {
38234653Sdougm 		case 'd':
38244653Sdougm 			description = optarg;
38254653Sdougm 			argsused++;
38264653Sdougm 			break;
38274653Sdougm 		case 'F':
38284653Sdougm 			protocol = optarg;
38294653Sdougm 			if (!sa_valid_protocol(protocol)) {
38304653Sdougm 				if (format_legacy_path(cmd, MAXPATHLEN,
38314653Sdougm 				    protocol, "share") == 0 &&
38324653Sdougm 				    check_legacy_cmd(cmd)) {
38334653Sdougm 					true_legacy++;
38344653Sdougm 				} else {
38354653Sdougm 					(void) fprintf(stderr, gettext(
38364653Sdougm 					    "Invalid protocol specified: "
38374653Sdougm 					    "%s\n"), protocol);
38384653Sdougm 					return (SA_INVALID_PROTOCOL);
38394653Sdougm 				}
38404653Sdougm 			}
38414653Sdougm 			break;
38424653Sdougm 		case 'o':
38434653Sdougm 			options = optarg;
38444653Sdougm 			argsused++;
38454653Sdougm 			break;
38464653Sdougm 		case 'p':
38474653Sdougm 			persist = SA_SHARE_PERMANENT;
38484653Sdougm 			argsused++;
38494653Sdougm 			break;
38504653Sdougm 		case 'h':
38514653Sdougm 		case '?':
38524653Sdougm 		default:
38534653Sdougm 			(void) fprintf(stderr, gettext("usage: %s\n"),
38544653Sdougm 			    sa_get_usage(USAGE_SHARE));
38554653Sdougm 			return (SA_OK);
38563034Sdougm 		}
38574653Sdougm 	}
38584653Sdougm 
38594653Sdougm 	/* Have the info so construct what is needed */
38604653Sdougm 	if (!argsused && optind == argc) {
38614653Sdougm 		/* display current info in share format */
38624653Sdougm 		(void) output_legacy_file(stdout, "nfs", handle);
38634653Sdougm 		return (ret);
38643034Sdougm 	}
38653034Sdougm 
38664653Sdougm 	/* We are modifying the configuration */
38674653Sdougm 	if (optind == argc) {
38683034Sdougm 		(void) fprintf(stderr, gettext("usage: %s\n"),
38694653Sdougm 		    sa_get_usage(USAGE_SHARE));
38703034Sdougm 		return (SA_LEGACY_ERR);
38714653Sdougm 	}
38724653Sdougm 	if (true_legacy) {
38734653Sdougm 		/* If still using legacy share/unshare, exec it */
38743034Sdougm 		ret = run_legacy_command(cmd, argv);
38753034Sdougm 		return (ret);
38764653Sdougm 	}
38774653Sdougm 
38784653Sdougm 	sharepath = argv[optind++];
38794653Sdougm 	if (optind < argc) {
38803034Sdougm 		resource = argv[optind];
38813034Sdougm 		groupname = strchr(resource, '@');
38823034Sdougm 		if (groupname != NULL)
38834653Sdougm 			*groupname++ = '\0';
38844653Sdougm 	}
38854653Sdougm 	if (realpath(sharepath, dir) == NULL)
38863034Sdougm 		ret = SA_BAD_PATH;
38874653Sdougm 	else
38883034Sdougm 		sharepath = dir;
38894653Sdougm 	if (ret == SA_OK)
38903910Sdougm 		share = sa_find_share(handle, sharepath);
38914653Sdougm 	else
38923034Sdougm 		share = NULL;
38934653Sdougm 
38944653Sdougm 	if (groupname != NULL) {
38954653Sdougm 		ret = SA_NOT_ALLOWED;
38964653Sdougm 	} else if (ret == SA_OK) {
38973034Sdougm 		char *legacygroup = "default";
38983034Sdougm 		/*
38994653Sdougm 		 * The legacy group is always present and zfs groups
39003034Sdougm 		 * come and go.  zfs shares may be in sub-groups and
39013034Sdougm 		 * the zfs share will already be in that group so it
39023034Sdougm 		 * isn't an error.
39033034Sdougm 		 */
39043034Sdougm 		/*
39054653Sdougm 		 * If the share exists (not NULL), then make sure it
39064653Sdougm 		 * is one we want to handle by getting the parent
39074653Sdougm 		 * group.
39083034Sdougm 		 */
39094653Sdougm 		if (share != NULL)
39104653Sdougm 			group = sa_get_parent_group(share);
39114653Sdougm 		else
39124653Sdougm 			group = sa_get_group(handle, legacygroup);
39134653Sdougm 
39143034Sdougm 		if (group != NULL) {
39154653Sdougm 			groupstatus = group_status(group);
39164653Sdougm 			if (share == NULL) {
39174653Sdougm 				share = sa_add_share(group, sharepath,
39184653Sdougm 				    persist, &ret);
39194653Sdougm 				if (share == NULL &&
39204653Sdougm 				    ret == SA_DUPLICATE_NAME) {
39214653Sdougm 					/*
39224653Sdougm 					 * Could be a ZFS path being started
39234653Sdougm 					 */
39244653Sdougm 					if (sa_zfs_is_shared(handle,
39254653Sdougm 					    sharepath)) {
39264653Sdougm 						ret = SA_OK;
39274653Sdougm 						group = sa_get_group(handle,
39284653Sdougm 						    "zfs");
39294653Sdougm 						if (group == NULL) {
39304653Sdougm 							/*
39314653Sdougm 							 * This shouldn't
39324653Sdougm 							 * happen.
39334653Sdougm 							 */
39344653Sdougm 							ret = SA_CONFIG_ERR;
39354653Sdougm 						} else {
39364653Sdougm 							share = sa_add_share(
39374653Sdougm 							    group, sharepath,
39384653Sdougm 							    persist, &ret);
39394653Sdougm 						}
39404653Sdougm 					}
39413034Sdougm 				}
39424653Sdougm 			} else {
39434653Sdougm 				char *type;
39444653Sdougm 				/*
39454653Sdougm 				 * May want to change persist state, but the
39464653Sdougm 				 * important thing is to change options. We
39474653Sdougm 				 * need to change them regardless of the
39484653Sdougm 				 * source.
39494653Sdougm 				 */
39504653Sdougm 				if (sa_zfs_is_shared(handle, sharepath)) {
39514653Sdougm 					zfs = 1;
39523034Sdougm 				}
39534653Sdougm 				remove_all_options(share, protocol);
39544653Sdougm 				type = sa_get_share_attr(share, "type");
39554653Sdougm 				if (type != NULL &&
39564653Sdougm 				    strcmp(type, "transient") != 0) {
39574653Sdougm 					curtype = SA_SHARE_PERMANENT;
39584653Sdougm 				}
39594653Sdougm 				if (type != NULL)
39604653Sdougm 					sa_free_attr_string(type);
39614653Sdougm 				if (curtype != persist) {
39624653Sdougm 					(void) sa_set_share_attr(share, "type",
39634653Sdougm 					    persist == SA_SHARE_PERMANENT ?
39644653Sdougm 					    "persist" : "transient");
39654653Sdougm 				}
39663108Sdougm 			}
39674653Sdougm 			/* Have a group to hold this share path */
39684653Sdougm 			if (ret == SA_OK && options != NULL &&
39694653Sdougm 			    strlen(options) > 0) {
39704653Sdougm 				ret = sa_parse_legacy_options(share,
39714653Sdougm 				    options,
39724653Sdougm 				    protocol);
39733034Sdougm 			}
39744653Sdougm 			if (!zfs) {
39754653Sdougm 				/*
39764653Sdougm 				 * ZFS shares never have resource or
39774653Sdougm 				 * description and we can't store the values
39784653Sdougm 				 * so don't try.
39794653Sdougm 				 */
39804653Sdougm 				if (ret == SA_OK && description != NULL)
39814653Sdougm 					ret = sa_set_share_description(share,
39824653Sdougm 					    description);
39834653Sdougm 				if (ret == SA_OK && resource != NULL)
39844653Sdougm 					ret = sa_set_share_attr(share,
39854653Sdougm 					    "resource", resource);
39863034Sdougm 			}
39874653Sdougm 			if (ret == SA_OK) {
39884653Sdougm 				if (strcmp(groupstatus, "enabled") == 0)
39894653Sdougm 					ret = sa_enable_share(share, protocol);
39904653Sdougm 				if (ret == SA_OK &&
39914653Sdougm 				    persist == SA_SHARE_PERMANENT) {
39924653Sdougm 					(void) sa_update_legacy(share,
39934653Sdougm 					    protocol);
39944653Sdougm 				}
39954653Sdougm 				if (ret == SA_OK)
39964653Sdougm 					ret = sa_update_config(handle);
39974653Sdougm 			}
39983034Sdougm 		} else {
39994653Sdougm 			ret = SA_SYSTEM_ERR;
40003034Sdougm 		}
40013034Sdougm 	}
40023034Sdougm 	if (ret != SA_OK) {
40034653Sdougm 		(void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
40044653Sdougm 		    sharepath, sa_errorstr(ret));
40054653Sdougm 		ret = SA_LEGACY_ERR;
40063034Sdougm 
40073034Sdougm 	}
40083034Sdougm 	return (ret);
40093034Sdougm }
40103034Sdougm 
40113034Sdougm /*
40123034Sdougm  * sa_legacy_unshare(flags, argc, argv)
40133034Sdougm  *
40143034Sdougm  * Implements the original unshare command.
40153034Sdougm  */
40164653Sdougm /*ARGSUSED*/
40173034Sdougm int
40183910Sdougm sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
40193034Sdougm {
40203034Sdougm 	char *protocol = "nfs"; /* for now */
40213034Sdougm 	char *options = NULL;
40223034Sdougm 	char *sharepath = NULL;
40233034Sdougm 	int persist = SA_SHARE_TRANSIENT;
40243034Sdougm 	int argsused = 0;
40253034Sdougm 	int c;
40263034Sdougm 	int ret = SA_OK;
40273034Sdougm 	int true_legacy = 0;
40283034Sdougm 	char cmd[MAXPATHLEN];
40293034Sdougm 
40303034Sdougm 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
40314653Sdougm 		switch (c) {
40324653Sdougm 		case 'h':
40334653Sdougm 		case '?':
40344653Sdougm 			break;
40354653Sdougm 		case 'F':
40364653Sdougm 			protocol = optarg;
40374653Sdougm 			if (!sa_valid_protocol(protocol)) {
40384653Sdougm 				if (format_legacy_path(cmd, MAXPATHLEN,
40394653Sdougm 				    protocol, "unshare") == 0 &&
40404653Sdougm 				    check_legacy_cmd(cmd)) {
40414653Sdougm 					true_legacy++;
40424653Sdougm 				} else {
40434653Sdougm 					(void) printf(gettext(
40444653Sdougm 					    "Invalid file system name\n"));
40454653Sdougm 					return (SA_INVALID_PROTOCOL);
40464653Sdougm 				}
40474653Sdougm 			}
40484653Sdougm 			break;
40494653Sdougm 		case 'o':
40504653Sdougm 			options = optarg;
40514653Sdougm 			argsused++;
40524653Sdougm 			break;
40534653Sdougm 		case 'p':
40544653Sdougm 			persist = SA_SHARE_PERMANENT;
40554653Sdougm 			argsused++;
40564653Sdougm 			break;
40574653Sdougm 		default:
40584653Sdougm 			(void) printf(gettext("usage: %s\n"),
40594653Sdougm 			    sa_get_usage(USAGE_UNSHARE));
40604653Sdougm 			return (SA_OK);
40613034Sdougm 		}
40623034Sdougm 	}
40633034Sdougm 
40644653Sdougm 	/* Have the info so construct what is needed */
40654653Sdougm 	if (optind == argc || (optind + 1) < argc || options != NULL) {
40664653Sdougm 		ret = SA_SYNTAX_ERR;
40673034Sdougm 	} else {
40684653Sdougm 		sa_share_t share;
40694653Sdougm 		char dir[MAXPATHLEN];
40704653Sdougm 		if (true_legacy) {
40714653Sdougm 			/* if still using legacy share/unshare, exec it */
40724653Sdougm 			ret = run_legacy_command(cmd, argv);
40734653Sdougm 			return (ret);
40744653Sdougm 		}
40753663Sdougm 		/*
40763663Sdougm 		 * Find the path in the internal configuration. If it
40773663Sdougm 		 * isn't found, attempt to resolve the path via
40783663Sdougm 		 * realpath() and try again.
40793663Sdougm 		 */
40804653Sdougm 		sharepath = argv[optind++];
40814653Sdougm 		share = sa_find_share(handle, sharepath);
40824653Sdougm 		if (share == NULL) {
40834653Sdougm 			if (realpath(sharepath, dir) == NULL) {
40844653Sdougm 				ret = SA_NO_SUCH_PATH;
40854653Sdougm 			} else {
40864653Sdougm 				share = sa_find_share(handle, dir);
40874653Sdougm 			}
40883663Sdougm 		}
40894653Sdougm 		if (share != NULL) {
40904653Sdougm 			ret = sa_disable_share(share, protocol);
40914653Sdougm 			/*
40924653Sdougm 			 * Errors are ok and removal should still occur. The
40934653Sdougm 			 * legacy unshare is more forgiving of errors than the
40944653Sdougm 			 * remove-share subcommand which may need the force
40954653Sdougm 			 * flag set for some error conditions. That is, the
40964653Sdougm 			 * "unshare" command will always unshare if it can
40974653Sdougm 			 * while "remove-share" might require the force option.
40984653Sdougm 			 */
40994653Sdougm 			if (persist == SA_SHARE_PERMANENT) {
41004653Sdougm 				ret = sa_remove_share(share);
41014653Sdougm 				if (ret == SA_OK)
41024653Sdougm 					ret = sa_update_config(handle);
41034653Sdougm 			}
41044653Sdougm 		} else {
41054653Sdougm 			ret = SA_NOT_SHARED;
41063663Sdougm 		}
41073034Sdougm 	}
41083034Sdougm 	switch (ret) {
41093034Sdougm 	default:
41104653Sdougm 		(void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
41114653Sdougm 		ret = SA_LEGACY_ERR;
41124653Sdougm 		break;
41133034Sdougm 	case SA_SYNTAX_ERR:
41144653Sdougm 		(void) printf(gettext("usage: %s\n"),
41154653Sdougm 		    sa_get_usage(USAGE_UNSHARE));
41164653Sdougm 		break;
41173034Sdougm 	case SA_OK:
41184653Sdougm 		break;
41193034Sdougm 	}
41203034Sdougm 	return (ret);
41213034Sdougm }
41223034Sdougm 
41233034Sdougm /*
41244653Sdougm  * Common commands that implement the sub-commands used by all
41253034Sdougm  * protcols. The entries are found via the lookup command
41263034Sdougm  */
41273034Sdougm 
41283034Sdougm static sa_command_t commands[] = {
41293034Sdougm 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
41303034Sdougm 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
41313034Sdougm 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
41323034Sdougm 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
41333034Sdougm 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
41343034Sdougm 	{"list", 0, sa_list, USAGE_LIST},
41353034Sdougm 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
41363034Sdougm 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
41373034Sdougm 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
41383034Sdougm 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
41393034Sdougm 	{"show", 0, sa_show, USAGE_SHOW},
41403034Sdougm 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
41413034Sdougm 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
41423034Sdougm 		SVC_SET|SVC_ACTION},
41433034Sdougm 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
41443034Sdougm 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
41453034Sdougm 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
41463034Sdougm 	{NULL, 0, NULL, NULL}
41473034Sdougm };
41483034Sdougm 
41493034Sdougm static char *
41503034Sdougm sa_get_usage(sa_usage_t index)
41513034Sdougm {
41523034Sdougm 	char *ret = NULL;
41533034Sdougm 	switch (index) {
41543034Sdougm 	case USAGE_ADD_SHARE:
41554653Sdougm 		ret = gettext("add-share [-nth] [-r resource-name] "
41564653Sdougm 		    "[-d \"description text\"] -s sharepath group");
41574653Sdougm 		break;
41583034Sdougm 	case USAGE_CREATE:
41594653Sdougm 		ret = gettext(
41604653Sdougm 		    "create [-nvh] [-P proto [-p property=value]] group");
41614653Sdougm 		break;
41623034Sdougm 	case USAGE_DELETE:
41634653Sdougm 		ret = gettext("delete [-nvh] [-P proto] [-f] group");
41644653Sdougm 		break;
41653034Sdougm 	case USAGE_DISABLE:
41664653Sdougm 		ret = gettext("disable [-nvh] {-a | group ...}");
41674653Sdougm 		break;
41683034Sdougm 	case USAGE_ENABLE:
41694653Sdougm 		ret = gettext("enable [-nvh] {-a | group ...}");
41704653Sdougm 		break;
41713034Sdougm 	case USAGE_LIST:
41724653Sdougm 		ret = gettext("list [-vh] [-P proto]");
41734653Sdougm 		break;
41743034Sdougm 	case USAGE_MOVE_SHARE:
41754653Sdougm 		ret = gettext(
41764653Sdougm 		    "move-share [-nvh] -s sharepath destination-group");
41774653Sdougm 		break;
41783034Sdougm 	case USAGE_REMOVE_SHARE:
41794653Sdougm 		ret = gettext("remove-share [-fnvh] -s sharepath group");
41804653Sdougm 		break;
41813034Sdougm 	case USAGE_SET:
41824653Sdougm 		ret = gettext("set [-nvh] -P proto [-S optspace] "
41834653Sdougm 		    "[-p property=value]* [-s sharepath] group");
41844653Sdougm 		break;
41853034Sdougm 	case USAGE_SET_SECURITY:
41864653Sdougm 		ret = gettext("set-security [-nvh] -P proto -S security-type "
41874653Sdougm 		    "[-p property=value]* group");
41884653Sdougm 		break;
41893034Sdougm 	case USAGE_SET_SHARE:
41904653Sdougm 		ret = gettext("set-share [-nh] [-r resource] "
41914653Sdougm 		    "[-d \"description text\"] -s sharepath group");
41924653Sdougm 		break;
41933034Sdougm 	case USAGE_SHOW:
41944653Sdougm 		ret = gettext("show [-pvxh] [-P proto] [group ...]");
41954653Sdougm 		break;
41963034Sdougm 	case USAGE_SHARE:
41974653Sdougm 		ret = gettext("share [-F fstype] [-p] [-o optionlist]"
41984653Sdougm 		    "[-d description] [pathname [resourcename]]");
41994653Sdougm 		break;
42003034Sdougm 	case USAGE_START:
42014653Sdougm 		ret = gettext("start [-vh] [-P proto] {-a | group ...}");
42024653Sdougm 		break;
42033034Sdougm 	case USAGE_STOP:
42044653Sdougm 		ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
42054653Sdougm 		break;
42063034Sdougm 	case USAGE_UNSET:
42074653Sdougm 		ret = gettext("unset [-nvh] -P proto [-S optspace] "
42084653Sdougm 		    "[-p property]* group");
42094653Sdougm 		break;
42103034Sdougm 	case USAGE_UNSET_SECURITY:
42114653Sdougm 		ret = gettext("unset-security [-nvh] -P proto -S security-type"
42124653Sdougm 		    " [-p property]* group");
42134653Sdougm 		break;
42143034Sdougm 	case USAGE_UNSHARE:
42154653Sdougm 		ret = gettext(
42164653Sdougm 		    "unshare [-F fstype] [-p] sharepath");
42174653Sdougm 		break;
42183034Sdougm 	}
42193034Sdougm 	return (ret);
42203034Sdougm }
42213034Sdougm 
42223034Sdougm /*
42233034Sdougm  * sa_lookup(cmd, proto)
42243034Sdougm  *
42253034Sdougm  * Lookup the sub-command. proto isn't currently used, but it may
42263034Sdougm  * eventually provide a way to provide protocol specific sub-commands.
42273034Sdougm  */
42284653Sdougm /*ARGSUSED*/
42293034Sdougm sa_command_t *
42303034Sdougm sa_lookup(char *cmd, char *proto)
42313034Sdougm {
42323034Sdougm 	int i;
42333034Sdougm 	size_t len;
42343034Sdougm 
42353034Sdougm 	len = strlen(cmd);
42363034Sdougm 	for (i = 0; commands[i].cmdname != NULL; i++) {
42374653Sdougm 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
42384653Sdougm 			return (&commands[i]);
42393034Sdougm 	}
42403034Sdougm 	return (NULL);
42413034Sdougm }
42423034Sdougm 
42434653Sdougm /*ARGSUSED*/
42443034Sdougm void
42453034Sdougm sub_command_help(char *proto)
42463034Sdougm {
42473034Sdougm 	int i;
42483034Sdougm 
42493034Sdougm 	(void) printf(gettext("\tsub-commands:\n"));
42503034Sdougm 	for (i = 0; commands[i].cmdname != NULL; i++) {
42514653Sdougm 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
42524653Sdougm 			(void) printf("\t%s\n",
42534653Sdougm 			    sa_get_usage((sa_usage_t)commands[i].cmdidx));
42543034Sdougm 	}
42553034Sdougm }
4256