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 /*
23*3348Sdougm  * 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) {
723034Sdougm 	    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) {
903034Sdougm 	    new->next = NULL;
913034Sdougm 	    new->item = item;
923034Sdougm 	    new->itemdata = data;
933034Sdougm 	} else {
943034Sdougm 	    return (listp);
953034Sdougm 	}
963034Sdougm 
973034Sdougm 	if (listp == NULL)
983034Sdougm 	    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) {
1163034Sdougm 	    tmp = listp;
1173034Sdougm 	    listp = listp->next;
1183034Sdougm 	    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;
1373034Sdougm 	int ret = 1;
1383034Sdougm 	uid_t uid;
1393034Sdougm 	struct passwd *pw = NULL;
1403034Sdougm 
1413034Sdougm 	uid = getuid();
1423034Sdougm 	pw = getpwuid(uid);
1433034Sdougm 	if (pw == NULL)
1443034Sdougm 	    ret = 0;
1453034Sdougm 
1463034Sdougm 	if (ret == 1) {
1473034Sdougm 	    /* since names  are restricted to SA_MAX_NAME_LEN won't overflow */
1483034Sdougm 	    (void) snprintf(svcstring, sizeof (svcstring),
1493034Sdougm 				"%s:%s", SA_SVC_FMRI_BASE, instname);
1503034Sdougm 	    handle = scf_handle_create(SCF_VERSION);
1513034Sdougm 	    if (handle != NULL) {
1523034Sdougm 		if (scf_handle_bind(handle) == 0) {
1533034Sdougm 		    switch (which) {
1543034Sdougm 		    case SVC_SET:
1553034Sdougm 			prop = scf_simple_prop_get(handle, svcstring,
1563034Sdougm 							"general",
1573034Sdougm 							SVC_AUTH_VALUE);
1583034Sdougm 			break;
1593034Sdougm 		    case SVC_ACTION:
1603034Sdougm 			prop = scf_simple_prop_get(handle, svcstring,
1613034Sdougm 							"general",
1623034Sdougm 							SVC_AUTH_ACTION);
1633034Sdougm 			break;
1643034Sdougm 		    }
1653034Sdougm 		}
1663034Sdougm 	    }
1673034Sdougm 	}
1683034Sdougm 	/* make sure we have an authorization string property */
1693034Sdougm 	if (prop != NULL) {
1703034Sdougm 	    int i;
1713034Sdougm 	    numauths = scf_simple_prop_numvalues(prop);
1723034Sdougm 	    for (ret = 0, i = 0; i < numauths; i++) {
1733034Sdougm 		authstr = scf_simple_prop_next_astring(prop);
1743034Sdougm 		if (authstr != NULL) {
1753034Sdougm 		    /* check if this user has one of the strings */
1763034Sdougm 		    if (chkauthattr(authstr, pw->pw_name)) {
1773034Sdougm 			ret = 1;
1783034Sdougm 			break;
1793034Sdougm 		    }
1803034Sdougm 		}
1813034Sdougm 	    }
1823034Sdougm 	    endauthattr();
1833034Sdougm 	    scf_simple_prop_free(prop);
1843034Sdougm 	} else {
1853034Sdougm 	    /* no authorization string defined */
1863034Sdougm 	    ret = 0;
1873034Sdougm 	}
1883034Sdougm 	if (handle != NULL)
1893034Sdougm 	    scf_handle_destroy(handle);
1903034Sdougm 	return (ret);
1913034Sdougm }
1923034Sdougm 
1933034Sdougm /*
1943034Sdougm  * check_authorizations(instname, flags)
1953034Sdougm  *
1963034Sdougm  * check all the needed authorizations for the user in this service
1973034Sdougm  * instance. Return value of 1(true) or 0(false) indicates whether
1983034Sdougm  * there are authorizations for the user or not.
1993034Sdougm  */
2003034Sdougm 
2013034Sdougm static int
2023034Sdougm check_authorizations(char *instname, int flags)
2033034Sdougm {
2043034Sdougm 	int ret1 = 0;
2053034Sdougm 	int ret2 = 0;
2063034Sdougm 	int ret;
2073034Sdougm 
2083034Sdougm 	if (flags & SVC_SET)
2093034Sdougm 	    ret1 = check_authorization(instname, SVC_SET);
2103034Sdougm 	if (flags & SVC_ACTION)
2113034Sdougm 	    ret2 = check_authorization(instname, SVC_ACTION);
2123034Sdougm 	switch (flags) {
2133034Sdougm 	case SVC_ACTION:
2143034Sdougm 	    ret = ret2;
2153034Sdougm 	    break;
2163034Sdougm 	case SVC_SET:
2173034Sdougm 	    ret = ret1;
2183034Sdougm 	    break;
2193034Sdougm 	case SVC_ACTION|SVC_SET:
2203034Sdougm 	    ret = ret1 & ret2;
2213034Sdougm 	    break;
2223034Sdougm 	default:
2233034Sdougm 	    /* if not flags set, we assume we don't need authorizations */
2243034Sdougm 	    ret = 1;
2253034Sdougm 	}
2263034Sdougm 	return (ret);
2273034Sdougm }
2283034Sdougm 
2293034Sdougm /*
2303082Sdougm  * enable_group(group, updateproto)
2313082Sdougm  *
2323082Sdougm  * enable all the shares in the specified group. This is a helper for
2333082Sdougm  * enable_all_groups in order to simplify regular and subgroup (zfs)
2343082Sdougm  * disabling. Group has already been checked for non-NULL.
2353082Sdougm  */
2363082Sdougm 
2373082Sdougm static void
2383082Sdougm enable_group(sa_group_t group, char *updateproto)
2393082Sdougm {
2403082Sdougm 	sa_share_t share;
2413082Sdougm 
2423082Sdougm 	for (share = sa_get_share(group, NULL);
2433082Sdougm 	    share != NULL;
2443082Sdougm 	    share = sa_get_next_share(share)) {
2453082Sdougm 	    if (updateproto != NULL)
2463082Sdougm 		(void) sa_update_legacy(share, updateproto);
2473082Sdougm 	    (void) sa_enable_share(share, NULL);
2483082Sdougm 	}
2493082Sdougm }
2503082Sdougm 
2513082Sdougm /*
2523082Sdougm  * enable_all_groups(list, setstate, online, updateproto)
2533082Sdougm  *	Given a list of groups, enable each one found.  If updateproto
2543082Sdougm  *	is not NULL, then update all the shares for the protocol that
2553082Sdougm  *	was passed in.
2563034Sdougm  */
2573034Sdougm static int
2583082Sdougm enable_all_groups(struct list *work, int setstate, int online,
2593082Sdougm 	char *updateproto)
2603034Sdougm {
2613034Sdougm 	int ret = SA_OK;
2623034Sdougm 	char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
2633034Sdougm 	char *state;
2643034Sdougm 	char *name;
2653034Sdougm 	char *zfs = NULL;
2663034Sdougm 	sa_group_t group;
2673082Sdougm 	sa_group_t subgroup;
2683034Sdougm 
2693034Sdougm 	while (work != NULL && ret == SA_OK) {
2703034Sdougm 	    group = (sa_group_t)work->item;
2713034Sdougm 	    /* if itemdata != NULL then a single share */
2723034Sdougm 	    if (work->itemdata != NULL) {
2733034Sdougm 		ret = sa_enable_share((sa_share_t)work->itemdata, NULL);
2743034Sdougm 	    }
2753034Sdougm 	    if (setstate)
2763034Sdougm 		ret = sa_set_group_attr(group, "state",
2773034Sdougm 					"enabled");
2783034Sdougm 	    if (ret == SA_OK) {
2793034Sdougm 		/* if itemdata == NULL then the whole group */
2803034Sdougm 		if (work->itemdata == NULL) {
2813082Sdougm 		    zfs = sa_get_group_attr(group, "zfs");
2823082Sdougm 			/*
2833082Sdougm 			 * if the share is managed by ZFS, don't
2843082Sdougm 			 * update any of the protocols since ZFS is
2853082Sdougm 			 * handling this.  updateproto will contain
2863082Sdougm 			 * the name of the protocol that we want to
2873082Sdougm 			 * update legacy files for.
2883082Sdougm 			 */
2893082Sdougm 		    enable_group(group, zfs == NULL ? updateproto : NULL);
2903082Sdougm 		    for (subgroup = sa_get_sub_group(group); subgroup != NULL;
2913082Sdougm 			subgroup = sa_get_next_group(subgroup)) {
2923082Sdougm 			/* never update legacy for ZFS subgroups */
2933082Sdougm 			enable_group(subgroup, NULL);
2943034Sdougm 		    }
2953034Sdougm 		}
2963034Sdougm 		if (online) {
2973082Sdougm 		    zfs = sa_get_group_attr(group, "zfs");
2983034Sdougm 		    name = sa_get_group_attr(group, "name");
2993034Sdougm 		    if (name != NULL) {
3003034Sdougm 			if (zfs == NULL) {
3013034Sdougm 			    (void) snprintf(instance, sizeof (instance),
3023034Sdougm 						"%s:%s",
3033034Sdougm 						SA_SVC_FMRI_BASE, name);
3043034Sdougm 			    state = smf_get_state(instance);
3053034Sdougm 			    if (state == NULL ||
3063034Sdougm 				strcmp(state, "online") != 0) {
3073034Sdougm 				(void) smf_enable_instance(instance, 0);
3083034Sdougm 				free(state);
3093034Sdougm 			    }
3103034Sdougm 			} else {
3113034Sdougm 			    sa_free_attr_string(zfs);
3123034Sdougm 			    zfs = NULL;
3133034Sdougm 			}
3143034Sdougm 			if (name != NULL)
3153034Sdougm 			    sa_free_attr_string(name);
3163034Sdougm 		    }
3173034Sdougm 		}
3183034Sdougm 		work = work->next;
3193034Sdougm 	    }
3203034Sdougm 	}
3213034Sdougm 	if (ret == SA_OK) {
3223034Sdougm 	    ret = sa_update_config();
3233034Sdougm 	}
3243034Sdougm 	return (ret);
3253034Sdougm }
3263034Sdougm 
3273034Sdougm /*
3283034Sdougm  * chk_opt(optlistp, security, proto)
3293034Sdougm  *
3303034Sdougm  * Do a sanity check on the optlist provided for the protocol.  This
3313034Sdougm  * is a syntax check and verification that the property is either a
3323034Sdougm  * general or specific to a names optionset.
3333034Sdougm  */
3343034Sdougm 
3353034Sdougm static int
3363034Sdougm chk_opt(struct options *optlistp, int security, char *proto)
3373034Sdougm {
3383034Sdougm 	struct options *optlist;
3393034Sdougm 	char *sep = "";
3403034Sdougm 	int notfirst = 0;
3413034Sdougm 	int ret;
3423034Sdougm 
3433034Sdougm 	for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
3443034Sdougm 	    char *optname;
3453034Sdougm 
3463034Sdougm 	    optname = optlist->optname;
3473034Sdougm 	    ret = OPT_ADD_OK;
3483034Sdougm 	    /* extract property/value pair */
3493034Sdougm 	    if (sa_is_security(optname, proto)) {
3503034Sdougm 		if (!security)
3513034Sdougm 		    ret = OPT_ADD_SECURITY;
3523034Sdougm 	    } else {
3533034Sdougm 		if (security)
3543034Sdougm 		    ret = OPT_ADD_PROPERTY;
3553034Sdougm 	    }
3563034Sdougm 	    if (ret != OPT_ADD_OK) {
3573034Sdougm 		if (notfirst == 0)
3583034Sdougm 		    (void) printf(gettext("Property syntax error: "));
3593034Sdougm 		switch (ret) {
3603034Sdougm 		case OPT_ADD_SYNTAX:
3613034Sdougm 		    (void) printf(gettext("%ssyntax error: %s"),
3623034Sdougm 				    sep, optname);
3633034Sdougm 		    sep = ", ";
3643034Sdougm 		    break;
3653034Sdougm 		case OPT_ADD_SECURITY:
3663034Sdougm 		    (void) printf(gettext("%s%s requires -S"),
3673034Sdougm 				    optname, sep);
3683034Sdougm 		    sep = ", ";
3693034Sdougm 		    break;
3703034Sdougm 		case OPT_ADD_PROPERTY:
3713034Sdougm 		    (void) printf(gettext("%s%s not supported with -S"),
3723034Sdougm 				    optname, sep);
3733034Sdougm 		    sep = ", ";
3743034Sdougm 		    break;
3753034Sdougm 		}
3763034Sdougm 		notfirst++;
3773034Sdougm 	    }
3783034Sdougm 	}
3793034Sdougm 	if (notfirst) {
3803034Sdougm 	    (void) printf("\n");
3813034Sdougm 	    ret = SA_SYNTAX_ERR;
3823034Sdougm 	}
3833034Sdougm 	return (ret);
3843034Sdougm }
3853034Sdougm 
3863034Sdougm /*
3873034Sdougm  * free_opt(optlist)
3883034Sdougm  *	Free the specified option list.
3893034Sdougm  */
3903034Sdougm static void
3913034Sdougm free_opt(struct options *optlist)
3923034Sdougm {
3933034Sdougm 	struct options *nextopt;
3943034Sdougm 	while (optlist != NULL) {
3953034Sdougm 		nextopt = optlist->next;
3963034Sdougm 		free(optlist);
3973034Sdougm 		optlist = nextopt;
3983034Sdougm 	}
3993034Sdougm }
4003034Sdougm 
4013034Sdougm /*
4023034Sdougm  * check property list for valid properties
4033034Sdougm  * A null value is a remove which is always valid.
4043034Sdougm  */
4053034Sdougm static int
4063034Sdougm valid_options(struct options *optlist, char *proto, void *object, char *sec)
4073034Sdougm {
4083034Sdougm 	int ret = SA_OK;
4093034Sdougm 	struct options *cur;
4103034Sdougm 	sa_property_t prop;
4113034Sdougm 	sa_optionset_t parent = NULL;
4123034Sdougm 
4133034Sdougm 	if (object != NULL) {
4143034Sdougm 	    if (sec == NULL)
4153034Sdougm 		parent = sa_get_optionset(object, proto);
4163034Sdougm 	    else
4173034Sdougm 		parent = sa_get_security(object, sec, proto);
4183034Sdougm 	}
4193034Sdougm 
4203034Sdougm 	for (cur = optlist; cur != NULL; cur = cur->next) {
4213034Sdougm 	    if (cur->optvalue != NULL) {
4223034Sdougm 		prop = sa_create_property(cur->optname, cur->optvalue);
4233034Sdougm 		if (prop == NULL)
4243034Sdougm 		    ret = SA_NO_MEMORY;
4253034Sdougm 		if (ret != SA_OK ||
4263034Sdougm 		    (ret = sa_valid_property(parent, proto, prop)) != SA_OK) {
4273034Sdougm 		    (void) printf(gettext("Could not add property %s: %s\n"),
4283034Sdougm 					cur->optname,
4293034Sdougm 					sa_errorstr(ret));
4303034Sdougm 		}
4313034Sdougm 		(void) sa_remove_property(prop);
4323034Sdougm 	    }
4333034Sdougm 	}
4343034Sdougm 	return (ret);
4353034Sdougm }
4363034Sdougm 
4373034Sdougm /*
4383034Sdougm  * add_optionset(group, optlist, protocol, *err)
4393034Sdougm  *	Add the options in optlist to an optionset and then add the optionset
4403034Sdougm  *	to the group.
4413034Sdougm  *
4423034Sdougm  *	The return value indicates if there was a "change" while errors are
4433034Sdougm  *	returned via the *err parameters.
4443034Sdougm  */
4453034Sdougm static int
4463034Sdougm add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
4473034Sdougm {
4483034Sdougm 	sa_optionset_t optionset;
4493034Sdougm 	int ret = SA_OK;
4503034Sdougm 	int result = 0;
4513034Sdougm 
4523034Sdougm 	optionset = sa_get_optionset(group, proto);
4533034Sdougm 	if (optionset == NULL) {
4543034Sdougm 	    optionset = sa_create_optionset(group, proto);
4553034Sdougm 	    result = 1; /* adding a protocol is a change */
4563034Sdougm 	}
4573034Sdougm 	if (optionset != NULL) {
4583034Sdougm 	    while (optlist != NULL) {
4593034Sdougm 		sa_property_t prop;
4603034Sdougm 		prop = sa_get_property(optionset, optlist->optname);
4613034Sdougm 		if (prop == NULL) {
4623034Sdougm 			/*
4633034Sdougm 			 * add the property, but only if it is
4643034Sdougm 			 * a non-NULL or non-zero length value
4653034Sdougm 			 */
4663034Sdougm 		    if (optlist->optvalue != NULL) {
4673034Sdougm 			prop = sa_create_property(optlist->optname,
4683034Sdougm 						    optlist->optvalue);
4693034Sdougm 			if (prop != NULL) {
4703034Sdougm 			    ret = sa_valid_property(optionset, proto, prop);
4713034Sdougm 			    if (ret != SA_OK) {
4723034Sdougm 				(void) sa_remove_property(prop);
4733034Sdougm 				(void) printf(gettext("Could not add property "
4743034Sdougm 							"%s: %s\n"),
4753034Sdougm 						optlist->optname,
4763034Sdougm 						sa_errorstr(ret));
4773034Sdougm 			    }
4783034Sdougm 			}
4793034Sdougm 			if (ret == SA_OK) {
4803034Sdougm 			    ret = sa_add_property(optionset, prop);
4813034Sdougm 			    if (ret != SA_OK) {
4823034Sdougm 				(void) printf(gettext("Could not add property"
4833034Sdougm 							" %s: %s\n"),
4843034Sdougm 						optlist->optname,
4853034Sdougm 						sa_errorstr(ret));
4863034Sdougm 			    } else {
4873034Sdougm 				/* there was a change */
4883034Sdougm 				result = 1;
4893034Sdougm 			    }
4903034Sdougm 			}
4913034Sdougm 		    }
4923034Sdougm 		} else {
4933034Sdougm 		    ret = sa_update_property(prop, optlist->optvalue);
4943034Sdougm 		    /* should check to see if value changed */
4953034Sdougm 		    if (ret != SA_OK) {
4963034Sdougm 			(void) printf(gettext("Could not update "
4973034Sdougm 						"property %s: %s\n"),
4983034Sdougm 					optlist->optname,
4993034Sdougm 					sa_errorstr(ret));
5003034Sdougm 		    } else {
5013034Sdougm 			result = 1;
5023034Sdougm 		    }
5033034Sdougm 		}
5043034Sdougm 		optlist = optlist->next;
5053034Sdougm 	    }
5063034Sdougm 	    ret = sa_commit_properties(optionset, 0);
5073034Sdougm 	}
5083034Sdougm 	if (err != NULL)
5093034Sdougm 	    *err = ret;
5103034Sdougm 	return (result);
5113034Sdougm }
5123034Sdougm 
5133034Sdougm /*
5143034Sdougm  * sa_create(flags, argc, argv)
5153034Sdougm  *	create a new group
5163034Sdougm  *	this may or may not have a protocol associated with it.
5173034Sdougm  *	No protocol means "all" protocols in this case.
5183034Sdougm  */
5193034Sdougm static int
5203034Sdougm sa_create(int flags, int argc, char *argv[])
5213034Sdougm {
5223034Sdougm 	char *groupname;
5233034Sdougm 
5243034Sdougm 	sa_group_t group;
5253034Sdougm 	int verbose = 0;
5263034Sdougm 	int dryrun = 0;
5273034Sdougm 	int c;
5283034Sdougm 	char *protocol = NULL;
5293034Sdougm 	int ret = SA_OK;
5303034Sdougm 	struct options *optlist = NULL;
5313034Sdougm 	int err = 0;
5323034Sdougm 	int auth;
5333034Sdougm 
5343034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:p:")) != EOF) {
5353034Sdougm 	    switch (c) {
5363034Sdougm 	    case 'v':
5373034Sdougm 		verbose++;
5383034Sdougm 		break;
5393034Sdougm 	    case 'n':
5403034Sdougm 		dryrun++;
5413034Sdougm 		break;
5423034Sdougm 	    case 'P':
5433034Sdougm 		protocol = optarg;
5443034Sdougm 		if (!sa_valid_protocol(protocol)) {
5453034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
5463034Sdougm 					protocol);
5473034Sdougm 		    return (SA_INVALID_PROTOCOL);
5483034Sdougm 		}
5493034Sdougm 		break;
5503034Sdougm 	    case 'p':
5513034Sdougm 		ret = add_opt(&optlist, optarg, 0);
5523034Sdougm 		switch (ret) {
5533034Sdougm 		case OPT_ADD_SYNTAX:
5543034Sdougm 		    (void) printf(gettext("Property syntax error for "
5553034Sdougm 						"property: %s\n"),
5563034Sdougm 				    optarg);
5573034Sdougm 		    return (SA_SYNTAX_ERR);
5583034Sdougm 		case OPT_ADD_SECURITY:
5593034Sdougm 		    (void) printf(gettext("Security properties need "
5603034Sdougm 					"to be set with set-security: %s\n"),
5613034Sdougm 				    optarg);
5623034Sdougm 		    return (SA_SYNTAX_ERR);
5633034Sdougm 		default:
5643034Sdougm 		    break;
5653034Sdougm 		}
5663034Sdougm 
5673034Sdougm 		break;
5683034Sdougm 	    default:
5693034Sdougm 	    case 'h':
5703034Sdougm 	    case '?':
5713034Sdougm 		(void) printf(gettext("usage: %s\n"),
5723034Sdougm 				sa_get_usage(USAGE_CREATE));
5733034Sdougm 		return (0);
5743034Sdougm 	    }
5753034Sdougm 	}
5763034Sdougm 
5773034Sdougm 	if (optind >= argc) {
5783034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_CREATE));
5793034Sdougm 	    (void) printf(gettext("\tgroup must be specified.\n"));
5803034Sdougm 	    return (SA_BAD_PATH);
5813034Sdougm 	}
5823034Sdougm 
5833034Sdougm 	if ((optind + 1) < argc) {
5843034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_CREATE));
5853034Sdougm 	    (void) printf(gettext("\textraneous group(s) at end\n"));
5863034Sdougm 	    return (SA_SYNTAX_ERR);
5873034Sdougm 	}
5883034Sdougm 
5893034Sdougm 	if (protocol == NULL && optlist != NULL) {
5903034Sdougm 	    /* lookup default protocol */
5913034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_CREATE));
5923034Sdougm 	    (void) printf(gettext("\tprotocol must be specified "
5933034Sdougm 				"with properties\n"));
5943034Sdougm 	    return (SA_INVALID_PROTOCOL);
5953034Sdougm 	}
5963034Sdougm 
5973034Sdougm 	if (optlist != NULL)
5983034Sdougm 	    ret = chk_opt(optlist, 0, protocol);
5993034Sdougm 	if (ret == OPT_ADD_SECURITY) {
6003034Sdougm 	    (void) printf(gettext("Security properties not "
6013034Sdougm 				"supported with create\n"));
6023034Sdougm 	    return (SA_SYNTAX_ERR);
6033034Sdougm 	}
6043034Sdougm 
6053034Sdougm 	/*
6063034Sdougm 	 * if a group already exists, we can only add a new protocol
6073034Sdougm 	 * to it and not create a new one or add the same protocol
6083034Sdougm 	 * again.
6093034Sdougm 	 */
6103034Sdougm 
6113034Sdougm 	groupname = argv[optind];
6123034Sdougm 
6133034Sdougm 	auth = check_authorizations(groupname, flags);
6143034Sdougm 
6153034Sdougm 	group = sa_get_group(groupname);
6163034Sdougm 	if (group != NULL) {
6173034Sdougm 	    /* group exists so must be a protocol add */
6183034Sdougm 	    if (protocol != NULL) {
6193034Sdougm 		if (has_protocol(group, protocol)) {
6203034Sdougm 		    (void) printf(gettext("Group \"%s\" already exists"
6213034Sdougm 						" with protocol %s\n"),
6223034Sdougm 					groupname, protocol);
6233034Sdougm 		    ret = SA_DUPLICATE_NAME;
6243034Sdougm 		}
6253034Sdougm 	    } else {
6263034Sdougm 		/* must add new protocol */
6273034Sdougm 		(void) printf(gettext("Group already exists and no protocol"
6283034Sdougm 					" specified.\n"));
6293034Sdougm 		ret = SA_DUPLICATE_NAME;
6303034Sdougm 	    }
6313034Sdougm 	} else {
6323034Sdougm 		/*
6333034Sdougm 		 * is it a valid name? Must comply with SMF instance
6343034Sdougm 		 * name restrictions.
6353034Sdougm 		 */
6363034Sdougm 	    if (!sa_valid_group_name(groupname)) {
6373034Sdougm 		ret = SA_INVALID_NAME;
6383034Sdougm 		(void) printf(gettext("Invalid group name: %s\n"), groupname);
6393034Sdougm 	    }
6403034Sdougm 	}
6413034Sdougm 	if (ret == SA_OK) {
6423034Sdougm 	    /* check protocol vs optlist */
6433034Sdougm 	    if (optlist != NULL) {
6443034Sdougm 		/* check options, if any, for validity */
6453034Sdougm 		ret = valid_options(optlist, protocol, group, NULL);
6463034Sdougm 	    }
6473034Sdougm 	}
6483034Sdougm 	if (ret == SA_OK && !dryrun) {
6493034Sdougm 	    if (group == NULL) {
6503034Sdougm 		group = sa_create_group((char *)groupname, &err);
6513034Sdougm 	    }
6523034Sdougm 	    if (group != NULL) {
6533034Sdougm 		sa_optionset_t optionset;
6543034Sdougm 		if (optlist != NULL) {
6553034Sdougm 		    (void) add_optionset(group, optlist, protocol, &ret);
6563034Sdougm 		} else if (protocol != NULL) {
6573034Sdougm 		    optionset = sa_create_optionset(group, protocol);
6583034Sdougm 		    if (optionset == NULL)
6593034Sdougm 			ret = SA_NO_MEMORY;
6603034Sdougm 		} else if (protocol == NULL) {
6613034Sdougm 		    char **protolist;
6623034Sdougm 		    int numprotos, i;
6633034Sdougm 		    numprotos = sa_get_protocols(&protolist);
6643034Sdougm 		    for (i = 0; i < numprotos; i++) {
6653034Sdougm 			optionset = sa_create_optionset(group, protolist[i]);
6663034Sdougm 		    }
6673034Sdougm 		    if (protolist != NULL)
6683034Sdougm 			free(protolist);
6693034Sdougm 		}
6703034Sdougm 		/*
6713034Sdougm 		 * we have a group and legal additions
6723034Sdougm 		 */
6733034Sdougm 		if (ret == SA_OK) {
6743034Sdougm 			/*
6753034Sdougm 			 * commit to configuration for protocols that
6763034Sdougm 			 * need to do block updates. For NFS, this
6773034Sdougm 			 * doesn't do anything but it will be run for
6783034Sdougm 			 * all protocols that implement the
6793034Sdougm 			 * appropriate plugin.
6803034Sdougm 			 */
6813034Sdougm 		    ret = sa_update_config();
6823034Sdougm 		} else {
6833034Sdougm 		    if (group != NULL)
6843034Sdougm 			(void) sa_remove_group(group);
6853034Sdougm 		}
6863034Sdougm 	    } else {
6873034Sdougm 		ret = err;
6883034Sdougm 		(void) printf(gettext("Could not create group: %s\n"),
6893034Sdougm 			sa_errorstr(ret));
6903034Sdougm 	    }
6913034Sdougm 	}
6923034Sdougm 	if (dryrun && ret == SA_OK && !auth && verbose) {
6933034Sdougm 	    (void) printf(gettext("Command would fail: %s\n"),
6943034Sdougm 			sa_errorstr(SA_NO_PERMISSION));
6953034Sdougm 	    ret = SA_NO_PERMISSION;
6963034Sdougm 	}
6973034Sdougm 	free_opt(optlist);
6983034Sdougm 	return (ret);
6993034Sdougm }
7003034Sdougm 
7013034Sdougm /*
7023034Sdougm  * group_status(group)
7033034Sdougm  *
7043034Sdougm  * return the current status (enabled/disabled) of the group.
7053034Sdougm  */
7063034Sdougm 
7073034Sdougm static char *
7083034Sdougm group_status(sa_group_t group)
7093034Sdougm {
7103034Sdougm 	char *state;
7113034Sdougm 	int enabled = 0;
7123034Sdougm 
7133034Sdougm 	state = sa_get_group_attr(group, "state");
7143034Sdougm 	if (state != NULL) {
7153034Sdougm 	    if (strcmp(state, "enabled") == 0) {
7163034Sdougm 		enabled = 1;
7173034Sdougm 	    }
7183034Sdougm 	    sa_free_attr_string(state);
7193034Sdougm 	}
7203034Sdougm 	return (enabled ? gettext("enabled") : gettext("disabled"));
7213034Sdougm }
7223034Sdougm 
7233034Sdougm /*
7243034Sdougm  * sa_delete(flags, argc, argv)
7253034Sdougm  *
7263034Sdougm  *	Delete a group.
7273034Sdougm  */
7283034Sdougm 
7293034Sdougm static int
7303034Sdougm sa_delete(int flags, int argc, char *argv[])
7313034Sdougm {
7323034Sdougm 	char *groupname;
7333034Sdougm 	sa_group_t group;
7343034Sdougm 	sa_share_t share;
7353034Sdougm 	int verbose = 0;
7363034Sdougm 	int dryrun = 0;
7373034Sdougm 	int force = 0;
7383034Sdougm 	int c;
7393034Sdougm 	char *protocol = NULL;
7403034Sdougm 	char *sectype = NULL;
7413034Sdougm 	int ret = SA_OK;
7423034Sdougm 	int auth;
7433034Sdougm 
7443034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
7453034Sdougm 	    switch (c) {
7463034Sdougm 	    case 'v':
7473034Sdougm 		verbose++;
7483034Sdougm 		break;
7493034Sdougm 	    case 'n':
7503034Sdougm 		dryrun++;
7513034Sdougm 		break;
7523034Sdougm 	    case 'P':
7533034Sdougm 		protocol = optarg;
7543034Sdougm 		if (!sa_valid_protocol(protocol)) {
7553034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
7563034Sdougm 				    protocol);
7573034Sdougm 		    return (SA_INVALID_PROTOCOL);
7583034Sdougm 		}
7593034Sdougm 		break;
7603034Sdougm 	    case 'S':
7613034Sdougm 		sectype = optarg;
7623034Sdougm 		break;
7633034Sdougm 	    case 'f':
7643034Sdougm 		force++;
7653034Sdougm 		break;
7663034Sdougm 	    default:
7673034Sdougm 	    case 'h':
7683034Sdougm 	    case '?':
7693034Sdougm 		(void) printf(gettext("usage: %s\n"),
7703034Sdougm 				sa_get_usage(USAGE_DELETE));
7713034Sdougm 		return (0);
7723034Sdougm 	    }
7733034Sdougm 	}
7743034Sdougm 
7753034Sdougm 	if (optind >= argc) {
7763034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_DELETE));
7773034Sdougm 	    (void) printf(gettext("\tgroup must be specified.\n"));
7783034Sdougm 	    return (SA_SYNTAX_ERR);
7793034Sdougm 	}
7803034Sdougm 
7813034Sdougm 	if ((optind + 1) < argc) {
7823034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_DELETE));
7833034Sdougm 	    (void) printf(gettext("\textraneous group(s) at end\n"));
7843034Sdougm 	    return (SA_SYNTAX_ERR);
7853034Sdougm 	}
7863034Sdougm 
7873034Sdougm 	if (sectype != NULL && protocol == NULL) {
7883034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_DELETE));
7893034Sdougm 	    (void) printf(gettext("\tsecurity requires protocol to be "
7903034Sdougm 					"specified.\n"));
7913034Sdougm 	    return (SA_SYNTAX_ERR);
7923034Sdougm 	}
7933034Sdougm 
7943034Sdougm 	/*
7953034Sdougm 	 * Determine if the group already exists since it must in
7963034Sdougm 	 * order to be removed.
7973034Sdougm 	 *
7983034Sdougm 	 * We can delete when:
7993034Sdougm 	 *
8003034Sdougm 	 *	- group is empty
8013034Sdougm 	 *	- force flag is set
8023034Sdougm 	 *	- if protocol specified, only delete the protocol
8033034Sdougm 	 */
8043034Sdougm 
8053034Sdougm 	groupname = argv[optind];
8063034Sdougm 	group = sa_get_group(groupname);
8073034Sdougm 	if (group == NULL) {
8083034Sdougm 		ret = SA_NO_SUCH_GROUP;
8093034Sdougm 	} else {
8103034Sdougm 	    auth = check_authorizations(groupname, flags);
8113034Sdougm 	    if (protocol == NULL) {
8123034Sdougm 		share = sa_get_share(group, NULL);
8133034Sdougm 		if (share != NULL)
8143034Sdougm 		    ret = SA_BUSY;
8153034Sdougm 		if (share == NULL || (share != NULL && force == 1)) {
8163034Sdougm 		    ret = SA_OK;
8173034Sdougm 		    if (!dryrun) {
8183034Sdougm 			while (share != NULL) {
8193034Sdougm 			    sa_share_t next_share;
8203034Sdougm 			    next_share = sa_get_next_share(share);
8213034Sdougm 				/*
8223034Sdougm 				 * need to do the disable of each
8233034Sdougm 				 * share, but don't actually do
8243034Sdougm 				 * anything on a dryrun.
8253034Sdougm 				 */
8263034Sdougm 			    ret = sa_disable_share(share, NULL);
8273034Sdougm 			    ret = sa_remove_share(share);
8283034Sdougm 			    share = next_share;
8293034Sdougm 			}
8303034Sdougm 			ret = sa_remove_group(group);
8313034Sdougm 		    }
8323034Sdougm 		}
8333034Sdougm 		/* commit to configuration if not a dryrun */
8343034Sdougm 		if (!dryrun && ret == SA_OK) {
8353034Sdougm 		    ret = sa_update_config();
8363034Sdougm 		}
8373034Sdougm 	    } else {
8383034Sdougm 		/* a protocol delete */
8393034Sdougm 		sa_optionset_t optionset;
8403034Sdougm 		sa_security_t security;
8413034Sdougm 		if (sectype != NULL) {
8423034Sdougm 		    /* only delete specified security */
8433034Sdougm 		    security = sa_get_security(group, sectype, protocol);
8443034Sdougm 		    if (security != NULL && !dryrun) {
8453034Sdougm 			ret = sa_destroy_security(security);
8463034Sdougm 		    } else {
8473034Sdougm 			ret = SA_INVALID_PROTOCOL;
8483034Sdougm 		    }
8493034Sdougm 		} else {
8503034Sdougm 		    optionset = sa_get_optionset(group, protocol);
8513034Sdougm 		    if (optionset != NULL && !dryrun) {
8523034Sdougm 			/* have an optionset with protocol to delete */
8533034Sdougm 			ret = sa_destroy_optionset(optionset);
8543034Sdougm 			/*
8553034Sdougm 			 * now find all security sets for the protocol
8563034Sdougm 			 * and remove them. Don't remove other
8573034Sdougm 			 * protocols.
8583034Sdougm 			 */
8593034Sdougm 			for (security = sa_get_security(group, NULL, NULL);
8603034Sdougm 			    ret == SA_OK && security != NULL;
8613034Sdougm 			    security = sa_get_next_security(security)) {
8623034Sdougm 			    char *secprot;
8633034Sdougm 
8643034Sdougm 			    secprot = sa_get_security_attr(security, "type");
8653034Sdougm 			    if (secprot != NULL &&
8663034Sdougm 				strcmp(secprot, protocol) == 0)
8673034Sdougm 				ret = sa_destroy_security(security);
8683034Sdougm 			    if (secprot != NULL)
8693034Sdougm 				sa_free_attr_string(secprot);
8703034Sdougm 			}
8713034Sdougm 		    } else {
8723034Sdougm 			if (!dryrun)
8733034Sdougm 			    ret = SA_INVALID_PROTOCOL;
8743034Sdougm 		    }
8753034Sdougm 		}
8763034Sdougm 	    }
8773034Sdougm 	}
8783034Sdougm 	if (ret != SA_OK) {
8793034Sdougm 	    (void) printf(gettext("Could not delete group: %s\n"),
8803034Sdougm 				sa_errorstr(ret));
8813034Sdougm 	} else if (dryrun && !auth && verbose) {
8823034Sdougm 	    (void) printf(gettext("Command would fail: %s\n"),
8833034Sdougm 			sa_errorstr(SA_NO_PERMISSION));
8843034Sdougm 	}
8853034Sdougm 	return (ret);
8863034Sdougm }
8873034Sdougm 
8883034Sdougm /*
8893034Sdougm  * strndupr(*buff, str, buffsize)
8903034Sdougm  *
8913034Sdougm  * used with small strings to duplicate and possibly increase the
8923034Sdougm  * buffer size of a string.
8933034Sdougm  */
8943034Sdougm static char *
8953034Sdougm strndupr(char *buff, char *str, int *buffsize)
8963034Sdougm {
8973034Sdougm 	int limit;
8983034Sdougm 	char *orig_buff = buff;
8993034Sdougm 
9003034Sdougm 	if (buff == NULL) {
9013034Sdougm 	    buff = (char *)malloc(64);
9023034Sdougm 	    if (buff == NULL)
9033034Sdougm 		return (NULL);
9043034Sdougm 	    *buffsize = 64;
9053034Sdougm 	    buff[0] = '\0';
9063034Sdougm 	}
9073034Sdougm 	limit = strlen(buff) + strlen(str) + 1;
9083034Sdougm 	if (limit > *buffsize) {
9093034Sdougm 	    limit = *buffsize = *buffsize + ((limit / 64) + 64);
9103034Sdougm 	    buff = realloc(buff, limit);
9113034Sdougm 	}
9123034Sdougm 	if (buff != NULL) {
9133034Sdougm 	    (void) strcat(buff, str);
9143034Sdougm 	} else {
9153034Sdougm 	    /* if it fails, fail it hard */
9163034Sdougm 	    if (orig_buff != NULL)
9173034Sdougm 		free(orig_buff);
9183034Sdougm 	}
9193034Sdougm 	return (buff);
9203034Sdougm }
9213034Sdougm 
9223034Sdougm /*
9233034Sdougm  * group_proto(group)
9243034Sdougm  *
9253034Sdougm  * return a string of all the protocols (space separated) associated
9263034Sdougm  * with this group.
9273034Sdougm  */
9283034Sdougm 
9293034Sdougm static char *
9303034Sdougm group_proto(sa_group_t group)
9313034Sdougm {
9323034Sdougm 	sa_optionset_t optionset;
9333034Sdougm 	char *proto;
9343034Sdougm 	char *buff = NULL;
9353034Sdougm 	int buffsize = 0;
9363034Sdougm 	int addspace = 0;
9373034Sdougm 	/*
9383034Sdougm 	 * get the protocol list by finding the optionsets on this
9393034Sdougm 	 * group and extracting the type value. The initial call to
9403034Sdougm 	 * strndupr() initailizes buff.
9413034Sdougm 	 */
9423034Sdougm 	buff = strndupr(buff, "", &buffsize);
9433034Sdougm 	if (buff != NULL) {
9443034Sdougm 	    for (optionset = sa_get_optionset(group, NULL);
9453034Sdougm 		optionset != NULL && buff != NULL;
9463034Sdougm 		optionset = sa_get_next_optionset(optionset)) {
9473034Sdougm 		/*
9483034Sdougm 		 * extract out the protocol type from this optionset
9493034Sdougm 		 * and append it to the buffer "buff". strndupr() will
9503034Sdougm 		 * reallocate space as necessay.
9513034Sdougm 		 */
9523034Sdougm 		proto = sa_get_optionset_attr(optionset, "type");
9533034Sdougm 		if (proto != NULL) {
9543034Sdougm 		    if (addspace++)
9553034Sdougm 			buff = strndupr(buff, " ", &buffsize);
9563034Sdougm 		    buff = strndupr(buff, proto, &buffsize);
9573034Sdougm 		    sa_free_attr_string(proto);
9583034Sdougm 		}
9593034Sdougm 	    }
9603034Sdougm 	}
9613034Sdougm 	return (buff);
9623034Sdougm }
9633034Sdougm 
9643034Sdougm /*
9653034Sdougm  * sa_list(flags, argc, argv)
9663034Sdougm  *
9673034Sdougm  * implements the "list" subcommand to list groups and optionally
9683034Sdougm  * their state and protocols.
9693034Sdougm  */
9703034Sdougm 
9713034Sdougm static int
9723034Sdougm sa_list(int flags, int argc, char *argv[])
9733034Sdougm {
9743034Sdougm 	sa_group_t group;
9753034Sdougm 	int verbose = 0;
9763034Sdougm 	int c;
9773034Sdougm 	char *protocol = NULL;
9783034Sdougm #ifdef lint
9793034Sdougm 	flags = flags;
9803034Sdougm #endif
9813034Sdougm 
9823034Sdougm 	while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
9833034Sdougm 	    switch (c) {
9843034Sdougm 	    case 'v':
9853034Sdougm 		verbose++;
9863034Sdougm 		break;
9873034Sdougm 	    case 'P':
9883034Sdougm 		protocol = optarg;
9893034Sdougm 		if (!sa_valid_protocol(protocol)) {
9903034Sdougm 		    (void) printf(gettext("Invalid protocol specified:"
9913034Sdougm 					    "%s\n"),
9923034Sdougm 					protocol);
9933034Sdougm 		    return (SA_INVALID_PROTOCOL);
9943034Sdougm 		}
9953034Sdougm 		break;
9963034Sdougm 	    default:
9973034Sdougm 	    case 'h':
9983034Sdougm 	    case '?':
9993034Sdougm 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_LIST));
10003034Sdougm 		return (0);
10013034Sdougm 	    }
10023034Sdougm 	}
10033034Sdougm 
10043034Sdougm 	for (group = sa_get_group(NULL); group != NULL;
10053034Sdougm 	    group = sa_get_next_group(group)) {
10063034Sdougm 	    char *name;
10073034Sdougm 	    char *proto;
10083034Sdougm 	    if (protocol == NULL || has_protocol(group, protocol)) {
10093034Sdougm 		name = sa_get_group_attr(group, "name");
10103034Sdougm 		if (name != NULL && (verbose > 1 || name[0] != '#')) {
10113034Sdougm 		    (void) printf("%s", (char *)name);
10123034Sdougm 		    if (verbose) {
10133034Sdougm 			/*
10143034Sdougm 			 * need the list of protocols
10153034Sdougm 			 * and current status once
10163034Sdougm 			 * available.
10173034Sdougm 			 */
10183034Sdougm 			(void) printf("\t%s", group_status(group));
10193034Sdougm 			proto = group_proto(group);
10203034Sdougm 			if (proto != NULL) {
10213034Sdougm 			    (void) printf("\t%s", (char *)proto);
10223034Sdougm 			    free(proto);
10233034Sdougm 			}
10243034Sdougm 		    }
10253034Sdougm 		    (void) printf("\n");
10263034Sdougm 		}
10273034Sdougm 		if (name != NULL)
10283034Sdougm 		    sa_free_attr_string(name);
10293034Sdougm 	    }
10303034Sdougm 	}
10313034Sdougm 	return (0);
10323034Sdougm }
10333034Sdougm 
10343034Sdougm /*
10353034Sdougm  * out_properties(optionset, proto, sec)
10363034Sdougm  *
10373034Sdougm  * Format the properties and encode the protocol and optional named
10383034Sdougm  * optionset into the string.
10393034Sdougm  *
10403034Sdougm  * format is protocol[:name]=(property-list)
10413034Sdougm  */
10423034Sdougm 
10433034Sdougm static void
10443034Sdougm out_properties(sa_optionset_t optionset, char *proto, char *sec)
10453034Sdougm {
10463034Sdougm 	char *type;
10473034Sdougm 	char *value;
10483034Sdougm 	int spacer;
10493034Sdougm 	sa_property_t prop;
10503034Sdougm 
10513034Sdougm 	if (sec == NULL) {
10523034Sdougm 	    (void) printf(" %s=(", proto ? proto : gettext("all"));
10533034Sdougm 	} else {
10543034Sdougm 	    (void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
10553034Sdougm 	}
10563034Sdougm 
10573034Sdougm 	for (spacer = 0, prop = sa_get_property(optionset, NULL);
10583034Sdougm 	    prop != NULL; prop = sa_get_next_property(prop)) {
10593034Sdougm 
10603034Sdougm 		/*
10613034Sdougm 		 * extract the property name/value and output with
10623034Sdougm 		 * appropriate spacing. I.e. no prefixed space the
10633034Sdougm 		 * first time through but a space on subsequent
10643034Sdougm 		 * properties.
10653034Sdougm 		 */
10663034Sdougm 	    type = sa_get_property_attr(prop, "type");
10673034Sdougm 	    value = sa_get_property_attr(prop, "value");
10683034Sdougm 	    if (type != NULL) {
10693034Sdougm 		(void) printf("%s%s=", spacer ? " " : "",	type);
10703034Sdougm 		spacer = 1;
10713034Sdougm 		if (value != NULL)
10723034Sdougm 		    (void) printf("\"%s\"", value);
10733034Sdougm 		else
10743034Sdougm 		    (void) printf("\"\"");
10753034Sdougm 	    }
10763034Sdougm 	    if (type != NULL)
10773034Sdougm 		sa_free_attr_string(type);
10783034Sdougm 	    if (value != NULL)
10793034Sdougm 		sa_free_attr_string(value);
10803034Sdougm 	}
10813034Sdougm 	(void) printf(")");
10823034Sdougm }
10833034Sdougm 
10843034Sdougm /*
10853034Sdougm  * show_properties(group, protocol, prefix)
10863034Sdougm  *
10873034Sdougm  * print the properties for a group. If protocol is NULL, do all
10883034Sdougm  * protocols otherwise only the specified protocol. All security
10893034Sdougm  * (named groups specific to the protocol) are included.
10903034Sdougm  *
10913034Sdougm  * The "prefix" is always applied. The caller knows whether it wants
10923034Sdougm  * some type of prefix string (white space) or not.  Once the prefix
10933034Sdougm  * has been output, it is reduced to the zero length string for the
10943034Sdougm  * remainder of the property output.
10953034Sdougm  */
10963034Sdougm 
10973034Sdougm static void
10983034Sdougm show_properties(sa_group_t group, char *protocol, char *prefix)
10993034Sdougm {
11003034Sdougm 	sa_optionset_t optionset;
11013034Sdougm 	sa_security_t security;
11023034Sdougm 	char *value;
11033034Sdougm 	char *secvalue;
11043034Sdougm 
11053034Sdougm 	if (protocol != NULL) {
11063034Sdougm 	    optionset = sa_get_optionset(group, protocol);
11073034Sdougm 	    if (optionset != NULL) {
11083034Sdougm 		(void) printf("%s", prefix);
11093034Sdougm 		prefix = "";
11103034Sdougm 		out_properties(optionset, protocol, NULL);
11113034Sdougm 	    }
11123034Sdougm 	    security = sa_get_security(group, protocol, NULL);
11133034Sdougm 	    if (security != NULL) {
11143034Sdougm 		(void) printf("%s", prefix);
11153034Sdougm 		prefix = "";
11163034Sdougm 		out_properties(security, protocol, NULL);
11173034Sdougm 	    }
11183034Sdougm 	} else {
11193034Sdougm 	    for (optionset = sa_get_optionset(group, protocol);
11203034Sdougm 		optionset != NULL;
11213034Sdougm 		optionset = sa_get_next_optionset(optionset)) {
11223034Sdougm 
11233034Sdougm 		value = sa_get_optionset_attr(optionset, "type");
11243034Sdougm 		(void) printf("%s", prefix);
11253034Sdougm 		prefix = "";
11263034Sdougm 		out_properties(optionset, value, 0);
11273034Sdougm 		if (value != NULL)
11283034Sdougm 		    sa_free_attr_string(value);
11293034Sdougm 	    }
11303034Sdougm 	    for (security = sa_get_security(group, NULL, protocol);
11313034Sdougm 		security != NULL;
11323034Sdougm 		security = sa_get_next_security(security)) {
11333034Sdougm 
11343034Sdougm 		value = sa_get_security_attr(security, "type");
11353034Sdougm 		secvalue = sa_get_security_attr(security, "sectype");
11363034Sdougm 		(void) printf("%s", prefix);
11373034Sdougm 		prefix = "";
11383034Sdougm 		out_properties(security, value, secvalue);
11393034Sdougm 		if (value != NULL)
11403034Sdougm 		    sa_free_attr_string(value);
11413034Sdougm 		if (secvalue != NULL)
11423034Sdougm 		    sa_free_attr_string(secvalue);
11433034Sdougm 	    }
11443034Sdougm 	}
11453034Sdougm }
11463034Sdougm 
11473034Sdougm /*
11483034Sdougm  * show_group(group, verbose, properties, proto, subgroup)
11493034Sdougm  *
11503034Sdougm  * helper function to show the contents of a group.
11513034Sdougm  */
11523034Sdougm 
11533034Sdougm static void
11543034Sdougm show_group(sa_group_t group, int verbose, int properties, char *proto,
11553034Sdougm 		char *subgroup)
11563034Sdougm {
11573034Sdougm 	sa_share_t share;
11583034Sdougm 	char *groupname;
11593034Sdougm 	char *sharepath;
11603034Sdougm 	char *resource;
11613034Sdougm 	char *description;
11623034Sdougm 	char *type;
11633034Sdougm 	char *zfs = NULL;
11643034Sdougm 	int iszfs = 0;
11653034Sdougm 
11663034Sdougm 	groupname = sa_get_group_attr(group, "name");
11673034Sdougm 	if (groupname != NULL) {
11683034Sdougm 	    if (proto != NULL && !has_protocol(group, proto)) {
11693034Sdougm 		sa_free_attr_string(groupname);
11703034Sdougm 		return;
11713034Sdougm 	    }
11723034Sdougm 		/*
11733034Sdougm 		 * check to see if the group is managed by ZFS. If
11743034Sdougm 		 * there is an attribute, then it is. A non-NULL zfs
11753034Sdougm 		 * variable will trigger the different way to display
11763034Sdougm 		 * and will remove the transient property indicator
11773034Sdougm 		 * from the output.
11783034Sdougm 		 */
11793034Sdougm 	    zfs = sa_get_group_attr(group, "zfs");
11803034Sdougm 	    if (zfs != NULL) {
11813034Sdougm 		iszfs = 1;
11823034Sdougm 		sa_free_attr_string(zfs);
11833034Sdougm 	    }
11843034Sdougm 	    share = sa_get_share(group, NULL);
11853034Sdougm 	    if (subgroup == NULL)
11863034Sdougm 		(void) printf("%s", groupname);
11873034Sdougm 	    else
11883034Sdougm 		(void) printf("    %s/%s", subgroup, groupname);
11893034Sdougm 	    if (properties) {
11903034Sdougm 		show_properties(group, proto, "");
11913034Sdougm 	    }
11923034Sdougm 	    (void) printf("\n");
11933034Sdougm 	    if (strcmp(groupname, "zfs") == 0) {
11943034Sdougm 		sa_group_t zgroup;
11953034Sdougm 
11963034Sdougm 		for (zgroup = sa_get_sub_group(group); zgroup != NULL;
11973034Sdougm 		    zgroup = sa_get_next_group(zgroup)) {
11983034Sdougm 		    show_group(zgroup, verbose, properties, proto, "zfs");
11993034Sdougm 		}
12003034Sdougm 		sa_free_attr_string(groupname);
12013034Sdougm 		return;
12023034Sdougm 	    }
12033034Sdougm 		/*
12043034Sdougm 		 * have a group, so list the contents. Resource and
12053034Sdougm 		 * description are only listed if verbose is set.
12063034Sdougm 		 */
12073034Sdougm 	    for (share = sa_get_share(group, NULL); share != NULL;
12083034Sdougm 		share = sa_get_next_share(share)) {
12093034Sdougm 		sharepath = sa_get_share_attr(share, "path");
12103034Sdougm 		if (sharepath != NULL) {
12113034Sdougm 		    if (verbose) {
12123034Sdougm 			resource = sa_get_share_attr(share, "resource");
12133034Sdougm 			description = sa_get_share_description(share);
12143034Sdougm 			type = sa_get_share_attr(share, "type");
12153034Sdougm 			if (type != NULL && !iszfs &&
12163034Sdougm 				strcmp(type, "transient") == 0)
12173034Sdougm 			    (void) printf("\t* ");
12183034Sdougm 			else
12193034Sdougm 			    (void) printf("\t  ");
12203034Sdougm 			if (resource != NULL && strlen(resource) > 0) {
12213034Sdougm 			    (void) printf("%s=%s", resource, sharepath);
12223034Sdougm 			} else {
12233034Sdougm 			    (void) printf("%s", sharepath);
12243034Sdougm 			}
12253034Sdougm 			if (resource != NULL)
12263034Sdougm 			    sa_free_attr_string(resource);
12273034Sdougm 			if (properties)
12283034Sdougm 			    show_properties(share, NULL, "\t");
12293034Sdougm 			if (description != NULL) {
12303034Sdougm 			    if (strlen(description) > 0) {
12313034Sdougm 				(void) printf("\t\"%s\"", description);
12323034Sdougm 			    }
12333034Sdougm 			    sa_free_share_description(description);
12343034Sdougm 			}
12353034Sdougm 			if (type != NULL)
12363034Sdougm 			    sa_free_attr_string(type);
12373034Sdougm 		    } else {
12383034Sdougm 			(void) printf("\t%s", sharepath);
12393034Sdougm 			if (properties)
12403034Sdougm 			    show_properties(share, NULL, "\t");
12413034Sdougm 		    }
12423034Sdougm 		    (void) printf("\n");
12433034Sdougm 		    sa_free_attr_string(sharepath);
12443034Sdougm 		}
12453034Sdougm 	    }
12463034Sdougm 	}
12473034Sdougm 	if (groupname != NULL) {
12483034Sdougm 		sa_free_attr_string(groupname);
12493034Sdougm 	}
12503034Sdougm }
12513034Sdougm 
12523034Sdougm /*
12533034Sdougm  * show_group_xml_init()
12543034Sdougm  *
12553034Sdougm  * Create an XML document that will be used to display config info via
12563034Sdougm  * XML format.
12573034Sdougm  */
12583034Sdougm 
12593034Sdougm xmlDocPtr
12603034Sdougm show_group_xml_init()
12613034Sdougm {
12623034Sdougm 	xmlDocPtr doc;
12633034Sdougm 	xmlNodePtr root;
12643034Sdougm 
12653034Sdougm 	doc = xmlNewDoc((xmlChar *)"1.0");
12663034Sdougm 	if (doc != NULL) {
12673034Sdougm 	    root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
12683034Sdougm 	    if (root != NULL)
12693034Sdougm 		xmlDocSetRootElement(doc, root);
12703034Sdougm 	}
12713034Sdougm 	return (doc);
12723034Sdougm }
12733034Sdougm 
12743034Sdougm /*
12753034Sdougm  * show_group_xml(doc, group)
12763034Sdougm  *
12773034Sdougm  * Copy the group info into the XML doc.
12783034Sdougm  */
12793034Sdougm 
12803034Sdougm static void
12813034Sdougm show_group_xml(xmlDocPtr doc, sa_group_t group)
12823034Sdougm {
12833034Sdougm 	xmlNodePtr node;
12843034Sdougm 	xmlNodePtr root;
12853034Sdougm 
12863034Sdougm 	root = xmlDocGetRootElement(doc);
12873034Sdougm 	node = xmlCopyNode((xmlNodePtr)group, 1);
12883034Sdougm 	if (node != NULL && root != NULL) {
12893034Sdougm 	    xmlAddChild(root, node);
12903034Sdougm 		/*
12913034Sdougm 		 * In the future, we may have interally used tags that
12923034Sdougm 		 * should not appear in the XML output. Remove
12933034Sdougm 		 * anything we don't want to show here.
12943034Sdougm 		 */
12953034Sdougm 	}
12963034Sdougm }
12973034Sdougm 
12983034Sdougm /*
12993034Sdougm  * sa_show(flags, argc, argv)
13003034Sdougm  *
13013034Sdougm  * Implements the show subcommand.
13023034Sdougm  */
13033034Sdougm 
13043034Sdougm int
13053034Sdougm sa_show(int flags, int argc, char *argv[])
13063034Sdougm {
13073034Sdougm 	sa_group_t group;
13083034Sdougm 	int verbose = 0;
13093034Sdougm 	int properties = 0;
13103034Sdougm 	int c;
13113034Sdougm 	int ret = SA_OK;
13123034Sdougm 	char *protocol = NULL;
13133034Sdougm 	int xml = 0;
13143034Sdougm 	xmlDocPtr doc;
13153034Sdougm #ifdef lint
13163034Sdougm 	flags = flags;
13173034Sdougm #endif
13183034Sdougm 
13193034Sdougm 	while ((c = getopt(argc, argv, "?hvP:px")) !=	EOF) {
13203034Sdougm 	    switch (c) {
13213034Sdougm 	    case 'v':
13223034Sdougm 		verbose++;
13233034Sdougm 		break;
13243034Sdougm 	    case 'p':
13253034Sdougm 		properties++;
13263034Sdougm 		break;
13273034Sdougm 	    case 'P':
13283034Sdougm 		protocol = optarg;
13293034Sdougm 		if (!sa_valid_protocol(protocol)) {
13303034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
13313034Sdougm 					protocol);
13323034Sdougm 		    return (SA_INVALID_PROTOCOL);
13333034Sdougm 		}
13343034Sdougm 		break;
13353034Sdougm 	    case 'x':
13363034Sdougm 		xml++;
13373034Sdougm 		break;
13383034Sdougm 	    default:
13393034Sdougm 	    case 'h':
13403034Sdougm 	    case '?':
13413034Sdougm 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SHOW));
13423034Sdougm 		return (0);
13433034Sdougm 	    }
13443034Sdougm 	}
13453034Sdougm 
13463034Sdougm 	if (xml) {
13473034Sdougm 	    doc = show_group_xml_init();
13483034Sdougm 	    if (doc == NULL)
13493034Sdougm 		ret = SA_NO_MEMORY;
13503034Sdougm 	}
13513034Sdougm 
13523034Sdougm 	if (optind == argc) {
13533034Sdougm 	    /* no group specified so go through them all */
13543034Sdougm 	    for (group = sa_get_group(NULL); group != NULL;
13553034Sdougm 		group = sa_get_next_group(group)) {
13563034Sdougm 		/*
13573034Sdougm 		 * have a group so check if one we want and then list
13583034Sdougm 		 * contents with appropriate options.
13593034Sdougm 		 */
13603034Sdougm 		if (xml)
13613034Sdougm 		    show_group_xml(doc, group);
13623034Sdougm 		else
13633034Sdougm 		    show_group(group, verbose, properties, protocol, NULL);
13643034Sdougm 	    }
13653034Sdougm 	} else {
13663034Sdougm 	    /* have a specified list of groups */
13673034Sdougm 	    for (; optind < argc; optind++) {
13683034Sdougm 		group = sa_get_group(argv[optind]);
13693034Sdougm 		if (group != NULL) {
13703034Sdougm 		    if (xml)
13713034Sdougm 			show_group_xml(doc, group);
13723034Sdougm 		    else
13733034Sdougm 			show_group(group, verbose, properties, protocol, NULL);
13743034Sdougm 		} else {
13753034Sdougm 		    (void) printf(gettext("%s: not found\n"), argv[optind]);
13763034Sdougm 		    ret = SA_NO_SUCH_GROUP;
13773034Sdougm 		}
13783034Sdougm 	    }
13793034Sdougm 	}
13803034Sdougm 	if (xml && ret == SA_OK) {
13813034Sdougm 	    xmlDocFormatDump(stdout, doc, 1);
13823034Sdougm 	    xmlFreeDoc(doc);
13833034Sdougm 	}
13843034Sdougm 	return (ret);
13853034Sdougm 
13863034Sdougm }
13873034Sdougm 
13883034Sdougm /*
13893034Sdougm  * enable_share(group, share, update_legacy)
13903034Sdougm  *
13913034Sdougm  * helper function to enable a share if the group is enabled.
13923034Sdougm  */
13933034Sdougm 
13943034Sdougm static int
13953034Sdougm enable_share(sa_group_t group, sa_share_t share, int update_legacy)
13963034Sdougm {
13973034Sdougm 	char *value;
13983034Sdougm 	int enabled;
13993034Sdougm 	sa_optionset_t optionset;
14003034Sdougm 	int ret = SA_OK;
14013034Sdougm 	char *zfs = NULL;
14023034Sdougm 	int iszfs = 0;
14033034Sdougm 
14043034Sdougm 	/*
14053034Sdougm 	 * need to enable this share if the group is enabled but not
14063034Sdougm 	 * otherwise. The enable is also done on each protocol
14073034Sdougm 	 * represented in the group.
14083034Sdougm 	 */
14093034Sdougm 	value = sa_get_group_attr(group, "state");
14103034Sdougm 	enabled = value != NULL && strcmp(value, "enabled") == 0;
14113034Sdougm 	if (value != NULL)
14123034Sdougm 	    sa_free_attr_string(value);
14133034Sdougm 	/* remove legacy config if necessary */
14143034Sdougm 	if (update_legacy)
14153034Sdougm 	    ret = sa_delete_legacy(share);
14163034Sdougm 	zfs = sa_get_group_attr(group, "zfs");
14173034Sdougm 	if (zfs != NULL) {
14183034Sdougm 	    iszfs++;
14193034Sdougm 	    sa_free_attr_string(zfs);
14203034Sdougm 	}
14213034Sdougm 
14223034Sdougm 	/*
14233034Sdougm 	 * Step through each optionset at the group level and
14243034Sdougm 	 * enable the share based on the protocol type. This
14253034Sdougm 	 * works because protocols must be set on the group
14263034Sdougm 	 * for the protocol to be enabled.
14273034Sdougm 	 */
14283034Sdougm 	for (optionset = sa_get_optionset(group, NULL);
14293034Sdougm 	    optionset != NULL && ret == SA_OK;
14303034Sdougm 	    optionset = sa_get_next_optionset(optionset)) {
14313034Sdougm 	    value = sa_get_optionset_attr(optionset, "type");
14323034Sdougm 	    if (value != NULL) {
14333034Sdougm 		if (enabled)
14343034Sdougm 		    ret = sa_enable_share(share, value);
14353034Sdougm 		if (update_legacy && !iszfs)
14363034Sdougm 		    (void) sa_update_legacy(share, value);
14373034Sdougm 		sa_free_attr_string(value);
14383034Sdougm 	    }
14393034Sdougm 	}
14403034Sdougm 	if (ret == SA_OK)
14413034Sdougm 	    (void) sa_update_config();
14423034Sdougm 	return (ret);
14433034Sdougm }
14443034Sdougm 
14453034Sdougm /*
14463034Sdougm  * sa_addshare(flags, argc, argv)
14473034Sdougm  *
14483034Sdougm  * implements add-share subcommand.
14493034Sdougm  */
14503034Sdougm 
14513034Sdougm int
14523034Sdougm sa_addshare(int flags, int argc, char *argv[])
14533034Sdougm {
14543034Sdougm 	int verbose = 0;
14553034Sdougm 	int dryrun = 0;
14563034Sdougm 	int c;
14573034Sdougm 	int ret = SA_OK;
14583034Sdougm 	sa_group_t group;
14593034Sdougm 	sa_share_t share;
14603034Sdougm 	char *sharepath = NULL;
14613034Sdougm 	char *description = NULL;
14623034Sdougm 	char *resource = NULL;
14633034Sdougm 	int persist = SA_SHARE_PERMANENT; /* default to persist */
14643034Sdougm 	int auth;
14653034Sdougm 	char dir[MAXPATHLEN];
14663034Sdougm 
14673034Sdougm 	while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
14683034Sdougm 	    switch (c) {
14693034Sdougm 	    case 'n':
14703034Sdougm 		dryrun++;
14713034Sdougm 		break;
14723034Sdougm 	    case 'v':
14733034Sdougm 		verbose++;
14743034Sdougm 		break;
14753034Sdougm 	    case 'd':
14763034Sdougm 		description = optarg;
14773034Sdougm 		break;
14783034Sdougm 	    case 'r':
14793034Sdougm 		resource = optarg;
14803034Sdougm 		break;
14813034Sdougm 	    case 's':
14823034Sdougm 		/*
14833034Sdougm 		 * save share path into group. Currently limit
14843034Sdougm 		 * to one share per command.
14853034Sdougm 		 */
14863034Sdougm 		if (sharepath != NULL) {
14873034Sdougm 		    (void) printf(gettext("Adding multiple shares not"
14883034Sdougm 				    "supported\n"));
14893034Sdougm 		    return (1);
14903034Sdougm 		}
14913034Sdougm 		sharepath = optarg;
14923034Sdougm 		break;
14933034Sdougm 	    case 't':
14943034Sdougm 		persist = SA_SHARE_TRANSIENT;
14953034Sdougm 		break;
14963034Sdougm 	    default:
14973034Sdougm 	    case 'h':
14983034Sdougm 	    case '?':
14993034Sdougm 		(void) printf(gettext("usage: %s\n"),
15003034Sdougm 				sa_get_usage(USAGE_ADD_SHARE));
15013034Sdougm 		return (0);
15023034Sdougm 	    }
15033034Sdougm 	}
15043034Sdougm 
15053034Sdougm 	if (optind >= argc) {
15063034Sdougm 	    (void) printf(gettext("usage: %s\n"),
15073034Sdougm 				sa_get_usage(USAGE_ADD_SHARE));
15083034Sdougm 	    if (dryrun || sharepath != NULL || description != NULL ||
15093034Sdougm 		resource != NULL || verbose || persist) {
15103034Sdougm 		(void) printf(gettext("\tgroup must be specified\n"));
15113034Sdougm 		ret = SA_NO_SUCH_GROUP;
15123034Sdougm 	    } else {
15133034Sdougm 		ret = SA_OK;
15143034Sdougm 	    }
15153034Sdougm 	} else {
15163034Sdougm 	    if (sharepath == NULL) {
15173034Sdougm 		(void) printf(gettext("usage: %s\n"),
15183034Sdougm 				sa_get_usage(USAGE_ADD_SHARE));
15193034Sdougm 		(void) printf(gettext("\t-s sharepath must be specified\n"));
15203034Sdougm 		ret = SA_BAD_PATH;
15213034Sdougm 	    }
15223034Sdougm 	    if (ret == SA_OK) {
15233034Sdougm 		if (realpath(sharepath, dir) == NULL) {
15243034Sdougm 		    ret = SA_BAD_PATH;
15253034Sdougm 		    (void) printf(gettext("Path is not valid: %s\n"),
15263034Sdougm 					sharepath);
15273034Sdougm 		} else {
15283034Sdougm 		    sharepath = dir;
15293034Sdougm 		}
15303034Sdougm 	    }
15313034Sdougm 	    if (ret == SA_OK && resource != NULL) {
15323034Sdougm 		/* check for valid syntax */
15333034Sdougm 		if (strpbrk(resource, " \t/") != NULL) {
15343034Sdougm 		    (void) printf(gettext("usage: %s\n"),
15353034Sdougm 				sa_get_usage(USAGE_ADD_SHARE));
15363034Sdougm 		    (void) printf(gettext("\tresource must not contain white"
15373034Sdougm 				    "space or '/' characters\n"));
15383034Sdougm 		    ret = SA_BAD_PATH;
15393034Sdougm 		}
15403034Sdougm 	    }
15413034Sdougm 	    if (ret == SA_OK) {
15423034Sdougm 		group = sa_get_group(argv[optind]);
15433034Sdougm 		if (group != NULL) {
15443034Sdougm 		    auth = check_authorizations(argv[optind], flags);
15453034Sdougm 		    share = sa_find_share(sharepath);
15463034Sdougm 		    if (share != NULL) {
15473034Sdougm 			group = sa_get_parent_group(share);
15483034Sdougm 			if (group != NULL) {
15493034Sdougm 			    char *groupname;
15503034Sdougm 			    groupname = sa_get_group_attr(group, "name");
15513034Sdougm 			    if (groupname != NULL) {
15523034Sdougm 				(void) printf(gettext("Share path already "
15533034Sdougm 							"shared in group "
15543034Sdougm 							"\"%s\": %s\n"),
15553034Sdougm 						groupname, sharepath);
15563034Sdougm 				sa_free_attr_string(groupname);
15573034Sdougm 			    } else {
15583034Sdougm 				(void) printf(gettext("Share path already"
15593034Sdougm 							"shared: %s\n"),
15603034Sdougm 						groupname, sharepath);
15613034Sdougm 			    }
15623034Sdougm 			} else {
15633034Sdougm 			    (void) printf(gettext("Share path %s already "
15643034Sdougm 							"shared\n"),
15653034Sdougm 				    sharepath);
15663034Sdougm 			}
15673034Sdougm 			ret = SA_DUPLICATE_NAME;
15683034Sdougm 		    } else {
15693034Sdougm 			/*
15703034Sdougm 			 * need to check that resource name is unique
1571*3348Sdougm 			 * at some point. Path checking should use the
1572*3348Sdougm 			 * "normal" rules which don't check the repository.
15733034Sdougm 			 */
15743034Sdougm 			if (dryrun)
1575*3348Sdougm 			    ret = sa_check_path(group, sharepath,
1576*3348Sdougm 						SA_CHECK_NORMAL);
15773034Sdougm 			else
15783034Sdougm 			    share = sa_add_share(group, sharepath,
15793034Sdougm 							persist, &ret);
15803034Sdougm 			if (!dryrun && share == NULL) {
15813034Sdougm 				(void) printf(gettext("Could not add share: "
15823034Sdougm 							"%s\n"),
15833034Sdougm 					sa_errorstr(ret));
15843034Sdougm 			} else {
15853034Sdougm 			    if (!dryrun && ret == SA_OK) {
15863034Sdougm 				if (resource != NULL) {
15873034Sdougm 				    if (strpbrk(resource, " \t/") == NULL) {
15883034Sdougm 					ret = sa_set_share_attr(share,
15893034Sdougm 								"resource",
15903034Sdougm 								resource);
15913034Sdougm 				    }
15923034Sdougm 				}
15933034Sdougm 				if (ret == SA_OK && description != NULL) {
15943034Sdougm 				    ret = sa_set_share_description(share,
15953034Sdougm 							    description);
15963034Sdougm 				}
15973034Sdougm 				if (ret == SA_OK) {
15983034Sdougm 				    /* now enable the share(s) */
15993034Sdougm 				    ret = enable_share(group, share, 1);
16003034Sdougm 				    ret = sa_update_config();
16013034Sdougm 				}
16023034Sdougm 				switch (ret) {
16033034Sdougm 				case SA_DUPLICATE_NAME:
16043034Sdougm 				    (void) printf(gettext("Resource name in"
16053034Sdougm 						    "use: %s\n"),
16063034Sdougm 					    resource);
16073034Sdougm 				    break;
16083034Sdougm 				default:
16093034Sdougm 				    (void) printf(gettext("Could not set "
16103034Sdougm 						    "attribute: %s\n"),
16113034Sdougm 					    sa_errorstr(ret));
16123034Sdougm 				    break;
16133034Sdougm 				case SA_OK:
16143034Sdougm 				    break;
16153034Sdougm 				}
16163034Sdougm 			    } else if (dryrun && ret == SA_OK &&
16173034Sdougm 					!auth && verbose) {
16183034Sdougm 				(void) printf(gettext("Command would fail: "
16193034Sdougm 							"%s\n"),
16203034Sdougm 					sa_errorstr(SA_NO_PERMISSION));
16213034Sdougm 				ret = SA_NO_PERMISSION;
16223034Sdougm 			    }
16233034Sdougm 			}
16243034Sdougm 		    }
16253034Sdougm 		} else {
16263034Sdougm 		    (void) printf(gettext("Group \"%s\" not found\n"),
16273034Sdougm 					argv[optind]);
16283034Sdougm 		    ret = SA_NO_SUCH_GROUP;
16293034Sdougm 		}
16303034Sdougm 	    }
16313034Sdougm 	}
16323034Sdougm 	return (ret);
16333034Sdougm }
16343034Sdougm 
16353034Sdougm /*
16363034Sdougm  * sa_moveshare(flags, argc, argv)
16373034Sdougm  *
16383034Sdougm  * implements move-share subcommand.
16393034Sdougm  */
16403034Sdougm 
16413034Sdougm int
16423034Sdougm sa_moveshare(int flags, int argc, char *argv[])
16433034Sdougm {
16443034Sdougm 	int verbose = 0;
16453034Sdougm 	int dryrun = 0;
16463034Sdougm 	int c;
16473034Sdougm 	int ret = SA_OK;
16483034Sdougm 	sa_group_t group;
16493034Sdougm 	sa_share_t share;
16503034Sdougm 	char *sharepath = NULL;
16513034Sdougm 	int authsrc = 0, authdst = 0;
16523034Sdougm 
16533034Sdougm 	while ((c = getopt(argc, argv, "?hvns:")) != EOF) {
16543034Sdougm 	    switch (c) {
16553034Sdougm 	    case 'n':
16563034Sdougm 		dryrun++;
16573034Sdougm 		break;
16583034Sdougm 	    case 'v':
16593034Sdougm 		verbose++;
16603034Sdougm 		break;
16613034Sdougm 	    case 's':
16623034Sdougm 		/*
16633034Sdougm 		 * remove share path from group. Currently limit
16643034Sdougm 		 * to one share per command.
16653034Sdougm 		 */
16663034Sdougm 		if (sharepath != NULL) {
16673034Sdougm 		    (void) printf(gettext("Moving multiple shares not"
16683034Sdougm 				    "supported\n"));
16693034Sdougm 		    return (SA_BAD_PATH);
16703034Sdougm 		}
16713034Sdougm 		sharepath = optarg;
16723034Sdougm 		break;
16733034Sdougm 	    default:
16743034Sdougm 	    case 'h':
16753034Sdougm 	    case '?':
16763034Sdougm 		(void) printf(gettext("usage: %s\n"),
16773034Sdougm 				sa_get_usage(USAGE_MOVE_SHARE));
16783034Sdougm 		return (0);
16793034Sdougm 	    }
16803034Sdougm 	}
16813034Sdougm 
16823034Sdougm 	if (optind >= argc || sharepath == NULL) {
16833034Sdougm 			(void) printf(gettext("usage: %s\n"),
16843034Sdougm 				sa_get_usage(USAGE_MOVE_SHARE));
16853034Sdougm 	    if (dryrun || verbose || sharepath != NULL) {
16863034Sdougm 		(void) printf(gettext("\tgroup must be specified\n"));
16873034Sdougm 		ret = SA_NO_SUCH_GROUP;
16883034Sdougm 	    } else {
16893034Sdougm 		if (sharepath == NULL) {
16903034Sdougm 		    ret = SA_SYNTAX_ERR;
16913034Sdougm 		    (void) printf(gettext("\tsharepath must be specified\n"));
16923034Sdougm 		} else
16933034Sdougm 		    ret = SA_OK;
16943034Sdougm 	    }
16953034Sdougm 	} else {
16963034Sdougm 	    if (sharepath == NULL) {
16973034Sdougm 		(void) printf(gettext("sharepath must be specified with "
16983034Sdougm 				"the -s option\n"));
16993034Sdougm 		ret = SA_BAD_PATH;
17003034Sdougm 	    } else {
17013034Sdougm 		group = sa_get_group(argv[optind]);
17023034Sdougm 		if (group != NULL) {
17033034Sdougm 		    share = sa_find_share(sharepath);
17043034Sdougm 		    authdst = check_authorizations(argv[optind], flags);
17053034Sdougm 		    if (share == NULL) {
17063034Sdougm 			(void) printf(gettext("Share not found: %s\n"),
17073034Sdougm 					sharepath);
17083034Sdougm 			ret = SA_NO_SUCH_PATH;
17093034Sdougm 		    } else {
17103034Sdougm 			sa_group_t parent;
17113034Sdougm 			char *zfsold;
17123034Sdougm 			char *zfsnew;
17133034Sdougm 
17143034Sdougm 			parent = sa_get_parent_group(share);
17153034Sdougm 			if (parent != NULL) {
17163034Sdougm 			    char *pname;
17173034Sdougm 			    pname = sa_get_group_attr(parent, "name");
17183034Sdougm 			    if (pname != NULL) {
17193034Sdougm 				authsrc = check_authorizations(pname, flags);
17203034Sdougm 				sa_free_attr_string(pname);
17213034Sdougm 			    }
17223034Sdougm 			    zfsold = sa_get_group_attr(parent, "zfs");
17233034Sdougm 			    zfsnew = sa_get_group_attr(group, "zfs");
17243034Sdougm 			    if ((zfsold != NULL && zfsnew == NULL) ||
17253034Sdougm 				(zfsold == NULL && zfsnew != NULL)) {
17263034Sdougm 				ret = SA_NOT_ALLOWED;
17273034Sdougm 			    }
17283034Sdougm 			    if (zfsold != NULL)
17293034Sdougm 				sa_free_attr_string(zfsold);
17303034Sdougm 			    if (zfsnew != NULL)
17313034Sdougm 				sa_free_attr_string(zfsnew);
17323034Sdougm 			}
17333034Sdougm 			if (!dryrun && ret == SA_OK) {
17343034Sdougm 			    ret = sa_move_share(group, share);
17353034Sdougm 			}
17363034Sdougm 			if (ret == SA_OK && parent != group && !dryrun) {
17373034Sdougm 			    char *oldstate;
17383034Sdougm 			    ret = sa_update_config();
17393034Sdougm 				/*
17403034Sdougm 				 * note that the share may need to be
17413034Sdougm 				 * "unshared" if the new group is
17423034Sdougm 				 * disabled and the old was enabled or
17433034Sdougm 				 * it may need to be share to update
17443034Sdougm 				 * if the new group is enabled.
17453034Sdougm 				 */
17463034Sdougm 			    oldstate = sa_get_group_attr(parent, "state");
17473034Sdougm 			    /* enable_share determines what to do */
17483034Sdougm 			    if (strcmp(oldstate, "enabled") == 0) {
17493034Sdougm 				(void) sa_disable_share(share, NULL);
17503034Sdougm 			    }
17513034Sdougm 			    (void) enable_share(group, share, 1);
17523034Sdougm 			    if (oldstate != NULL)
17533034Sdougm 				sa_free_attr_string(oldstate);
17543034Sdougm 			}
17553034Sdougm 			if (ret != SA_OK) {
17563034Sdougm 			    (void) printf(gettext("Could not move share: %s\n"),
17573034Sdougm 				    sa_errorstr(ret));
17583034Sdougm 			}
17593034Sdougm 			if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
17603034Sdougm 			    verbose) {
17613034Sdougm 			    (void) printf(gettext("Command would fail: %s\n"),
17623034Sdougm 					sa_errorstr(SA_NO_PERMISSION));
17633034Sdougm 			}
17643034Sdougm 		    }
17653034Sdougm 		} else {
17663034Sdougm 		    (void) printf(gettext("Group \"%s\" not found\n"),
17673034Sdougm 					argv[optind]);
17683034Sdougm 		    ret = SA_NO_SUCH_GROUP;
17693034Sdougm 		}
17703034Sdougm 	    }
17713034Sdougm 	}
17723034Sdougm 	return (ret);
17733034Sdougm }
17743034Sdougm 
17753034Sdougm /*
17763034Sdougm  * sa_removeshare(flags, argc, argv)
17773034Sdougm  *
17783034Sdougm  * implements remove-share subcommand.
17793034Sdougm  */
17803034Sdougm 
17813034Sdougm int
17823034Sdougm sa_removeshare(int flags, int argc, char *argv[])
17833034Sdougm {
17843034Sdougm 	int verbose = 0;
17853034Sdougm 	int dryrun = 0;
17863034Sdougm 	int force = 0;
17873034Sdougm 	int c;
17883034Sdougm 	int ret = SA_OK;
17893034Sdougm 	sa_group_t group;
17903034Sdougm 	sa_share_t share;
17913034Sdougm 	char *sharepath = NULL;
17923034Sdougm 	char dir[MAXPATHLEN];
17933034Sdougm 	int auth;
17943034Sdougm 
17953034Sdougm 	while ((c = getopt(argc, argv, "?hfns:v")) != EOF) {
17963034Sdougm 	    switch (c) {
17973034Sdougm 	    case 'n':
17983034Sdougm 		dryrun++;
17993034Sdougm 		break;
18003034Sdougm 	    case 'v':
18013034Sdougm 		verbose++;
18023034Sdougm 		break;
18033034Sdougm 	    case 'f':
18043034Sdougm 		force++;
18053034Sdougm 		break;
18063034Sdougm 	    case 's':
18073034Sdougm 		/*
18083034Sdougm 		 * remove share path from group. Currently limit
18093034Sdougm 		 * to one share per command.
18103034Sdougm 		 */
18113034Sdougm 		if (sharepath != NULL) {
18123034Sdougm 		    (void) printf(gettext("Removing multiple shares not"
18133034Sdougm 				    "supported\n"));
18143034Sdougm 		    return (SA_SYNTAX_ERR);
18153034Sdougm 		}
18163034Sdougm 		sharepath = optarg;
18173034Sdougm 		break;
18183034Sdougm 	    default:
18193034Sdougm 	    case 'h':
18203034Sdougm 	    case '?':
18213034Sdougm 		(void) printf(gettext("usage: %s\n"),
18223034Sdougm 				sa_get_usage(USAGE_REMOVE_SHARE));
18233034Sdougm 		return (0);
18243034Sdougm 	    }
18253034Sdougm 	}
18263034Sdougm 
18273034Sdougm 	if (optind >= argc || sharepath == NULL) {
18283034Sdougm 	    if (sharepath == NULL) {
18293034Sdougm 			(void) printf(gettext("usage: %s\n"),
18303034Sdougm 				sa_get_usage(USAGE_REMOVE_SHARE));
18313034Sdougm 		(void) printf(gettext("\t-s sharepath must be specified\n"));
18323034Sdougm 		ret = SA_BAD_PATH;
18333034Sdougm 	    } else {
18343034Sdougm 		ret = SA_OK;
18353034Sdougm 	    }
18363034Sdougm 	}
18373034Sdougm 	if (ret == SA_OK) {
18383034Sdougm 	    if (optind < argc) {
18393034Sdougm 		if ((optind + 1) < argc) {
18403034Sdougm 		    (void) printf(gettext("Extraneous group(s) at end of "
18413034Sdougm 						"command\n"));
18423034Sdougm 		    ret = SA_SYNTAX_ERR;
18433034Sdougm 		} else {
18443034Sdougm 		    group = sa_get_group(argv[optind]);
18453034Sdougm 		    if (group == NULL) {
18463034Sdougm 			(void) printf(gettext("Group \"%s\" not found\n"),
18473034Sdougm 					argv[optind]);
18483034Sdougm 			ret = SA_NO_SUCH_GROUP;
18493034Sdougm 		    }
18503034Sdougm 		}
18513034Sdougm 	    } else {
18523034Sdougm 		group = NULL;
18533034Sdougm 	    }
18543034Sdougm 	    if (ret == SA_OK) {
18553034Sdougm 		if (realpath(sharepath, dir) == NULL) {
18563034Sdougm 		    ret = SA_BAD_PATH;
18573034Sdougm 		    (void) printf(gettext("Path is not valid: %s\n"),
18583034Sdougm 					sharepath);
18593034Sdougm 		} else {
18603034Sdougm 		    sharepath = dir;
18613034Sdougm 		}
18623034Sdougm 	    }
18633034Sdougm 	    if (ret == SA_OK) {
18643034Sdougm 		if (group != NULL)
18653034Sdougm 		    share = sa_get_share(group, sharepath);
18663034Sdougm 		else
18673034Sdougm 		    share = sa_find_share(sharepath);
18683034Sdougm 		if (share == NULL) {
18693034Sdougm 		    if (group != NULL)
18703034Sdougm 			(void) printf(gettext("Share not found in group %s:"
18713034Sdougm 						"%s\n"),
18723034Sdougm 					argv[optind], sharepath);
18733034Sdougm 		    else
18743034Sdougm 			(void) printf(gettext("Share not found: %s\n"),
18753034Sdougm 					sharepath);
18763034Sdougm 		    ret = SA_NO_SUCH_PATH;
18773034Sdougm 		} else {
18783034Sdougm 		    if (group == NULL)
18793034Sdougm 			group = sa_get_parent_group(share);
18803034Sdougm 		    if (!dryrun) {
18813034Sdougm 			if (ret == SA_OK) {
18823034Sdougm 			    ret = sa_disable_share(share, NULL);
18833034Sdougm 				/*
18843034Sdougm 				 * we don't care if it fails since it
18853034Sdougm 				 * could be disabled already.
18863034Sdougm 				 */
18873034Sdougm 			    if (ret == SA_OK || ret == SA_NO_SUCH_PATH ||
18883034Sdougm 				ret == SA_NOT_SUPPORTED) {
18893034Sdougm 				ret = sa_remove_share(share);
18903034Sdougm 			    }
18913034Sdougm 			    if (ret == SA_OK)
18923034Sdougm 				ret = sa_update_config();
18933034Sdougm 			}
18943034Sdougm 			if (ret != SA_OK) {
18953034Sdougm 			    (void) printf(gettext("Could not remove share:"
18963034Sdougm 							" %s\n"),
18973034Sdougm 					sa_errorstr(ret));
18983034Sdougm 			}
18993034Sdougm 		    } else if (ret == SA_OK) {
19003034Sdougm 			char *pname;
19013034Sdougm 			pname = sa_get_group_attr(group, "name");
19023034Sdougm 			if (pname != NULL) {
19033034Sdougm 			    auth = check_authorizations(pname, flags);
19043034Sdougm 			    sa_free_attr_string(pname);
19053034Sdougm 			}
19063034Sdougm 			if (!auth && verbose) {
19073034Sdougm 			    (void) printf(gettext("Command would fail: %s\n"),
19083034Sdougm 					sa_errorstr(SA_NO_PERMISSION));
19093034Sdougm 			}
19103034Sdougm 		    }
19113034Sdougm 		}
19123034Sdougm 	    }
19133034Sdougm 	}
19143034Sdougm 	return (ret);
19153034Sdougm }
19163034Sdougm 
19173034Sdougm /*
19183034Sdougm  * sa_set_share(flags, argc, argv)
19193034Sdougm  *
19203034Sdougm  * implements set-share subcommand.
19213034Sdougm  */
19223034Sdougm 
19233034Sdougm int
19243034Sdougm sa_set_share(int flags, int argc, char *argv[])
19253034Sdougm {
19263034Sdougm 	int dryrun = 0;
19273034Sdougm 	int c;
19283034Sdougm 	int ret = SA_OK;
19293034Sdougm 	sa_group_t group, sharegroup;
19303034Sdougm 	sa_share_t share;
19313034Sdougm 	char *sharepath = NULL;
19323034Sdougm 	char *description = NULL;
19333034Sdougm 	char *resource = NULL;
19343034Sdougm 	int auth;
19353034Sdougm 	int verbose = 0;
19363034Sdougm 
19373034Sdougm 	while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
19383034Sdougm 	    switch (c) {
19393034Sdougm 	    case 'n':
19403034Sdougm 		dryrun++;
19413034Sdougm 		break;
19423034Sdougm 	    case 'd':
19433034Sdougm 		description = optarg;
19443034Sdougm 		break;
19453034Sdougm 	    case 'r':
19463034Sdougm 		resource = optarg;
19473034Sdougm 		break;
19483034Sdougm 	    case 'v':
19493034Sdougm 		verbose++;
19503034Sdougm 		break;
19513034Sdougm 	    case 's':
19523034Sdougm 		/*
19533034Sdougm 		 * save share path into group. Currently limit
19543034Sdougm 		 * to one share per command.
19553034Sdougm 		 */
19563034Sdougm 		if (sharepath != NULL) {
19573034Sdougm 		    (void) printf(gettext("Updating multiple shares not"
19583034Sdougm 				    "supported\n"));
19593034Sdougm 		    return (SA_BAD_PATH);
19603034Sdougm 		}
19613034Sdougm 		sharepath = optarg;
19623034Sdougm 		break;
19633034Sdougm 	    default:
19643034Sdougm 	    case 'h':
19653034Sdougm 	    case '?':
19663034Sdougm 		(void) printf(gettext("usage: %s\n"),
19673034Sdougm 				sa_get_usage(USAGE_SET_SHARE));
19683034Sdougm 		return (SA_OK);
19693034Sdougm 	    }
19703034Sdougm 	}
19713034Sdougm 	if (optind >= argc || sharepath == NULL) {
19723034Sdougm 	    if (sharepath == NULL) {
19733034Sdougm 		(void) printf(gettext("usage: %s\n"),
19743034Sdougm 				sa_get_usage(USAGE_SET_SHARE));
19753034Sdougm 		(void) printf(gettext("\tgroup must be specified\n"));
19763034Sdougm 	    ret = SA_BAD_PATH;
19773034Sdougm 	    } else {
19783034Sdougm 		ret = SA_OK;
19793034Sdougm 	    }
19803034Sdougm 	}
19813034Sdougm 	if ((optind + 1) < argc) {
19823034Sdougm 	    (void) printf(gettext("usage: %s\n"),
19833034Sdougm 				sa_get_usage(USAGE_SET_SHARE));
19843034Sdougm 	    (void) printf(gettext("\tExtraneous group(s) at end\n"));
19853034Sdougm 	    ret = SA_SYNTAX_ERR;
19863034Sdougm 	}
19873034Sdougm 	if (ret == SA_OK) {
19883034Sdougm 	    char *groupname;
19893034Sdougm 	    if (optind < argc) {
19903034Sdougm 		groupname = argv[optind];
19913034Sdougm 		group = sa_get_group(groupname);
19923034Sdougm 	    } else {
19933034Sdougm 		group = NULL;
19943034Sdougm 		groupname = NULL;
19953034Sdougm 	    }
19963034Sdougm 	    share = sa_find_share(sharepath);
19973034Sdougm 	    if (share != NULL) {
19983034Sdougm 		sharegroup = sa_get_parent_group(share);
19993034Sdougm 		if (group != NULL && group != sharegroup) {
20003034Sdougm 		    (void) printf(gettext("Group \"%s\" does not contain "
20013034Sdougm 						"share %s\n"),
20023034Sdougm 			    argv[optind], sharepath);
20033034Sdougm 		    ret = SA_BAD_PATH;
20043034Sdougm 		} else {
20053034Sdougm 		    int delgroupname = 0;
20063034Sdougm 		    if (groupname == NULL) {
20073034Sdougm 			groupname = sa_get_group_attr(sharegroup, "name");
20083034Sdougm 			delgroupname = 1;
20093034Sdougm 		    }
20103034Sdougm 		    if (groupname != NULL) {
20113034Sdougm 			auth = check_authorizations(groupname, flags);
20123034Sdougm 			if (delgroupname) {
20133034Sdougm 			    sa_free_attr_string(groupname);
20143034Sdougm 			    groupname = NULL;
20153034Sdougm 			}
20163034Sdougm 		    } else {
20173034Sdougm 			ret = SA_NO_MEMORY;
20183034Sdougm 		    }
20193034Sdougm 		    if (resource != NULL) {
20203034Sdougm 			if (strpbrk(resource, " \t/") == NULL) {
20213034Sdougm 			    if (!dryrun) {
20223034Sdougm 				ret = sa_set_share_attr(share, "resource",
20233034Sdougm 						    resource);
20243034Sdougm 			    } else {
20253034Sdougm 				sa_share_t resshare;
20263034Sdougm 				resshare = sa_get_resource(sharegroup,
20273034Sdougm 							    resource);
20283034Sdougm 				if (resshare != NULL && resshare != share)
20293034Sdougm 				    ret = SA_DUPLICATE_NAME;
20303034Sdougm 			    }
20313034Sdougm 			} else {
20323034Sdougm 			    ret = SA_BAD_PATH;
20333034Sdougm 			    (void) printf(gettext("Resource must not contain "
20343034Sdougm 						"white space or '/'\n"));
20353034Sdougm 			}
20363034Sdougm 		    }
20373034Sdougm 		    if (ret == SA_OK && description != NULL) {
20383034Sdougm 			ret = sa_set_share_description(share, description);
20393034Sdougm 		    }
20403034Sdougm 		}
20413034Sdougm 		if (!dryrun && ret == SA_OK) {
20423034Sdougm 		    ret = sa_update_config();
20433034Sdougm 		}
20443034Sdougm 		switch (ret) {
20453034Sdougm 		case SA_DUPLICATE_NAME:
20463034Sdougm 		    (void) printf(gettext("Resource name in use: %s\n"),
20473034Sdougm 					resource);
20483034Sdougm 		    break;
20493034Sdougm 		default:
20503034Sdougm 		    (void) printf(gettext("Could not set attribute: %s\n"),
20513034Sdougm 			    sa_errorstr(ret));
20523034Sdougm 		    break;
20533034Sdougm 		case SA_OK:
20543034Sdougm 		    if (dryrun && !auth && verbose) {
20553034Sdougm 			(void) printf(gettext("Command would fail: %s\n"),
20563034Sdougm 				sa_errorstr(SA_NO_PERMISSION));
20573034Sdougm 		    }
20583034Sdougm 		    break;
20593034Sdougm 		}
20603034Sdougm 	    } else {
20613034Sdougm 		(void) printf(gettext("Share path \"%s\" not found\n"),
20623034Sdougm 				sharepath);
20633034Sdougm 		ret = SA_NO_SUCH_PATH;
20643034Sdougm 	    }
20653034Sdougm 	}
20663034Sdougm 	return (ret);
20673034Sdougm }
20683034Sdougm 
20693034Sdougm /*
20703034Sdougm  * add_security(group, sectype, optlist, proto, *err)
20713034Sdougm  *
20723034Sdougm  * Helper function to add a security option (named optionset) to the
20733034Sdougm  * group.
20743034Sdougm  */
20753034Sdougm 
20763034Sdougm static int
20773034Sdougm add_security(sa_group_t group, char *sectype,
20783034Sdougm 		struct options *optlist, char *proto, int *err)
20793034Sdougm {
20803034Sdougm 	sa_security_t security;
20813034Sdougm 	int ret = SA_OK;
20823034Sdougm 	int result = 0;
20833034Sdougm 
20843034Sdougm 	sectype = sa_proto_space_alias(proto, sectype);
20853034Sdougm 	security = sa_get_security(group, sectype, proto);
20863034Sdougm 	if (security == NULL) {
20873034Sdougm 	    security = sa_create_security(group, sectype, proto);
20883034Sdougm 	}
20893034Sdougm 	if (sectype != NULL)
20903034Sdougm 	    sa_free_attr_string(sectype);
20913034Sdougm 	if (security != NULL) {
20923034Sdougm 	    while (optlist != NULL) {
20933034Sdougm 		sa_property_t prop;
20943034Sdougm 		prop = sa_get_property(security, optlist->optname);
20953034Sdougm 		if (prop == NULL) {
20963034Sdougm 			/*
20973034Sdougm 			 * add the property, but only if it is
20983034Sdougm 			 * a non-NULL or non-zero length value
20993034Sdougm 			 */
21003034Sdougm 		    if (optlist->optvalue != NULL) {
21013034Sdougm 			prop = sa_create_property(optlist->optname,
21023034Sdougm 							optlist->optvalue);
21033034Sdougm 			if (prop != NULL) {
21043034Sdougm 			    ret = sa_valid_property(security, proto, prop);
21053034Sdougm 			    if (ret != SA_OK) {
21063034Sdougm 				(void) sa_remove_property(prop);
21073034Sdougm 				(void) printf(gettext("Could not add "
21083034Sdougm 							"property %s: %s\n"),
21093034Sdougm 							optlist->optname,
21103034Sdougm 						sa_errorstr(ret));
21113034Sdougm 			    }
21123034Sdougm 			    if (ret == SA_OK) {
21133034Sdougm 				ret = sa_add_property(security, prop);
21143034Sdougm 				if (ret != SA_OK) {
21153034Sdougm 				    (void) printf(gettext("Could not add "
21163034Sdougm 						    "property (%s=%s): %s\n"),
21173034Sdougm 						optlist->optname,
21183034Sdougm 						optlist->optvalue,
21193034Sdougm 						sa_errorstr(ret));
21203034Sdougm 				} else {
21213034Sdougm 				    result = 1;
21223034Sdougm 				}
21233034Sdougm 			    }
21243034Sdougm 			}
21253034Sdougm 		    }
21263034Sdougm 		} else {
21273034Sdougm 		    ret = sa_update_property(prop, optlist->optvalue);
21283034Sdougm 		    result = 1; /* should check if really changed */
21293034Sdougm 		}
21303034Sdougm 		optlist = optlist->next;
21313034Sdougm 	    }
21323034Sdougm 		/*
21333034Sdougm 		 * when done, properties may have all been removed but
21343034Sdougm 		 * we need to keep the security type itself until
21353034Sdougm 		 * explicitly removed.
21363034Sdougm 		 */
21373034Sdougm 	    if (result)
21383034Sdougm 		ret = sa_commit_properties(security, 0);
21393034Sdougm 	}
21403034Sdougm 	*err = ret;
21413034Sdougm 	return (result);
21423034Sdougm }
21433034Sdougm 
21443034Sdougm /*
21453034Sdougm  * basic_set(groupname, optlist, protocol, sharepath, dryrun)
21463034Sdougm  *
21473034Sdougm  * This function implements "set" when a name space (-S) is not
21483034Sdougm  * specified. It is a basic set. Options and other CLI parsing has
21493034Sdougm  * already been done.
21503034Sdougm  */
21513034Sdougm 
21523034Sdougm static int
21533034Sdougm basic_set(char *groupname, struct options *optlist, char *protocol,
21543034Sdougm 		char *sharepath, int dryrun)
21553034Sdougm {
21563034Sdougm 	sa_group_t group;
21573034Sdougm 	int ret = SA_OK;
21583034Sdougm 	int change = 0;
21593034Sdougm 	struct list *worklist = NULL;
21603034Sdougm 
21613034Sdougm 	group = sa_get_group(groupname);
21623034Sdougm 	if (group != NULL) {
21633034Sdougm 	    sa_share_t share = NULL;
21643034Sdougm 	    if (sharepath != NULL) {
21653034Sdougm 		share = sa_get_share(group, sharepath);
21663034Sdougm 		if (share == NULL) {
21673034Sdougm 		    (void) printf(gettext("Share does not exist in group %s\n"),
21683034Sdougm 				groupname, sharepath);
21693034Sdougm 		    ret = SA_NO_SUCH_PATH;
21703034Sdougm 		}
21713034Sdougm 	    }
21723034Sdougm 	    if (ret == SA_OK) {
21733034Sdougm 		/* group must exist */
21743034Sdougm 		ret = valid_options(optlist, protocol,
21753034Sdougm 				    share == NULL ? group : share, NULL);
21763034Sdougm 		if (ret == SA_OK && !dryrun) {
21773034Sdougm 		    if (share != NULL)
21783034Sdougm 			change |= add_optionset(share, optlist, protocol,
21793034Sdougm 						&ret);
21803034Sdougm 		    else
21813034Sdougm 			change |= add_optionset(group, optlist, protocol,
21823034Sdougm 						&ret);
21833034Sdougm 		    if (ret == SA_OK && change) {
21843034Sdougm 			worklist = add_list(worklist, group, share);
21853034Sdougm 		    }
21863034Sdougm 		}
21873034Sdougm 	    }
21883034Sdougm 	    free_opt(optlist);
21893034Sdougm 	} else {
21903034Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
21913034Sdougm 		ret = SA_NO_SUCH_GROUP;
21923034Sdougm 	}
21933034Sdougm 	/*
21943034Sdougm 	 * we have a group and potentially legal additions
21953034Sdougm 	 */
21963034Sdougm 
21973034Sdougm 	/* commit to configuration if not a dryrun */
21983034Sdougm 	if (!dryrun && ret == SA_OK) {
21993034Sdougm 	    if (change && worklist != NULL) {
22003034Sdougm 		/* properties changed, so update all shares */
22013034Sdougm 		(void) enable_all_groups(worklist, 0, 0, protocol);
22023034Sdougm 	    }
22033034Sdougm 	}
22043034Sdougm 	if (worklist != NULL)
22053034Sdougm 	    free_list(worklist);
22063034Sdougm 	return (ret);
22073034Sdougm }
22083034Sdougm 
22093034Sdougm /*
22103034Sdougm  * space_set(groupname, optlist, protocol, sharepath, dryrun)
22113034Sdougm  *
22123034Sdougm  * This function implements "set" when a name space (-S) is
22133034Sdougm  * specified. It is a namespace set. Options and other CLI parsing has
22143034Sdougm  * already been done.
22153034Sdougm  */
22163034Sdougm 
22173034Sdougm static int
22183034Sdougm space_set(char *groupname, struct options *optlist, char *protocol,
22193034Sdougm 		char *sharepath, int dryrun, char *sectype)
22203034Sdougm {
22213034Sdougm 	sa_group_t group;
22223034Sdougm 	int ret = SA_OK;
22233034Sdougm 	int change = 0;
22243034Sdougm 	struct list *worklist = NULL;
22253034Sdougm 
22263034Sdougm 	/*
22273034Sdougm 	 * make sure protcol and sectype are valid
22283034Sdougm 	 */
22293034Sdougm 
22303034Sdougm 	if (sa_proto_valid_space(protocol, sectype) == 0) {
22313034Sdougm 	    (void) printf(gettext("Option space \"%s\" not valid "
22323034Sdougm 					"for protocol.\n"),
22333034Sdougm 				sectype);
22343034Sdougm 	    return (SA_INVALID_SECURITY);
22353034Sdougm 	}
22363034Sdougm 
22373034Sdougm 	group = sa_get_group(groupname);
22383034Sdougm 	if (group != NULL) {
22393034Sdougm 	    sa_share_t share = NULL;
22403034Sdougm 	    if (sharepath != NULL) {
22413034Sdougm 		share = sa_get_share(group, sharepath);
22423034Sdougm 		if (share == NULL) {
22433034Sdougm 		    (void) printf(gettext("Share does not exist in group %s\n"),
22443034Sdougm 				groupname, sharepath);
22453034Sdougm 		    ret = SA_NO_SUCH_PATH;
22463034Sdougm 		}
22473034Sdougm 	    }
22483034Sdougm 	    if (ret == SA_OK) {
22493034Sdougm 		/* group must exist */
22503034Sdougm 		ret = valid_options(optlist, protocol,
22513034Sdougm 				    share == NULL ? group : share, sectype);
22523034Sdougm 		if (ret == SA_OK && !dryrun) {
22533034Sdougm 		    if (share != NULL)
22543034Sdougm 			change = add_security(share, sectype, optlist,
22553034Sdougm 						protocol,
22563034Sdougm 						&ret);
22573034Sdougm 		    else
22583034Sdougm 			change = add_security(group, sectype, optlist,
22593034Sdougm 						protocol,
22603034Sdougm 						&ret);
22613034Sdougm 		    if (ret != SA_OK)
22623034Sdougm 			(void) printf(gettext("Could not set property: %s\n"),
22633034Sdougm 				sa_errorstr(ret));
22643034Sdougm 		}
22653034Sdougm 		if (ret == SA_OK && change)
22663034Sdougm 		    worklist = add_list(worklist, group, share);
22673034Sdougm 	    }
22683034Sdougm 	    free_opt(optlist);
22693034Sdougm 	} else {
22703034Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
22713034Sdougm 		ret = SA_NO_SUCH_GROUP;
22723034Sdougm 	}
22733034Sdougm 	/*
22743034Sdougm 	 * we have a group and potentially legal additions
22753034Sdougm 	 */
22763034Sdougm 
22773034Sdougm 	/* commit to configuration if not a dryrun */
22783034Sdougm 	if (!dryrun && ret == 0) {
22793034Sdougm 	    if (change && worklist != NULL) {
22803034Sdougm 		/* properties changed, so update all shares */
22813034Sdougm 		(void) enable_all_groups(worklist, 0, 0, protocol);
22823034Sdougm 	    }
22833034Sdougm 	    ret = sa_update_config();
22843034Sdougm 	}
22853034Sdougm 	if (worklist != NULL)
22863034Sdougm 	    free_list(worklist);
22873034Sdougm 	return (ret);
22883034Sdougm }
22893034Sdougm 
22903034Sdougm /*
22913034Sdougm  * sa_set(flags, argc, argv)
22923034Sdougm  *
22933034Sdougm  * Implements the set subcommand. It keys off of -S to determine which
22943034Sdougm  * set of operations to actually do.
22953034Sdougm  */
22963034Sdougm 
22973034Sdougm int
22983034Sdougm sa_set(int flags, int argc, char *argv[])
22993034Sdougm {
23003034Sdougm 	char *groupname;
23013034Sdougm 	int verbose = 0;
23023034Sdougm 	int dryrun = 0;
23033034Sdougm 	int c;
23043034Sdougm 	char *protocol = NULL;
23053034Sdougm 	int ret = SA_OK;
23063034Sdougm 	struct options *optlist = NULL;
23073034Sdougm 	char *sharepath = NULL;
23083034Sdougm 	char *optset = NULL;
23093034Sdougm 	int auth;
23103034Sdougm 
23113034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
23123034Sdougm 	    switch (c) {
23133034Sdougm 	    case 'v':
23143034Sdougm 		verbose++;
23153034Sdougm 		break;
23163034Sdougm 	    case 'n':
23173034Sdougm 		dryrun++;
23183034Sdougm 		break;
23193034Sdougm 	    case 'P':
23203034Sdougm 		protocol = optarg;
23213034Sdougm 		if (!sa_valid_protocol(protocol)) {
23223034Sdougm 		    (void) printf(gettext("Invalid protocol specified:"
23233034Sdougm 				    "%s\n"),
23243034Sdougm 					protocol);
23253034Sdougm 		    return (SA_INVALID_PROTOCOL);
23263034Sdougm 		}
23273034Sdougm 		break;
23283034Sdougm 	    case 'p':
23293034Sdougm 		ret = add_opt(&optlist, optarg, 0);
23303034Sdougm 		switch (ret) {
23313034Sdougm 		case OPT_ADD_SYNTAX:
23323034Sdougm 		    (void) printf(gettext("Property syntax error: %s\n"),
23333034Sdougm 					optarg);
23343034Sdougm 		    return (SA_SYNTAX_ERR);
23353034Sdougm 		case OPT_ADD_MEMORY:
23363034Sdougm 		    (void) printf(gettext("No memory to set property: %s\n"),
23373034Sdougm 					optarg);
23383034Sdougm 		    return (SA_NO_MEMORY);
23393034Sdougm 		default:
23403034Sdougm 		    break;
23413034Sdougm 		}
23423034Sdougm 		break;
23433034Sdougm 	    case 's':
23443034Sdougm 		sharepath = optarg;
23453034Sdougm 		break;
23463034Sdougm 	    case 'S':
23473034Sdougm 		optset = optarg;
23483034Sdougm 		break;
23493034Sdougm 	    default:
23503034Sdougm 	    case 'h':
23513034Sdougm 	    case '?':
23523034Sdougm 		(void) printf(gettext("usage: %s\n"),
23533034Sdougm 				sa_get_usage(USAGE_SET));
23543034Sdougm 		return (SA_OK);
23553034Sdougm 	    }
23563034Sdougm 	}
23573034Sdougm 
23583034Sdougm 	if (optlist != NULL)
23593034Sdougm 	    ret = chk_opt(optlist, optset != NULL, protocol);
23603034Sdougm 
23613034Sdougm 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
23623034Sdougm 	    protocol == NULL ||
23633034Sdougm 	    ret != OPT_ADD_OK) {
23643034Sdougm 	    char *sep = "\t";
23653034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
23663034Sdougm 	    if (optind >= argc) {
23673034Sdougm 		(void) printf(gettext("%sgroup must be specified"), sep);
23683034Sdougm 		sep = ", ";
23693034Sdougm 	    }
23703034Sdougm 	    if (optlist == NULL) {
23713034Sdougm 		(void) printf(gettext("%sat least one property must be"
23723034Sdougm 				" specified"), sep);
23733034Sdougm 		sep = ", ";
23743034Sdougm 	    }
23753034Sdougm 	    if (protocol == NULL) {
23763034Sdougm 		(void) printf(gettext("%sprotocol must be specified"), sep);
23773034Sdougm 		sep = ", ";
23783034Sdougm 	    }
23793034Sdougm 	    (void) printf("\n");
23803034Sdougm 	    ret = SA_SYNTAX_ERR;
23813034Sdougm 	} else {
23823034Sdougm 		/*
23833034Sdougm 		 * if a group already exists, we can only add a new
23843034Sdougm 		 * protocol to it and not create a new one or add the
23853034Sdougm 		 * same protocol again.
23863034Sdougm 		 */
23873034Sdougm 
23883034Sdougm 	    groupname = argv[optind];
23893034Sdougm 	    auth = check_authorizations(groupname, flags);
23903034Sdougm 	    if (optset == NULL)
23913034Sdougm 		ret = basic_set(groupname, optlist, protocol,
23923034Sdougm 				sharepath, dryrun);
23933034Sdougm 	    else
23943034Sdougm 		ret = space_set(groupname, optlist, protocol,
23953034Sdougm 				sharepath, dryrun, optset);
23963034Sdougm 	    if (dryrun && ret == SA_OK && !auth && verbose) {
23973034Sdougm 		(void) printf(gettext("Command would fail: %s\n"),
23983034Sdougm 			sa_errorstr(SA_NO_PERMISSION));
23993034Sdougm 	    }
24003034Sdougm 	}
24013034Sdougm 	return (ret);
24023034Sdougm }
24033034Sdougm 
24043034Sdougm /*
24053034Sdougm  * remove_options(group, optlist, proto, *err)
24063034Sdougm  *
24073034Sdougm  * helper function to actually remove options from a group after all
24083034Sdougm  * preprocessing is done.
24093034Sdougm  */
24103034Sdougm 
24113034Sdougm static int
24123034Sdougm remove_options(sa_group_t group, struct options *optlist,
24133034Sdougm 		char *proto, int *err)
24143034Sdougm {
24153034Sdougm 	struct options *cur;
24163034Sdougm 	sa_optionset_t optionset;
24173034Sdougm 	sa_property_t prop;
24183034Sdougm 	int change = 0;
24193034Sdougm 	int ret = SA_OK;
24203034Sdougm 
24213034Sdougm 	optionset = sa_get_optionset(group, proto);
24223034Sdougm 	if (optionset != NULL) {
24233034Sdougm 	    for (cur = optlist; cur != NULL; cur = cur->next) {
24243034Sdougm 		prop = sa_get_property(optionset, cur->optname);
24253034Sdougm 		if (prop != NULL) {
24263034Sdougm 		    ret = sa_remove_property(prop);
24273034Sdougm 		    if (ret != SA_OK)
24283034Sdougm 			break;
24293034Sdougm 		    change = 1;
24303034Sdougm 		}
24313034Sdougm 	    }
24323034Sdougm 	}
24333034Sdougm 	if (ret == SA_OK && change)
24343034Sdougm 	    ret = sa_commit_properties(optionset, 0);
24353034Sdougm 
24363034Sdougm 	if (err != NULL)
24373034Sdougm 	    *err = ret;
24383034Sdougm 	return (change);
24393034Sdougm }
24403034Sdougm 
24413034Sdougm /*
24423034Sdougm  * valid_unset(group, optlist, proto)
24433034Sdougm  *
24443034Sdougm  * Sanity check the optlist to make sure they can be removed. Issue an
24453034Sdougm  * error if a property doesn't exist.
24463034Sdougm  */
24473034Sdougm 
24483034Sdougm static int
24493034Sdougm valid_unset(sa_group_t group, struct options *optlist, char *proto)
24503034Sdougm {
24513034Sdougm 	struct options *cur;
24523034Sdougm 	sa_optionset_t optionset;
24533034Sdougm 	sa_property_t prop;
24543034Sdougm 	int ret = SA_OK;
24553034Sdougm 
24563034Sdougm 	optionset = sa_get_optionset(group, proto);
24573034Sdougm 	if (optionset != NULL) {
24583034Sdougm 	    for (cur = optlist; cur != NULL; cur = cur->next) {
24593034Sdougm 		prop = sa_get_property(optionset, cur->optname);
24603034Sdougm 		if (prop == NULL) {
24613034Sdougm 		    (void) printf(gettext("Could not unset property %s:"
24623034Sdougm 						" not set\n"),
24633034Sdougm 			    cur->optname);
24643034Sdougm 		    ret = SA_NO_SUCH_PROP;
24653034Sdougm 		}
24663034Sdougm 	    }
24673034Sdougm 	}
24683034Sdougm 	return (ret);
24693034Sdougm }
24703034Sdougm 
24713034Sdougm /*
24723034Sdougm  * valid_unset_security(group, optlist, proto)
24733034Sdougm  *
24743034Sdougm  * Sanity check the optlist to make sure they can be removed. Issue an
24753034Sdougm  * error if a property doesn't exist.
24763034Sdougm  */
24773034Sdougm 
24783034Sdougm static int
24793034Sdougm valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
24803034Sdougm 	    char *sectype)
24813034Sdougm {
24823034Sdougm 	struct options *cur;
24833034Sdougm 	sa_security_t security;
24843034Sdougm 	sa_property_t prop;
24853034Sdougm 	int ret = SA_OK;
24863034Sdougm 	char *sec;
24873034Sdougm 
24883034Sdougm 	sec = sa_proto_space_alias(proto, sectype);
24893034Sdougm 	security = sa_get_security(group, sec, proto);
24903034Sdougm 	if (security != NULL) {
24913034Sdougm 	    for (cur = optlist; cur != NULL; cur = cur->next) {
24923034Sdougm 		prop = sa_get_property(security, cur->optname);
24933034Sdougm 		if (prop == NULL) {
24943034Sdougm 		    (void) printf(gettext("Could not unset property %s:"
24953034Sdougm 						" not set\n"),
24963034Sdougm 					cur->optname);
24973034Sdougm 		    ret = SA_NO_SUCH_PROP;
24983034Sdougm 		}
24993034Sdougm 	    }
25003034Sdougm 	} else {
25013034Sdougm 	    (void) printf(gettext("Could not unset %s: space not defined\n"),
25023034Sdougm 			    sectype);
25033034Sdougm 	    ret = SA_NO_SUCH_SECURITY;
25043034Sdougm 	}
25053034Sdougm 	if (sec != NULL)
25063034Sdougm 	    sa_free_attr_string(sec);
25073034Sdougm 	return (ret);
25083034Sdougm }
25093034Sdougm 
25103034Sdougm /*
25113034Sdougm  * remove_security(group, optlist, proto)
25123034Sdougm  *
25133034Sdougm  * Remove the properties since they were checked as valid.
25143034Sdougm  */
25153034Sdougm 
25163034Sdougm static int
25173034Sdougm remove_security(sa_group_t group, char *sectype,
25183034Sdougm 		struct options *optlist, char *proto, int *err)
25193034Sdougm {
25203034Sdougm 	sa_security_t security;
25213034Sdougm 	int ret = SA_OK;
25223034Sdougm 	int change = 0;
25233034Sdougm 
25243034Sdougm 	sectype = sa_proto_space_alias(proto, sectype);
25253034Sdougm 	security = sa_get_security(group, sectype, proto);
25263034Sdougm 	if (sectype != NULL)
25273034Sdougm 	    sa_free_attr_string(sectype);
25283034Sdougm 
25293034Sdougm 	if (security != NULL) {
25303034Sdougm 	    while (optlist != NULL) {
25313034Sdougm 		sa_property_t prop;
25323034Sdougm 		prop = sa_get_property(security, optlist->optname);
25333034Sdougm 		if (prop != NULL) {
25343034Sdougm 		    ret = sa_remove_property(prop);
25353034Sdougm 		    if (ret != SA_OK)
25363034Sdougm 			break;
25373034Sdougm 		    change = 1;
25383034Sdougm 		}
25393034Sdougm 		optlist = optlist->next;
25403034Sdougm 	    }
25413034Sdougm 		/*
25423034Sdougm 		 * when done, properties may have all been removed but
25433034Sdougm 		 * we need to keep the security type itself until
25443034Sdougm 		 * explicitly removed.
25453034Sdougm 		 */
25463034Sdougm 	    if (ret == SA_OK && change)
25473034Sdougm 		ret = sa_commit_properties(security, 0);
25483034Sdougm 	} else {
25493034Sdougm 	    ret = SA_NO_SUCH_PROP;
25503034Sdougm 	}
25513034Sdougm 	if (err != NULL)
25523034Sdougm 	    *err = ret;
25533034Sdougm 	return (change);
25543034Sdougm }
25553034Sdougm 
25563034Sdougm /*
25573034Sdougm  * basic_unset(groupname, optlist, protocol, sharepath, dryrun)
25583034Sdougm  *
25593034Sdougm  * unset non-named optionset properties.
25603034Sdougm  */
25613034Sdougm 
25623034Sdougm static int
25633034Sdougm basic_unset(char *groupname, struct options *optlist, char *protocol,
25643034Sdougm 		char *sharepath, int dryrun)
25653034Sdougm {
25663034Sdougm 	sa_group_t group;
25673034Sdougm 	int ret = SA_OK;
25683034Sdougm 	int change = 0;
25693034Sdougm 	struct list *worklist = NULL;
25703034Sdougm 
25713034Sdougm 	group = sa_get_group(groupname);
25723034Sdougm 	if (group != NULL) {
25733034Sdougm 	    sa_share_t share = NULL;
25743034Sdougm 	    if (sharepath != NULL) {
25753034Sdougm 		share = sa_get_share(group, sharepath);
25763034Sdougm 		if (share == NULL) {
25773034Sdougm 		    (void) printf(gettext("Share does not exist in group %s\n"),
25783034Sdougm 				groupname, sharepath);
25793034Sdougm 		    ret = SA_NO_SUCH_PATH;
25803034Sdougm 		}
25813034Sdougm 	    }
25823034Sdougm 	    if (ret == SA_OK) {
25833034Sdougm 		/* group must exist */
25843034Sdougm 		ret = valid_unset(share != NULL ? share : group,
25853034Sdougm 					optlist, protocol);
25863034Sdougm 		if (ret == SA_OK && !dryrun) {
25873034Sdougm 		    if (share != NULL) {
25883034Sdougm 			sa_optionset_t optionset;
25893034Sdougm 			sa_property_t prop;
25903034Sdougm 			change |= remove_options(share, optlist, protocol,
25913034Sdougm 							&ret);
25923034Sdougm 			/* if a share optionset is empty, remove it */
25933034Sdougm 			optionset = sa_get_optionset((sa_share_t)share,
25943034Sdougm 							protocol);
25953034Sdougm 			if (optionset != NULL) {
25963034Sdougm 			    prop = sa_get_property(optionset, NULL);
25973034Sdougm 			    if (prop == NULL)
25983034Sdougm 				(void) sa_destroy_optionset(optionset);
25993034Sdougm 			}
26003034Sdougm 		    } else {
26013034Sdougm 			change |= remove_options(group, optlist, protocol,
26023034Sdougm 							&ret);
26033034Sdougm 		    }
26043034Sdougm 		    if (ret == SA_OK && change)
26053034Sdougm 			worklist = add_list(worklist, group, share);
26063034Sdougm 		    if (ret != SA_OK)
26073034Sdougm 			(void) printf(gettext("Could not remove properties:"
26083034Sdougm 						"%s\n"),
26093034Sdougm 				sa_errorstr(ret));
26103034Sdougm 		}
26113034Sdougm 	    } else {
26123034Sdougm 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
26133034Sdougm 		ret = SA_NO_SUCH_GROUP;
26143034Sdougm 	    }
26153034Sdougm 	    free_opt(optlist);
26163034Sdougm 	}
26173034Sdougm 
26183034Sdougm 	/*
26193034Sdougm 	 * we have a group and potentially legal additions
26203034Sdougm 	 */
26213034Sdougm 	/* commit to configuration if not a dryrun */
26223034Sdougm 	if (!dryrun && ret == SA_OK) {
26233034Sdougm 	    if (change && worklist != NULL) {
26243034Sdougm 		/* properties changed, so update all shares */
26253034Sdougm 		(void) enable_all_groups(worklist, 0, 0, protocol);
26263034Sdougm 	    }
26273034Sdougm 	}
26283034Sdougm 	if (worklist != NULL)
26293034Sdougm 	    free_list(worklist);
26303034Sdougm 	return (ret);
26313034Sdougm }
26323034Sdougm 
26333034Sdougm /*
26343034Sdougm  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
26353034Sdougm  *
26363034Sdougm  * unset named optionset properties.
26373034Sdougm  */
26383034Sdougm static int
26393034Sdougm space_unset(char *groupname, struct options *optlist, char *protocol,
26403034Sdougm 		char *sharepath, int dryrun, char *sectype)
26413034Sdougm {
26423034Sdougm 	sa_group_t group;
26433034Sdougm 	int ret = SA_OK;
26443034Sdougm 	int change = 0;
26453034Sdougm 	struct list *worklist = NULL;
26463034Sdougm 
26473034Sdougm 	group = sa_get_group(groupname);
26483034Sdougm 	if (group != NULL) {
26493034Sdougm 	    sa_share_t share = NULL;
26503034Sdougm 	    if (sharepath != NULL) {
26513034Sdougm 		share = sa_get_share(group, sharepath);
26523034Sdougm 		if (share == NULL) {
26533034Sdougm 		    (void) printf(gettext("Share does not exist in group %s\n"),
26543034Sdougm 				groupname, sharepath);
26553034Sdougm 		    ret = SA_NO_SUCH_PATH;
26563034Sdougm 		}
26573034Sdougm 	    }
26583034Sdougm 	    if (ret == SA_OK) {
26593034Sdougm 		ret = valid_unset_security(share != NULL ? share : group,
26603034Sdougm 						optlist, protocol, sectype);
26613034Sdougm 		if (ret == SA_OK && !dryrun) {
26623034Sdougm 		    if (optlist != NULL) {
26633034Sdougm 			if (share != NULL) {
26643034Sdougm 			    sa_security_t optionset;
26653034Sdougm 			    sa_property_t prop;
26663034Sdougm 			    change = remove_security(share, sectype,
26673034Sdougm 							optlist, protocol,
26683034Sdougm 							&ret);
26693034Sdougm 			    /* if a share security is empty, remove it */
26703034Sdougm 			    optionset = sa_get_security((sa_group_t)share,
26713034Sdougm 							sectype,
26723034Sdougm 							protocol);
26733034Sdougm 			    if (optionset != NULL) {
26743034Sdougm 				prop = sa_get_property(optionset, NULL);
26753034Sdougm 				if (prop == NULL)
26763034Sdougm 				    ret = sa_destroy_security(optionset);
26773034Sdougm 			    }
26783034Sdougm 			} else {
26793034Sdougm 			    change = remove_security(group, sectype,
26803034Sdougm 							optlist, protocol,
26813034Sdougm 							&ret);
26823034Sdougm 			}
26833034Sdougm 		    } else {
26843034Sdougm 			sa_security_t security;
26853034Sdougm 			char *sec;
26863034Sdougm 			sec = sa_proto_space_alias(protocol, sectype);
26873034Sdougm 			security = sa_get_security(group, sec, protocol);
26883034Sdougm 			if (sec != NULL)
26893034Sdougm 			    sa_free_attr_string(sec);
26903034Sdougm 			if (security != NULL) {
26913034Sdougm 			    ret = sa_destroy_security(security);
26923034Sdougm 			    if (ret == SA_OK)
26933034Sdougm 				change = 1;
26943034Sdougm 			} else {
26953034Sdougm 			    ret = SA_NO_SUCH_PROP;
26963034Sdougm 			}
26973034Sdougm 		    }
26983034Sdougm 		    if (ret != SA_OK)
26993034Sdougm 			(void) printf(gettext("Could not unset property: %s\n"),
27003034Sdougm 				sa_errorstr(ret));
27013034Sdougm 		}
27023034Sdougm 
27033034Sdougm 		if (ret == SA_OK && change)
27043034Sdougm 		    worklist = add_list(worklist, group, 0);
27053034Sdougm 	    }
27063034Sdougm 	} else {
27073034Sdougm 	    (void) printf(gettext("Group \"%s\" not found\n"), groupname);
27083034Sdougm 	    ret = SA_NO_SUCH_GROUP;
27093034Sdougm 	}
27103034Sdougm 	free_opt(optlist);
27113034Sdougm 	/*
27123034Sdougm 	 * we have a group and potentially legal additions
27133034Sdougm 	 */
27143034Sdougm 
27153034Sdougm 	/* commit to configuration if not a dryrun */
27163034Sdougm 	if (!dryrun && ret == 0) {
27173034Sdougm 	    if (change && worklist != NULL) {
27183034Sdougm 		/* properties changed, so update all shares */
27193034Sdougm 		(void) enable_all_groups(worklist, 0, 0, protocol);
27203034Sdougm 	    }
27213034Sdougm 	    ret = sa_update_config();
27223034Sdougm 	}
27233034Sdougm 	if (worklist != NULL)
27243034Sdougm 	    free_list(worklist);
27253034Sdougm 	return (ret);
27263034Sdougm }
27273034Sdougm 
27283034Sdougm /*
27293034Sdougm  * sa_unset(flags, argc, argv)
27303034Sdougm  *
27313034Sdougm  * implements the unset subcommand. Parsing done here and then basic
27323034Sdougm  * or space versions of the real code are called.
27333034Sdougm  */
27343034Sdougm 
27353034Sdougm int
27363034Sdougm sa_unset(int flags, int argc, char *argv[])
27373034Sdougm {
27383034Sdougm 	char *groupname;
27393034Sdougm 	int verbose = 0;
27403034Sdougm 	int dryrun = 0;
27413034Sdougm 	int c;
27423034Sdougm 	char *protocol = NULL;
27433034Sdougm 	int ret = SA_OK;
27443034Sdougm 	struct options *optlist = NULL;
27453034Sdougm 	char *sharepath = NULL;
27463034Sdougm 	char *optset = NULL;
27473034Sdougm 	int auth;
27483034Sdougm 
27493034Sdougm 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
27503034Sdougm 	    switch (c) {
27513034Sdougm 	    case 'v':
27523034Sdougm 		verbose++;
27533034Sdougm 		break;
27543034Sdougm 	    case 'n':
27553034Sdougm 		dryrun++;
27563034Sdougm 		break;
27573034Sdougm 	    case 'P':
27583034Sdougm 		protocol = optarg;
27593034Sdougm 		if (!sa_valid_protocol(protocol)) {
27603034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
27613034Sdougm 					protocol);
27623034Sdougm 		    return (SA_INVALID_PROTOCOL);
27633034Sdougm 		}
27643034Sdougm 		break;
27653034Sdougm 	    case 'p':
27663034Sdougm 		ret = add_opt(&optlist, optarg, 1);
27673034Sdougm 		switch (ret) {
27683034Sdougm 		case OPT_ADD_SYNTAX:
27693034Sdougm 		    (void) printf(gettext("Property syntax error for "
27703034Sdougm 						"property %s\n"),
27713034Sdougm 					optarg);
27723034Sdougm 		    return (SA_SYNTAX_ERR);
27733034Sdougm 		case OPT_ADD_PROPERTY:
27743034Sdougm 		    (void) printf(gettext("Properties need to be set"
27753034Sdougm 						" with set command: %s\n"),
27763034Sdougm 					optarg);
27773034Sdougm 		    return (SA_SYNTAX_ERR);
27783034Sdougm 		default:
27793034Sdougm 		    break;
27803034Sdougm 		}
27813034Sdougm 		break;
27823034Sdougm 	    case 's':
27833034Sdougm 		sharepath = optarg;
27843034Sdougm 		break;
27853034Sdougm 	    case 'S':
27863034Sdougm 		optset = optarg;
27873034Sdougm 		break;
27883034Sdougm 	    default:
27893034Sdougm 	    case 'h':
27903034Sdougm 	    case '?':
27913034Sdougm 		(void) printf(gettext("usage: %s\n"),
27923034Sdougm 				sa_get_usage(USAGE_UNSET));
27933034Sdougm 		return (SA_OK);
27943034Sdougm 	    }
27953034Sdougm 	}
27963034Sdougm 
27973034Sdougm 	if (optlist != NULL)
27983034Sdougm 	    ret = chk_opt(optlist, optset != NULL, protocol);
27993034Sdougm 
28003034Sdougm 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
28013034Sdougm 	    protocol == NULL) {
28023034Sdougm 	    char *sep = "\t";
28033034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_UNSET));
28043034Sdougm 	    if (optind >= argc) {
28053034Sdougm 		(void) printf(gettext("%sgroup must be specified"), sep);
28063034Sdougm 		sep = ", ";
28073034Sdougm 	    }
28083034Sdougm 	    if (optlist == NULL) {
28093034Sdougm 		(void) printf(gettext("%sat least one property must be "
28103034Sdougm 					"specified"),
28113034Sdougm 			sep);
28123034Sdougm 		sep = ", ";
28133034Sdougm 	    }
28143034Sdougm 	    if (protocol == NULL) {
28153034Sdougm 		(void) printf(gettext("%sprotocol must be specified"), sep);
28163034Sdougm 		sep = ", ";
28173034Sdougm 	    }
28183034Sdougm 	    (void) printf("\n");
28193034Sdougm 	    ret = SA_SYNTAX_ERR;
28203034Sdougm 	} else {
28213034Sdougm 
28223034Sdougm 		/*
28233034Sdougm 		 * if a group already exists, we can only add a new
28243034Sdougm 		 * protocol to it and not create a new one or add the
28253034Sdougm 		 * same protocol again.
28263034Sdougm 		 */
28273034Sdougm 
28283034Sdougm 	    groupname = argv[optind];
28293034Sdougm 	    auth = check_authorizations(groupname, flags);
28303034Sdougm 	    if (optset == NULL)
28313034Sdougm 		ret = basic_unset(groupname, optlist, protocol,
28323034Sdougm 					sharepath, dryrun);
28333034Sdougm 	    else
28343034Sdougm 		ret = space_unset(groupname, optlist, protocol,
28353034Sdougm 					sharepath, dryrun, optset);
28363034Sdougm 
28373034Sdougm 	    if (dryrun && ret == SA_OK && !auth && verbose) {
28383034Sdougm 		(void) printf(gettext("Command would fail: %s\n"),
28393034Sdougm 			sa_errorstr(SA_NO_PERMISSION));
28403034Sdougm 	    }
28413034Sdougm 	}
28423034Sdougm 	return (ret);
28433034Sdougm }
28443034Sdougm 
28453034Sdougm /*
28463034Sdougm  * sa_enable_group(flags, argc, argv)
28473034Sdougm  *
28483034Sdougm  * Implements the enable subcommand
28493034Sdougm  */
28503034Sdougm 
28513034Sdougm int
28523034Sdougm sa_enable_group(int flags, int argc, char *argv[])
28533034Sdougm {
28543034Sdougm 	int verbose = 0;
28553034Sdougm 	int dryrun = 0;
28563034Sdougm 	int all = 0;
28573034Sdougm 	int c;
28583034Sdougm 	int ret = SA_OK;
28593034Sdougm 	char *protocol = NULL;
28603034Sdougm 	char *state;
28613034Sdougm 	struct list *worklist = NULL;
28623034Sdougm 	int auth = 1;
28633034Sdougm 
28643034Sdougm 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
28653034Sdougm 	    switch (c) {
28663034Sdougm 	    case 'a':
28673034Sdougm 		all = 1;
28683034Sdougm 		break;
28693034Sdougm 	    case 'n':
28703034Sdougm 		dryrun++;
28713034Sdougm 		break;
28723034Sdougm 	    case 'P':
28733034Sdougm 		protocol = optarg;
28743034Sdougm 		if (!sa_valid_protocol(protocol)) {
28753034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
28763034Sdougm 				    protocol);
28773034Sdougm 		    return (SA_INVALID_PROTOCOL);
28783034Sdougm 		}
28793034Sdougm 		break;
28803034Sdougm 	    case 'v':
28813034Sdougm 		verbose++;
28823034Sdougm 		break;
28833034Sdougm 	    default:
28843034Sdougm 	    case 'h':
28853034Sdougm 	    case '?':
28863034Sdougm 		(void) printf(gettext("usage: %s\n"),
28873034Sdougm 				sa_get_usage(USAGE_ENABLE));
28883034Sdougm 		return (0);
28893034Sdougm 	    }
28903034Sdougm 	}
28913034Sdougm 
28923034Sdougm 	if (optind == argc && !all) {
28933034Sdougm 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_ENABLE));
28943034Sdougm 	    (void) printf(gettext("\tmust specify group\n"));
28953034Sdougm 	    ret = SA_NO_SUCH_PATH;
28963034Sdougm 	} else {
28973034Sdougm 	    sa_group_t group;
28983034Sdougm 	    if (!all) {
28993034Sdougm 		while (optind < argc) {
29003034Sdougm 		    group = sa_get_group(argv[optind]);
29013034Sdougm 		    if (group != NULL) {
29023034Sdougm 			auth &= check_authorizations(argv[optind], flags);
29033034Sdougm 			state = sa_get_group_attr(group, "state");
29043034Sdougm 			if (state != NULL &&
29053034Sdougm 			    strcmp(state, "enabled") == 0) {
29063034Sdougm 			    /* already enabled */
29073034Sdougm 			    if (verbose)
29083034Sdougm 				(void) printf(gettext("Group \"%s\" is already "
29093034Sdougm 						"enabled\n"),
29103034Sdougm 					argv[optind]);
29113034Sdougm 			    ret = SA_BUSY; /* already enabled */
29123034Sdougm 			} else {
29133034Sdougm 			    worklist = add_list(worklist, group, 0);
29143034Sdougm 			    if (verbose)
29153034Sdougm 				(void) printf(gettext("Enabling group "
29163034Sdougm 							"\"%s\"\n"),
29173034Sdougm 					argv[optind]);
29183034Sdougm 			}
29193034Sdougm 			if (state != NULL)
29203034Sdougm 			    sa_free_attr_string(state);
29213034Sdougm 		    } else {
29223034Sdougm 			ret = SA_NO_SUCH_GROUP;
29233034Sdougm 		    }
29243034Sdougm 		    optind++;
29253034Sdougm 		}
29263034Sdougm 	    } else {
29273034Sdougm 		for (group = sa_get_group(NULL); group != NULL;
29283034Sdougm 		    group = sa_get_next_group(group)) {
29293034Sdougm 		    worklist = add_list(worklist, group, 0);
29303034Sdougm 		}
29313034Sdougm 	    }
29323034Sdougm 	    if (!dryrun && ret == SA_OK) {
29333034Sdougm 		ret = enable_all_groups(worklist, 1, 0, NULL);
29343034Sdougm 	    }
29353034Sdougm 	    if (ret != SA_OK && ret != SA_BUSY)
29363034Sdougm 		(void) printf(gettext("Could not enable group: %s\n"),
29373034Sdougm 			sa_errorstr(ret));
29383034Sdougm 	    if (ret == SA_BUSY)
29393034Sdougm 		ret = SA_OK;
29403034Sdougm 	}
29413034Sdougm 	if (worklist != NULL)
29423034Sdougm 	    free_list(worklist);
29433034Sdougm 	if (dryrun && ret == SA_OK && !auth && verbose) {
29443034Sdougm 	    (void) printf(gettext("Command would fail: %s\n"),
29453034Sdougm 			sa_errorstr(SA_NO_PERMISSION));
29463034Sdougm 	}
29473034Sdougm 	return (ret);
29483034Sdougm }
29493034Sdougm 
29503034Sdougm /*
29513034Sdougm  * disable_group(group, setstate)
29523034Sdougm  *
29533034Sdougm  * disable all the shares in the specified group honoring the setstate
29543034Sdougm  * argument. This is a helper for disable_all_groups in order to
29553034Sdougm  * simplify regular and subgroup (zfs) disabling. Group has already
29563034Sdougm  * been checked for non-NULL.
29573034Sdougm  */
29583034Sdougm 
29593034Sdougm static int
29603034Sdougm disable_group(sa_group_t group)
29613034Sdougm {
29623034Sdougm 	sa_share_t share;
29633034Sdougm 	int ret = SA_OK;
29643034Sdougm 
29653034Sdougm 	for (share = sa_get_share(group, NULL);
29663034Sdougm 	    share != NULL && ret == SA_OK;
29673034Sdougm 	    share = sa_get_next_share(share)) {
29683034Sdougm 	    ret = sa_disable_share(share, NULL);
29693034Sdougm 	    if (ret == SA_NO_SUCH_PATH) {
29703034Sdougm 		/*
29713034Sdougm 		 * this is OK since the path is gone. we can't
29723034Sdougm 		 * re-share it anyway so no error.
29733034Sdougm 		 */
29743034Sdougm 		ret = SA_OK;
29753034Sdougm 	    }
29763034Sdougm 	}
29773034Sdougm 	return (ret);
29783034Sdougm }
29793034Sdougm 
29803034Sdougm 
29813034Sdougm /*
29823034Sdougm  * disable_all_groups(work, setstate)
29833034Sdougm  *
29843034Sdougm  * helper function that disables the shares in the list of groups
29853034Sdougm  * provided. It optionally marks the group as disabled. Used by both
29863034Sdougm  * enable and start subcommands.
29873034Sdougm  */
29883034Sdougm 
29893034Sdougm static int
29903034Sdougm disable_all_groups(struct list *work, int setstate)
29913034Sdougm {
29923034Sdougm 	int ret = SA_OK;
29933034Sdougm 	sa_group_t subgroup, group;
29943034Sdougm 
29953034Sdougm 	while (work != NULL && ret == SA_OK) {
29963034Sdougm 	    group = (sa_group_t)work->item;
29973034Sdougm 	    if (setstate)
29983034Sdougm 		ret = sa_set_group_attr(group, "state", "disabled");
29993034Sdougm 	    if (ret == SA_OK) {
30003034Sdougm 		char *name;
30013034Sdougm 		name = sa_get_group_attr(group, "name");
30023034Sdougm 		if (name != NULL && strcmp(name, "zfs") == 0) {
30033034Sdougm 		    /* need to get the sub-groups for stopping */
30043034Sdougm 		    for (subgroup = sa_get_sub_group(group); subgroup != NULL;
30053034Sdougm 			subgroup = sa_get_next_group(subgroup)) {
30063034Sdougm 			ret = disable_group(subgroup);
30073034Sdougm 		    }
30083034Sdougm 		} else {
30093034Sdougm 		    ret = disable_group(group);
30103034Sdougm 		}
30113034Sdougm 		/*
30123034Sdougm 		 * we don't want to "disable" since it won't come
30133034Sdougm 		 * up after a reboot.  The SMF framework should do
30143034Sdougm 		 * the right thing. On enable we do want to do
30153034Sdougm 		 * something.
30163034Sdougm 		 */
30173034Sdougm 	    }
30183034Sdougm 	    work = work->next;
30193034Sdougm 	}
30203034Sdougm 	if (ret == SA_OK)
30213034Sdougm 	    ret = sa_update_config();
30223034Sdougm 	return (ret);
30233034Sdougm }
30243034Sdougm 
30253034Sdougm /*
30263034Sdougm  * sa_disable_group(flags, argc, argv)
30273034Sdougm  *
30283034Sdougm  * Implements the disable subcommand
30293034Sdougm  */
30303034Sdougm 
30313034Sdougm int
30323034Sdougm sa_disable_group(int flags, int argc, char *argv[])
30333034Sdougm {
30343034Sdougm 	int verbose = 0;
30353034Sdougm 	int dryrun = 0;
30363034Sdougm 	int all = 0;
30373034Sdougm 	int c;
30383034Sdougm 	int ret = SA_OK;
30393034Sdougm 	char *protocol;
30403034Sdougm 	char *state;
30413034Sdougm 	struct list *worklist = NULL;
30423034Sdougm 	int auth = 1;
30433034Sdougm 
30443034Sdougm 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
30453034Sdougm 	    switch (c) {
30463034Sdougm 	    case 'a':
30473034Sdougm 		all = 1;
30483034Sdougm 		break;
30493034Sdougm 	    case 'n':
30503034Sdougm 		dryrun++;
30513034Sdougm 		break;
30523034Sdougm 	    case 'P':
30533034Sdougm 		protocol = optarg;
30543034Sdougm 		if (!sa_valid_protocol(protocol)) {
30553034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
30563034Sdougm 					protocol);
30573034Sdougm 		    return (SA_INVALID_PROTOCOL);
30583034Sdougm 		}
30593034Sdougm 		break;
30603034Sdougm 	    case 'v':
30613034Sdougm 		verbose++;
30623034Sdougm 		break;
30633034Sdougm 	    default:
30643034Sdougm 	    case 'h':
30653034Sdougm 	    case '?':
30663034Sdougm 		(void) printf(gettext("usage: %s\n"),
30673034Sdougm 				sa_get_usage(USAGE_DISABLE));
30683034Sdougm 		return (0);
30693034Sdougm 	    }
30703034Sdougm 	}
30713034Sdougm 
30723034Sdougm 	if (optind == argc && !all) {
30733034Sdougm 		(void) printf(gettext("usage: %s\n"),
30743034Sdougm 				sa_get_usage(USAGE_DISABLE));
30753034Sdougm 		(void) printf(gettext("\tmust specify group\n"));
30763034Sdougm 		ret = SA_NO_SUCH_PATH;
30773034Sdougm 	} else {
30783034Sdougm 		sa_group_t group;
30793034Sdougm 		if (!all) {
30803034Sdougm 		    while (optind < argc) {
30813034Sdougm 			group = sa_get_group(argv[optind]);
30823034Sdougm 			if (group != NULL) {
30833034Sdougm 			    auth &= check_authorizations(argv[optind], flags);
30843034Sdougm 			    state = sa_get_group_attr(group, "state");
30853034Sdougm 			    if (state == NULL ||
30863034Sdougm 				strcmp(state, "disabled") == 0) {
30873034Sdougm 				/* already disabled */
30883034Sdougm 				if (verbose)
30893034Sdougm 				    (void) printf(gettext("Group \"%s\" is "
30903034Sdougm 							"already disabled\n"),
30913034Sdougm 					    argv[optind]);
30923034Sdougm 				ret = SA_BUSY; /* already disable */
30933034Sdougm 			    } else {
30943034Sdougm 				worklist = add_list(worklist, group, 0);
30953034Sdougm 				if (verbose)
30963034Sdougm 				    (void) printf(gettext("Disabling group "
30973034Sdougm 							    "\"%s\"\n"),
30983034Sdougm 					    argv[optind]);
30993034Sdougm 			    }
31003034Sdougm 			    if (state != NULL)
31013034Sdougm 				sa_free_attr_string(state);
31023034Sdougm 			} else {
31033034Sdougm 			    ret = SA_NO_SUCH_GROUP;
31043034Sdougm 			}
31053034Sdougm 			optind++;
31063034Sdougm 		    }
31073034Sdougm 		} else {
31083034Sdougm 		    for (group = sa_get_group(NULL); group != NULL;
31093034Sdougm 			    group = sa_get_next_group(group)) {
31103034Sdougm 			worklist = add_list(worklist, group, 0);
31113034Sdougm 		    }
31123034Sdougm 		}
31133034Sdougm 		if (ret == SA_OK && !dryrun) {
31143034Sdougm 			ret = disable_all_groups(worklist, 1);
31153034Sdougm 		}
31163034Sdougm 		if (ret != SA_OK && ret != SA_BUSY)
31173034Sdougm 		    (void) printf(gettext("Could not disable group: %s\n"),
31183034Sdougm 				sa_errorstr(ret));
31193034Sdougm 		if (ret == SA_BUSY)
31203034Sdougm 		    ret = SA_OK;
31213034Sdougm 	}
31223034Sdougm 	if (worklist != NULL)
31233034Sdougm 	    free_list(worklist);
31243034Sdougm 	if (dryrun && ret == SA_OK && !auth && verbose) {
31253034Sdougm 	    (void) printf(gettext("Command would fail: %s\n"),
31263034Sdougm 			sa_errorstr(SA_NO_PERMISSION));
31273034Sdougm 	}
31283034Sdougm 	return (ret);
31293034Sdougm }
31303034Sdougm 
31313034Sdougm /*
31323034Sdougm  * check_sharetab()
31333034Sdougm  *
31343034Sdougm  * Checks to see if the /etc/dfs/sharetab file is stale (exists from
31353034Sdougm  * before the current boot). If it is, truncate it since nothing is
31363034Sdougm  * really shared.
31373034Sdougm  */
31383034Sdougm 
31393034Sdougm static void
31403034Sdougm check_sharetab()
31413034Sdougm {
31423034Sdougm 	int fd;
31433034Sdougm 	struct utmpx *utmpxp;
31443034Sdougm 	struct stat st;
31453034Sdougm 
31463034Sdougm 	fd = open(SA_LEGACY_SHARETAB, O_RDWR);
31473034Sdougm 	if (fd >= 0) {
31483034Sdougm 		/*
31493034Sdougm 		 * Attempt to get a lock on the file. Whgen we get
31503034Sdougm 		 * one, then check to see if it is older than the boot
31513034Sdougm 		 * time. Truncate if older than boot.
31523034Sdougm 		 */
31533034Sdougm 	    (void) lockf(fd, F_LOCK, 0);
31543034Sdougm 	    if ((fstat(fd, &st) == 0) && /* does sharetab exist? */
31553034Sdougm 		(utmpxp = getutxent()) != NULL && /* does utmpx exist? */
31563034Sdougm 			(utmpxp->ut_xtime > st.st_mtime)) /* sharetab older? */
31573034Sdougm 		(void) ftruncate(fd, 0);
31583034Sdougm 
31593034Sdougm 	    (void) lockf(fd, F_ULOCK, 0);
31603034Sdougm 	    (void) close(fd);
31613034Sdougm 	    endutxent();
31623034Sdougm 	}
31633034Sdougm }
31643034Sdougm 
31653034Sdougm /*
31663034Sdougm  * sa_start_group(flags, argc, argv)
31673034Sdougm  *
31683034Sdougm  * Implements the start command.
31693034Sdougm  * This is similar to enable except it doesn't change the state
31703034Sdougm  * of the group(s) and only enables shares if the group is already
31713034Sdougm  * enabled.
31723034Sdougm  */
31733034Sdougm 
31743034Sdougm int
31753034Sdougm sa_start_group(int flags, int argc, char *argv[])
31763034Sdougm {
31773034Sdougm 	int verbose = 0;
31783034Sdougm 	int all = 0;
31793034Sdougm 	int c;
31803034Sdougm 	int ret = SMF_EXIT_OK;
31813034Sdougm 	char *protocol = NULL;
31823034Sdougm 	char *state;
31833034Sdougm 	struct list *worklist = NULL;
31843034Sdougm #ifdef lint
31853034Sdougm 	flags = flags;
31863034Sdougm #endif
31873034Sdougm 
31883034Sdougm 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
31893034Sdougm 	    switch (c) {
31903034Sdougm 	    case 'a':
31913034Sdougm 		all = 1;
31923034Sdougm 		break;
31933034Sdougm 	    case 'P':
31943034Sdougm 		protocol = optarg;
31953034Sdougm 		if (!sa_valid_protocol(protocol)) {
31963034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
31973034Sdougm 				    protocol);
31983034Sdougm 		    return (SA_INVALID_PROTOCOL);
31993034Sdougm 		}
32003034Sdougm 		break;
32013034Sdougm 	    case 'v':
32023034Sdougm 		verbose++;
32033034Sdougm 		break;
32043034Sdougm 	    default:
32053034Sdougm 	    case 'h':
32063034Sdougm 	    case '?':
32073034Sdougm 		(void) printf(gettext("usage: %s\n"),
32083034Sdougm 				sa_get_usage(USAGE_START));
32093034Sdougm 		return (SA_OK);
32103034Sdougm 	    }
32113034Sdougm 	}
32123034Sdougm 
32133034Sdougm 	if (optind == argc && !all) {
32143034Sdougm 		(void) printf(gettext("usage: %s\n"),
32153034Sdougm 				sa_get_usage(USAGE_START));
32163034Sdougm 		ret = SMF_EXIT_ERR_FATAL;
32173034Sdougm 	} else {
32183034Sdougm 		sa_group_t group;
32193034Sdougm 
32203034Sdougm 		check_sharetab();
32213034Sdougm 
32223034Sdougm 		if (!all) {
32233034Sdougm 		    while (optind < argc) {
32243034Sdougm 			group = sa_get_group(argv[optind]);
32253034Sdougm 			if (group != NULL) {
32263034Sdougm 			    state = sa_get_group_attr(group, "state");
32273034Sdougm 			    if (state == NULL ||
32283034Sdougm 				strcmp(state, "enabled") == 0) {
32293034Sdougm 				worklist = add_list(worklist, group, 0);
32303034Sdougm 				if (verbose)
32313034Sdougm 				    (void) printf(gettext("Starting group "
32323034Sdougm 								"\"%s\"\n"),
32333034Sdougm 					    argv[optind]);
32343034Sdougm 			    } else {
32353034Sdougm 				/*
32363034Sdougm 				 * determine if there are any
32373034Sdougm 				 * protocols.  if there aren't any,
32383034Sdougm 				 * then there isn't anything to do in
32393034Sdougm 				 * any case so no error.
32403034Sdougm 				 */
32413034Sdougm 				if (sa_get_optionset(group, protocol) != NULL) {
32423034Sdougm 				    ret = SMF_EXIT_OK;
32433034Sdougm 				}
32443034Sdougm 			    }
32453034Sdougm 			    if (state != NULL)
32463034Sdougm 				sa_free_attr_string(state);
32473034Sdougm 			}
32483034Sdougm 			optind++;
32493034Sdougm 		    }
32503034Sdougm 		} else {
32513034Sdougm 		    for (group = sa_get_group(NULL); group != NULL;
32523034Sdougm 			    group = sa_get_next_group(group)) {
32533034Sdougm 			state = sa_get_group_attr(group, "state");
32543034Sdougm 			if (state == NULL || strcmp(state, "enabled") == 0)
32553034Sdougm 			    worklist = add_list(worklist, group, 0);
32563034Sdougm 			if (state != NULL)
32573034Sdougm 			    sa_free_attr_string(state);
32583034Sdougm 		    }
32593034Sdougm 		}
32603034Sdougm 		(void) enable_all_groups(worklist, 0, 1, NULL);
32613034Sdougm 	}
32623034Sdougm 	if (worklist != NULL)
32633034Sdougm 	    free_list(worklist);
32643034Sdougm 	return (ret);
32653034Sdougm }
32663034Sdougm 
32673034Sdougm /*
32683034Sdougm  * sa_stop_group(flags, argc, argv)
32693034Sdougm  *
32703034Sdougm  * Implements the stop command.
32713034Sdougm  * This is similar to disable except it doesn't change the state
32723034Sdougm  * of the group(s) and only disables shares if the group is already
32733034Sdougm  * enabled.
32743034Sdougm  */
32753034Sdougm 
32763034Sdougm int
32773034Sdougm sa_stop_group(int flags, int argc, char *argv[])
32783034Sdougm {
32793034Sdougm 	int verbose = 0;
32803034Sdougm 	int all = 0;
32813034Sdougm 	int c;
32823034Sdougm 	int ret = SMF_EXIT_OK;
32833034Sdougm 	char *protocol = NULL;
32843034Sdougm 	char *state;
32853034Sdougm 	struct list *worklist = NULL;
32863034Sdougm #ifdef lint
32873034Sdougm 	flags = flags;
32883034Sdougm #endif
32893034Sdougm 
32903034Sdougm 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
32913034Sdougm 	    switch (c) {
32923034Sdougm 	    case 'a':
32933034Sdougm 		all = 1;
32943034Sdougm 		break;
32953034Sdougm 	    case 'P':
32963034Sdougm 		protocol = optarg;
32973034Sdougm 		if (!sa_valid_protocol(protocol)) {
32983034Sdougm 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
32993034Sdougm 					protocol);
33003034Sdougm 		    return (SA_INVALID_PROTOCOL);
33013034Sdougm 		}
33023034Sdougm 		break;
33033034Sdougm 	    case 'v':
33043034Sdougm 		verbose++;
33053034Sdougm 		break;
33063034Sdougm 	    default:
33073034Sdougm 	    case 'h':
33083034Sdougm 	    case '?':
33093034Sdougm 		(void) printf(gettext("usage: %s\n"),
33103034Sdougm 				sa_get_usage(USAGE_STOP));
33113034Sdougm 		return (0);
33123034Sdougm 	    }
33133034Sdougm 	}
33143034Sdougm 
33153034Sdougm 	if (optind == argc && !all) {
33163034Sdougm 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_STOP));
33173034Sdougm 		ret = SMF_EXIT_ERR_FATAL;
33183034Sdougm 	} else {
33193034Sdougm 		sa_group_t group;
33203034Sdougm 		if (!all) {
33213034Sdougm 		    while (optind < argc) {
33223034Sdougm 			group = sa_get_group(argv[optind]);
33233034Sdougm 			if (group != NULL) {
33243034Sdougm 			    state = sa_get_group_attr(group, "state");
33253034Sdougm 			    if (state == NULL ||
33263034Sdougm 				strcmp(state, "enabled") == 0) {
33273034Sdougm 				worklist = add_list(worklist, group, 0);
33283034Sdougm 				if (verbose)
33293034Sdougm 				    (void) printf(gettext("Stopping group "
33303034Sdougm 								"\"%s\"\n"),
33313034Sdougm 					    argv[optind]);
33323034Sdougm 			    } else {
33333034Sdougm 				ret = SMF_EXIT_OK;
33343034Sdougm 			    }
33353034Sdougm 			    if (state != NULL)
33363034Sdougm 				sa_free_attr_string(state);
33373034Sdougm 			}
33383034Sdougm 			optind++;
33393034Sdougm 		    }
33403034Sdougm 		} else {
33413034Sdougm 		    for (group = sa_get_group(NULL); group != NULL;
33423034Sdougm 			    group = sa_get_next_group(group)) {
33433034Sdougm 			state = sa_get_group_attr(group, "state");
33443034Sdougm 			if (state == NULL || strcmp(state, "enabled") == 0)
33453034Sdougm 			    worklist = add_list(worklist, group, 0);
33463034Sdougm 			if (state != NULL)
33473034Sdougm 			    sa_free_attr_string(state);
33483034Sdougm 		    }
33493034Sdougm 		}
33503034Sdougm 		(void) disable_all_groups(worklist, 0);
33513034Sdougm 		ret = sa_update_config();
33523034Sdougm 	}
33533034Sdougm 	if (worklist != NULL)
33543034Sdougm 	    free_list(worklist);
33553034Sdougm 	return (ret);
33563034Sdougm }
33573034Sdougm 
33583034Sdougm /*
33593034Sdougm  * remove_all_options(share, proto)
33603034Sdougm  *
33613034Sdougm  * Removes all options on a share.
33623034Sdougm  */
33633034Sdougm 
33643034Sdougm static void
33653034Sdougm remove_all_options(sa_share_t share, char *proto)
33663034Sdougm {
33673034Sdougm 	sa_optionset_t optionset;
33683034Sdougm 	sa_security_t security;
33693034Sdougm 	sa_security_t prevsec = NULL;
33703034Sdougm 
33713034Sdougm 	optionset = sa_get_optionset(share, proto);
33723034Sdougm 	if (optionset != NULL)
33733034Sdougm 	    (void) sa_destroy_optionset(optionset);
33743034Sdougm 	for (security = sa_get_security(share, NULL, NULL);
33753034Sdougm 	    security != NULL;
33763034Sdougm 	    security = sa_get_next_security(security)) {
33773034Sdougm 	    char *type;
33783034Sdougm 		/*
33793034Sdougm 		 * we walk through the list.  prevsec keeps the
33803034Sdougm 		 * previous security so we can delete it without
33813034Sdougm 		 * destroying the list.
33823034Sdougm 		 */
33833034Sdougm 	    if (prevsec != NULL) {
33843034Sdougm 		/* remove the previously seen security */
33853034Sdougm 		(void) sa_destroy_security(prevsec);
33863034Sdougm 		/* set to NULL so we don't try multiple times */
33873034Sdougm 		prevsec = NULL;
33883034Sdougm 	    }
33893034Sdougm 	    type = sa_get_security_attr(security, "type");
33903034Sdougm 	    if (type != NULL) {
33913034Sdougm 		/*
33923034Sdougm 		 * if the security matches the specified protocol, we
33933034Sdougm 		 * want to remove it. prevsec holds it until either
33943034Sdougm 		 * the next pass or we fall out of the loop.
33953034Sdougm 		 */
33963034Sdougm 		if (strcmp(type, proto) == 0)
33973034Sdougm 		    prevsec = security;
33983034Sdougm 		sa_free_attr_string(type);
33993034Sdougm 	    }
34003034Sdougm 	}
34013034Sdougm 	/* in case there is one left */
34023034Sdougm 	if (prevsec != NULL)
34033034Sdougm 	    (void) sa_destroy_security(prevsec);
34043034Sdougm }
34053034Sdougm 
34063034Sdougm 
34073034Sdougm /*
34083034Sdougm  * for legacy support, we need to handle the old syntax. This is what
34093034Sdougm  * we get if sharemgr is called with the name "share" rather than
34103034Sdougm  * sharemgr.
34113034Sdougm  */
34123034Sdougm 
34133034Sdougm static int
34143034Sdougm format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
34153034Sdougm {
34163034Sdougm 	int err;
34173034Sdougm 
34183034Sdougm 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
34193034Sdougm 	if (err > buffsize)
34203034Sdougm 	    return (-1);
34213034Sdougm 	return (0);
34223034Sdougm }
34233034Sdougm 
34243034Sdougm 
34253034Sdougm /*
34263034Sdougm  * check_legacy_cmd(proto, cmd)
34273034Sdougm  *
34283034Sdougm  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
34293034Sdougm  * executable.
34303034Sdougm  */
34313034Sdougm 
34323034Sdougm static int
34333034Sdougm check_legacy_cmd(char *path)
34343034Sdougm {
34353034Sdougm 	struct stat st;
34363034Sdougm 	int ret = 0;
34373034Sdougm 
34383034Sdougm 	if (stat(path, &st) == 0) {
34393034Sdougm 	    if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
34403034Sdougm 		ret = 1;
34413034Sdougm 	}
34423034Sdougm 	return (ret);
34433034Sdougm }
34443034Sdougm 
34453034Sdougm /*
34463034Sdougm  * run_legacy_command(proto, cmd, argv)
34473034Sdougm  *
34483034Sdougm  * we know the command exists, so attempt to execute it with all the
34493034Sdougm  * arguments. This implements full legacy share support for those
34503034Sdougm  * protocols that don't have plugin providers.
34513034Sdougm  */
34523034Sdougm 
34533034Sdougm static int
34543034Sdougm run_legacy_command(char *path, char *argv[])
34553034Sdougm {
34563034Sdougm 	int ret;
34573034Sdougm 
34583034Sdougm 	ret = execv(path, argv);
34593034Sdougm 	if (ret < 0) {
34603034Sdougm 	    switch (errno) {
34613034Sdougm 	    case EACCES:
34623034Sdougm 		ret = SA_NO_PERMISSION;
34633034Sdougm 		break;
34643034Sdougm 	    default:
34653034Sdougm 		ret = SA_SYSTEM_ERR;
34663034Sdougm 		break;
34673034Sdougm 	    }
34683034Sdougm 	}
34693034Sdougm 	return (ret);
34703034Sdougm }
34713034Sdougm 
34723034Sdougm /*
3473*3348Sdougm  * out_share(out, group, proto)
34743034Sdougm  *
34753034Sdougm  * Display the share information in the format that the "share"
34763034Sdougm  * command has traditionally used.
34773034Sdougm  */
34783034Sdougm 
34793034Sdougm static void
3480*3348Sdougm out_share(FILE *out, sa_group_t group, char *proto)
34813034Sdougm {
34823034Sdougm 	sa_share_t share;
34833034Sdougm 	char resfmt[128];
34843034Sdougm 
34853034Sdougm 	for (share = sa_get_share(group, NULL); share != NULL;
34863034Sdougm 		share = sa_get_next_share(share)) {
34873034Sdougm 	    char *path;
34883034Sdougm 	    char *type;
34893034Sdougm 	    char *resource;
34903034Sdougm 	    char *description;
34913034Sdougm 	    char *groupname;
34923034Sdougm 	    char *sharedstate;
34933034Sdougm 	    int shared = 1;
34943034Sdougm 	    char *soptions;
34953034Sdougm 
34963034Sdougm 	    sharedstate = sa_get_share_attr(share, "shared");
34973034Sdougm 	    path = sa_get_share_attr(share, "path");
34983034Sdougm 	    type = sa_get_share_attr(share, "type");
34993034Sdougm 	    resource = sa_get_share_attr(share, "resource");
35003034Sdougm 	    groupname = sa_get_group_attr(group, "name");
35013034Sdougm 
35023034Sdougm 	    if (groupname != NULL && strcmp(groupname, "default") == 0) {
35033034Sdougm 		sa_free_attr_string(groupname);
35043034Sdougm 		groupname = NULL;
35053034Sdougm 	    }
35063034Sdougm 	    description = sa_get_share_description(share);
3507*3348Sdougm 
3508*3348Sdougm 	    /* want the sharetab version if it exists */
3509*3348Sdougm 	    soptions = sa_get_share_attr(share, "shareopts");
35103034Sdougm 
35113034Sdougm 	    if (sharedstate == NULL)
35123034Sdougm 		shared = 0;
35133034Sdougm 
3514*3348Sdougm 	    if (soptions == NULL)
3515*3348Sdougm 		soptions = sa_proto_legacy_format(proto, share, 1);
35163034Sdougm 
35173034Sdougm 	    if (shared) {
3518*3348Sdougm 		/* only active shares go here */
35193034Sdougm 		(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
35203034Sdougm 			resource != NULL ? resource : "-",
35213034Sdougm 			groupname != NULL ? "@" : "",
35223034Sdougm 			groupname != NULL ? groupname : "");
35233034Sdougm 		(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
35243034Sdougm 			resfmt,
35253034Sdougm 			path,
35263034Sdougm 			(soptions != NULL && strlen(soptions) > 0) ?
35273034Sdougm 					soptions : "rw",
35283034Sdougm 			(description != NULL) ? description : "");
35293034Sdougm 	    }
35303034Sdougm 
35313034Sdougm 	    if (path != NULL)
35323034Sdougm 		sa_free_attr_string(path);
35333034Sdougm 	    if (type != NULL)
35343034Sdougm 		sa_free_attr_string(type);
35353034Sdougm 	    if (resource != NULL)
35363034Sdougm 		sa_free_attr_string(resource);
35373034Sdougm 	    if (groupname != NULL)
35383034Sdougm 		sa_free_attr_string(groupname);
35393034Sdougm 	    if (description != NULL)
35403034Sdougm 		sa_free_share_description(description);
35413034Sdougm 	    if (sharedstate != NULL)
35423034Sdougm 		sa_free_attr_string(sharedstate);
3543*3348Sdougm 	    if (soptions != NULL)
35443034Sdougm 		sa_format_free(soptions);
35453034Sdougm 	}
35463034Sdougm }
35473034Sdougm 
35483034Sdougm /*
35493034Sdougm  * output_legacy_file(out, proto)
35503034Sdougm  *
35513034Sdougm  * Walk all of the groups for the specified protocol and call
35523034Sdougm  * out_share() to format and write in the format displayed by the
35533034Sdougm  * "share" command with no arguments.
35543034Sdougm  */
35553034Sdougm 
35563034Sdougm static void
35573034Sdougm output_legacy_file(FILE *out, char *proto)
35583034Sdougm {
35593034Sdougm 	sa_group_t group;
35603034Sdougm 
35613034Sdougm 	for (group = sa_get_group(NULL); group != NULL;
35623034Sdougm 		group = sa_get_next_group(group)) {
35633034Sdougm 	    char *options;
35643034Sdougm 	    char *zfs;
35653034Sdougm 
35663034Sdougm 		/*
35673034Sdougm 		 * get default options preformated, being careful to
35683034Sdougm 		 * handle legacy shares differently from new style
35693034Sdougm 		 * shares. Legacy share have options on the share.
35703034Sdougm 		 */
35713034Sdougm 
35723034Sdougm 	    zfs = sa_get_group_attr(group, "zfs");
35733034Sdougm 	    if (zfs != NULL) {
35743034Sdougm 		sa_group_t zgroup;
35753034Sdougm 		sa_free_attr_string(zfs);
35763034Sdougm 		options = sa_proto_legacy_format(proto, group, 1);
35773034Sdougm 		for (zgroup = sa_get_sub_group(group); zgroup != NULL;
35783034Sdougm 		    zgroup = sa_get_next_group(zgroup)) {
35793034Sdougm 
35803034Sdougm 		    /* got a group, so display it */
3581*3348Sdougm 		    out_share(out, zgroup, proto);
35823034Sdougm 		}
35833034Sdougm 	    } else {
35843034Sdougm 		options = sa_proto_legacy_format(proto, group, 1);
3585*3348Sdougm 		out_share(out, group, proto);
35863034Sdougm 	    }
35873034Sdougm 	    if (options != NULL)
35883034Sdougm 		free(options);
35893034Sdougm 	}
35903034Sdougm }
35913034Sdougm 
35923034Sdougm int
35933034Sdougm sa_legacy_share(int flags, int argc, char *argv[])
35943034Sdougm {
35953034Sdougm 	char *protocol = "nfs";
35963034Sdougm 	char *options = NULL;
35973034Sdougm 	char *description = NULL;
35983034Sdougm 	char *groupname = NULL;
35993034Sdougm 	char *sharepath = NULL;
36003034Sdougm 	char *resource = NULL;
36013034Sdougm 	char *groupstatus = NULL;
36023034Sdougm 	int persist = SA_SHARE_TRANSIENT;
36033034Sdougm 	int argsused = 0;
36043034Sdougm 	int c;
36053034Sdougm 	int ret = SA_OK;
36063034Sdougm 	int zfs = 0;
36073034Sdougm 	int true_legacy = 0;
36083034Sdougm 	int curtype = SA_SHARE_TRANSIENT;
36093034Sdougm 	char cmd[MAXPATHLEN];
36103034Sdougm #ifdef lint
36113034Sdougm 	flags = flags;
36123034Sdougm #endif
36133034Sdougm 
36143034Sdougm 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
36153034Sdougm 	    switch (c) {
36163034Sdougm 	    case 'd':
36173034Sdougm 		description = optarg;
36183034Sdougm 		argsused++;
36193034Sdougm 		break;
36203034Sdougm 	    case 'F':
36213034Sdougm 		protocol = optarg;
36223034Sdougm 		if (!sa_valid_protocol(protocol)) {
36233034Sdougm 		    if (format_legacy_path(cmd, MAXPATHLEN,
36243034Sdougm 			    protocol, "share") == 0 && check_legacy_cmd(cmd)) {
36253034Sdougm 			true_legacy++;
36263034Sdougm 		    } else {
36273034Sdougm 			(void) fprintf(stderr,
36283034Sdougm 					gettext("Invalid protocol specified:"
36293034Sdougm 						"%s\n"),
36303034Sdougm 				protocol);
36313034Sdougm 			return (SA_INVALID_PROTOCOL);
36323034Sdougm 		    }
36333034Sdougm 		}
36343034Sdougm 		break;
36353034Sdougm 	    case 'o':
36363034Sdougm 		options = optarg;
36373034Sdougm 		argsused++;
36383034Sdougm 		break;
36393034Sdougm 	    case 'p':
36403034Sdougm 		persist = SA_SHARE_PERMANENT;
36413034Sdougm 		argsused++;
36423034Sdougm 		break;
36433034Sdougm 	    case 'h':
36443034Sdougm 	    case '?':
36453034Sdougm 	    default:
36463034Sdougm 		(void) fprintf(stderr, gettext("usage: %s\n"),
36473034Sdougm 						sa_get_usage(USAGE_SHARE));
36483034Sdougm 		return (SA_OK);
36493034Sdougm 	    }
36503034Sdougm 	}
36513034Sdougm 
36523034Sdougm 	/* have the info so construct what is needed */
36533034Sdougm 	if (!argsused && optind == argc) {
36543034Sdougm 	    /* display current info in share format */
36553034Sdougm 	    (void) output_legacy_file(stdout, "nfs");
36563034Sdougm 	} else {
36573034Sdougm 	    sa_group_t group = NULL;
36583034Sdougm 	    sa_share_t share;
36593034Sdougm 	    char dir[MAXPATHLEN];
36603034Sdougm 
36613034Sdougm 	    /* we are modifying the configuration */
36623034Sdougm 	    if (optind == argc) {
36633034Sdougm 		(void) fprintf(stderr, gettext("usage: %s\n"),
36643034Sdougm 				sa_get_usage(USAGE_SHARE));
36653034Sdougm 		return (SA_LEGACY_ERR);
36663034Sdougm 	    }
36673034Sdougm 
36683034Sdougm 	    if (true_legacy) {
36693034Sdougm 		/* if still using legacy share/unshare, exec it */
36703034Sdougm 		ret = run_legacy_command(cmd, argv);
36713034Sdougm 		return (ret);
36723034Sdougm 	    }
36733034Sdougm 
36743034Sdougm 	    sharepath = argv[optind++];
36753034Sdougm 	    if (optind < argc) {
36763034Sdougm 		resource = argv[optind];
36773034Sdougm 		groupname = strchr(resource, '@');
36783034Sdougm 		if (groupname != NULL)
36793034Sdougm 		    *groupname++ = '\0';
36803034Sdougm 	    }
36813034Sdougm 	    if (realpath(sharepath, dir) == NULL)
36823034Sdougm 		ret = SA_BAD_PATH;
36833034Sdougm 	    else
36843034Sdougm 		sharepath = dir;
36853034Sdougm 	    if (ret == SA_OK) {
36863034Sdougm 		share = sa_find_share(sharepath);
36873034Sdougm 	    } else {
36883034Sdougm 		share = NULL;
36893034Sdougm 	    }
36903034Sdougm 	    if (groupname != NULL) {
36913034Sdougm 		    ret = SA_NOT_ALLOWED;
36923034Sdougm 	    } else if (ret == SA_OK) {
36933034Sdougm 		char *legacygroup = "default";
36943034Sdougm 		/*
36953034Sdougm 		 * the legacy group is always present and zfs groups
36963034Sdougm 		 * come and go.  zfs shares may be in sub-groups and
36973034Sdougm 		 * the zfs share will already be in that group so it
36983034Sdougm 		 * isn't an error.
36993034Sdougm 		 */
37003034Sdougm 		if (share != NULL) {
37013034Sdougm 		/*
37023034Sdougm 		 * if the share exists, then make sure it is one we
37033034Sdougm 		 * want to handle.
37043034Sdougm 		 */
37053034Sdougm 		    group = sa_get_parent_group(share);
37063034Sdougm 		} else {
37073034Sdougm 		    group = sa_get_group(legacygroup);
37083034Sdougm 		}
37093034Sdougm 		if (group != NULL) {
37103034Sdougm 		    groupstatus = group_status(group);
37113034Sdougm 		    if (share == NULL) {
37123034Sdougm 			share = sa_add_share(group, sharepath, persist, &ret);
37133034Sdougm 			if (share == NULL && ret == SA_DUPLICATE_NAME) {
37143034Sdougm 			    /* could be a ZFS path being started */
37153034Sdougm 			    if (sa_zfs_is_shared(sharepath)) {
37163034Sdougm 				ret = SA_OK;
37173034Sdougm 				group = sa_get_group("zfs");
37183034Sdougm 				if (group == NULL) {
37193034Sdougm 				    /* this shouldn't happen */
37203034Sdougm 				    ret = SA_CONFIG_ERR;
37213034Sdougm 				}
37223034Sdougm 				if (group != NULL) {
37233034Sdougm 				    share = sa_add_share(group, sharepath,
37243034Sdougm 							    persist, &ret);
37253034Sdougm 				}
37263034Sdougm 			    }
37273034Sdougm 			}
37283034Sdougm 		    } else {
37293108Sdougm 			char *type;
37303034Sdougm 			/*
37313034Sdougm 			 * may want to change persist state, but the
37323108Sdougm 			 * important thing is to change options. We
37333108Sdougm 			 * need to change them regardless of the
37343108Sdougm 			 * source.
37353034Sdougm 			 */
37363108Sdougm 			if (sa_zfs_is_shared(sharepath)) {
37373108Sdougm 			    zfs = 1;
37383108Sdougm 			}
37393108Sdougm 			remove_all_options(share, protocol);
37403108Sdougm 			type = sa_get_share_attr(share, "type");
37413108Sdougm 			if (type != NULL &&
37423108Sdougm 			    strcmp(type, "transient") != 0) {
37433108Sdougm 			    curtype = SA_SHARE_PERMANENT;
37443108Sdougm 			}
37453108Sdougm 			if (type != NULL)
37463108Sdougm 			    sa_free_attr_string(type);
37473108Sdougm 			if (curtype != persist) {
37483108Sdougm 			    (void) sa_set_share_attr(share, "type",
37493034Sdougm 					persist == SA_SHARE_PERMANENT ?
37503034Sdougm 						"persist" : "transient");
37513034Sdougm 			}
37523034Sdougm 		    }
37533108Sdougm 		    /* have a group to hold this share path */
37543108Sdougm 		    if (ret == SA_OK && options != NULL &&
37553108Sdougm 			strlen(options) > 0) {
37563108Sdougm 			ret = sa_parse_legacy_options(share,
37573108Sdougm 							options,
37583108Sdougm 							protocol);
37593108Sdougm 		    }
37603034Sdougm 		    if (!zfs) {
37613108Sdougm 			/*
37623108Sdougm 			 * zfs shares never have resource or
37633108Sdougm 			 * description and we can't store the values
37643108Sdougm 			 * so don't try.
37653108Sdougm 			 */
37663034Sdougm 			if (ret == SA_OK && description != NULL)
37673034Sdougm 			    ret = sa_set_share_description(share, description);
37683034Sdougm 			if (ret == SA_OK && resource != NULL)
37693034Sdougm 			    ret = sa_set_share_attr(share, "resource",
37703034Sdougm 						    resource);
37713034Sdougm 		    }
37723034Sdougm 		    if (ret == SA_OK) {
37733034Sdougm 			if (strcmp(groupstatus, "enabled") == 0)
37743034Sdougm 			    ret = sa_enable_share(share, protocol);
37753034Sdougm 			if (ret == SA_OK && persist == SA_SHARE_PERMANENT) {
37763034Sdougm 			    (void) sa_update_legacy(share, protocol);
37773034Sdougm 			}
37783034Sdougm 			if (ret == SA_OK)
37793034Sdougm 			    ret = sa_update_config();
37803034Sdougm 		    }
37813034Sdougm 		} else {
37823034Sdougm 		    ret = SA_SYSTEM_ERR;
37833034Sdougm 		}
37843034Sdougm 	    }
37853034Sdougm 	}
37863034Sdougm 	if (ret != SA_OK) {
37873034Sdougm 	    (void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
37883034Sdougm 				sharepath, sa_errorstr(ret));
37893034Sdougm 	    ret = SA_LEGACY_ERR;
37903034Sdougm 
37913034Sdougm 	}
37923034Sdougm 	return (ret);
37933034Sdougm }
37943034Sdougm 
37953034Sdougm /*
37963034Sdougm  * sa_legacy_unshare(flags, argc, argv)
37973034Sdougm  *
37983034Sdougm  * Implements the original unshare command.
37993034Sdougm  */
38003034Sdougm 
38013034Sdougm int
38023034Sdougm sa_legacy_unshare(int flags, int argc, char *argv[])
38033034Sdougm {
38043034Sdougm 	char *protocol = "nfs"; /* for now */
38053034Sdougm 	char *options = NULL;
38063034Sdougm 	char *sharepath = NULL;
38073034Sdougm 	int persist = SA_SHARE_TRANSIENT;
38083034Sdougm 	int argsused = 0;
38093034Sdougm 	int c;
38103034Sdougm 	int ret = SA_OK;
38113034Sdougm 	int true_legacy = 0;
38123034Sdougm 	char cmd[MAXPATHLEN];
38133034Sdougm #ifdef lint
38143034Sdougm 	flags = flags;
38153034Sdougm 	options = options;
38163034Sdougm #endif
38173034Sdougm 
38183034Sdougm 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
38193034Sdougm 	    switch (c) {
38203034Sdougm 	    case 'h':
38213034Sdougm 	    case '?':
38223034Sdougm 		break;
38233034Sdougm 	    case 'F':
38243034Sdougm 		protocol = optarg;
38253034Sdougm 		if (!sa_valid_protocol(protocol)) {
38263034Sdougm 		    if (format_legacy_path(cmd, MAXPATHLEN,
38273034Sdougm 						protocol, "unshare") == 0 &&
38283034Sdougm 			check_legacy_cmd(cmd)) {
38293034Sdougm 			true_legacy++;
38303034Sdougm 		    } else {
38313034Sdougm 			(void) printf(gettext("Invalid file system name\n"));
38323034Sdougm 			return (SA_INVALID_PROTOCOL);
38333034Sdougm 		    }
38343034Sdougm 		}
38353034Sdougm 		break;
38363034Sdougm 	    case 'o':
38373034Sdougm 		options = optarg;
38383034Sdougm 		argsused++;
38393034Sdougm 		break;
38403034Sdougm 	    case 'p':
38413034Sdougm 		persist = SA_SHARE_PERMANENT;
38423034Sdougm 		argsused++;
38433034Sdougm 		break;
38443034Sdougm 	    default:
38453034Sdougm 		(void) printf(gettext("usage: %s\n"),
38463034Sdougm 				sa_get_usage(USAGE_UNSHARE));
38473034Sdougm 		return (SA_OK);
38483034Sdougm 	    }
38493034Sdougm 	}
38503034Sdougm 
38513034Sdougm 	/* have the info so construct what is needed */
38523034Sdougm 	if (optind == argc || (optind + 1) < argc) {
38533034Sdougm 	    ret = SA_SYNTAX_ERR;
38543034Sdougm 	} else {
38553034Sdougm 	    sa_share_t share;
38563034Sdougm 	    char dir[MAXPATHLEN];
38573034Sdougm 	    if (true_legacy) {
38583034Sdougm 		/* if still using legacy share/unshare, exec it */
38593034Sdougm 		ret = run_legacy_command(cmd, argv);
38603034Sdougm 		return (ret);
38613034Sdougm 	    }
38623034Sdougm 	    sharepath = argv[optind++];
38633034Sdougm 	    if (realpath(sharepath, dir) == NULL) {
38643034Sdougm 		ret = SA_NO_SUCH_PATH;
38653034Sdougm 	    } else {
38663034Sdougm 		sharepath = dir;
38673034Sdougm 		share = sa_find_share(sharepath);
38683034Sdougm 		if (share != NULL) {
38693034Sdougm 		    ret = sa_disable_share(share, protocol);
38703034Sdougm 		    if (ret == SA_OK) {
38713034Sdougm 			if (persist == SA_SHARE_PERMANENT)
38723034Sdougm 			    ret = sa_remove_share(share);
38733034Sdougm 			ret = sa_update_config();
38743034Sdougm 		    }
38753034Sdougm 		} else {
38763034Sdougm 		    ret = SA_NOT_SHARED;
38773034Sdougm 		}
38783034Sdougm 	    }
38793034Sdougm 	}
38803034Sdougm 	switch (ret) {
38813034Sdougm 	default:
38823034Sdougm 	    (void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
38833034Sdougm 	    ret = SA_LEGACY_ERR;
38843034Sdougm 	    break;
38853034Sdougm 	case SA_SYNTAX_ERR:
38863034Sdougm 	    (void) printf(gettext("usage: %s\n"),
38873034Sdougm 				sa_get_usage(USAGE_UNSHARE));
38883034Sdougm 	    break;
38893034Sdougm 	case SA_OK:
38903034Sdougm 	    break;
38913034Sdougm 	}
38923034Sdougm 	return (ret);
38933034Sdougm }
38943034Sdougm 
38953034Sdougm /*
38963034Sdougm  * common commands that implement the sub-commands used by all
38973034Sdougm  * protcols. The entries are found via the lookup command
38983034Sdougm  */
38993034Sdougm 
39003034Sdougm static sa_command_t commands[] = {
39013034Sdougm 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
39023034Sdougm 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
39033034Sdougm 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
39043034Sdougm 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
39053034Sdougm 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
39063034Sdougm 	{"list", 0, sa_list, USAGE_LIST},
39073034Sdougm 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
39083034Sdougm 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
39093034Sdougm 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
39103034Sdougm 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
39113034Sdougm 	{"show", 0, sa_show, USAGE_SHOW},
39123034Sdougm 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
39133034Sdougm 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
39143034Sdougm 		SVC_SET|SVC_ACTION},
39153034Sdougm 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
39163034Sdougm 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
39173034Sdougm 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
39183034Sdougm 	{NULL, 0, NULL, NULL}
39193034Sdougm };
39203034Sdougm 
39213034Sdougm static char *
39223034Sdougm sa_get_usage(sa_usage_t index)
39233034Sdougm {
39243034Sdougm 	char *ret = NULL;
39253034Sdougm 	switch (index) {
39263034Sdougm 	case USAGE_ADD_SHARE:
39273034Sdougm 	    ret = gettext("add-share [-nth] [-r resource-name] "
39283034Sdougm 			    "[-d \"description text\"] -s sharepath group");
39293034Sdougm 	    break;
39303034Sdougm 	case USAGE_CREATE:
39313034Sdougm 	    ret = gettext("create [-nvh] [-P proto [-p property=value]] group");
39323034Sdougm 	    break;
39333034Sdougm 	case USAGE_DELETE:
39343034Sdougm 	    ret = gettext("delete [-nvh] [-P proto] [-f] group");
39353034Sdougm 	    break;
39363034Sdougm 	case USAGE_DISABLE:
39373034Sdougm 	    ret = gettext("disable [-nvh] {-a | group ...}");
39383034Sdougm 	    break;
39393034Sdougm 	case USAGE_ENABLE:
39403034Sdougm 	    ret = gettext("enable [-nvh] {-a | group ...}");
39413034Sdougm 	    break;
39423034Sdougm 	case USAGE_LIST:
39433034Sdougm 	    ret = gettext("list [-vh] [-P proto]");
39443034Sdougm 	    break;
39453034Sdougm 	case USAGE_MOVE_SHARE:
39463034Sdougm 	    ret = gettext("move-share [-nvh] -s sharepath destination-group");
39473034Sdougm 	    break;
39483034Sdougm 	case USAGE_REMOVE_SHARE:
39493034Sdougm 	    ret = gettext("remove-share [-fnvh] -s sharepath group");
39503034Sdougm 	    break;
39513034Sdougm 	case USAGE_SET:
39523034Sdougm 	    ret = gettext("set [-nvh] -P proto [-S optspace] "
39533034Sdougm 				"[-p property=value]* [-s sharepath] group");
39543034Sdougm 	    break;
39553034Sdougm 	case USAGE_SET_SECURITY:
39563034Sdougm 	    ret = gettext("set-security [-nvh] -P proto -S security-type "
39573034Sdougm 			    "[-p property=value]* group");
39583034Sdougm 	    break;
39593034Sdougm 	case USAGE_SET_SHARE:
39603034Sdougm 	    ret = gettext("set-share [-nh] [-r resource] "
39613034Sdougm 			    "[-d \"description text\"] -s sharepath group");
39623034Sdougm 	    break;
39633034Sdougm 	case USAGE_SHOW:
39643034Sdougm 	    ret = gettext("show [-pvxh] [-P proto] [group ...]");
39653034Sdougm 	    break;
39663034Sdougm 	case USAGE_SHARE:
39673034Sdougm 	    ret = gettext("share [-F fstype] [-p] [-o optionlist]"
39683034Sdougm 			    "[-d description] [pathname [resourcename]]");
39693034Sdougm 	    break;
39703034Sdougm 	case USAGE_START:
39713034Sdougm 	    ret = gettext("start [-vh] [-P proto] {-a | group ...}");
39723034Sdougm 	    break;
39733034Sdougm 	case USAGE_STOP:
39743034Sdougm 	    ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
39753034Sdougm 	    break;
39763034Sdougm 	case USAGE_UNSET:
39773034Sdougm 	    ret = gettext("unset [-nvh] -P proto [-S optspace] "
39783034Sdougm 			    "[-p property]* group");
39793034Sdougm 	    break;
39803034Sdougm 	case USAGE_UNSET_SECURITY:
39813034Sdougm 	    ret = gettext("unset-security [-nvh] -P proto -S security-type "
39823034Sdougm 				"[-p property]* group");
39833034Sdougm 	    break;
39843034Sdougm 	case USAGE_UNSHARE:
39853034Sdougm 	    ret = gettext("unshare [-F fstype] [-p] [-o optionlist] sharepath");
39863034Sdougm 	    break;
39873034Sdougm 	}
39883034Sdougm 	return (ret);
39893034Sdougm }
39903034Sdougm 
39913034Sdougm /*
39923034Sdougm  * sa_lookup(cmd, proto)
39933034Sdougm  *
39943034Sdougm  * Lookup the sub-command. proto isn't currently used, but it may
39953034Sdougm  * eventually provide a way to provide protocol specific sub-commands.
39963034Sdougm  */
39973034Sdougm 
39983034Sdougm sa_command_t *
39993034Sdougm sa_lookup(char *cmd, char *proto)
40003034Sdougm {
40013034Sdougm 	int i;
40023034Sdougm 	size_t len;
40033034Sdougm #ifdef lint
40043034Sdougm 	proto = proto;
40053034Sdougm #endif
40063034Sdougm 
40073034Sdougm 	len = strlen(cmd);
40083034Sdougm 	for (i = 0; commands[i].cmdname != NULL; i++) {
40093034Sdougm 	    if (strncmp(cmd, commands[i].cmdname, len) == 0)
40103034Sdougm 		return (&commands[i]);
40113034Sdougm 	}
40123034Sdougm 	return (NULL);
40133034Sdougm }
40143034Sdougm 
40153034Sdougm void
40163034Sdougm sub_command_help(char *proto)
40173034Sdougm {
40183034Sdougm 	int i;
40193034Sdougm #ifdef lint
40203034Sdougm 	proto = proto;
40213034Sdougm #endif
40223034Sdougm 
40233034Sdougm 	(void) printf(gettext("\tsub-commands:\n"));
40243034Sdougm 	for (i = 0; commands[i].cmdname != NULL; i++) {
40253034Sdougm 	    if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
40263034Sdougm 		(void) printf("\t%s\n",
40273034Sdougm 				sa_get_usage((sa_usage_t)commands[i].cmdidx));
40283034Sdougm 	}
40293034Sdougm }
4030