Home | History | Annotate | Download | only in sharemgr
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <sys/types.h>
     28 #include <sys/stat.h>
     29 #include <fcntl.h>
     30 #include <stdlib.h>
     31 #include <stdio.h>
     32 #include <string.h>
     33 #include <ctype.h>
     34 #include <unistd.h>
     35 #include <getopt.h>
     36 #include <utmpx.h>
     37 #include <pwd.h>
     38 #include <auth_attr.h>
     39 #include <secdb.h>
     40 #include <sys/param.h>
     41 #include <sys/stat.h>
     42 #include <errno.h>
     43 
     44 #include <libshare.h>
     45 #include "sharemgr.h"
     46 #include <libscf.h>
     47 #include <libxml/tree.h>
     48 #include <libintl.h>
     49 #include <assert.h>
     50 #include <iconv.h>
     51 #include <langinfo.h>
     52 #include <dirent.h>
     53 
     54 static char *sa_get_usage(sa_usage_t);
     55 
     56 /*
     57  * Implementation of the common sub-commands supported by sharemgr.
     58  * A number of helper functions are also included.
     59  */
     60 
     61 /*
     62  * has_protocol(group, proto)
     63  *	If the group has an optionset with the specified protocol,
     64  *	return true (1) otherwise false (0).
     65  */
     66 static int
     67 has_protocol(sa_group_t group, char *protocol)
     68 {
     69 	sa_optionset_t optionset;
     70 	int result = 0;
     71 
     72 	optionset = sa_get_optionset(group, protocol);
     73 	if (optionset != NULL) {
     74 		result++;
     75 	}
     76 	return (result);
     77 }
     78 
     79 /*
     80  * validresource(name)
     81  *
     82  * Check that name only has valid characters in it. The current valid
     83  * set are the printable characters but not including:
     84  *	" / \ [ ] : | < > + ; , ? * = \t
     85  * Note that space is included and there is a maximum length.
     86  */
     87 static int
     88 validresource(const char *name)
     89 {
     90 	const char *cp;
     91 	size_t len;
     92 
     93 	if (name == NULL)
     94 		return (B_FALSE);
     95 
     96 	len = strlen(name);
     97 	if (len == 0 || len > SA_MAX_RESOURCE_NAME)
     98 		return (B_FALSE);
     99 
    100 	if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
    101 		return (B_FALSE);
    102 	}
    103 
    104 	for (cp = name; *cp != '\0'; cp++)
    105 		if (iscntrl(*cp))
    106 			return (B_FALSE);
    107 
    108 	return (B_TRUE);
    109 }
    110 
    111 /*
    112  * conv_to_utf8(input)
    113  *
    114  * Convert the input string to utf8 from the current locale.  If the
    115  * conversion fails, use the current locale, it is likely close
    116  * enough. For example, the "C" locale is a subset of utf-8. The
    117  * return value may be a new string or the original input string.
    118  */
    119 
    120 static char *
    121 conv_to_utf8(char *input)
    122 {
    123 	iconv_t cd;
    124 	char *inval = input;
    125 	char *output = input;
    126 	char *outleft;
    127 	char *curlocale;
    128 	size_t bytesleft;
    129 	size_t size;
    130 	size_t osize;
    131 	static int warned = 0;
    132 
    133 	curlocale = nl_langinfo(CODESET);
    134 	if (curlocale == NULL)
    135 		curlocale = "C";
    136 	cd = iconv_open("UTF-8", curlocale);
    137 	if (cd != NULL && cd != (iconv_t)-1) {
    138 		size = strlen(input);
    139 		/* Assume worst case of characters expanding to 4 bytes. */
    140 		bytesleft = size * 4;
    141 		output = calloc(bytesleft, 1);
    142 		if (output != NULL) {
    143 			outleft = output;
    144 			/* inval can be modified on return */
    145 			osize = iconv(cd, (const char **)&inval, &size,
    146 			    &outleft, &bytesleft);
    147 			if (osize == (size_t)-1 || size != 0) {
    148 				free(output);
    149 				output = input;
    150 			}
    151 		} else {
    152 			/* Need to return something. */
    153 			output = input;
    154 		}
    155 		(void) iconv_close(cd);
    156 	} else {
    157 		if (!warned)
    158 			(void) fprintf(stderr,
    159 			    gettext("Cannot convert to UTF-8 from %s\n"),
    160 			    curlocale ? curlocale : gettext("unknown"));
    161 		warned = 1;
    162 	}
    163 	return (output);
    164 }
    165 
    166 /*
    167  * conv_from(input)
    168  *
    169  * Convert the input string from utf8 to current locale.  If the
    170  * conversion isn't supported, just use as is. The return value may be
    171  * a new string or the original input string.
    172  */
    173 
    174 static char *
    175 conv_from_utf8(char *input)
    176 {
    177 	iconv_t cd;
    178 	char *output = input;
    179 	char *inval = input;
    180 	char *outleft;
    181 	char *curlocale;
    182 	size_t bytesleft;
    183 	size_t size;
    184 	size_t osize;
    185 	static int warned = 0;
    186 
    187 	curlocale = nl_langinfo(CODESET);
    188 	if (curlocale == NULL)
    189 		curlocale = "C";
    190 	cd = iconv_open(curlocale, "UTF-8");
    191 	if (cd != NULL && cd != (iconv_t)-1) {
    192 		size = strlen(input);
    193 		/* Assume worst case of characters expanding to 4 bytes. */
    194 		bytesleft = size * 4;
    195 		output = calloc(bytesleft, 1);
    196 		if (output != NULL) {
    197 			outleft = output;
    198 			osize = iconv(cd, (const char **)&inval, &size,
    199 			    &outleft, &bytesleft);
    200 			if (osize == (size_t)-1 || size != 0)
    201 				output = input;
    202 		} else {
    203 			/* Need to return something. */
    204 			output = input;
    205 		}
    206 		(void) iconv_close(cd);
    207 	} else {
    208 		if (!warned)
    209 			(void) fprintf(stderr,
    210 			    gettext("Cannot convert to %s from UTF-8\n"),
    211 			    curlocale ? curlocale : gettext("unknown"));
    212 		warned = 1;
    213 	}
    214 	return (output);
    215 }
    216 
    217 /*
    218  * print_rsrc_desc(resource, sharedesc)
    219  *
    220  * Print the resource description string after converting from UTF8 to
    221  * the current locale. If sharedesc is not NULL and there is no
    222  * description on the resource, use sharedesc. sharedesc will already
    223  * be converted to UTF8.
    224  */
    225 
    226 static void
    227 print_rsrc_desc(sa_resource_t resource, char *sharedesc)
    228 {
    229 	char *description;
    230 	char *desc;
    231 
    232 	if (resource == NULL)
    233 		return;
    234 
    235 	description = sa_get_resource_description(resource);
    236 	if (description != NULL) {
    237 		desc = conv_from_utf8(description);
    238 		if (desc != description) {
    239 			sa_free_share_description(description);
    240 			description = desc;
    241 		}
    242 	} else if (sharedesc != NULL) {
    243 		description = strdup(sharedesc);
    244 	}
    245 	if (description != NULL) {
    246 		(void) printf("\t\"%s\"", description);
    247 		sa_free_share_description(description);
    248 	}
    249 }
    250 
    251 /*
    252  * set_resource_desc(share, description)
    253  *
    254  * Set the share description value after converting the description
    255  * string to UTF8 from the current locale.
    256  */
    257 
    258 static int
    259 set_resource_desc(sa_share_t share, char *description)
    260 {
    261 	char *desc;
    262 	int ret;
    263 
    264 	desc = conv_to_utf8(description);
    265 	ret = sa_set_resource_description(share, desc);
    266 	if (description != desc)
    267 		sa_free_share_description(desc);
    268 	return (ret);
    269 }
    270 
    271 /*
    272  * set_share_desc(share, description)
    273  *
    274  * Set the resource description value after converting the description
    275  * string to UTF8 from the current locale.
    276  */
    277 
    278 static int
    279 set_share_desc(sa_share_t share, char *description)
    280 {
    281 	char *desc;
    282 	int ret;
    283 
    284 	desc = conv_to_utf8(description);
    285 	ret = sa_set_share_description(share, desc);
    286 	if (description != desc)
    287 		sa_free_share_description(desc);
    288 	return (ret);
    289 }
    290 
    291 /*
    292  * add_list(list, item, data, proto)
    293  *	Adds a new list member that points holds item in the list.
    294  *	If list is NULL, it starts a new list.  The function returns
    295  *	the first member of the list.
    296  */
    297 struct list *
    298 add_list(struct list *listp, void *item, void *data, char *proto)
    299 {
    300 	struct list *new, *tmp;
    301 
    302 	new = malloc(sizeof (struct list));
    303 	if (new != NULL) {
    304 		new->next = NULL;
    305 		new->item = item;
    306 		new->itemdata = data;
    307 		new->proto = proto;
    308 	} else {
    309 		return (listp);
    310 	}
    311 
    312 	if (listp == NULL)
    313 		return (new);
    314 
    315 	for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
    316 		/* get to end of list */
    317 	}
    318 	tmp->next = new;
    319 	return (listp);
    320 }
    321 
    322 /*
    323  * free_list(list)
    324  *	Given a list, free all the members of the list;
    325  */
    326 static void
    327 free_list(struct list *listp)
    328 {
    329 	struct list *tmp;
    330 	while (listp != NULL) {
    331 		tmp = listp;
    332 		listp = listp->next;
    333 		free(tmp);
    334 	}
    335 }
    336 
    337 /*
    338  * check_authorization(instname, which)
    339  *
    340  * Checks to see if the specific type of authorization in which is
    341  * enabled for the user in this SMF service instance.
    342  */
    343 
    344 static int
    345 check_authorization(char *instname, int which)
    346 {
    347 	scf_handle_t *handle = NULL;
    348 	scf_simple_prop_t *prop = NULL;
    349 	char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
    350 	char *authstr = NULL;
    351 	ssize_t numauths;
    352 	int ret = B_TRUE;
    353 	uid_t uid;
    354 	struct passwd *pw = NULL;
    355 
    356 	uid = getuid();
    357 	pw = getpwuid(uid);
    358 	if (pw == NULL) {
    359 		ret = B_FALSE;
    360 	} else {
    361 		/*
    362 		 * Since names are restricted to SA_MAX_NAME_LEN won't
    363 		 * overflow.
    364 		 */
    365 		(void) snprintf(svcstring, sizeof (svcstring), "%s:%s",
    366 		    SA_SVC_FMRI_BASE, instname);
    367 		handle = scf_handle_create(SCF_VERSION);
    368 		if (handle != NULL) {
    369 			if (scf_handle_bind(handle) == 0) {
    370 				switch (which) {
    371 				case SVC_SET:
    372 					prop = scf_simple_prop_get(handle,
    373 					    svcstring, "general",
    374 					    SVC_AUTH_VALUE);
    375 					break;
    376 				case SVC_ACTION:
    377 					prop = scf_simple_prop_get(handle,
    378 					    svcstring, "general",
    379 					    SVC_AUTH_ACTION);
    380 					break;
    381 				}
    382 			}
    383 		}
    384 	}
    385 	/* make sure we have an authorization string property */
    386 	if (prop != NULL) {
    387 		int i;
    388 		numauths = scf_simple_prop_numvalues(prop);
    389 		for (ret = 0, i = 0; i < numauths; i++) {
    390 			authstr = scf_simple_prop_next_astring(prop);
    391 			if (authstr != NULL) {
    392 				/* check if this user has one of the strings */
    393 				if (chkauthattr(authstr, pw->pw_name)) {
    394 					ret = 1;
    395 					break;
    396 				}
    397 			}
    398 		}
    399 		endauthattr();
    400 		scf_simple_prop_free(prop);
    401 	} else {
    402 		/* no authorization string defined */
    403 		ret = 0;
    404 	}
    405 	if (handle != NULL)
    406 		scf_handle_destroy(handle);
    407 	return (ret);
    408 }
    409 
    410 /*
    411  * check_authorizations(instname, flags)
    412  *
    413  * check all the needed authorizations for the user in this service
    414  * instance. Return value of 1(true) or 0(false) indicates whether
    415  * there are authorizations for the user or not.
    416  */
    417 
    418 static int
    419 check_authorizations(char *instname, int flags)
    420 {
    421 	int ret1 = 0;
    422 	int ret2 = 0;
    423 	int ret;
    424 
    425 	if (flags & SVC_SET)
    426 		ret1 = check_authorization(instname, SVC_SET);
    427 	if (flags & SVC_ACTION)
    428 		ret2 = check_authorization(instname, SVC_ACTION);
    429 	switch (flags) {
    430 	case SVC_ACTION:
    431 		ret = ret2;
    432 		break;
    433 	case SVC_SET:
    434 		ret = ret1;
    435 		break;
    436 	case SVC_ACTION|SVC_SET:
    437 		ret = ret1 & ret2;
    438 		break;
    439 	default:
    440 		/* if not flags set, we assume we don't need authorizations */
    441 		ret = 1;
    442 	}
    443 	return (ret);
    444 }
    445 
    446 /*
    447  * notify_or_enable_share(share, protocol)
    448  *
    449  * Since some protocols don't want an "enable" when properties change,
    450  * this function will use the protocol specific notify function
    451  * first. If that fails, it will then attempt to use the
    452  * sa_enable_share().  "protocol" is the protocol that was specified
    453  * on the command line.
    454  */
    455 static void
    456 notify_or_enable_share(sa_share_t share, char *protocol)
    457 {
    458 	sa_group_t group;
    459 	sa_optionset_t opt;
    460 	int ret = SA_OK;
    461 	char *path;
    462 	char *groupproto;
    463 	sa_share_t parent = share;
    464 
    465 	/* If really a resource, get parent share */
    466 	if (!sa_is_share(share)) {
    467 		parent = sa_get_resource_parent((sa_resource_t)share);
    468 	}
    469 
    470 	/*
    471 	 * Now that we've got a share in "parent", make sure it has a path.
    472 	 */
    473 	path = sa_get_share_attr(parent, "path");
    474 	if (path == NULL)
    475 		return;
    476 
    477 	group = sa_get_parent_group(parent);
    478 
    479 	if (group == NULL) {
    480 		sa_free_attr_string(path);
    481 		return;
    482 	}
    483 	for (opt = sa_get_optionset(group, NULL);
    484 	    opt != NULL;
    485 	    opt = sa_get_next_optionset(opt)) {
    486 		groupproto = sa_get_optionset_attr(opt, "type");
    487 		if (groupproto == NULL ||
    488 		    (protocol != NULL && strcmp(groupproto, protocol) != 0)) {
    489 			if (groupproto != NULL)
    490 				sa_free_attr_string(groupproto);
    491 			continue;
    492 		}
    493 		if (sa_is_share(share)) {
    494 			if ((ret = sa_proto_change_notify(share,
    495 			    groupproto)) != SA_OK) {
    496 				ret = sa_enable_share(share, groupproto);
    497 				if (ret != SA_OK) {
    498 					(void) printf(
    499 					    gettext("Could not reenable"
    500 					    " share %s: %s\n"),
    501 					    path, sa_errorstr(ret));
    502 				}
    503 			}
    504 		} else {
    505 			/* Must be a resource */
    506 			if ((ret = sa_proto_notify_resource(share,
    507 			    groupproto)) != SA_OK) {
    508 				ret = sa_enable_resource(share, groupproto);
    509 				if (ret != SA_OK) {
    510 					(void) printf(
    511 					    gettext("Could not "
    512 					    "reenable resource %s: "
    513 					    "%s\n"), path,
    514 					    sa_errorstr(ret));
    515 				}
    516 			}
    517 		}
    518 		sa_free_attr_string(groupproto);
    519 	}
    520 	sa_free_attr_string(path);
    521 }
    522 
    523 /*
    524  * enable_group(group, updateproto, notify, proto)
    525  *
    526  * enable all the shares in the specified group. This is a helper for
    527  * enable_all_groups in order to simplify regular and subgroup (zfs)
    528  * enabling. Group has already been checked for non-NULL. If notify
    529  * is non-zero, attempt to use the notify interface rather than
    530  * enable.
    531  */
    532 static void
    533 enable_group(sa_group_t group, char *updateproto, int notify, char *proto)
    534 {
    535 	sa_share_t share;
    536 
    537 	for (share = sa_get_share(group, NULL);
    538 	    share != NULL;
    539 	    share = sa_get_next_share(share)) {
    540 		if (updateproto != NULL)
    541 			(void) sa_update_legacy(share, updateproto);
    542 		if (notify)
    543 			notify_or_enable_share(share, proto);
    544 		else
    545 			(void) sa_enable_share(share, proto);
    546 	}
    547 }
    548 
    549 /*
    550  * isenabled(group)
    551  *
    552  * Returns B_TRUE if the group is enabled or B_FALSE if it isn't.
    553  * Moved to separate function to reduce clutter in the code.
    554  */
    555 
    556 static int
    557 isenabled(sa_group_t group)
    558 {
    559 	char *state;
    560 	int ret = B_FALSE;
    561 
    562 	if (group != NULL) {
    563 		state = sa_get_group_attr(group, "state");
    564 		if (state != NULL) {
    565 
    566 			if (strcmp(state, "enabled") == 0)
    567 				ret = B_TRUE;
    568 			sa_free_attr_string(state);
    569 		}
    570 	}
    571 	return (ret);
    572 }
    573 
    574 /*
    575  * enable_all_groups(list, setstate, online, updateproto)
    576  *
    577  * Given a list of groups, enable each one found.  If updateproto is
    578  * not NULL, then update all the shares for the protocol that was
    579  * passed in. If enable is non-zero, tell enable_group to try the
    580  * notify interface since this is a property change.
    581  */
    582 static int
    583 enable_all_groups(sa_handle_t handle, struct list *work, int setstate,
    584     int online, char *updateproto, int enable)
    585 {
    586 	int ret;
    587 	char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
    588 	char *state;
    589 	char *name;
    590 	char *zfs = NULL;
    591 	sa_group_t group;
    592 	sa_group_t subgroup;
    593 
    594 	for (ret = SA_OK; work != NULL;	work = work->next) {
    595 		group = (sa_group_t)work->item;
    596 
    597 		/*
    598 		 * If setstate == TRUE, then make sure to set
    599 		 * enabled. This needs to be done here in order for
    600 		 * the isenabled check to succeed on a newly enabled
    601 		 * group.
    602 		 */
    603 		if (setstate == B_TRUE) {
    604 			ret = sa_set_group_attr(group, "state",	"enabled");
    605 			if (ret != SA_OK)
    606 				break;
    607 		}
    608 
    609 		/*
    610 		 * Check to see if group is enabled. If it isn't, skip
    611 		 * the rest.  We don't want shares starting if the
    612 		 * group is disabled. The properties may have been
    613 		 * updated, but there won't be a change until the
    614 		 * group is enabled.
    615 		 */
    616 		if (!isenabled(group))
    617 			continue;
    618 
    619 		/* if itemdata != NULL then a single share */
    620 		if (work->itemdata != NULL) {
    621 			if (enable) {
    622 				if (work->itemdata != NULL)
    623 					notify_or_enable_share(work->itemdata,
    624 					    updateproto);
    625 				else
    626 					ret = SA_CONFIG_ERR;
    627 			} else {
    628 				if (sa_is_share(work->itemdata)) {
    629 					ret = sa_enable_share(
    630 					    (sa_share_t)work->itemdata,
    631 					    updateproto);
    632 				} else {
    633 					ret = sa_enable_resource(
    634 					    (sa_resource_t)work->itemdata,
    635 					    updateproto);
    636 				}
    637 			}
    638 		}
    639 		if (ret != SA_OK)
    640 			break;
    641 
    642 		/* if itemdata == NULL then the whole group */
    643 		if (work->itemdata == NULL) {
    644 			zfs = sa_get_group_attr(group, "zfs");
    645 			/*
    646 			 * If the share is managed by ZFS, don't
    647 			 * update any of the protocols since ZFS is
    648 			 * handling this.  Updateproto will contain
    649 			 * the name of the protocol that we want to
    650 			 * update legacy files for.
    651 			 */
    652 			enable_group(group, zfs == NULL ? updateproto : NULL,
    653 			    enable, work->proto);
    654 			if (zfs != NULL)
    655 				sa_free_attr_string(zfs);
    656 
    657 			for (subgroup = sa_get_sub_group(group);
    658 			    subgroup != NULL;
    659 			    subgroup = sa_get_next_group(subgroup)) {
    660 				/* never update legacy for ZFS subgroups */
    661 				enable_group(subgroup, NULL, enable,
    662 				    work->proto);
    663 			}
    664 		}
    665 		if (online) {
    666 			zfs = sa_get_group_attr(group, "zfs");
    667 			name = sa_get_group_attr(group, "name");
    668 			if (name != NULL) {
    669 				if (zfs == NULL) {
    670 					(void) snprintf(instance,
    671 					    sizeof (instance), "%s:%s",
    672 					    SA_SVC_FMRI_BASE, name);
    673 					state = smf_get_state(instance);
    674 					if (state == NULL ||
    675 					    strcmp(state, "online") != 0) {
    676 						(void) smf_enable_instance(
    677 						    instance, 0);
    678 						free(state);
    679 					}
    680 				} else {
    681 					sa_free_attr_string(zfs);
    682 					zfs = NULL;
    683 				}
    684 				if (name != NULL)
    685 					sa_free_attr_string(name);
    686 			}
    687 		}
    688 	}
    689 	if (ret == SA_OK) {
    690 		ret = sa_update_config(handle);
    691 	}
    692 	return (ret);
    693 }
    694 
    695 /*
    696  * chk_opt(optlistp, security, proto)
    697  *
    698  * Do a sanity check on the optlist provided for the protocol.  This
    699  * is a syntax check and verification that the property is either a
    700  * general or specific to a names optionset.
    701  */
    702 
    703 static int
    704 chk_opt(struct options *optlistp, int security, char *proto)
    705 {
    706 	struct options *optlist;
    707 	char *sep = "";
    708 	int notfirst = 0;
    709 	int ret;
    710 
    711 	for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
    712 		char *optname;
    713 
    714 		optname = optlist->optname;
    715 		ret = OPT_ADD_OK;
    716 		/* extract property/value pair */
    717 		if (sa_is_security(optname, proto)) {
    718 			if (!security)
    719 				ret = OPT_ADD_SECURITY;
    720 		} else {
    721 			if (security)
    722 				ret = OPT_ADD_PROPERTY;
    723 		}
    724 		if (ret != OPT_ADD_OK) {
    725 			if (notfirst == 0)
    726 				(void) printf(
    727 				    gettext("Property syntax error: "));
    728 			switch (ret) {
    729 			case OPT_ADD_SYNTAX:
    730 				(void) printf(gettext("%ssyntax error: %s"),
    731 				    sep, optname);
    732 				sep = ", ";
    733 				break;
    734 			case OPT_ADD_SECURITY:
    735 				(void) printf(gettext("%s%s requires -S"),
    736 				    optname, sep);
    737 				sep = ", ";
    738 				break;
    739 			case OPT_ADD_PROPERTY:
    740 				(void) printf(
    741 				    gettext("%s%s not supported with -S"),
    742 				    optname, sep);
    743 				sep = ", ";
    744 				break;
    745 			}
    746 			notfirst++;
    747 		}
    748 	}
    749 	if (notfirst) {
    750 		(void) printf("\n");
    751 		ret = SA_SYNTAX_ERR;
    752 	}
    753 	return (ret);
    754 }
    755 
    756 /*
    757  * free_opt(optlist)
    758  *	Free the specified option list.
    759  */
    760 static void
    761 free_opt(struct options *optlist)
    762 {
    763 	struct options *nextopt;
    764 	while (optlist != NULL) {
    765 		nextopt = optlist->next;
    766 		free(optlist);
    767 		optlist = nextopt;
    768 	}
    769 }
    770 
    771 /*
    772  * check property list for valid properties
    773  * A null value is a remove which is always valid.
    774  */
    775 static int
    776 valid_options(sa_handle_t handle, struct options *optlist, char *proto,
    777     void *object, char *sec)
    778 {
    779 	int ret = SA_OK;
    780 	struct options *cur;
    781 	sa_property_t prop;
    782 	sa_optionset_t parent = NULL;
    783 
    784 	if (object != NULL) {
    785 		if (sec == NULL)
    786 			parent = sa_get_optionset(object, proto);
    787 		else
    788 			parent = sa_get_security(object, sec, proto);
    789 	}
    790 
    791 	for (cur = optlist; cur != NULL; cur = cur->next) {
    792 		if (cur->optvalue == NULL)
    793 			continue;
    794 		prop = sa_create_property(cur->optname, cur->optvalue);
    795 		if (prop == NULL)
    796 			ret = SA_NO_MEMORY;
    797 		if (ret != SA_OK ||
    798 		    (ret = sa_valid_property(handle, parent, proto, prop)) !=
    799 		    SA_OK) {
    800 			(void) printf(
    801 			    gettext("Could not add property %s: %s\n"),
    802 			    cur->optname, sa_errorstr(ret));
    803 		}
    804 		(void) sa_remove_property(prop);
    805 	}
    806 	return (ret);
    807 }
    808 
    809 /*
    810  * add_optionset(group, optlist, protocol, *err)
    811  *	Add the options in optlist to an optionset and then add the optionset
    812  *	to the group.
    813  *
    814  *	The return value indicates if there was a "change" while errors are
    815  *	returned via the *err parameters.
    816  */
    817 static int
    818 add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
    819 {
    820 	sa_optionset_t optionset;
    821 	int ret = SA_OK;
    822 	int result = B_FALSE;
    823 	sa_handle_t handle;
    824 
    825 	optionset = sa_get_optionset(group, proto);
    826 	if (optionset == NULL) {
    827 		optionset = sa_create_optionset(group, proto);
    828 		if (optionset == NULL)
    829 			ret = SA_NO_MEMORY;
    830 		result = B_TRUE; /* adding a protocol is a change */
    831 	}
    832 	if (optionset == NULL) {
    833 		ret = SA_NO_MEMORY;
    834 		goto out;
    835 	}
    836 	handle = sa_find_group_handle(group);
    837 	if (handle == NULL) {
    838 		ret = SA_CONFIG_ERR;
    839 		goto out;
    840 	}
    841 	while (optlist != NULL) {
    842 		sa_property_t prop;
    843 		prop = sa_get_property(optionset, optlist->optname);
    844 		if (prop == NULL) {
    845 			/*
    846 			 * add the property, but only if it is
    847 			 * a non-NULL or non-zero length value
    848 			 */
    849 			if (optlist->optvalue != NULL) {
    850 				prop = sa_create_property(optlist->optname,
    851 				    optlist->optvalue);
    852 				if (prop != NULL) {
    853 					ret = sa_valid_property(handle,
    854 					    optionset, proto, prop);
    855 					if (ret != SA_OK) {
    856 						(void) sa_remove_property(prop);
    857 						(void) printf(gettext("Could "
    858 						    "not add property "
    859 						    "%s: %s\n"),
    860 						    optlist->optname,
    861 						    sa_errorstr(ret));
    862 					}
    863 				}
    864 				if (ret == SA_OK) {
    865 					ret = sa_add_property(optionset, prop);
    866 					if (ret != SA_OK) {
    867 						(void) printf(gettext(
    868 						    "Could not add property "
    869 						    "%s: %s\n"),
    870 						    optlist->optname,
    871 						    sa_errorstr(ret));
    872 					} else {
    873 						/* there was a change */
    874 						result = B_TRUE;
    875 					}
    876 				}
    877 			}
    878 		} else {
    879 			ret = sa_update_property(prop, optlist->optvalue);
    880 			/* should check to see if value changed */
    881 			if (ret != SA_OK) {
    882 				(void) printf(gettext("Could not update "
    883 				    "property %s: %s\n"), optlist->optname,
    884 				    sa_errorstr(ret));
    885 			} else {
    886 				result = B_TRUE;
    887 			}
    888 		}
    889 		optlist = optlist->next;
    890 	}
    891 	ret = sa_commit_properties(optionset, 0);
    892 
    893 out:
    894 	if (err != NULL)
    895 		*err = ret;
    896 	return (result);
    897 }
    898 
    899 /*
    900  * resource_compliant(group)
    901  *
    902  * Go through all the shares in the group. Assume compliant, but if
    903  * any share doesn't have at least one resource name, it isn't
    904  * compliant.
    905  */
    906 static int
    907 resource_compliant(sa_group_t group)
    908 {
    909 	sa_share_t share;
    910 
    911 	for (share = sa_get_share(group, NULL); share != NULL;
    912 	    share = sa_get_next_share(share)) {
    913 		if (sa_get_share_resource(share, NULL) == NULL) {
    914 			return (B_FALSE);
    915 		}
    916 	}
    917 	return (B_TRUE);
    918 }
    919 
    920 /*
    921  * fix_path(path)
    922  *
    923  * change all illegal characters to something else.  For now, all get
    924  * converted to '_' and the leading '/' is stripped off. This is used
    925  * to construct an resource name (SMB share name) that is valid.
    926  * Caller must pass a valid path.
    927  */
    928 static void
    929 fix_path(char *path)
    930 {
    931 	char *cp;
    932 	size_t len;
    933 
    934 	assert(path != NULL);
    935 
    936 	/* make sure we are appropriate length */
    937 	cp = path + 1; /* skip leading slash */
    938 	while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) {
    939 		cp = strchr(cp, '/');
    940 		if (cp != NULL)
    941 			cp++;
    942 	}
    943 	/* two cases - cp == NULL and cp is substring of path */
    944 	if (cp == NULL) {
    945 		/* just take last SA_MAX_RESOURCE_NAME chars */
    946 		len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME;
    947 		(void) memmove(path, path + len, SA_MAX_RESOURCE_NAME);
    948 		path[SA_MAX_RESOURCE_NAME] = '\0';
    949 	} else {
    950 		len = strlen(cp) + 1;
    951 		(void) memmove(path, cp, len);
    952 	}
    953 
    954 	/*
    955 	 * Don't want any of the characters that are not allowed
    956 	 * in and SMB share name. Replace them with '_'.
    957 	 */
    958 	while (*path) {
    959 		switch (*path) {
    960 		case '/':
    961 		case '"':
    962 		case '\\':
    963 		case '[':
    964 		case ']':
    965 		case ':':
    966 		case '|':
    967 		case '<':
    968 		case '>':
    969 		case '+':
    970 		case ';':
    971 		case ',':
    972 		case '?':
    973 		case '*':
    974 		case '=':
    975 		case '\t':
    976 			*path = '_';
    977 			break;
    978 		}
    979 		path++;
    980 	}
    981 }
    982 
    983 /*
    984  * name_adjust(path, count)
    985  *
    986  * Add a ~<count> in place of last few characters. The total number of
    987  * characters is dependent on count.
    988  */
    989 #define	MAX_MANGLE_NUMBER	10000
    990 
    991 static int
    992 name_adjust(char *path, int count)
    993 {
    994 	size_t len;
    995 
    996 	len = strlen(path) - 2;
    997 	if (count > 10)
    998 		len--;
    999 	if (count > 100)
   1000 		len--;
   1001 	if (count > 1000)
   1002 		len--;
   1003 	if (len > 0)
   1004 		(void) sprintf(path + len, "~%d", count);
   1005 	else
   1006 		return (SA_BAD_VALUE);
   1007 
   1008 	return (SA_OK);
   1009 }
   1010 
   1011 /*
   1012  * make_resources(group)
   1013  *
   1014  * Go through all the shares in the group and make them have resource
   1015  * names.
   1016  */
   1017 static void
   1018 make_resources(sa_group_t group)
   1019 {
   1020 	sa_share_t share;
   1021 	int count;
   1022 	int err = SA_OK;
   1023 
   1024 	for (share = sa_get_share(group, NULL); share != NULL;
   1025 	    share = sa_get_next_share(share)) {
   1026 		/* Skip those with resources */
   1027 		if (sa_get_share_resource(share, NULL) == NULL) {
   1028 			char *path;
   1029 			path = sa_get_share_attr(share, "path");
   1030 			if (path == NULL)
   1031 				continue;
   1032 			fix_path(path);
   1033 			count = 0;	/* reset for next resource */
   1034 			while (sa_add_resource(share, path,
   1035 			    SA_SHARE_PERMANENT, &err) == NULL &&
   1036 			    err == SA_DUPLICATE_NAME) {
   1037 				int ret;
   1038 				ret = name_adjust(path, count);
   1039 				count++;
   1040 				if (ret != SA_OK ||
   1041 				    count >= MAX_MANGLE_NUMBER) {
   1042 					(void) printf(gettext(
   1043 					    "Cannot create resource name for"
   1044 					    " path: %s\n"), path);
   1045 					break;
   1046 				}
   1047 			}
   1048 			sa_free_attr_string(path);
   1049 		}
   1050 	}
   1051 }
   1052 
   1053 /*
   1054  * check_valid_group(group, protocol)
   1055  *
   1056  * Check to see that the group should have the protocol added (if
   1057  * there is one specified).
   1058  */
   1059 
   1060 static int
   1061 check_valid_group(sa_group_t group, char *groupname, char *protocol)
   1062 {
   1063 
   1064 	if (protocol != NULL) {
   1065 		if (has_protocol(group, protocol)) {
   1066 			(void) printf(gettext(
   1067 			    "Group \"%s\" already exists"
   1068 			    " with protocol %s\n"), groupname,
   1069 			    protocol);
   1070 			return (SA_DUPLICATE_NAME);
   1071 		} else if (strcmp(groupname, "default") == 0 &&
   1072 		    strcmp(protocol, "nfs") != 0) {
   1073 			(void) printf(gettext(
   1074 			    "Group \"%s\" only allows protocol "
   1075 			    "\"%s\"\n"), groupname, "nfs");
   1076 			return (SA_INVALID_PROTOCOL);
   1077 		}
   1078 	} else {
   1079 		/* must add new protocol */
   1080 		(void) printf(gettext(
   1081 		    "Group already exists and no protocol "
   1082 		    "specified.\n"));
   1083 		return (SA_DUPLICATE_NAME);
   1084 	}
   1085 	return (SA_OK);
   1086 }
   1087 
   1088 /*
   1089  * enforce_featureset(group, protocol, dryrun, force)
   1090  *
   1091  * Check the protocol featureset against the group and enforce any
   1092  * rules that might be imposed.
   1093  */
   1094 
   1095 static int
   1096 enforce_featureset(sa_group_t group, char *protocol, boolean_t dryrun,
   1097     boolean_t force)
   1098 {
   1099 	uint64_t features;
   1100 
   1101 	if (protocol == NULL)
   1102 		return (SA_OK);
   1103 
   1104 	/*
   1105 	 * First check to see if specified protocol is one we want to
   1106 	 * allow on a group. Only server protocols are allowed here.
   1107 	 */
   1108 	features = sa_proto_get_featureset(protocol);
   1109 	if (!(features & SA_FEATURE_SERVER)) {
   1110 		(void) printf(
   1111 		    gettext("Protocol \"%s\" not supported.\n"), protocol);
   1112 		return (SA_INVALID_PROTOCOL);
   1113 	}
   1114 
   1115 	/*
   1116 	 * Check to see if the new protocol is one that requires
   1117 	 * resource names and make sure we are compliant before
   1118 	 * proceeding.
   1119 	 */
   1120 	if ((features & SA_FEATURE_RESOURCE) &&
   1121 	    !resource_compliant(group)) {
   1122 		if (force && !dryrun) {
   1123 			make_resources(group);
   1124 		} else {
   1125 			(void) printf(
   1126 			    gettext("Protocol requires resource names to be "
   1127 			    "set: %s\n"), protocol);
   1128 			return (SA_RESOURCE_REQUIRED);
   1129 		}
   1130 	}
   1131 	return (SA_OK);
   1132 }
   1133 
   1134 /*
   1135  * set_all_protocols(group)
   1136  *
   1137  * Get the list of all protocols and add all server protocols to the
   1138  * group.
   1139  */
   1140 
   1141 static int
   1142 set_all_protocols(sa_group_t group)
   1143 {
   1144 	char **protolist;
   1145 	int numprotos, i;
   1146 	uint64_t features;
   1147 	sa_optionset_t optionset;
   1148 	int ret = SA_OK;
   1149 
   1150 	/*
   1151 	 * Now make sure we really want to put this protocol on a
   1152 	 * group. Only server protocols can go here.
   1153 	 */
   1154 	numprotos = sa_get_protocols(&protolist);
   1155 	for (i = 0; i < numprotos; i++) {
   1156 		features = sa_proto_get_featureset(protolist[i]);
   1157 		if (features & SA_FEATURE_SERVER) {
   1158 			optionset = sa_create_optionset(group, protolist[i]);
   1159 			if (optionset == NULL) {
   1160 				ret = SA_NO_MEMORY;
   1161 				break;
   1162 			}
   1163 		}
   1164 	}
   1165 
   1166 	if (protolist != NULL)
   1167 		free(protolist);
   1168 
   1169 	return (ret);
   1170 }
   1171 
   1172 /*
   1173  * sa_create(flags, argc, argv)
   1174  *	create a new group
   1175  *	this may or may not have a protocol associated with it.
   1176  *	No protocol means "all" protocols in this case.
   1177  */
   1178 static int
   1179 sa_create(sa_handle_t handle, int flags, int argc, char *argv[])
   1180 {
   1181 	char *groupname;
   1182 
   1183 	sa_group_t group;
   1184 	boolean_t force = B_FALSE;
   1185 	boolean_t verbose = B_FALSE;
   1186 	boolean_t dryrun = B_FALSE;
   1187 	int c;
   1188 	char *protocol = NULL;
   1189 	int ret = SA_OK;
   1190 	struct options *optlist = NULL;
   1191 	int err = SA_OK;
   1192 	int auth;
   1193 	boolean_t created = B_FALSE;
   1194 
   1195 	while ((c = getopt(argc, argv, "?fhvnP:p:")) != EOF) {
   1196 		switch (c) {
   1197 		case 'f':
   1198 			force = B_TRUE;
   1199 			break;
   1200 		case 'v':
   1201 			verbose = B_TRUE;
   1202 			break;
   1203 		case 'n':
   1204 			dryrun = B_TRUE;
   1205 			break;
   1206 		case 'P':
   1207 			if (protocol != NULL) {
   1208 				(void) printf(gettext("Specifying "
   1209 				    "multiple protocols "
   1210 				    "not supported: %s\n"), protocol);
   1211 				return (SA_SYNTAX_ERR);
   1212 			}
   1213 			protocol = optarg;
   1214 			if (sa_valid_protocol(protocol))
   1215 				break;
   1216 			(void) printf(gettext(
   1217 			    "Invalid protocol specified: %s\n"), protocol);
   1218 			return (SA_INVALID_PROTOCOL);
   1219 			break;
   1220 		case 'p':
   1221 			ret = add_opt(&optlist, optarg, 0);
   1222 			switch (ret) {
   1223 			case OPT_ADD_SYNTAX:
   1224 				(void) printf(gettext(
   1225 				    "Property syntax error for property: %s\n"),
   1226 				    optarg);
   1227 				return (SA_SYNTAX_ERR);
   1228 			case OPT_ADD_SECURITY:
   1229 				(void) printf(gettext(
   1230 				    "Security properties need "
   1231 				    "to be set with set-security: %s\n"),
   1232 				    optarg);
   1233 				return (SA_SYNTAX_ERR);
   1234 			default:
   1235 				break;
   1236 			}
   1237 			break;
   1238 		case 'h':
   1239 			/* optopt on valid arg isn't defined */
   1240 			optopt = c;
   1241 			/*FALLTHROUGH*/
   1242 		case '?':
   1243 		default:
   1244 			/*
   1245 			 * Since a bad option gets to here, sort it
   1246 			 * out and return a syntax error return value
   1247 			 * if necessary.
   1248 			 */
   1249 			switch (optopt) {
   1250 			default:
   1251 				err = SA_SYNTAX_ERR;
   1252 				break;
   1253 			case 'h':
   1254 			case '?':
   1255 				break;
   1256 			}
   1257 			(void) printf(gettext("usage: %s\n"),
   1258 			    sa_get_usage(USAGE_CREATE));
   1259 			return (err);
   1260 		}
   1261 	}
   1262 
   1263 	if (optind >= argc) {
   1264 		(void) printf(gettext("usage: %s\n"),
   1265 		    sa_get_usage(USAGE_CREATE));
   1266 		(void) printf(gettext("\tgroup must be specified.\n"));
   1267 		return (SA_BAD_PATH);
   1268 	}
   1269 
   1270 	if ((optind + 1) < argc) {
   1271 		(void) printf(gettext("usage: %s\n"),
   1272 		    sa_get_usage(USAGE_CREATE));
   1273 		(void) printf(gettext("\textraneous group(s) at end\n"));
   1274 		return (SA_SYNTAX_ERR);
   1275 	}
   1276 
   1277 	if (protocol == NULL && optlist != NULL) {
   1278 		/* lookup default protocol */
   1279 		(void) printf(gettext("usage: %s\n"),
   1280 		    sa_get_usage(USAGE_CREATE));
   1281 		(void) printf(gettext("\tprotocol must be specified "
   1282 		    "with properties\n"));
   1283 		return (SA_INVALID_PROTOCOL);
   1284 	}
   1285 
   1286 	if (optlist != NULL)
   1287 		ret = chk_opt(optlist, 0, protocol);
   1288 	if (ret == OPT_ADD_SECURITY) {
   1289 		(void) printf(gettext("Security properties not "
   1290 		    "supported with create\n"));
   1291 		return (SA_SYNTAX_ERR);
   1292 	}
   1293 
   1294 	/*
   1295 	 * If a group already exists, we can only add a new protocol
   1296 	 * to it and not create a new one or add the same protocol
   1297 	 * again.
   1298 	 */
   1299 
   1300 	groupname = argv[optind];
   1301 
   1302 	auth = check_authorizations(groupname, flags);
   1303 
   1304 	group = sa_get_group(handle, groupname);
   1305 	if (group != NULL) {
   1306 		/* group exists so must be a protocol add */
   1307 		ret = check_valid_group(group, groupname, protocol);
   1308 	} else {
   1309 		/*
   1310 		 * is it a valid name? Must comply with SMF instance
   1311 		 * name restrictions.
   1312 		 */
   1313 		if (!sa_valid_group_name(groupname)) {
   1314 			ret = SA_INVALID_NAME;
   1315 			(void) printf(gettext("Invalid group name: %s\n"),
   1316 			    groupname);
   1317 		}
   1318 	}
   1319 	if (ret == SA_OK) {
   1320 		/* check protocol vs optlist */
   1321 		if (optlist != NULL) {
   1322 			/* check options, if any, for validity */
   1323 			ret = valid_options(handle, optlist, protocol,
   1324 			    group, NULL);
   1325 		}
   1326 	}
   1327 	if (ret == SA_OK && !dryrun) {
   1328 		if (group == NULL) {
   1329 			group = sa_create_group(handle, (char *)groupname,
   1330 			    &err);
   1331 			created = B_TRUE;
   1332 		}
   1333 		if (group != NULL) {
   1334 			sa_optionset_t optionset;
   1335 
   1336 			/*
   1337 			 * Check group and protocol against featureset
   1338 			 * requirements.
   1339 			 */
   1340 			ret = enforce_featureset(group, protocol,
   1341 			    dryrun, force);
   1342 			if (ret != SA_OK)
   1343 				goto err;
   1344 
   1345 			/*
   1346 			 * So far so good. Now add the required
   1347 			 * optionset(s) to the group.
   1348 			 */
   1349 			if (optlist != NULL) {
   1350 				(void) add_optionset(group, optlist, protocol,
   1351 				    &ret);
   1352 			} else if (protocol != NULL) {
   1353 				optionset = sa_create_optionset(group,
   1354 				    protocol);
   1355 				if (optionset == NULL)
   1356 					ret = SA_NO_MEMORY;
   1357 			} else if (protocol == NULL) {
   1358 				/* default group create so add all protocols */
   1359 				ret = set_all_protocols(group);
   1360 			}
   1361 			/*
   1362 			 * We have a group and legal additions
   1363 			 */
   1364 			if (ret == SA_OK) {
   1365 				/*
   1366 				 * Commit to configuration for protocols that
   1367 				 * need to do block updates. For NFS, this
   1368 				 * doesn't do anything but it will be run for
   1369 				 * all protocols that implement the
   1370 				 * appropriate plugin.
   1371 				 */
   1372 				ret = sa_update_config(handle);
   1373 			} else {
   1374 				if (group != NULL)
   1375 					(void) sa_remove_group(group);
   1376 			}
   1377 		} else {
   1378 			ret = err;
   1379 			(void) printf(gettext("Could not create group: %s\n"),
   1380 			    sa_errorstr(ret));
   1381 		}
   1382 	}
   1383 	if (dryrun && ret == SA_OK && !auth && verbose) {
   1384 		(void) printf(gettext("Command would fail: %s\n"),
   1385 		    sa_errorstr(SA_NO_PERMISSION));
   1386 		ret = SA_NO_PERMISSION;
   1387 	}
   1388 err:
   1389 	if (ret != SA_OK && created)
   1390 		ret = sa_remove_group(group);
   1391 
   1392 	free_opt(optlist);
   1393 	return (ret);
   1394 }
   1395 
   1396 /*
   1397  * group_status(group)
   1398  *
   1399  * return the current status (enabled/disabled) of the group.
   1400  */
   1401 
   1402 static char *
   1403 group_status(sa_group_t group)
   1404 {
   1405 	char *state;
   1406 	int enabled = 0;
   1407 
   1408 	state = sa_get_group_attr(group, "state");
   1409 	if (state != NULL) {
   1410 		if (strcmp(state, "enabled") == 0) {
   1411 			enabled = 1;
   1412 		}
   1413 		sa_free_attr_string(state);
   1414 	}
   1415 	return (enabled ? "enabled" : "disabled");
   1416 }
   1417 
   1418 /*
   1419  * sa_delete(flags, argc, argv)
   1420  *
   1421  *	Delete a group.
   1422  */
   1423 
   1424 static int
   1425 sa_delete(sa_handle_t handle, int flags, int argc, char *argv[])
   1426 {
   1427 	char *groupname;
   1428 	sa_group_t group;
   1429 	sa_share_t share;
   1430 	int verbose = 0;
   1431 	int dryrun = 0;
   1432 	int force = 0;
   1433 	int c;
   1434 	char *protocol = NULL;
   1435 	char *sectype = NULL;
   1436 	int ret = SA_OK;
   1437 	int auth;
   1438 
   1439 	while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
   1440 		switch (c) {
   1441 		case 'v':
   1442 			verbose++;
   1443 			break;
   1444 		case 'n':
   1445 			dryrun++;
   1446 			break;
   1447 		case 'P':
   1448 			if (protocol != NULL) {
   1449 				(void) printf(gettext("Specifying "
   1450 				    "multiple protocols "
   1451 				    "not supported: %s\n"), protocol);
   1452 				return (SA_SYNTAX_ERR);
   1453 			}
   1454 			protocol = optarg;
   1455 			if (!sa_valid_protocol(protocol)) {
   1456 				(void) printf(gettext("Invalid protocol "
   1457 				    "specified: %s\n"), protocol);
   1458 				return (SA_INVALID_PROTOCOL);
   1459 			}
   1460 			break;
   1461 		case 'S':
   1462 			if (sectype != NULL) {
   1463 				(void) printf(gettext("Specifying "
   1464 				    "multiple property "
   1465 				    "spaces not supported: %s\n"), sectype);
   1466 				return (SA_SYNTAX_ERR);
   1467 			}
   1468 			sectype = optarg;
   1469 			break;
   1470 		case 'f':
   1471 			force++;
   1472 			break;
   1473 		case 'h':
   1474 			/* optopt on valid arg isn't defined */
   1475 			optopt = c;
   1476 			/*FALLTHROUGH*/
   1477 		case '?':
   1478 		default:
   1479 			/*
   1480 			 * Since a bad option gets to here, sort it
   1481 			 * out and return a syntax error return value
   1482 			 * if necessary.
   1483 			 */
   1484 			switch (optopt) {
   1485 			default:
   1486 				ret = SA_SYNTAX_ERR;
   1487 				break;
   1488 			case 'h':
   1489 			case '?':
   1490 				break;
   1491 			}
   1492 			(void) printf(gettext("usage: %s\n"),
   1493 			    sa_get_usage(USAGE_DELETE));
   1494 			return (ret);
   1495 		}
   1496 	}
   1497 
   1498 	if (optind >= argc) {
   1499 		(void) printf(gettext("usage: %s\n"),
   1500 		    sa_get_usage(USAGE_DELETE));
   1501 		(void) printf(gettext("\tgroup must be specified.\n"));
   1502 		return (SA_SYNTAX_ERR);
   1503 	}
   1504 
   1505 	if ((optind + 1) < argc) {
   1506 		(void) printf(gettext("usage: %s\n"),
   1507 		    sa_get_usage(USAGE_DELETE));
   1508 		(void) printf(gettext("\textraneous group(s) at end\n"));
   1509 		return (SA_SYNTAX_ERR);
   1510 	}
   1511 
   1512 	if (sectype != NULL && protocol == NULL) {
   1513 		(void) printf(gettext("usage: %s\n"),
   1514 		    sa_get_usage(USAGE_DELETE));
   1515 		(void) printf(gettext("\tsecurity requires protocol to be "
   1516 		    "specified.\n"));
   1517 		return (SA_SYNTAX_ERR);
   1518 	}
   1519 
   1520 	/*
   1521 	 * Determine if the group already exists since it must in
   1522 	 * order to be removed.
   1523 	 *
   1524 	 * We can delete when:
   1525 	 *
   1526 	 *	- group is empty
   1527 	 *	- force flag is set
   1528 	 *	- if protocol specified, only delete the protocol
   1529 	 */
   1530 
   1531 	groupname = argv[optind];
   1532 	group = sa_get_group(handle, groupname);
   1533 	if (group == NULL) {
   1534 		ret = SA_NO_SUCH_GROUP;
   1535 		goto done;
   1536 	}
   1537 	auth = check_authorizations(groupname, flags);
   1538 	if (protocol == NULL) {
   1539 		share = sa_get_share(group, NULL);
   1540 		if (share != NULL)
   1541 			ret = SA_BUSY;
   1542 		if (share == NULL || (share != NULL && force == 1)) {
   1543 			ret = SA_OK;
   1544 			if (!dryrun) {
   1545 				while (share != NULL) {
   1546 					sa_share_t next_share;
   1547 					next_share = sa_get_next_share(share);
   1548 					/*
   1549 					 * need to do the disable of
   1550 					 * each share, but don't
   1551 					 * actually do anything on a
   1552 					 * dryrun.
   1553 					 */
   1554 					ret = sa_disable_share(share, NULL);
   1555 					ret = sa_remove_share(share);
   1556 					share = next_share;
   1557 				}
   1558 				ret = sa_remove_group(group);
   1559 			}
   1560 		}
   1561 		/* Commit to configuration if not a dryrun */
   1562 		if (!dryrun && ret == SA_OK) {
   1563 			ret = sa_update_config(handle);
   1564 		}
   1565 	} else {
   1566 		/* a protocol delete */
   1567 		sa_optionset_t optionset;
   1568 		sa_security_t security;
   1569 		if (sectype != NULL) {
   1570 			/* only delete specified security */
   1571 			security = sa_get_security(group, sectype, protocol);
   1572 			if (security != NULL && !dryrun)
   1573 				ret = sa_destroy_security(security);
   1574 			else
   1575 				ret = SA_INVALID_PROTOCOL;
   1576 		} else {
   1577 			optionset = sa_get_optionset(group, protocol);
   1578 			if (optionset != NULL && !dryrun) {
   1579 				/*
   1580 				 * have an optionset with
   1581 				 * protocol to delete
   1582 				 */
   1583 				ret = sa_destroy_optionset(optionset);
   1584 				/*
   1585 				 * Now find all security sets
   1586 				 * for the protocol and remove
   1587 				 * them. Don't remove other
   1588 				 * protocols.
   1589 				 */
   1590 				for (security =
   1591 				    sa_get_security(group, NULL, NULL);
   1592 				    ret == SA_OK && security != NULL;
   1593 				    security = sa_get_next_security(security)) {
   1594 					char *secprot;
   1595 					secprot = sa_get_security_attr(security,
   1596 					    "type");
   1597 					if (secprot != NULL &&
   1598 					    strcmp(secprot, protocol) == 0)
   1599 						ret = sa_destroy_security(
   1600 						    security);
   1601 					if (secprot != NULL)
   1602 						sa_free_attr_string(secprot);
   1603 				}
   1604 			} else {
   1605 				if (!dryrun)
   1606 					ret = SA_INVALID_PROTOCOL;
   1607 			}
   1608 		}
   1609 		/*
   1610 		 * With the protocol items removed, make sure that all
   1611 		 * the shares are updated in the legacy files, if
   1612 		 * necessary.
   1613 		 */
   1614 		for (share = sa_get_share(group, NULL);
   1615 		    share != NULL;
   1616 		    share = sa_get_next_share(share)) {
   1617 			(void) sa_delete_legacy(share, protocol);
   1618 		}
   1619 	}
   1620 
   1621 done:
   1622 	if (ret != SA_OK) {
   1623 		(void) printf(gettext("Could not delete group: %s\n"),
   1624 		    sa_errorstr(ret));
   1625 	} else if (dryrun && !auth && verbose) {
   1626 		(void) printf(gettext("Command would fail: %s\n"),
   1627 		    sa_errorstr(SA_NO_PERMISSION));
   1628 	}
   1629 	return (ret);
   1630 }
   1631 
   1632 /*
   1633  * strndupr(*buff, str, buffsize)
   1634  *
   1635  * used with small strings to duplicate and possibly increase the
   1636  * buffer size of a string.
   1637  */
   1638 static char *
   1639 strndupr(char *buff, char *str, int *buffsize)
   1640 {
   1641 	int limit;
   1642 	char *orig_buff = buff;
   1643 
   1644 	if (buff == NULL) {
   1645 		buff = (char *)malloc(64);
   1646 		if (buff == NULL)
   1647 			return (NULL);
   1648 		*buffsize = 64;
   1649 		buff[0] = '\0';
   1650 	}
   1651 	limit = strlen(buff) + strlen(str) + 1;
   1652 	if (limit > *buffsize) {
   1653 		limit = *buffsize = *buffsize + ((limit / 64) + 64);
   1654 		buff = realloc(buff, limit);
   1655 	}
   1656 	if (buff != NULL) {
   1657 		(void) strcat(buff, str);
   1658 	} else {
   1659 		/* if it fails, fail it hard */
   1660 		if (orig_buff != NULL)
   1661 			free(orig_buff);
   1662 	}
   1663 	return (buff);
   1664 }
   1665 
   1666 /*
   1667  * group_proto(group)
   1668  *
   1669  * return a string of all the protocols (space separated) associated
   1670  * with this group.
   1671  */
   1672 
   1673 static char *
   1674 group_proto(sa_group_t group)
   1675 {
   1676 	sa_optionset_t optionset;
   1677 	char *proto;
   1678 	char *buff = NULL;
   1679 	int buffsize = 0;
   1680 	int addspace = 0;
   1681 	/*
   1682 	 * get the protocol list by finding the optionsets on this
   1683 	 * group and extracting the type value. The initial call to
   1684 	 * strndupr() initailizes buff.
   1685 	 */
   1686 	buff = strndupr(buff, "", &buffsize);
   1687 	if (buff != NULL) {
   1688 		for (optionset = sa_get_optionset(group, NULL);
   1689 		    optionset != NULL && buff != NULL;
   1690 		    optionset = sa_get_next_optionset(optionset)) {
   1691 			/*
   1692 			 * extract out the protocol type from this optionset
   1693 			 * and append it to the buffer "buff". strndupr() will
   1694 			 * reallocate space as necessay.
   1695 			 */
   1696 			proto = sa_get_optionset_attr(optionset, "type");
   1697 			if (proto != NULL) {
   1698 				if (addspace++)
   1699 					buff = strndupr(buff, " ", &buffsize);
   1700 				buff = strndupr(buff, proto, &buffsize);
   1701 				sa_free_attr_string(proto);
   1702 			}
   1703 		}
   1704 	}
   1705 	return (buff);
   1706 }
   1707 
   1708 /*
   1709  * sa_list(flags, argc, argv)
   1710  *
   1711  * implements the "list" subcommand to list groups and optionally
   1712  * their state and protocols.
   1713  */
   1714 
   1715 static int
   1716 sa_list(sa_handle_t handle, int flags, int argc, char *argv[])
   1717 {
   1718 	sa_group_t group;
   1719 	int verbose = 0;
   1720 	int c;
   1721 	char *protocol = NULL;
   1722 	int ret = SA_OK;
   1723 #ifdef lint
   1724 	flags = flags;
   1725 #endif
   1726 
   1727 	while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
   1728 		switch (c) {
   1729 		case 'v':
   1730 			verbose++;
   1731 			break;
   1732 		case 'P':
   1733 			if (protocol != NULL) {
   1734 				(void) printf(gettext(
   1735 				    "Specifying multiple protocols "
   1736 				    "not supported: %s\n"),
   1737 				    protocol);
   1738 				return (SA_SYNTAX_ERR);
   1739 			}
   1740 			protocol = optarg;
   1741 			if (!sa_valid_protocol(protocol)) {
   1742 				(void) printf(gettext(
   1743 				    "Invalid protocol specified: %s\n"),
   1744 				    protocol);
   1745 				return (SA_INVALID_PROTOCOL);
   1746 			}
   1747 			break;
   1748 		case 'h':
   1749 			/* optopt on valid arg isn't defined */
   1750 			optopt = c;
   1751 			/*FALLTHROUGH*/
   1752 		case '?':
   1753 		default:
   1754 			/*
   1755 			 * Since a bad option gets to here, sort it
   1756 			 * out and return a syntax error return value
   1757 			 * if necessary.
   1758 			 */
   1759 			switch (optopt) {
   1760 			default:
   1761 				ret = SA_SYNTAX_ERR;
   1762 				break;
   1763 			case 'h':
   1764 			case '?':
   1765 				break;
   1766 			}
   1767 			(void) printf(gettext("usage: %s\n"),
   1768 			    sa_get_usage(USAGE_LIST));
   1769 				return (ret);
   1770 		}
   1771 	}
   1772 
   1773 	if (optind != argc) {
   1774 		(void) printf(gettext("usage: %s\n"),
   1775 		    sa_get_usage(USAGE_LIST));
   1776 		return (SA_SYNTAX_ERR);
   1777 	}
   1778 
   1779 	for (group = sa_get_group(handle, NULL);
   1780 	    group != NULL;
   1781 	    group = sa_get_next_group(group)) {
   1782 		char *name;
   1783 		char *proto;
   1784 		if (protocol == NULL || has_protocol(group, protocol)) {
   1785 			name = sa_get_group_attr(group, "name");
   1786 			if (name != NULL && (verbose > 1 || name[0] != '#')) {
   1787 				(void) printf("%s", (char *)name);
   1788 				if (verbose) {
   1789 					/*
   1790 					 * Need the list of protocols
   1791 					 * and current status once
   1792 					 * available. We do want to
   1793 					 * translate the
   1794 					 * enabled/disabled text here.
   1795 					 */
   1796 					(void) printf("\t%s", isenabled(group) ?
   1797 					    gettext("enabled") :
   1798 					    gettext("disabled"));
   1799 					proto = group_proto(group);
   1800 					if (proto != NULL) {
   1801 						(void) printf("\t%s",
   1802 						    (char *)proto);
   1803 						free(proto);
   1804 					}
   1805 				}
   1806 				(void) printf("\n");
   1807 			}
   1808 			if (name != NULL)
   1809 				sa_free_attr_string(name);
   1810 		}
   1811 	}
   1812 	return (0);
   1813 }
   1814 
   1815 /*
   1816  * out_properties(optionset, proto, sec)
   1817  *
   1818  * Format the properties and encode the protocol and optional named
   1819  * optionset into the string.
   1820  *
   1821  * format is protocol[:name]=(property-list)
   1822  */
   1823 
   1824 static void
   1825 out_properties(sa_optionset_t optionset, char *proto, char *sec)
   1826 {
   1827 	char *type;
   1828 	char *value;
   1829 	int spacer;
   1830 	sa_property_t prop;
   1831 
   1832 	if (sec == NULL)
   1833 		(void) printf(" %s=(", proto ? proto : gettext("all"));
   1834 	else
   1835 		(void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
   1836 
   1837 	for (spacer = 0, prop = sa_get_property(optionset, NULL);
   1838 	    prop != NULL;
   1839 	    prop = sa_get_next_property(prop)) {
   1840 
   1841 		/*
   1842 		 * extract the property name/value and output with
   1843 		 * appropriate spacing. I.e. no prefixed space the
   1844 		 * first time through but a space on subsequent
   1845 		 * properties.
   1846 		 */
   1847 		type = sa_get_property_attr(prop, "type");
   1848 		value = sa_get_property_attr(prop, "value");
   1849 		if (type != NULL) {
   1850 			(void) printf("%s%s=", spacer ? " " : "",	type);
   1851 			spacer = 1;
   1852 			if (value != NULL)
   1853 				(void) printf("\"%s\"", value);
   1854 			else
   1855 				(void) printf("\"\"");
   1856 		}
   1857 		if (type != NULL)
   1858 			sa_free_attr_string(type);
   1859 		if (value != NULL)
   1860 			sa_free_attr_string(value);
   1861 	}
   1862 	(void) printf(")");
   1863 }
   1864 
   1865 /*
   1866  * show_properties(group, protocol, prefix)
   1867  *
   1868  * print the properties for a group. If protocol is NULL, do all
   1869  * protocols otherwise only the specified protocol. All security
   1870  * (named groups specific to the protocol) are included.
   1871  *
   1872  * The "prefix" is always applied. The caller knows whether it wants
   1873  * some type of prefix string (white space) or not.  Once the prefix
   1874  * has been output, it is reduced to the zero length string for the
   1875  * remainder of the property output.
   1876  */
   1877 
   1878 static void
   1879 show_properties(sa_group_t group, char *protocol, char *prefix)
   1880 {
   1881 	sa_optionset_t optionset;
   1882 	sa_security_t security;
   1883 	char *value;
   1884 	char *secvalue;
   1885 
   1886 	if (protocol != NULL) {
   1887 		optionset = sa_get_optionset(group, protocol);
   1888 		if (optionset != NULL) {
   1889 			(void) printf("%s", prefix);
   1890 			prefix = "";
   1891 			out_properties(optionset, protocol, NULL);
   1892 		}
   1893 		security = sa_get_security(group, protocol, NULL);
   1894 		if (security != NULL) {
   1895 			(void) printf("%s", prefix);
   1896 			prefix = "";
   1897 			out_properties(security, protocol, NULL);
   1898 		}
   1899 	} else {
   1900 		for (optionset = sa_get_optionset(group, protocol);
   1901 		    optionset != NULL;
   1902 		    optionset = sa_get_next_optionset(optionset)) {
   1903 
   1904 			value = sa_get_optionset_attr(optionset, "type");
   1905 			(void) printf("%s", prefix);
   1906 			prefix = "";
   1907 			out_properties(optionset, value, 0);
   1908 			if (value != NULL)
   1909 				sa_free_attr_string(value);
   1910 		}
   1911 		for (security = sa_get_security(group, NULL, protocol);
   1912 		    security != NULL;
   1913 		    security = sa_get_next_security(security)) {
   1914 
   1915 			value = sa_get_security_attr(security, "type");
   1916 			secvalue = sa_get_security_attr(security, "sectype");
   1917 			(void) printf("%s", prefix);
   1918 			prefix = "";
   1919 			out_properties(security, value, secvalue);
   1920 			if (value != NULL)
   1921 				sa_free_attr_string(value);
   1922 			if (secvalue != NULL)
   1923 				sa_free_attr_string(secvalue);
   1924 		}
   1925 	}
   1926 }
   1927 
   1928 /*
   1929  * get_resource(share)
   1930  *
   1931  * Get the first resource name, if any, and fix string to be in
   1932  * current locale and have quotes if it has embedded spaces.  Return
   1933  * an attr string that must be freed.
   1934  */
   1935 
   1936 static char *
   1937 get_resource(sa_share_t share)
   1938 {
   1939 	sa_resource_t resource;
   1940 	char *resstring = NULL;
   1941 	char *retstring;
   1942 
   1943 	if ((resource = sa_get_share_resource(share, NULL)) != NULL) {
   1944 		resstring = sa_get_resource_attr(resource, "name");
   1945 		if (resstring != NULL) {
   1946 			char *cp;
   1947 			int len;
   1948 
   1949 			retstring = conv_from_utf8(resstring);
   1950 			if (retstring != resstring) {
   1951 				sa_free_attr_string(resstring);
   1952 				resstring = retstring;
   1953 			}
   1954 			if (strpbrk(resstring, " ") != NULL) {
   1955 				/* account for quotes */
   1956 				len = strlen(resstring) + 3;
   1957 				cp = calloc(len, sizeof (char));
   1958 				if (cp != NULL) {
   1959 					(void) snprintf(cp, len,
   1960 					    "\"%s\"", resstring);
   1961 					sa_free_attr_string(resstring);
   1962 					resstring = cp;
   1963 				} else {
   1964 					sa_free_attr_string(resstring);
   1965 					resstring = NULL;
   1966 				}
   1967 			}
   1968 		}
   1969 	}
   1970 	return (resstring);
   1971 }
   1972 
   1973 /*
   1974  * has_resource_with_opt(share)
   1975  *
   1976  * Check to see if the share has any resource names with optionsets
   1977  * set. Also indicate if multiple resource names since the syntax
   1978  * would be about the same.
   1979  */
   1980 static int
   1981 has_resource_with_opt(sa_share_t share)
   1982 {
   1983 	sa_resource_t resource;
   1984 	int ret = B_FALSE;
   1985 
   1986 	for (resource = sa_get_share_resource(share, NULL);
   1987 	    resource != NULL;
   1988 	    resource = sa_get_next_resource(resource)) {
   1989 
   1990 		if (sa_get_optionset(resource, NULL) != NULL) {
   1991 			ret = B_TRUE;
   1992 			break;
   1993 		}
   1994 	}
   1995 	return (ret);
   1996 }
   1997 
   1998 /*
   1999  * has_multiple_resource(share)
   2000  *
   2001  * Check to see if the share has multiple resource names since
   2002  * the syntax would be about the same.
   2003  */
   2004 static boolean_t
   2005 has_multiple_resource(sa_share_t share)
   2006 {
   2007 	sa_resource_t resource;
   2008 	int num;
   2009 
   2010 	for (num = 0, resource = sa_get_share_resource(share, NULL);
   2011 	    resource != NULL;
   2012 	    resource = sa_get_next_resource(resource)) {
   2013 		num++;
   2014 		if (num > 1)
   2015 			return (B_TRUE);
   2016 	}
   2017 	return (B_FALSE);
   2018 }
   2019 
   2020 /*
   2021  * show_share(share, verbose, properties, proto, iszfs, sharepath)
   2022  *
   2023  * print out the share information. With the addition of resource as a
   2024  * full object that can have multiple instances below the share, we
   2025  * need to display that as well.
   2026  */
   2027 
   2028 static void
   2029 show_share(sa_share_t share, int verbose, int properties, char *proto,
   2030     int iszfs, char *sharepath)
   2031 {
   2032 	char *drive;
   2033 	char *exclude;
   2034 	sa_resource_t resource = NULL;
   2035 	char *description;
   2036 	char *rsrcname;
   2037 	int rsrcwithopt;
   2038 	boolean_t multiple;
   2039 	char *type;
   2040 
   2041 	rsrcwithopt = has_resource_with_opt(share);
   2042 
   2043 	if (verbose || (properties && rsrcwithopt)) {
   2044 		/* First, indicate if transient */
   2045 		type = sa_get_share_attr(share, "type");
   2046 		if (type != NULL && !iszfs && verbose &&
   2047 		    strcmp(type, "transient") == 0)
   2048 			(void) printf("\t* ");
   2049 		else
   2050 			(void) printf("\t  ");
   2051 
   2052 		if (type != NULL)
   2053 			sa_free_attr_string(type);
   2054 
   2055 		/*
   2056 		 * If we came in with verbose, we want to handle the case of
   2057 		 * multiple resources as though they had properties set.
   2058 		 */
   2059 		multiple = has_multiple_resource(share);
   2060 
   2061 		/*
   2062 		 * if there is a description on the share and there
   2063 		 * are resources, treat as multiple resources in order
   2064 		 * to get all descriptions displayed.
   2065 		 */
   2066 		description = sa_get_share_description(share);
   2067 		resource = sa_get_share_resource(share, NULL);
   2068 
   2069 		if (description != NULL && resource != NULL)
   2070 			multiple = B_TRUE;
   2071 
   2072 		/* Next, if not multiple follow old model */
   2073 		if (!multiple && !rsrcwithopt) {
   2074 			rsrcname = get_resource(share);
   2075 			if (rsrcname != NULL && strlen(rsrcname) > 0) {
   2076 				(void) printf("%s=%s", rsrcname, sharepath);
   2077 			} else {
   2078 				(void) printf("%s", sharepath);
   2079 			}
   2080 			if (rsrcname != NULL)
   2081 				sa_free_attr_string(rsrcname);
   2082 			/* Print the description string if there is one. */
   2083 			print_rsrc_desc(resource, description);
   2084 		} else {
   2085 			/* Treat as simple and then resources come later */
   2086 			(void) printf("%s", sharepath);
   2087 		}
   2088 		drive = sa_get_share_attr(share, "drive-letter");
   2089 		if (drive != NULL) {
   2090 			if (strlen(drive) > 0)
   2091 				(void) printf(gettext("\tdrive-letter=\"%s:\""),
   2092 				    drive);
   2093 			sa_free_attr_string(drive);
   2094 		}
   2095 		if (properties)
   2096 			show_properties(share, proto, "\t");
   2097 		exclude = sa_get_share_attr(share, "exclude");
   2098 		if (exclude != NULL) {
   2099 			(void) printf(gettext("\tnot-shared-with=[%s]"),
   2100 			    exclude);
   2101 			sa_free_attr_string(exclude);
   2102 		}
   2103 
   2104 		if (description != NULL) {
   2105 			print_rsrc_desc((sa_resource_t)share, description);
   2106 		}
   2107 		/*
   2108 		 * If there are resource names with options, show them
   2109 		 * here, with one line per resource. Resource specific
   2110 		 * options are at the end of the line followed by
   2111 		 * description, if any.
   2112 		 */
   2113 		if (rsrcwithopt || multiple) {
   2114 			for (resource = sa_get_share_resource(share, NULL);
   2115 			    resource != NULL;
   2116 			    resource = sa_get_next_resource(resource)) {
   2117 				int has_space;
   2118 				char *rsrc;
   2119 
   2120 				(void) printf("\n\t\t  ");
   2121 				rsrcname = sa_get_resource_attr(resource,
   2122 				    "name");
   2123 				if (rsrcname == NULL)
   2124 					continue;
   2125 
   2126 				rsrc = conv_from_utf8(rsrcname);
   2127 				has_space = strpbrk(rsrc, " ") != NULL;
   2128 
   2129 				if (has_space)
   2130 					(void) printf("\"%s\"=%s", rsrc,
   2131 					    sharepath);
   2132 				else
   2133 					(void) printf("%s=%s", rsrc,
   2134 					    sharepath);
   2135 				if (rsrc != rsrcname)
   2136 					sa_free_attr_string(rsrc);
   2137 				sa_free_attr_string(rsrcname);
   2138 				if (properties || rsrcwithopt)
   2139 					show_properties(resource, proto, "\t");
   2140 
   2141 				/* Get description string if any */
   2142 				print_rsrc_desc(resource, description);
   2143 			}
   2144 		}
   2145 		if (description != NULL)
   2146 			sa_free_share_description(description);
   2147 	} else {
   2148 		(void) printf("\t  %s", sharepath);
   2149 		if (properties)
   2150 			show_properties(share, proto, "\t");
   2151 	}
   2152 	(void) printf("\n");
   2153 }
   2154 
   2155 /*
   2156  * show_group(group, verbose, properties, proto, subgroup)
   2157  *
   2158  * helper function to show the contents of a group.
   2159  */
   2160 
   2161 static void
   2162 show_group(sa_group_t group, int verbose, int properties, char *proto,
   2163     char *subgroup)
   2164 {
   2165 	sa_share_t share;
   2166 	char *groupname;
   2167 	char *zfs = NULL;
   2168 	int iszfs = 0;
   2169 	char *sharepath;
   2170 
   2171 	groupname = sa_get_group_attr(group, "name");
   2172 	if (groupname != NULL) {
   2173 		if (proto != NULL && !has_protocol(group, proto)) {
   2174 			sa_free_attr_string(groupname);
   2175 			return;
   2176 		}
   2177 		/*
   2178 		 * check to see if the group is managed by ZFS. If
   2179 		 * there is an attribute, then it is. A non-NULL zfs
   2180 		 * variable will trigger the different way to display
   2181 		 * and will remove the transient property indicator
   2182 		 * from the output.
   2183 		 */
   2184 		zfs = sa_get_group_attr(group, "zfs");
   2185 		if (zfs != NULL) {
   2186 			iszfs = 1;
   2187 			sa_free_attr_string(zfs);
   2188 		}
   2189 		share = sa_get_share(group, NULL);
   2190 		if (subgroup == NULL)
   2191 			(void) printf("%s", groupname);
   2192 		else
   2193 			(void) printf("    %s/%s", subgroup, groupname);
   2194 		if (properties)
   2195 			show_properties(group, proto, "");
   2196 		(void) printf("\n");
   2197 		if (strcmp(groupname, "zfs") == 0) {
   2198 			sa_group_t zgroup;
   2199 
   2200 			for (zgroup = sa_get_sub_group(group);
   2201 			    zgroup != NULL;
   2202 			    zgroup = sa_get_next_group(zgroup)) {
   2203 				show_group(zgroup, verbose, properties, proto,
   2204 				    "zfs");
   2205 			}
   2206 			sa_free_attr_string(groupname);
   2207 			return;
   2208 		}
   2209 		/*
   2210 		 * Have a group, so list the contents. Resource and
   2211 		 * description are only listed if verbose is set.
   2212 		 */
   2213 		for (share = sa_get_share(group, NULL);
   2214 		    share != NULL;
   2215 		    share = sa_get_next_share(share)) {
   2216 			sharepath = sa_get_share_attr(share, "path");
   2217 			if (sharepath != NULL) {
   2218 				show_share(share, verbose, properties, proto,
   2219 				    iszfs, sharepath);
   2220 				sa_free_attr_string(sharepath);
   2221 			}
   2222 		}
   2223 	}
   2224 	if (groupname != NULL) {
   2225 		sa_free_attr_string(groupname);
   2226 	}
   2227 }
   2228 
   2229 /*
   2230  * show_group_xml_init()
   2231  *
   2232  * Create an XML document that will be used to display config info via
   2233  * XML format.
   2234  */
   2235 
   2236 xmlDocPtr
   2237 show_group_xml_init()
   2238 {
   2239 	xmlDocPtr doc;
   2240 	xmlNodePtr root;
   2241 
   2242 	doc = xmlNewDoc((xmlChar *)"1.0");
   2243 	if (doc != NULL) {
   2244 		root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
   2245 		if (root != NULL)
   2246 			(void) xmlDocSetRootElement(doc, root);
   2247 	}
   2248 	return (doc);
   2249 }
   2250 
   2251 /*
   2252  * show_group_xml(doc, group)
   2253  *
   2254  * Copy the group info into the XML doc.
   2255  */
   2256 
   2257 static void
   2258 show_group_xml(xmlDocPtr doc, sa_group_t group)
   2259 {
   2260 	xmlNodePtr node;
   2261 	xmlNodePtr root;
   2262 
   2263 	root = xmlDocGetRootElement(doc);
   2264 	node = xmlCopyNode((xmlNodePtr)group, 1);
   2265 	if (node != NULL && root != NULL) {
   2266 		(void) xmlAddChild(root, node);
   2267 		/*
   2268 		 * In the future, we may have interally used tags that
   2269 		 * should not appear in the XML output. Remove
   2270 		 * anything we don't want to show here.
   2271 		 */
   2272 	}
   2273 }
   2274 
   2275 /*
   2276  * sa_show(flags, argc, argv)
   2277  *
   2278  * Implements the show subcommand.
   2279  */
   2280 
   2281 int
   2282 sa_show(sa_handle_t handle, int flags, int argc, char *argv[])
   2283 {
   2284 	sa_group_t group;
   2285 	int verbose = 0;
   2286 	int properties = 0;
   2287 	int c;
   2288 	int ret = SA_OK;
   2289 	char *protocol = NULL;
   2290 	int xml = 0;
   2291 	xmlDocPtr doc;
   2292 #ifdef lint
   2293 	flags = flags;
   2294 #endif
   2295 
   2296 	while ((c = getopt(argc, argv, "?hvP:px")) !=	EOF) {
   2297 		switch (c) {
   2298 		case 'v':
   2299 			verbose++;
   2300 			break;
   2301 		case 'p':
   2302 			properties++;
   2303 			break;
   2304 		case 'P':
   2305 			if (protocol != NULL) {
   2306 				(void) printf(gettext(
   2307 				    "Specifying multiple protocols "
   2308 				    "not supported: %s\n"),
   2309 				    protocol);
   2310 				return (SA_SYNTAX_ERR);
   2311 			}
   2312 			protocol = optarg;
   2313 			if (!sa_valid_protocol(protocol)) {
   2314 				(void) printf(gettext(
   2315 				    "Invalid protocol specified: %s\n"),
   2316 				    protocol);
   2317 				return (SA_INVALID_PROTOCOL);
   2318 			}
   2319 			break;
   2320 		case 'x':
   2321 			xml++;
   2322 			break;
   2323 		case 'h':
   2324 			/* optopt on valid arg isn't defined */
   2325 			optopt = c;
   2326 			/*FALLTHROUGH*/
   2327 		case '?':
   2328 		default:
   2329 			/*
   2330 			 * Since a bad option gets to here, sort it
   2331 			 * out and return a syntax error return value
   2332 			 * if necessary.
   2333 			 */
   2334 			switch (optopt) {
   2335 			default:
   2336 				ret = SA_SYNTAX_ERR;
   2337 				break;
   2338 			case 'h':
   2339 			case '?':
   2340 				break;
   2341 			}
   2342 			(void) printf(gettext("usage: %s\n"),
   2343 			    sa_get_usage(USAGE_SHOW));
   2344 			return (ret);
   2345 		}
   2346 	}
   2347 
   2348 	if (xml) {
   2349 		doc = show_group_xml_init();
   2350 		if (doc == NULL)
   2351 			ret = SA_NO_MEMORY;
   2352 	}
   2353 
   2354 	if (optind == argc) {
   2355 		/* No group specified so go through them all */
   2356 		for (group = sa_get_group(handle, NULL);
   2357 		    group != NULL;
   2358 		    group = sa_get_next_group(group)) {
   2359 			/*
   2360 			 * Have a group so check if one we want and then list
   2361 			 * contents with appropriate options.
   2362 			 */
   2363 			if (xml)
   2364 				show_group_xml(doc, group);
   2365 			else
   2366 				show_group(group, verbose, properties, protocol,
   2367 				    NULL);
   2368 		}
   2369 	} else {
   2370 		/* Have a specified list of groups */
   2371 		for (; optind < argc; optind++) {
   2372 			group = sa_get_group(handle, argv[optind]);
   2373 			if (group != NULL) {
   2374 				if (xml)
   2375 					show_group_xml(doc, group);
   2376 				else
   2377 					show_group(group, verbose, properties,
   2378 					    protocol, NULL);
   2379 			} else {
   2380 				(void) printf(gettext("%s: not found\n"),
   2381 				    argv[optind]);
   2382 				ret = SA_NO_SUCH_GROUP;
   2383 			}
   2384 		}
   2385 	}
   2386 	if (xml && ret == SA_OK) {
   2387 		(void) xmlDocFormatDump(stdout, doc, 1);
   2388 		xmlFreeDoc(doc);
   2389 	}
   2390 	return (ret);
   2391 
   2392 }
   2393 
   2394 /*
   2395  * enable_share(group, share, update_legacy)
   2396  *
   2397  * helper function to enable a share if the group is enabled.
   2398  */
   2399 
   2400 static int
   2401 enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
   2402     int update_legacy)
   2403 {
   2404 	char *value;
   2405 	int enabled;
   2406 	sa_optionset_t optionset;
   2407 	int err;
   2408 	int ret = SA_OK;
   2409 	char *zfs = NULL;
   2410 	int iszfs = 0;
   2411 	int isshare;
   2412 
   2413 	/*
   2414 	 * need to enable this share if the group is enabled but not
   2415 	 * otherwise. The enable is also done on each protocol
   2416 	 * represented in the group.
   2417 	 */
   2418 	value = sa_get_group_attr(group, "state");
   2419 	enabled = value != NULL && strcmp(value, "enabled") == 0;
   2420 	if (value != NULL)
   2421 		sa_free_attr_string(value);
   2422 	/* remove legacy config if necessary */
   2423 	if (update_legacy)
   2424 		ret = sa_delete_legacy(share, NULL);
   2425 	zfs = sa_get_group_attr(group, "zfs");
   2426 	if (zfs != NULL) {
   2427 		iszfs++;
   2428 		sa_free_attr_string(zfs);
   2429 	}
   2430 
   2431 	/*
   2432 	 * Step through each optionset at the group level and
   2433 	 * enable the share based on the protocol type. This
   2434 	 * works because protocols must be set on the group
   2435 	 * for the protocol to be enabled.
   2436 	 */
   2437 	isshare = sa_is_share(share);
   2438 	for (optionset = sa_get_optionset(group, NULL);
   2439 	    optionset != NULL && ret == SA_OK;
   2440 	    optionset = sa_get_next_optionset(optionset)) {
   2441 		value = sa_get_optionset_attr(optionset, "type");
   2442 		if (value != NULL) {
   2443 			if (enabled) {
   2444 				if (isshare) {
   2445 					err = sa_enable_share(share, value);
   2446 				} else {
   2447 					err = sa_enable_resource(share, value);
   2448 					if (err == SA_NOT_SUPPORTED) {
   2449 						sa_share_t parent;
   2450 						parent = sa_get_resource_parent(
   2451 						    share);
   2452 						if (parent != NULL)
   2453 							err = sa_enable_share(
   2454 							    parent, value);
   2455 					}
   2456 				}
   2457 				if (err != SA_OK) {
   2458 					ret = err;
   2459 					(void) printf(gettext(
   2460 					    "Failed to enable share for "
   2461 					    "\"%s\": %s\n"),
   2462 					    value, sa_errorstr(ret));
   2463 				}
   2464 			}
   2465 			/*
   2466 			 * If we want to update the legacy, use a copy of
   2467 			 * share so we can avoid breaking the loop we are in
   2468 			 * since we might also need to go up the tree to the
   2469 			 * parent.
   2470 			 */
   2471 			if (update_legacy && !iszfs) {
   2472 				sa_share_t update = share;
   2473 				if (!sa_is_share(share)) {
   2474 					update = sa_get_resource_parent(share);
   2475 				}
   2476 				(void) sa_update_legacy(update, value);
   2477 			}
   2478 			sa_free_attr_string(value);
   2479 		}
   2480 	}
   2481 	if (ret == SA_OK)
   2482 		(void) sa_update_config(handle);
   2483 	return (ret);
   2484 }
   2485 
   2486 /*
   2487  * sa_require_resource(group)
   2488  *
   2489  * if any of the defined protocols on the group require resource
   2490  * names, then all shares must have them.
   2491  */
   2492 
   2493 static int
   2494 sa_require_resource(sa_group_t group)
   2495 {
   2496 	sa_optionset_t optionset;
   2497 
   2498 	for (optionset = sa_get_optionset(group, NULL);
   2499 	    optionset != NULL;
   2500 	    optionset = sa_get_next_optionset(optionset)) {
   2501 		char *proto;
   2502 
   2503 		proto = sa_get_optionset_attr(optionset, "type");
   2504 		if (proto != NULL) {
   2505 			uint64_t features;
   2506 
   2507 			features = sa_proto_get_featureset(proto);
   2508 			if (features & SA_FEATURE_RESOURCE) {
   2509 				sa_free_attr_string(proto);
   2510 				return (B_TRUE);
   2511 			}
   2512 			sa_free_attr_string(proto);
   2513 		}
   2514 	}
   2515 	return (B_FALSE);
   2516 }
   2517 
   2518 /*
   2519  * sa_addshare(flags, argc, argv)
   2520  *
   2521  * implements add-share subcommand.
   2522  */
   2523 
   2524 static int
   2525 sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[])
   2526 {
   2527 	int verbose = 0;
   2528 	int dryrun = 0;
   2529 	int c;
   2530 	int ret = SA_OK;
   2531 	sa_group_t group;
   2532 	sa_share_t share;
   2533 	sa_resource_t resource = NULL;
   2534 	char *sharepath = NULL;
   2535 	char *description = NULL;
   2536 	char *rsrcname = NULL;
   2537 	char *rsrc = NULL;
   2538 	int persist = SA_SHARE_PERMANENT; /* default to persist */
   2539 	int auth;
   2540 	char dir[MAXPATHLEN];
   2541 
   2542 	while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
   2543 		switch (c) {
   2544 		case 'n':
   2545 			dryrun++;
   2546 			break;
   2547 		case 'v':
   2548 			verbose++;
   2549 			break;
   2550 		case 'd':
   2551 			description = optarg;
   2552 			break;
   2553 		case 'r':
   2554 			if (rsrcname != NULL) {
   2555 				(void) printf(gettext("Adding multiple "
   2556 				    "resource names not"
   2557 				    " supported\n"));
   2558 				return (SA_SYNTAX_ERR);
   2559 			}
   2560 			rsrcname = optarg;
   2561 			break;
   2562 		case 's':
   2563 			/*
   2564 			 * Save share path into group. Currently limit
   2565 			 * to one share per command.
   2566 			 */
   2567 			if (sharepath != NULL) {
   2568 				(void) printf(gettext(
   2569 				    "Adding multiple shares not supported\n"));
   2570 				return (SA_SYNTAX_ERR);
   2571 			}
   2572 			sharepath = optarg;
   2573 			break;
   2574 		case 't':
   2575 			persist = SA_SHARE_TRANSIENT;
   2576 			break;
   2577 		case 'h':
   2578 			/* optopt on valid arg isn't defined */
   2579 			optopt = c;
   2580 			/*FALLTHROUGH*/
   2581 		case '?':
   2582 		default:
   2583 			/*
   2584 			 * Since a bad option gets to here, sort it
   2585 			 * out and return a syntax error return value
   2586 			 * if necessary.
   2587 			 */
   2588 			switch (optopt) {
   2589 			default:
   2590 				ret = SA_SYNTAX_ERR;
   2591 				break;
   2592 			case 'h':
   2593 			case '?':
   2594 				break;
   2595 			}
   2596 			(void) printf(gettext("usage: %s\n"),
   2597 			    sa_get_usage(USAGE_ADD_SHARE));
   2598 			return (ret);
   2599 		}
   2600 	}
   2601 
   2602 	if (optind >= argc) {
   2603 		(void) printf(gettext("usage: %s\n"),
   2604 		    sa_get_usage(USAGE_ADD_SHARE));
   2605 		if (dryrun || sharepath != NULL || description != NULL ||
   2606 		    rsrcname != NULL || verbose || persist) {
   2607 			(void) printf(gettext("\tgroup must be specified\n"));
   2608 			ret = SA_NO_SUCH_GROUP;
   2609 		} else {
   2610 			ret = SA_OK;
   2611 		}
   2612 	} else {
   2613 		if (sharepath == NULL) {
   2614 			(void) printf(gettext("usage: %s\n"),
   2615 			    sa_get_usage(USAGE_ADD_SHARE));
   2616 			(void) printf(gettext(
   2617 			    "\t-s sharepath must be specified\n"));
   2618 			ret = SA_BAD_PATH;
   2619 		}
   2620 		if (ret == SA_OK) {
   2621 			if (realpath(sharepath, dir) == NULL) {
   2622 				ret = SA_BAD_PATH;
   2623 				(void) printf(gettext("Path "
   2624 				    "is not valid: %s\n"),
   2625 				    sharepath);
   2626 			} else {
   2627 				sharepath = dir;
   2628 			}
   2629 		}
   2630 		if (ret == SA_OK && rsrcname != NULL) {
   2631 			/* check for valid syntax */
   2632 			if (validresource(rsrcname)) {
   2633 				rsrc = conv_to_utf8(rsrcname);
   2634 				resource = sa_find_resource(handle, rsrc);
   2635 				if (resource != NULL) {
   2636 					/*
   2637 					 * Resource names must be
   2638 					 * unique in the system
   2639 					 */
   2640 					ret = SA_DUPLICATE_NAME;
   2641 					(void) printf(gettext("usage: %s\n"),
   2642 					    sa_get_usage(USAGE_ADD_SHARE));
   2643 					(void) printf(gettext(
   2644 					    "\tresource names must be unique "
   2645 					    "in the system\n"));
   2646 				}
   2647 			} else {
   2648 				(void) printf(gettext("usage: %s\n"),
   2649 				    sa_get_usage(USAGE_ADD_SHARE));
   2650 				(void) printf(gettext(
   2651 				    "\tresource names use restricted "
   2652 				    "character set\n"));
   2653 				ret = SA_INVALID_NAME;
   2654 			}
   2655 		}
   2656 
   2657 		if (ret != SA_OK) {
   2658 			if (rsrc != NULL && rsrcname != rsrc)
   2659 				sa_free_attr_string(rsrc);
   2660 			return (ret);
   2661 		}
   2662 
   2663 		share = sa_find_share(handle, sharepath);
   2664 		if (share != NULL) {
   2665 			if (rsrcname == NULL) {
   2666 				/*
   2667 				 * Can only have a duplicate share if a new
   2668 				 * resource name is being added.
   2669 				 */
   2670 				ret = SA_DUPLICATE_NAME;
   2671 				(void) printf(gettext("Share path already "
   2672 				    "shared: %s\n"), sharepath);
   2673 			}
   2674 		}
   2675 		if (ret != SA_OK)
   2676 			return (ret);
   2677 
   2678 		group = sa_get_group(handle, argv[optind]);
   2679 		if (group != NULL) {
   2680 			if (sa_require_resource(group) == B_TRUE &&
   2681 			    rsrcname == NULL) {
   2682 				(void) printf(gettext(
   2683 				    "Resource name is required "
   2684 				    "by at least one enabled protocol "
   2685 				    "in group\n"));
   2686 				return (SA_RESOURCE_REQUIRED);
   2687 			}
   2688 			if (share == NULL && ret == SA_OK) {
   2689 				if (dryrun)
   2690 					ret = sa_check_path(group, sharepath,
   2691 					    SA_CHECK_NORMAL);
   2692 				else
   2693 					share = sa_add_share(group, sharepath,
   2694 					    persist, &ret);
   2695 			}
   2696 			/*
   2697 			 * Make sure this isn't an attempt to put a resourced
   2698 			 * share into a different group than it already is in.
   2699 			 */
   2700 			if (share != NULL) {
   2701 				sa_group_t parent;
   2702 				parent = sa_get_parent_group(share);
   2703 				if (parent != group) {
   2704 					ret = SA_DUPLICATE_NAME;
   2705 					(void) printf(gettext(
   2706 					    "Share path already "
   2707 					    "shared: %s\n"), sharepath);
   2708 				}
   2709 			}
   2710 			if (!dryrun && share == NULL) {
   2711 				(void) printf(gettext(
   2712 				    "Could not add share: %s\n"),
   2713 				    sa_errorstr(ret));
   2714 			} else {
   2715 				auth = check_authorizations(argv[optind],
   2716 				    flags);
   2717 				if (!dryrun && ret == SA_OK) {
   2718 					if (rsrcname != NULL) {
   2719 						resource = sa_add_resource(
   2720 						    share,
   2721 						    rsrc,
   2722 						    SA_SHARE_PERMANENT,
   2723 						    &ret);
   2724 					}
   2725 					if (ret == SA_OK &&
   2726 					    description != NULL) {
   2727 						if (resource != NULL)
   2728 							ret =
   2729 							    set_resource_desc(
   2730 							    resource,
   2731 							    description);
   2732 						else
   2733 							ret =
   2734 							    set_share_desc(
   2735 							    share,
   2736 							    description);
   2737 					}
   2738 					if (ret == SA_OK) {
   2739 						/* now enable the share(s) */
   2740 						if (resource != NULL) {
   2741 							ret = enable_share(
   2742 							    handle,
   2743 							    group,
   2744 							    resource,
   2745 							    1);
   2746 						} else {
   2747 							ret = enable_share(
   2748 							    handle,
   2749 							    group,
   2750 							    share,
   2751 							    1);
   2752 						}
   2753 						ret = sa_update_config(handle);
   2754 					}
   2755 					switch (ret) {
   2756 					case SA_DUPLICATE_NAME:
   2757 						(void) printf(gettext(
   2758 						    "Resource name in"
   2759 						    "use: %s\n"),
   2760 						    rsrcname);
   2761 						break;
   2762 					default:
   2763 						(void) printf(gettext(
   2764 						    "Could not set "
   2765 						    "attribute: %s\n"),
   2766 						    sa_errorstr(ret));
   2767 						break;
   2768 					case SA_OK:
   2769 						break;
   2770 					}
   2771 				} else if (dryrun && ret == SA_OK &&
   2772 				    !auth && verbose) {
   2773 					(void) printf(gettext(
   2774 					    "Command would fail: %s\n"),
   2775 					    sa_errorstr(SA_NO_PERMISSION));
   2776 					ret = SA_NO_PERMISSION;
   2777 				}
   2778 			}
   2779 		} else {
   2780 			switch (ret) {
   2781 			default:
   2782 				(void) printf(gettext(
   2783 				    "Group \"%s\" not found\n"), argv[optind]);
   2784 				ret = SA_NO_SUCH_GROUP;
   2785 				break;
   2786 			case SA_BAD_PATH:
   2787 			case SA_DUPLICATE_NAME:
   2788 				break;
   2789 			}
   2790 		}
   2791 	}
   2792 	return (ret);
   2793 }
   2794 
   2795 /*
   2796  * sa_moveshare(flags, argc, argv)
   2797  *
   2798  * implements move-share subcommand.
   2799  */
   2800 
   2801 int
   2802 sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[])
   2803 {
   2804 	int verbose = 0;
   2805 	int dryrun = 0;
   2806 	int c;
   2807 	int ret = SA_OK;
   2808 	sa_group_t group;
   2809 	sa_share_t share;
   2810 	char *rsrcname = NULL;
   2811 	char *sharepath = NULL;
   2812 	int authsrc = 0, authdst = 0;
   2813 	char dir[MAXPATHLEN];
   2814 
   2815 	while ((c = getopt(argc, argv, "?hvnr:s:")) != EOF) {
   2816 		switch (c) {
   2817 		case 'n':
   2818 			dryrun++;
   2819 			break;
   2820 		case 'v':
   2821 			verbose++;
   2822 			break;
   2823 		case 'r':
   2824 			if (rsrcname != NULL) {
   2825 				(void) printf(gettext(
   2826 				    "Moving multiple resource names not"
   2827 				    " supported\n"));
   2828 				return (SA_SYNTAX_ERR);
   2829 			}
   2830 			rsrcname = optarg;
   2831 			break;
   2832 		case 's':
   2833 			/*
   2834 			 * Remove share path from group. Currently limit
   2835 			 * to one share per command.
   2836 			 */
   2837 			if (sharepath != NULL) {
   2838 				(void) printf(gettext("Moving multiple shares"
   2839 				    " not supported\n"));
   2840 				return (SA_SYNTAX_ERR);
   2841 			}
   2842 			sharepath = optarg;
   2843 			break;
   2844 		case 'h':
   2845 			/* optopt on valid arg isn't defined */
   2846 			optopt = c;
   2847 			/*FALLTHROUGH*/
   2848 		case '?':
   2849 		default:
   2850 			/*
   2851 			 * Since a bad option gets to here, sort it
   2852 			 * out and return a syntax error return value
   2853 			 * if necessary.
   2854 			 */
   2855 			switch (optopt) {
   2856 			default:
   2857 				ret = SA_SYNTAX_ERR;
   2858 				break;
   2859 			case 'h':
   2860 			case '?':
   2861 				break;
   2862 			}
   2863 			(void) printf(gettext("usage: %s\n"),
   2864 			    sa_get_usage(USAGE_MOVE_SHARE));
   2865 			return (ret);
   2866 		}
   2867 	}
   2868 
   2869 	if (optind >= argc || sharepath == NULL) {
   2870 		(void) printf(gettext("usage: %s\n"),
   2871 		    sa_get_usage(USAGE_MOVE_SHARE));
   2872 		if (dryrun || verbose || sharepath != NULL) {
   2873 			(void) printf(gettext("\tgroup must be specified\n"));
   2874 			ret = SA_NO_SUCH_GROUP;
   2875 		} else {
   2876 			if (sharepath == NULL) {
   2877 				ret = SA_SYNTAX_ERR;
   2878 				(void) printf(gettext(
   2879 				    "\tsharepath must be specified\n"));
   2880 			} else {
   2881 				ret = SA_OK;
   2882 			}
   2883 		}
   2884 	} else {
   2885 		sa_group_t parent;
   2886 		char *zfsold;
   2887 		char *zfsnew;
   2888 
   2889 		if (sharepath == NULL) {
   2890 			(void) printf(gettext(
   2891 			    "sharepath must be specified with the -s "
   2892 			    "option\n"));
   2893 			return (SA_BAD_PATH);
   2894 		}
   2895 		group = sa_get_group(handle, argv[optind]);
   2896 		if (group == NULL) {
   2897 			(void) printf(gettext("Group \"%s\" not found\n"),
   2898 			    argv[optind]);
   2899 			return (SA_NO_SUCH_GROUP);
   2900 		}
   2901 		share = sa_find_share(handle, sharepath);
   2902 		/*
   2903 		 * If a share wasn't found, it may have been a symlink
   2904 		 * or has a trailing '/'. Try again after resolving
   2905 		 * with realpath().
   2906 		 */
   2907 		if (share == NULL) {
   2908 			if (realpath(sharepath, dir) == NULL) {
   2909 				(void) printf(gettext("Path "
   2910 				    "is not valid: %s\n"),
   2911 				    sharepath);
   2912 				return (SA_BAD_PATH);
   2913 			}
   2914 			sharepath = dir;
   2915 			share = sa_find_share(handle, sharepath);
   2916 		}
   2917 		if (share == NULL) {
   2918 			(void) printf(gettext("Share not found: %s\n"),
   2919 			    sharepath);
   2920 			return (SA_NO_SUCH_PATH);
   2921 		}
   2922 		authdst = check_authorizations(argv[optind], flags);
   2923 
   2924 		parent = sa_get_parent_group(share);
   2925 		if (parent != NULL) {
   2926 			char *pname;
   2927 			pname = sa_get_group_attr(parent, "name");
   2928 			if (pname != NULL) {
   2929 				authsrc = check_authorizations(pname, flags);
   2930 				sa_free_attr_string(pname);
   2931 			}
   2932 			zfsold = sa_get_group_attr(parent, "zfs");
   2933 			zfsnew = sa_get_group_attr(group, "zfs");
   2934 			if ((zfsold != NULL && zfsnew == NULL) ||
   2935 			    (zfsold == NULL && zfsnew != NULL)) {
   2936 				ret = SA_NOT_ALLOWED;
   2937 			}
   2938 			if (zfsold != NULL)
   2939 				sa_free_attr_string(zfsold);
   2940 			if (zfsnew != NULL)
   2941 				sa_free_attr_string(zfsnew);
   2942 		}
   2943 
   2944 		if (ret == SA_OK && parent != group && !dryrun) {
   2945 			char *oldstate;
   2946 			/*
   2947 			 * Note that the share may need to be
   2948 			 * "unshared" if the new group is disabled and
   2949 			 * the old was enabled or it may need to be
   2950 			 * share to update if the new group is
   2951 			 * enabled. We disable before the move and
   2952 			 * will have to enable after the move in order
   2953 			 * to cleanup entries for protocols that
   2954 			 * aren't in the new group.
   2955 			 */
   2956 			oldstate = sa_get_group_attr(parent, "state");
   2957 			if (oldstate != NULL) {
   2958 				/* enable_share determines what to do */
   2959 				if (strcmp(oldstate, "enabled") == 0)
   2960 					(void) sa_disable_share(share, NULL);
   2961 				sa_free_attr_string(oldstate);
   2962 			}
   2963 		}
   2964 
   2965 		if (!dryrun && ret == SA_OK)
   2966 			ret = sa_move_share(group, share);
   2967 
   2968 		/*
   2969 		 * Reenable and update any config information.
   2970 		 */
   2971 		if (ret == SA_OK && parent != group && !dryrun) {
   2972 			ret = sa_update_config(handle);
   2973 
   2974 			(void) enable_share(handle, group, share, 1);
   2975 		}
   2976 
   2977 		if (ret != SA_OK)
   2978 			(void) printf(gettext("Could not move share: %s\n"),
   2979 			    sa_errorstr(ret));
   2980 
   2981 		if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
   2982 		    verbose) {
   2983 			(void) printf(gettext("Command would fail: %s\n"),
   2984 			    sa_errorstr(SA_NO_PERMISSION));
   2985 		}
   2986 	}
   2987 	return (ret);
   2988 }
   2989 
   2990 /*
   2991  * sa_removeshare(flags, argc, argv)
   2992  *
   2993  * implements remove-share subcommand.
   2994  */
   2995 
   2996 int
   2997 sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[])
   2998 {
   2999 	int verbose = 0;
   3000 	int dryrun = 0;
   3001 	int force = 0;
   3002 	int c;
   3003 	int ret = SA_OK;
   3004 	sa_group_t group;
   3005 	sa_resource_t resource = NULL;
   3006 	sa_share_t share = NULL;
   3007 	char *rsrcname = NULL;
   3008 	char *sharepath = NULL;
   3009 	char dir[MAXPATHLEN];
   3010 	int auth;
   3011 
   3012 	while ((c = getopt(argc, argv, "?hfnr:s:v")) != EOF) {
   3013 		switch (c) {
   3014 		case 'n':
   3015 			dryrun++;
   3016 			break;
   3017 		case 'v':
   3018 			verbose++;
   3019 			break;
   3020 		case 'f':
   3021 			force++;
   3022 			break;
   3023 		case 's':
   3024 			/*
   3025 			 * Remove share path from group. Currently limit
   3026 			 * to one share per command.
   3027 			 */
   3028 			if (sharepath != NULL) {
   3029 				(void) printf(gettext(
   3030 				    "Removing multiple shares not "
   3031 				    "supported\n"));
   3032 				return (SA_SYNTAX_ERR);
   3033 			}
   3034 			sharepath = optarg;
   3035 			break;
   3036 		case 'r':
   3037 			/*
   3038 			 * Remove share from group if last resource or remove
   3039 			 * resource from share if multiple resources.
   3040 			 */
   3041 			if (rsrcname != NULL) {
   3042 				(void) printf(gettext(
   3043 				    "Removing multiple resource names not "
   3044 				    "supported\n"));
   3045 				return (SA_SYNTAX_ERR);
   3046 			}
   3047 			rsrcname = optarg;
   3048 			break;
   3049 		case 'h':
   3050 			/* optopt on valid arg isn't defined */
   3051 			optopt = c;
   3052 			/*FALLTHROUGH*/
   3053 		case '?':
   3054 		default:
   3055 			/*
   3056 			 * Since a bad option gets to here, sort it
   3057 			 * out and return a syntax error return value
   3058 			 * if necessary.
   3059 			 */
   3060 			switch (optopt) {
   3061 			default:
   3062 				ret = SA_SYNTAX_ERR;
   3063 				break;
   3064 			case 'h':
   3065 			case '?':
   3066 				break;
   3067 			}
   3068 			(void) printf(gettext("usage: %s\n"),
   3069 			    sa_get_usage(USAGE_REMOVE_SHARE));
   3070 			return (ret);
   3071 		}
   3072 	}
   3073 
   3074 	if (optind >= argc || (rsrcname == NULL && sharepath == NULL)) {
   3075 		if (sharepath == NULL && rsrcname == NULL) {
   3076 			(void) printf(gettext("usage: %s\n"),
   3077 			    sa_get_usage(USAGE_REMOVE_SHARE));
   3078 			(void) printf(gettext("\t-s sharepath or -r resource"
   3079 			    " must be specified\n"));
   3080 			ret = SA_BAD_PATH;
   3081 		} else {
   3082 			ret = SA_OK;
   3083 		}
   3084 	}
   3085 	if (ret != SA_OK) {
   3086 		return (ret);
   3087 	}
   3088 
   3089 	if (optind < argc) {
   3090 		if ((optind + 1) < argc) {
   3091 			(void) printf(gettext("Extraneous group(s) at end of "
   3092 			    "command\n"));
   3093 			ret = SA_SYNTAX_ERR;
   3094 		} else {
   3095 			group = sa_get_group(handle, argv[optind]);
   3096 			if (group == NULL) {
   3097 				(void) printf(gettext(
   3098 				    "Group \"%s\" not found\n"), argv[optind]);
   3099 				ret = SA_NO_SUCH_GROUP;
   3100 			}
   3101 		}
   3102 	} else {
   3103 		group = NULL;
   3104 	}
   3105 
   3106 	if (rsrcname != NULL) {
   3107 		resource = sa_find_resource(handle, rsrcname);
   3108 		if (resource == NULL) {
   3109 			ret = SA_NO_SUCH_RESOURCE;
   3110 			(void) printf(gettext(
   3111 			    "Resource name not found for share: %s\n"),
   3112 			    rsrcname);
   3113 		}
   3114 	}
   3115 
   3116 	/*
   3117 	 * Lookup the path in the internal configuration. Care
   3118 	 * must be taken to handle the case where the
   3119 	 * underlying path has been removed since we need to
   3120 	 * be able to deal with that as well.
   3121 	 */
   3122 	if (ret == SA_OK) {
   3123 		if (sharepath != NULL) {
   3124 			if (group != NULL)
   3125 				share = sa_get_share(group, sharepath);
   3126 			else
   3127 				share = sa_find_share(handle, sharepath);
   3128 		}
   3129 
   3130 		if (resource != NULL) {
   3131 			sa_share_t rsrcshare;
   3132 			rsrcshare = sa_get_resource_parent(resource);
   3133 			if (share == NULL)
   3134 				share = rsrcshare;
   3135 			else if (share != rsrcshare) {
   3136 				ret = SA_NO_SUCH_RESOURCE;
   3137 				(void) printf(gettext(
   3138 				    "Bad resource name for share: %s\n"),
   3139 				    rsrcname);
   3140 				share = NULL;
   3141 			}
   3142 		}
   3143 
   3144 		/*
   3145 		 * If we didn't find the share with the provided path,
   3146 		 * it may be a symlink so attempt to resolve it using
   3147 		 * realpath and try again. Realpath will resolve any
   3148 		 * symlinks and place them in "dir". Note that
   3149 		 * sharepath is only used for the lookup the first
   3150 		 * time and later for error messages. dir will be used
   3151 		 * on the second attempt. Once a share is found, all
   3152 		 * operations are based off of the share variable.
   3153 		 */
   3154 		if (share == NULL) {
   3155 			if (realpath(sharepath, dir) == NULL) {
   3156 				ret = SA_BAD_PATH;
   3157 				(void) printf(gettext(
   3158 				    "Path is not valid: %s\n"), sharepath);
   3159 			} else {
   3160 				if (group != NULL)
   3161 					share = sa_get_share(group, dir);
   3162 				else
   3163 					share = sa_find_share(handle, dir);
   3164 			}
   3165 		}
   3166 	}
   3167 
   3168 	/*
   3169 	 * If there hasn't been an error, there was likely a
   3170 	 * path found. If not, give the appropriate error
   3171 	 * message and set the return error. If it was found,
   3172 	 * then disable the share and then remove it from the
   3173 	 * configuration.
   3174 	 */
   3175 	if (ret != SA_OK) {
   3176 		return (ret);
   3177 	}
   3178 	if (share == NULL) {
   3179 		if (group != NULL)
   3180 			(void) printf(gettext("Share not found in group %s:"
   3181 			    " %s\n"), argv[optind], sharepath);
   3182 		else
   3183 			(void) printf(gettext("Share not found: %s\n"),
   3184 			    sharepath);
   3185 		ret = SA_NO_SUCH_PATH;
   3186 	} else {
   3187 		if (group == NULL)
   3188 			group = sa_get_parent_group(share);
   3189 		if (!dryrun) {
   3190 			if (ret == SA_OK) {
   3191 				if (resource != NULL)
   3192 					ret = sa_disable_resource(resource,
   3193 					    NULL);
   3194 				else
   3195 					ret = sa_disable_share(share, NULL);
   3196 				/*
   3197 				 * We don't care if it fails since it
   3198 				 * could be disabled already. Some
   3199 				 * unexpected errors could occur that
   3200 				 * prevent removal, so also check for
   3201 				 * force being set.
   3202 				 */
   3203 				if ((ret == SA_OK || ret == SA_NO_SUCH_PATH ||
   3204 				    ret == SA_NOT_SUPPORTED ||
   3205 				    ret == SA_SYSTEM_ERR || force) &&
   3206 				    resource == NULL)
   3207 					ret = sa_remove_share(share);
   3208 
   3209 				if ((ret == SA_OK || ret == SA_NO_SUCH_PATH ||
   3210 				    ret == SA_NOT_SUPPORTED ||
   3211 				    ret == SA_SYSTEM_ERR || force) &&
   3212 				    resource != NULL) {
   3213 					ret = sa_remove_resource(resource);
   3214 					if (ret == SA_OK) {
   3215 						/*
   3216 						 * If this was the
   3217 						 * last one, remove
   3218 						 * the share as well.
   3219 						 */
   3220 						resource =
   3221 						    sa_get_share_resource(
   3222 						    share, NULL);
   3223 						if (resource == NULL)
   3224 							ret = sa_remove_share(
   3225 							    share);
   3226 					}
   3227 				}
   3228 				if (ret == SA_OK)
   3229 					ret = sa_update_config(handle);
   3230 			}
   3231 			if (ret != SA_OK)
   3232 				(void) printf(gettext("Could not remove share:"
   3233 				    " %s\n"), sa_errorstr(ret));
   3234 		} else if (ret == SA_OK) {
   3235 			char *pname;
   3236 			pname = sa_get_group_attr(group, "name");
   3237 			if (pname != NULL) {
   3238 				auth = check_authorizations(pname, flags);
   3239 				sa_free_attr_string(pname);
   3240 			}
   3241 			if (!auth && verbose) {
   3242 				(void) printf(gettext(
   3243 				    "Command would fail: %s\n"),
   3244 				    sa_errorstr(SA_NO_PERMISSION));
   3245 			}
   3246 		}
   3247 	}
   3248 	return (ret);
   3249 }
   3250 
   3251 /*
   3252  * sa_set_share(flags, argc, argv)
   3253  *
   3254  * implements set-share subcommand.
   3255  */
   3256 
   3257 int
   3258 sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[])
   3259 {
   3260 	int dryrun = 0;
   3261 	int c;
   3262 	int ret = SA_OK;
   3263 	sa_group_t group, sharegroup;
   3264 	sa_share_t share = NULL;
   3265 	sa_resource_t resource = NULL;
   3266 	char *sharepath = NULL;
   3267 	char *description = NULL;
   3268 	char *rsrcname = NULL;
   3269 	char *rsrc = NULL;
   3270 	char *newname = NULL;
   3271 	char *newrsrc;
   3272 	char *groupname = NULL;
   3273 	int auth;
   3274 	int verbose = 0;
   3275 
   3276 	while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
   3277 		switch (c) {
   3278 		case 'n':
   3279 			dryrun++;
   3280 			break;
   3281 		case 'd':
   3282 			description = optarg;
   3283 			break;
   3284 		case 'v':
   3285 			verbose++;
   3286 			break;
   3287 		case 'r':
   3288 			/*
   3289 			 * Update share by resource name
   3290 			 */
   3291 			if (rsrcname != NULL) {
   3292 				(void) printf(gettext(
   3293 				    "Updating multiple resource names not "
   3294 				    "supported\n"));
   3295 				return (SA_SYNTAX_ERR);
   3296 			}
   3297 			rsrcname = optarg;
   3298 			break;
   3299 		case 's':
   3300 			/*
   3301 			 * Save share path into group. Currently limit
   3302 			 * to one share per command.
   3303 			 */
   3304 			if (sharepath != NULL) {
   3305 				(void) printf(gettext(
   3306 				    "Updating multiple shares not "
   3307 				    "supported\n"));
   3308 				return (SA_SYNTAX_ERR);
   3309 			}
   3310 			sharepath = optarg;
   3311 			break;
   3312 		case 'h':
   3313 			/* optopt on valid arg isn't defined */
   3314 			optopt = c;
   3315 			/*FALLTHROUGH*/
   3316 		case '?':
   3317 		default:
   3318 			/*
   3319 			 * Since a bad option gets to here, sort it
   3320 			 * out and return a syntax error return value
   3321 			 * if necessary.
   3322 			 */
   3323 			switch (optopt) {
   3324 			default:
   3325 				ret = SA_SYNTAX_ERR;
   3326 				break;
   3327 			case 'h':
   3328 			case '?':
   3329 				break;
   3330 			}
   3331 			(void) printf(gettext("usage: %s\n"),
   3332 			    sa_get_usage(USAGE_SET_SHARE));
   3333 			return (ret);
   3334 		}
   3335 	}
   3336 
   3337 	if (optind >= argc && sharepath == NULL && rsrcname == NULL) {
   3338 		if (sharepath == NULL) {
   3339 			(void) printf(gettext("usage: %s\n"),
   3340 			    sa_get_usage(USAGE_SET_SHARE));
   3341 			(void) printf(gettext("\tgroup must be specified\n"));
   3342 			ret = SA_BAD_PATH;
   3343 		} else {
   3344 			ret = SA_OK;
   3345 		}
   3346 	}
   3347 	if ((optind + 1) < argc) {
   3348 		(void) printf(gettext("usage: %s\n"),
   3349 		    sa_get_usage(USAGE_SET_SHARE));
   3350 		(void) printf(gettext("\tExtraneous group(s) at end\n"));
   3351 		ret = SA_SYNTAX_ERR;
   3352 	}
   3353 
   3354 	/*
   3355 	 * Must have at least one of sharepath and rsrcrname.
   3356 	 * It is a syntax error to be missing both.
   3357 	 */
   3358 	if (sharepath == NULL && rsrcname == NULL) {
   3359 		(void) printf(gettext("usage: %s\n"),
   3360 		    sa_get_usage(USAGE_SET_SHARE));
   3361 		ret = SA_SYNTAX_ERR;
   3362 	}
   3363 
   3364 	if (ret != SA_OK)
   3365 		return (ret);
   3366 
   3367 	if (optind < argc) {
   3368 		groupname = argv[optind];
   3369 		group = sa_get_group(handle, groupname);
   3370 	} else {
   3371 		group = NULL;
   3372 		groupname = NULL;
   3373 	}
   3374 	if (rsrcname != NULL) {
   3375 		/*
   3376 		 * If rsrcname exists, split rename syntax and then
   3377 		 * convert to utf 8 if no errors.
   3378 		 */
   3379 		newname = strchr(rsrcname, '=');
   3380 		if (newname != NULL) {
   3381 			*newname++ = '\0';
   3382 		}
   3383 		if (!validresource(rsrcname)) {
   3384 			ret = SA_INVALID_NAME;
   3385 			(void) printf(gettext("Invalid resource name: "
   3386 			    "\"%s\"\n"), rsrcname);
   3387 		} else {
   3388 			rsrc = conv_to_utf8(rsrcname);
   3389 		}
   3390 		if (newname != NULL) {
   3391 			if (!validresource(newname)) {
   3392 				ret = SA_INVALID_NAME;
   3393 				(void) printf(gettext("Invalid resource name: "
   3394 				    "%s\n"), newname);
   3395 				newname = NULL;
   3396 			} else {
   3397 				newrsrc = conv_to_utf8(newname);
   3398 			}
   3399 		}
   3400 	}
   3401 
   3402 	if (ret != SA_OK) {
   3403 		if (rsrcname != NULL && rsrcname != rsrc)
   3404 			sa_free_attr_string(rsrc);
   3405 		if (newname != NULL && newname != newrsrc)
   3406 			sa_free_attr_string(newrsrc);
   3407 		return (ret);
   3408 	}
   3409 
   3410 	if (sharepath != NULL) {
   3411 		share = sa_find_share(handle, sharepath);
   3412 	} else if (rsrcname != NULL) {
   3413 		resource = sa_find_resource(handle, rsrc);
   3414 		if (resource != NULL)
   3415 			share = sa_get_resource_parent(resource);
   3416 		else
   3417 			ret = SA_NO_SUCH_RESOURCE;
   3418 	}
   3419 	if (share != NULL) {
   3420 		sharegroup = sa_get_parent_group(share);
   3421 		if (group != NULL && group != sharegroup) {
   3422 			(void) printf(gettext("Group \"%s\" does not contain "
   3423 			    "share %s\n"),
   3424 			    argv[optind], sharepath);
   3425 			ret = SA_BAD_PATH;
   3426 		} else {
   3427 			int delgroupname = 0;
   3428 			if (groupname == NULL) {
   3429 				groupname = sa_get_group_attr(sharegroup,
   3430 				    "name");
   3431 				delgroupname = 1;
   3432 			}
   3433 			if (groupname != NULL) {
   3434 				auth = check_authorizations(groupname, flags);
   3435 				if (delgroupname) {
   3436 					sa_free_attr_string(groupname);
   3437 					groupname = NULL;
   3438 				}
   3439 			} else {
   3440 				ret = SA_NO_MEMORY;
   3441 			}
   3442 			if (rsrcname != NULL) {
   3443 				resource = sa_find_resource(handle, rsrc);
   3444 				if (!dryrun) {
   3445 					if (newname != NULL &&
   3446 					    resource != NULL)
   3447 						ret = sa_rename_resource(
   3448 						    resource, newrsrc);
   3449 					else if (newname != NULL)
   3450 						ret = SA_NO_SUCH_RESOURCE;
   3451 					if (newname != NULL &&
   3452 					    newname != newrsrc)
   3453 						sa_free_attr_string(newrsrc);
   3454 				}
   3455 				if (rsrc != rsrcname)
   3456 					sa_free_attr_string(rsrc);
   3457 			}
   3458 
   3459 			/*
   3460 			 * If the user has set a description, it will be
   3461 			 * on the resource if -r was used otherwise it
   3462 			 * must be on the share.
   3463 			 */
   3464 			if (!dryrun && ret == SA_OK && description != NULL) {
   3465 				char *desc;
   3466 				desc = conv_to_utf8(description);
   3467 				if (resource != NULL)
   3468 					ret = sa_set_resource_description(
   3469 					    resource, desc);
   3470 				else
   3471 					ret = sa_set_share_description(share,
   3472 					    desc);
   3473 				if (desc != description)
   3474 					sa_free_share_description(desc);
   3475 			}
   3476 		}
   3477 		if (!dryrun && ret == SA_OK) {
   3478 			if (resource != NULL)
   3479 				(void) sa_enable_resource(resource, NULL);
   3480 			ret = sa_update_config(handle);
   3481 		}
   3482 		switch (ret) {
   3483 		case SA_DUPLICATE_NAME:
   3484 			(void) printf(gettext("Resource name in use: %s\n"),
   3485 			    rsrcname);
   3486 			break;
   3487 		default:
   3488 			(void) printf(gettext("Could not set: %s\n"),
   3489 			    sa_errorstr(ret));
   3490 			break;
   3491 		case SA_OK:
   3492 			if (dryrun && !auth && verbose) {
   3493 				(void) printf(gettext(
   3494 				    "Command would fail: %s\n"),
   3495 				    sa_errorstr(SA_NO_PERMISSION));
   3496 			}
   3497 			break;
   3498 		}
   3499 	} else {
   3500 		switch (ret) {
   3501 		case SA_NO_SUCH_RESOURCE:
   3502 			(void) printf(gettext("Resource \"%s\" not found\n"),
   3503 			    rsrcname);
   3504 			break;
   3505 		default:
   3506 			if (sharepath != NULL) {
   3507 				(void) printf(
   3508 				    gettext("Share path \"%s\" not found\n"),
   3509 				    sharepath);
   3510 				ret = SA_NO_SUCH_PATH;
   3511 			} else {
   3512 				(void) printf(gettext("Set failed: %s\n"),
   3513 				    sa_errorstr(ret));
   3514 			}
   3515 		}
   3516 	}
   3517 
   3518 	return (ret);
   3519 }
   3520 
   3521 /*
   3522  * add_security(group, sectype, optlist, proto, *err)
   3523  *
   3524  * Helper function to add a security option (named optionset) to the
   3525  * group.
   3526  */
   3527 
   3528 static int
   3529 add_security(sa_group_t group, char *sectype,
   3530     struct options *optlist, char *proto, int *err)
   3531 {
   3532 	sa_security_t security;
   3533 	int ret = SA_OK;
   3534 	int result = 0;
   3535 	sa_handle_t handle;
   3536 
   3537 	sectype = sa_proto_space_alias(proto, sectype);
   3538 	security = sa_get_security(group, sectype, proto);
   3539 	if (security == NULL)
   3540 		security = sa_create_security(group, sectype, proto);
   3541 
   3542 	if (sectype != NULL)
   3543 		sa_free_attr_string(sectype);
   3544 
   3545 	if (security == NULL)
   3546 		goto done;
   3547 
   3548 	handle = sa_find_group_handle(group);
   3549 	if (handle == NULL) {
   3550 		ret = SA_CONFIG_ERR;
   3551 		goto done;
   3552 	}
   3553 	while (optlist != NULL) {
   3554 		sa_property_t prop;
   3555 		prop = sa_get_property(security, optlist->optname);
   3556 		if (prop == NULL) {
   3557 			/*
   3558 			 * Add the property, but only if it is
   3559 			 * a non-NULL or non-zero length value
   3560 			 */
   3561 			if (optlist->optvalue != NULL) {
   3562 				prop = sa_create_property(optlist->optname,
   3563 				    optlist->optvalue);
   3564 				if (prop != NULL) {
   3565 					ret = sa_valid_property(handle,
   3566 					    security, proto, prop);
   3567 					if (ret != SA_OK) {
   3568 						(void) sa_remove_property(prop);
   3569 						(void) printf(gettext(
   3570 						    "Could not add "
   3571 						    "property %s: %s\n"),
   3572 						    optlist->optname,
   3573 						    sa_errorstr(ret));
   3574 					}
   3575 					if (ret == SA_OK) {
   3576 						ret = sa_add_property(security,
   3577 						    prop);
   3578 						if (ret != SA_OK) {
   3579 							(void) printf(gettext(
   3580 							    "Could not add "
   3581 							    "property (%s=%s):"
   3582 							    " %s\n"),
   3583 							    optlist->optname,
   3584 							    optlist->optvalue,
   3585 							    sa_errorstr(ret));
   3586 						} else {
   3587 							result = 1;
   3588 						}
   3589 					}
   3590 				}
   3591 			}
   3592 		} else {
   3593 			ret = sa_update_property(prop, optlist->optvalue);
   3594 			result = 1; /* should check if really changed */
   3595 		}
   3596 		optlist = optlist->next;
   3597 	}
   3598 	/*
   3599 	 * When done, properties may have all been removed but
   3600 	 * we need to keep the security type itself until
   3601 	 * explicitly removed.
   3602 	 */
   3603 	if (result)
   3604 		ret = sa_commit_properties(security, 0);
   3605 done:
   3606 	*err = ret;
   3607 	return (result);
   3608 }
   3609 
   3610 /*
   3611  * zfscheck(group, share)
   3612  *
   3613  * For the special case where a share was provided, make sure it is a
   3614  * compatible path for a ZFS property change.  The only path
   3615  * acceptable is the path that defines the zfs sub-group (dataset with
   3616  * the sharenfs property set) and not one of the paths that inherited
   3617  * the NFS properties. Returns SA_OK if it is usable and
   3618  * SA_NOT_ALLOWED if it isn't.
   3619  *
   3620  * If group is not a ZFS group/subgroup, we assume OK since the check
   3621  * on return will catch errors for those cases.  What we are looking
   3622  * for here is that the group is ZFS and the share is not the defining
   3623  * share.  All else is SA_OK.
   3624  */
   3625 
   3626 static int
   3627 zfscheck(sa_group_t group, sa_share_t share)
   3628 {
   3629 	int ret = SA_OK;
   3630 	char *attr;
   3631 
   3632 	if (sa_group_is_zfs(group)) {
   3633 		/*
   3634 		 * The group is a ZFS group.  Does the share represent
   3635 		 * the dataset that defined the group? It is only OK
   3636 		 * if the attribute "subgroup" exists on the share and
   3637 		 * has a value of "true".
   3638 		 */
   3639 
   3640 		ret = SA_NOT_ALLOWED;
   3641 		attr = sa_get_share_attr(share, "subgroup");
   3642 		if (attr != NULL) {
   3643 			if (strcmp(attr, "true") == 0)
   3644 				ret = SA_OK;
   3645 			sa_free_attr_string(attr);
   3646 		}
   3647 	}
   3648 	return (ret);
   3649 }
   3650 
   3651 /*
   3652  * basic_set(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
   3653  *
   3654  * This function implements "set" when a name space (-S) is not
   3655  * specified. It is a basic set. Options and other CLI parsing has
   3656  * already been done.
   3657  *
   3658  * "rsrcname" is a "resource name". If it is non-NULL, it must match
   3659  * the sharepath if present or group if present, otherwise it is used
   3660  * to set options.
   3661  *
   3662  * Resource names may take options if the protocol supports it. If the
   3663  * protocol doesn't support resource level options, rsrcname is just
   3664  * an alias for the share.
   3665  */
   3666 
   3667 static int
   3668 basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
   3669     char *protocol, char *sharepath, char *rsrcname, int dryrun)
   3670 {
   3671 	sa_group_t group;
   3672 	int ret = SA_OK;
   3673 	int change = 0;
   3674 	struct list *worklist = NULL;
   3675 
   3676 	group = sa_get_group(handle, groupname);
   3677 	if (group != NULL) {
   3678 		sa_share_t share = NULL;
   3679 		sa_resource_t resource = NULL;
   3680 
   3681 		/*
   3682 		 * If there is a sharepath, make sure it belongs to
   3683 		 * the group.
   3684 		 */
   3685 		if (sharepath != NULL) {
   3686 			share = sa_get_share(group, sharepath);
   3687 			if (share == NULL) {
   3688 				(void) printf(gettext(
   3689 				    "Share does not exist in group %s\n"),
   3690 				    groupname, sharepath);
   3691 				ret = SA_NO_SUCH_PATH;
   3692 			} else {
   3693 				/* if ZFS and OK, then only group */
   3694 				ret = zfscheck(group, share);
   3695 				if (ret == SA_OK &&
   3696 				    sa_group_is_zfs(group))
   3697 					share = NULL;
   3698 				if (ret == SA_NOT_ALLOWED)
   3699 					(void) printf(gettext(
   3700 					    "Properties on ZFS group shares "
   3701 					    "not supported: %s\n"), sharepath);
   3702 			}
   3703 		}
   3704 
   3705 		/*
   3706 		 * If a resource name exists, make sure it belongs to
   3707 		 * the share if present else it belongs to the
   3708 		 * group. Also check the protocol to see if it
   3709 		 * supports resource level properties or not. If not,
   3710 		 * use share only.
   3711 		 */
   3712 		if (rsrcname != NULL) {
   3713 			if (share != NULL) {
   3714 				resource = sa_get_share_resource(share,
   3715 				    rsrcname);
   3716 				if (resource == NULL)
   3717 					ret = SA_NO_SUCH_RESOURCE;
   3718 			} else {
   3719 				resource = sa_get_resource(group, rsrcname);
   3720 				if (resource != NULL)
   3721 					share = sa_get_resource_parent(
   3722 					    resource);
   3723 				else
   3724 					ret = SA_NO_SUCH_RESOURCE;
   3725 			}
   3726 			if (ret == SA_OK && resource != NULL) {
   3727 				uint64_t features;
   3728 				/*
   3729 				 * Check to see if the resource can take
   3730 				 * properties. If so, stick the resource into
   3731 				 * "share" so it will all just work.
   3732 				 */
   3733 				features = sa_proto_get_featureset(protocol);
   3734 				if (features & SA_FEATURE_RESOURCE)
   3735 					share = (sa_share_t)resource;
   3736 			}
   3737 		}
   3738 
   3739 		if (ret == SA_OK) {
   3740 			/* group must exist */
   3741 			ret = valid_options(handle, optlist, protocol,
   3742 			    share == NULL ? group : share, NULL);
   3743 			if (ret == SA_OK && !dryrun) {
   3744 				if (share != NULL)
   3745 					change |= add_optionset(share, optlist,
   3746 					    protocol, &ret);
   3747 				else
   3748 					change |= add_optionset(group, optlist,
   3749 					    protocol, &ret);
   3750 				if (ret == SA_OK && change)
   3751 					worklist = add_list(worklist, group,
   3752 					    share, protocol);
   3753 			}
   3754 		}
   3755 		free_opt(optlist);
   3756 	} else {
   3757 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
   3758 		ret = SA_NO_SUCH_GROUP;
   3759 	}
   3760 	/*
   3761 	 * we have a group and potentially legal additions
   3762 	 */
   3763 
   3764 	/*
   3765 	 * Commit to configuration if not a dryrunp and properties
   3766 	 * have changed.
   3767 	 */
   3768 	if (!dryrun && ret == SA_OK && change && worklist != NULL)
   3769 		/* properties changed, so update all shares */
   3770 		(void) enable_all_groups(handle, worklist, 0, 0, protocol,
   3771 		    B_TRUE);
   3772 
   3773 	if (worklist != NULL)
   3774 		free_list(worklist);
   3775 	return (ret);
   3776 }
   3777 
   3778 /*
   3779  * space_set(groupname, optlist, protocol, sharepath, dryrun)
   3780  *
   3781  * This function implements "set" when a name space (-S) is
   3782  * specified. It is a namespace set. Options and other CLI parsing has
   3783  * already been done.
   3784  */
   3785 
   3786 static int
   3787 space_set(sa_handle_t handle, char *groupname, struct options *optlist,
   3788     char *protocol, char *sharepath, int dryrun, char *sectype)
   3789 {
   3790 	sa_group_t group;
   3791 	int ret = SA_OK;
   3792 	int change = 0;
   3793 	struct list *worklist = NULL;
   3794 
   3795 	/*
   3796 	 * make sure protcol and sectype are valid
   3797 	 */
   3798 
   3799 	if (sa_proto_valid_space(protocol, sectype) == 0) {
   3800 		(void) printf(gettext("Option space \"%s\" not valid "
   3801 		    "for protocol.\n"), sectype);
   3802 		return (SA_INVALID_SECURITY);
   3803 	}
   3804 
   3805 	group = sa_get_group(handle, groupname);
   3806 	if (group != NULL) {
   3807 		sa_share_t share = NULL;
   3808 		if (sharepath != NULL) {
   3809 			share = sa_get_share(group, sharepath);
   3810 			if (share == NULL) {
   3811 				(void) printf(gettext(
   3812 				    "Share does not exist in group %s\n"),
   3813 				    groupname, sharepath);
   3814 				ret = SA_NO_SUCH_PATH;
   3815 			} else {
   3816 				/* if ZFS and OK, then only group */
   3817 				ret = zfscheck(group, share);
   3818 				if (ret == SA_OK &&
   3819 				    sa_group_is_zfs(group))
   3820 					share = NULL;
   3821 				if (ret == SA_NOT_ALLOWED)
   3822 					(void) printf(gettext(
   3823 					    "Properties on ZFS group shares "
   3824 					    "not supported: %s\n"), sharepath);
   3825 			}
   3826 		}
   3827 		if (ret == SA_OK) {
   3828 			/* group must exist */
   3829 			ret = valid_options(handle, optlist, protocol,
   3830 			    share == NULL ? group : share, sectype);
   3831 			if (ret == SA_OK && !dryrun) {
   3832 				if (share != NULL)
   3833 					change = add_security(share, sectype,
   3834 					    optlist, protocol, &ret);
   3835 				else
   3836 					change = add_security(group, sectype,
   3837 					    optlist, protocol, &ret);
   3838 				if (ret != SA_OK)
   3839 					(void) printf(gettext(
   3840 					    "Could not set property: %s\n"),
   3841 					    sa_errorstr(ret));
   3842 			}
   3843 			if (ret == SA_OK && change)
   3844 				worklist = add_list(worklist, group, share,
   3845 				    protocol);
   3846 		}
   3847 		free_opt(optlist);
   3848 	} else {
   3849 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
   3850 		ret = SA_NO_SUCH_GROUP;
   3851 	}
   3852 
   3853 	/*
   3854 	 * We have a group and potentially legal additions.
   3855 	 */
   3856 
   3857 	/* Commit to configuration if not a dryrun */
   3858 	if (!dryrun && ret == 0) {
   3859 		if (change && worklist != NULL) {
   3860 			/* properties changed, so update all shares */
   3861 			(void) enable_all_groups(handle, worklist, 0, 0,
   3862 			    protocol, B_TRUE);
   3863 		}
   3864 		ret = sa_update_config(handle);
   3865 	}
   3866 	if (worklist != NULL)
   3867 		free_list(worklist);
   3868 	return (ret);
   3869 }
   3870 
   3871 /*
   3872  * sa_set(flags, argc, argv)
   3873  *
   3874  * Implements the set subcommand. It keys off of -S to determine which
   3875  * set of operations to actually do.
   3876  */
   3877 
   3878 int
   3879 sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
   3880 {
   3881 	char *groupname;
   3882 	int verbose = 0;
   3883 	int dryrun = 0;
   3884 	int c;
   3885 	char *protocol = NULL;
   3886 	int ret = SA_OK;
   3887 	struct options *optlist = NULL;
   3888 	char *rsrcname = NULL;
   3889 	char *sharepath = NULL;
   3890 	char *optset = NULL;
   3891 	int auth;
   3892 
   3893 	while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
   3894 		switch (c) {
   3895 		case 'v':
   3896 			verbose++;
   3897 			break;
   3898 		case 'n':
   3899 			dryrun++;
   3900 			break;
   3901 		case 'P':
   3902 			if (protocol != NULL) {
   3903 				(void) printf(gettext(
   3904 				    "Specifying multiple protocols "
   3905 				    "not supported: %s\n"), protocol);
   3906 				return (SA_SYNTAX_ERR);
   3907 			}
   3908 			protocol = optarg;
   3909 			if (!sa_valid_protocol(protocol)) {
   3910 				(void) printf(gettext(
   3911 				    "Invalid protocol specified: %s\n"),
   3912 				    protocol);
   3913 				return (SA_INVALID_PROTOCOL);
   3914 			}
   3915 			break;
   3916 		case 'p':
   3917 			ret = add_opt(&optlist, optarg, 0);
   3918 			switch (ret) {
   3919 			case OPT_ADD_SYNTAX:
   3920 				(void) printf(gettext("Property syntax error:"
   3921 				    " %s\n"), optarg);
   3922 				return (SA_SYNTAX_ERR);
   3923 			case OPT_ADD_MEMORY:
   3924 				(void) printf(gettext("No memory to set "
   3925 				    "property: %s\n"), optarg);
   3926 				return (SA_NO_MEMORY);
   3927 			default:
   3928 				break;
   3929 			}
   3930 			break;
   3931 		case 'r':
   3932 			if (rsrcname != NULL) {
   3933 				(void) printf(gettext(
   3934 				    "Setting multiple resource names not"
   3935 				    " supported\n"));
   3936 				return (SA_SYNTAX_ERR);
   3937 			}
   3938 			rsrcname = optarg;
   3939 			break;
   3940 		case 's':
   3941 			if (sharepath != NULL) {
   3942 				(void) printf(gettext(
   3943 				    "Setting multiple shares not supported\n"));
   3944 				return (SA_SYNTAX_ERR);
   3945 			}
   3946 			sharepath = optarg;
   3947 			break;
   3948 		case 'S':
   3949 			if (optset != NULL) {
   3950 				(void) printf(gettext(
   3951 				    "Specifying multiple property "
   3952 				    "spaces not supported: %s\n"), optset);
   3953 				return (SA_SYNTAX_ERR);
   3954 			}
   3955 			optset = optarg;
   3956 			break;
   3957 		case 'h':
   3958 			/* optopt on valid arg isn't defined */
   3959 			optopt = c;
   3960 			/*FALLTHROUGH*/
   3961 		case '?':
   3962 		default:
   3963 			/*
   3964 			 * Since a bad option gets to here, sort it
   3965 			 * out and return a syntax error return value
   3966 			 * if necessary.
   3967 			 */
   3968 			switch (optopt) {
   3969 			default:
   3970 				ret = SA_SYNTAX_ERR;
   3971 				break;
   3972 			case 'h':
   3973 			case '?':
   3974 				break;
   3975 			}
   3976 			(void) printf(gettext("usage: %s\n"),
   3977 			    sa_get_usage(USAGE_SET));
   3978 			return (ret);
   3979 		}
   3980 	}
   3981 
   3982 	if (optlist != NULL)
   3983 		ret = chk_opt(optlist, optset != NULL, protocol);
   3984 
   3985 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
   3986 	    protocol == NULL || ret != OPT_ADD_OK) {
   3987 		char *sep = "\t";
   3988 
   3989 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
   3990 		if (optind >= argc) {
   3991 			(void) printf(gettext("%sgroup must be specified"),
   3992 			    sep);
   3993 			sep = ", ";
   3994 		}
   3995 		if (optlist == NULL) {
   3996 			(void) printf(gettext("%sat least one property must be"
   3997 			    " specified"), sep);
   3998 			sep = ", ";
   3999 		}
   4000 		if (protocol == NULL) {
   4001 			(void) printf(gettext("%sprotocol must be specified"),
   4002 			    sep);
   4003 			sep = ", ";
   4004 		}
   4005 		(void) printf("\n");
   4006 		ret = SA_SYNTAX_ERR;
   4007 	} else {
   4008 		/*
   4009 		 * Group already exists so we can proceed after a few
   4010 		 * additional checks related to ZFS handling.
   4011 		 */
   4012 
   4013 		groupname = argv[optind];
   4014 		if (strcmp(groupname, "zfs") == 0) {
   4015 			(void) printf(gettext("Changing properties for group "
   4016 			    "\"zfs\" not allowed\n"));
   4017 			return (SA_NOT_ALLOWED);
   4018 		}
   4019 
   4020 		auth = check_authorizations(groupname, flags);
   4021 		if (optset == NULL)
   4022 			ret = basic_set(handle, groupname, optlist, protocol,
   4023 			    sharepath, rsrcname, dryrun);
   4024 		else
   4025 			ret = space_set(handle, groupname, optlist, protocol,
   4026 			    sharepath, dryrun, optset);
   4027 		if (dryrun && ret == SA_OK && !auth && verbose) {
   4028 			(void) printf(gettext("Command would fail: %s\n"),
   4029 			    sa_errorstr(SA_NO_PERMISSION));
   4030 		}
   4031 	}
   4032 	return (ret);
   4033 }
   4034 
   4035 /*
   4036  * remove_options(group, optlist, proto, *err)
   4037  *
   4038  * Helper function to actually remove options from a group after all
   4039  * preprocessing is done.
   4040  */
   4041 
   4042 static int
   4043 remove_options(sa_group_t group, struct options *optlist,
   4044     char *proto, int *err)
   4045 {
   4046 	struct options *cur;
   4047 	sa_optionset_t optionset;
   4048 	sa_property_t prop;
   4049 	int change = 0;
   4050 	int ret = SA_OK;
   4051 
   4052 	optionset = sa_get_optionset(group, proto);
   4053 	if (optionset != NULL) {
   4054 		for (cur = optlist; cur != NULL; cur = cur->next) {
   4055 			prop = sa_get_property(optionset, cur->optname);
   4056 			if (prop != NULL) {
   4057 				ret = sa_remove_property(prop);
   4058 				if (ret != SA_OK)
   4059 					break;
   4060 				change = 1;
   4061 			}
   4062 		}
   4063 	}
   4064 	if (ret == SA_OK && change)
   4065 		ret = sa_commit_properties(optionset, 0);
   4066 
   4067 	if (err != NULL)
   4068 		*err = ret;
   4069 	return (change);
   4070 }
   4071 
   4072 /*
   4073  * valid_unset(group, optlist, proto)
   4074  *
   4075  * Sanity check the optlist to make sure they can be removed. Issue an
   4076  * error if a property doesn't exist.
   4077  */
   4078 
   4079 static int
   4080 valid_unset(sa_group_t group, struct options *optlist, char *proto)
   4081 {
   4082 	struct options *cur;
   4083 	sa_optionset_t optionset;
   4084 	sa_property_t prop;
   4085 	int ret = SA_OK;
   4086 
   4087 	optionset = sa_get_optionset(group, proto);
   4088 	if (optionset != NULL) {
   4089 		for (cur = optlist; cur != NULL; cur = cur->next) {
   4090 			prop = sa_get_property(optionset, cur->optname);
   4091 			if (prop == NULL) {
   4092 				(void) printf(gettext(
   4093 				    "Could not unset property %s: not set\n"),
   4094 				    cur->optname);
   4095 				ret = SA_NO_SUCH_PROP;
   4096 			}
   4097 		}
   4098 	}
   4099 	return (ret);
   4100 }
   4101 
   4102 /*
   4103  * valid_unset_security(group, optlist, proto)
   4104  *
   4105  * Sanity check the optlist to make sure they can be removed. Issue an
   4106  * error if a property doesn't exist.
   4107  */
   4108 
   4109 static int
   4110 valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
   4111     char *sectype)
   4112 {
   4113 	struct options *cur;
   4114 	sa_security_t security;
   4115 	sa_property_t prop;
   4116 	int ret = SA_OK;
   4117 	char *sec;
   4118 
   4119 	sec = sa_proto_space_alias(proto, sectype);
   4120 	security = sa_get_security(group, sec, proto);
   4121 	if (security != NULL) {
   4122 		for (cur = optlist; cur != NULL; cur = cur->next) {
   4123 			prop = sa_get_property(security, cur->optname);
   4124 			if (prop == NULL) {
   4125 				(void) printf(gettext(
   4126 				    "Could not unset property %s: not set\n"),
   4127 				    cur->optname);
   4128 				ret = SA_NO_SUCH_PROP;
   4129 			}
   4130 		}
   4131 	} else {
   4132 		(void) printf(gettext(
   4133 		    "Could not unset %s: space not defined\n"), sectype);
   4134 		ret = SA_NO_SUCH_SECURITY;
   4135 	}
   4136 	if (sec != NULL)
   4137 		sa_free_attr_string(sec);
   4138 	return (ret);
   4139 }
   4140 
   4141 /*
   4142  * remove_security(group, optlist, proto)
   4143  *
   4144  * Remove the properties since they were checked as valid.
   4145  */
   4146 
   4147 static int
   4148 remove_security(sa_group_t group, char *sectype,
   4149     struct options *optlist, char *proto, int *err)
   4150 {
   4151 	sa_security_t security;
   4152 	int ret = SA_OK;
   4153 	int change = 0;
   4154 
   4155 	sectype = sa_proto_space_alias(proto, sectype);
   4156 	security = sa_get_security(group, sectype, proto);
   4157 	if (sectype != NULL)
   4158 		sa_free_attr_string(sectype);
   4159 
   4160 	if (security != NULL) {
   4161 		while (optlist != NULL) {
   4162 			sa_property_t prop;
   4163 			prop = sa_get_property(security, optlist->optname);
   4164 			if (prop != NULL) {
   4165 				ret = sa_remove_property(prop);
   4166 				if (ret != SA_OK)
   4167 					break;
   4168 				change = 1;
   4169 			}
   4170 			optlist = optlist->next;
   4171 		}
   4172 		/*
   4173 		 * when done, properties may have all been removed but
   4174 		 * we need to keep the security type itself until
   4175 		 * explicitly removed.
   4176 		 */
   4177 		if (ret == SA_OK && change)
   4178 			ret = sa_commit_properties(security, 0);
   4179 	} else {
   4180 		ret = SA_NO_SUCH_PROP;
   4181 	}
   4182 	if (err != NULL)
   4183 		*err = ret;
   4184 	return (change);
   4185 }
   4186 
   4187 /*
   4188  * basic_unset(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
   4189  *
   4190  * Unset non-named optionset properties.
   4191  */
   4192 
   4193 static int
   4194 basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
   4195     char *protocol, char *sharepath, char *rsrcname, int dryrun)
   4196 {
   4197 	sa_group_t group;
   4198 	int ret = SA_OK;
   4199 	int change = 0;
   4200 	struct list *worklist = NULL;
   4201 	sa_share_t share = NULL;
   4202 	sa_resource_t resource = NULL;
   4203 
   4204 	group = sa_get_group(handle, groupname);
   4205 	if (group == NULL)
   4206 		return (ret);
   4207 
   4208 	/*
   4209 	 * If there is a sharepath, make sure it belongs to
   4210 	 * the group.
   4211 	 */
   4212 	if (sharepath != NULL) {
   4213 		share = sa_get_share(group, sharepath);
   4214 		if (share == NULL) {
   4215 			(void) printf(gettext(
   4216 			    "Share does not exist in group %s\n"),
   4217 			    groupname, sharepath);
   4218 			ret = SA_NO_SUCH_PATH;
   4219 		}
   4220 	}
   4221 	/*
   4222 	 * If a resource name exists, make sure it belongs to
   4223 	 * the share if present else it belongs to the
   4224 	 * group. Also check the protocol to see if it
   4225 	 * supports resource level properties or not. If not,
   4226 	 * use share only.
   4227 	 */
   4228 	if (rsrcname != NULL) {
   4229 		if (share != NULL) {
   4230 			resource = sa_get_share_resource(share, rsrcname);
   4231 			if (resource == NULL)
   4232 				ret = SA_NO_SUCH_RESOURCE;
   4233 		} else {
   4234 			resource = sa_get_resource(group, rsrcname);
   4235 			if (resource != NULL) {
   4236 				share = sa_get_resource_parent(resource);
   4237 			} else {
   4238 				ret = SA_NO_SUCH_RESOURCE;
   4239 			}
   4240 		}
   4241 		if (ret == SA_OK && resource != NULL) {
   4242 			uint64_t features;
   4243 			/*
   4244 			 * Check to see if the resource can take
   4245 			 * properties. If so, stick the resource into
   4246 			 * "share" so it will all just work.
   4247 			 */
   4248 			features = sa_proto_get_featureset(protocol);
   4249 			if (features & SA_FEATURE_RESOURCE)
   4250 				share = (sa_share_t)resource;
   4251 		}
   4252 	}
   4253 
   4254 	if (ret == SA_OK) {
   4255 		/* group must exist */
   4256 		ret = valid_unset(share != NULL ? share : group,
   4257 		    optlist, protocol);
   4258 		if (ret == SA_OK && !dryrun) {
   4259 			if (share != NULL) {
   4260 				sa_optionset_t optionset;
   4261 				sa_property_t prop;
   4262 				change |= remove_options(share, optlist,
   4263 				    protocol, &ret);
   4264 				/*
   4265 				 * If a share optionset is
   4266 				 * empty, remove it.
   4267 				 */
   4268 				optionset = sa_get_optionset((sa_share_t)share,
   4269 				    protocol);
   4270 				if (optionset != NULL) {
   4271 					prop = sa_get_property(optionset, NULL);
   4272 					if (prop == NULL)
   4273 						(void) sa_destroy_optionset(
   4274 						    optionset);
   4275 				}
   4276 			} else {
   4277 				change |= remove_options(group,
   4278 				    optlist, protocol, &ret);
   4279 			}
   4280 			if (ret == SA_OK && change)
   4281 				worklist = add_list(worklist, group, share,
   4282 				    protocol);
   4283 			if (ret != SA_OK)
   4284 				(void) printf(gettext(
   4285 				    "Could not remove properties: "
   4286 				    "%s\n"), sa_errorstr(ret));
   4287 		}
   4288 	} else {
   4289 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
   4290 		ret = SA_NO_SUCH_GROUP;
   4291 	}
   4292 	free_opt(optlist);
   4293 
   4294 	/*
   4295 	 * We have a group and potentially legal additions
   4296 	 *
   4297 	 * Commit to configuration if not a dryrun
   4298 	 */
   4299 	if (!dryrun && ret == SA_OK) {
   4300 		if (change && worklist != NULL) {
   4301 			/* properties changed, so update all shares */
   4302 			(void) enable_all_groups(handle, worklist, 0, 0,
   4303 			    protocol, B_TRUE);
   4304 		}
   4305 	}
   4306 	if (worklist != NULL)
   4307 		free_list(worklist);
   4308 	return (ret);
   4309 }
   4310 
   4311 /*
   4312  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
   4313  *
   4314  * Unset named optionset properties.
   4315  */
   4316 static int
   4317 space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
   4318     char *protocol, char *sharepath, int dryrun, char *sectype)
   4319 {
   4320 	sa_group_t group;
   4321 	int ret = SA_OK;
   4322 	int change = 0;
   4323 	struct list *worklist = NULL;
   4324 	sa_share_t share = NULL;
   4325 
   4326 	group = sa_get_group(handle, groupname);
   4327 	if (group == NULL) {
   4328 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
   4329 		return (SA_NO_SUCH_GROUP);
   4330 	}
   4331 	if (sharepath != NULL) {
   4332 		share = sa_get_share(group, sharepath);
   4333 		if (share == NULL) {
   4334 			(void) printf(gettext(
   4335 			    "Share does not exist in group %s\n"),
   4336 			    groupname, sharepath);
   4337 			return (SA_NO_SUCH_PATH);
   4338 		}
   4339 	}
   4340 	ret = valid_unset_security(share != NULL ? share : group,
   4341 	    optlist, protocol, sectype);
   4342 
   4343 	if (ret == SA_OK && !dryrun) {
   4344 		if (optlist != NULL) {
   4345 			if (share != NULL) {
   4346 				sa_security_t optionset;
   4347 				sa_property_t prop;
   4348 				change = remove_security(share,
   4349 				    sectype, optlist, protocol, &ret);
   4350 
   4351 				/* If a share security is empty, remove it */
   4352 				optionset = sa_get_security((sa_group_t)share,
   4353 				    sectype, protocol);
   4354 				if (optionset != NULL) {
   4355 					prop = sa_get_property(optionset,
   4356 					    NULL);
   4357 					if (prop == NULL)
   4358 						ret = sa_destroy_security(
   4359 						    optionset);
   4360 				}
   4361 			} else {
   4362 				change = remove_security(group, sectype,
   4363 				    optlist, protocol, &ret);
   4364 			}
   4365 		} else {
   4366 			sa_security_t security;
   4367 			char *sec;
   4368 			sec = sa_proto_space_alias(protocol, sectype);
   4369 			security = sa_get_security(group, sec, protocol);
   4370 			if (sec != NULL)
   4371 				sa_free_attr_string(sec);
   4372 			if (security != NULL) {
   4373 				ret = sa_destroy_security(security);
   4374 				if (ret == SA_OK)
   4375 					change = 1;
   4376 			} else {
   4377 				ret = SA_NO_SUCH_PROP;
   4378 			}
   4379 		}
   4380 		if (ret != SA_OK)
   4381 			(void) printf(gettext("Could not unset property: %s\n"),
   4382 			    sa_errorstr(ret));
   4383 	}
   4384 
   4385 	if (ret == SA_OK && change)
   4386 		worklist = add_list(worklist, group, 0, protocol);
   4387 
   4388 	free_opt(optlist);
   4389 	/*
   4390 	 * We have a group and potentially legal additions
   4391 	 */
   4392 
   4393 	/* Commit to configuration if not a dryrun */
   4394 	if (!dryrun && ret == 0) {
   4395 		/* properties changed, so update all shares */
   4396 		if (change && worklist != NULL)
   4397 			(void) enable_all_groups(handle, worklist, 0, 0,
   4398 			    protocol, B_TRUE);
   4399 		ret = sa_update_config(handle);
   4400 	}
   4401 	if (worklist != NULL)
   4402 		free_list(worklist);
   4403 	return (ret);
   4404 }
   4405 
   4406 /*
   4407  * sa_unset(flags, argc, argv)
   4408  *
   4409  * Implements the unset subcommand. Parsing done here and then basic
   4410  * or space versions of the real code are called.
   4411  */
   4412 
   4413 int
   4414 sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
   4415 {
   4416 	char *groupname;
   4417 	int verbose = 0;
   4418 	int dryrun = 0;
   4419 	int c;
   4420 	char *protocol = NULL;
   4421 	int ret = SA_OK;
   4422 	struct options *optlist = NULL;
   4423 	char *rsrcname = NULL;
   4424 	char *sharepath = NULL;
   4425 	char *optset = NULL;
   4426 	int auth;
   4427 
   4428 	while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
   4429 		switch (c) {
   4430 		case 'v':
   4431 			verbose++;
   4432 			break;
   4433 		case 'n':
   4434 			dryrun++;
   4435 			break;
   4436 		case 'P':
   4437 			if (protocol != NULL) {
   4438 				(void) printf(gettext(
   4439 				    "Specifying multiple protocols "
   4440 				    "not supported: %s\n"), protocol);
   4441 				return (SA_SYNTAX_ERR);
   4442 			}
   4443 			protocol = optarg;
   4444 			if (!sa_valid_protocol(protocol)) {
   4445 				(void) printf(gettext(
   4446 				    "Invalid protocol specified: %s\n"),
   4447 				    protocol);
   4448 				return (SA_INVALID_PROTOCOL);
   4449 			}
   4450 			break;
   4451 		case 'p':
   4452 			ret = add_opt(&optlist, optarg, 1);
   4453 			switch (ret) {
   4454 			case OPT_ADD_SYNTAX:
   4455 				(void) printf(gettext("Property syntax error "
   4456 				    "for property %s\n"), optarg);
   4457 				return (SA_SYNTAX_ERR);
   4458 
   4459 			case OPT_ADD_PROPERTY:
   4460 				(void) printf(gettext("Properties need to be "
   4461 				    "set with set command: %s\n"), optarg);
   4462 				return (SA_SYNTAX_ERR);
   4463 
   4464 			default:
   4465 				break;
   4466 			}
   4467 			break;
   4468 		case 'r':
   4469 			/*
   4470 			 * Unset properties on resource if applicable or on
   4471 			 * share if resource for this protocol doesn't use
   4472 			 * resources.
   4473 			 */
   4474 			if (rsrcname != NULL) {
   4475 				(void) printf(gettext(
   4476 				    "Unsetting multiple resource "
   4477 				    "names not supported\n"));
   4478 				return (SA_SYNTAX_ERR);
   4479 			}
   4480 			rsrcname = optarg;
   4481 			break;
   4482 		case 's':
   4483 			if (sharepath != NULL) {
   4484 				(void) printf(gettext(
   4485 				    "Adding multiple shares not supported\n"));
   4486 				return (SA_SYNTAX_ERR);
   4487 			}
   4488 			sharepath = optarg;
   4489 			break;
   4490 		case 'S':
   4491 			if (optset != NULL) {
   4492 				(void) printf(gettext(
   4493 				    "Specifying multiple property "
   4494 				    "spaces not supported: %s\n"), optset);
   4495 				return (SA_SYNTAX_ERR);
   4496 			}
   4497 			optset = optarg;
   4498 			break;
   4499 		case 'h':
   4500 			/* optopt on valid arg isn't defined */
   4501 			optopt = c;
   4502 			/*FALLTHROUGH*/
   4503 		case '?':
   4504 		default:
   4505 			/*
   4506 			 * Since a bad option gets to here, sort it
   4507 			 * out and return a syntax error return value
   4508 			 * if necessary.
   4509 			 */
   4510 			switch (optopt) {
   4511 			default:
   4512 				ret = SA_SYNTAX_ERR;
   4513 				break;
   4514 			case 'h':
   4515 			case '?':
   4516 				break;
   4517 			}
   4518 			(void) printf(gettext("usage: %s\n"),
   4519 			    sa_get_usage(USAGE_UNSET));
   4520 			return (ret);
   4521 		}
   4522 	}
   4523 
   4524 	if (optlist != NULL)
   4525 		ret = chk_opt(optlist, optset != NULL, protocol);
   4526 
   4527 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
   4528 	    protocol == NULL) {
   4529 		char *sep = "\t";
   4530 		(void) printf(gettext("usage: %s\n"),
   4531 		    sa_get_usage(USAGE_UNSET));
   4532 		if (optind >= argc) {
   4533 			(void) printf(gettext("%sgroup must be specified"),
   4534 			    sep);
   4535 			sep = ", ";
   4536 		}
   4537 		if (optlist == NULL) {
   4538 			(void) printf(gettext("%sat least one property must "
   4539 			    "be specified"), sep);
   4540 			sep = ", ";
   4541 		}
   4542 		if (protocol == NULL) {
   4543 			(void) printf(gettext("%sprotocol must be specified"),
   4544 			    sep);
   4545 			sep = ", ";
   4546 		}
   4547 		(void) printf("\n");
   4548 		ret = SA_SYNTAX_ERR;
   4549 	} else {
   4550 
   4551 		/*
   4552 		 * If a group already exists, we can only add a new
   4553 		 * protocol to it and not create a new one or add the
   4554 		 * same protocol again.
   4555 		 */
   4556 
   4557 		groupname = argv[optind];
   4558 		auth = check_authorizations(groupname, flags);
   4559 		if (optset == NULL)
   4560 			ret = basic_unset(handle, groupname, optlist, protocol,
   4561 			    sharepath, rsrcname, dryrun);
   4562 		else
   4563 			ret = space_unset(handle, groupname, optlist, protocol,
   4564 			    sharepath, dryrun, optset);
   4565 
   4566 		if (dryrun && ret == SA_OK && !auth && verbose)
   4567 			(void) printf(gettext("Command would fail: %s\n"),
   4568 			    sa_errorstr(SA_NO_PERMISSION));
   4569 	}
   4570 	return (ret);
   4571 }
   4572 
   4573 /*
   4574  * sa_enable_group(flags, argc, argv)
   4575  *
   4576  * Implements the enable subcommand
   4577  */
   4578 
   4579 int
   4580 sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
   4581 {
   4582 	int verbose = 0;
   4583 	int dryrun = 0;
   4584 	int all = 0;
   4585 	int c;
   4586 	int ret = SA_OK;
   4587 	char *protocol = NULL;
   4588 	char *state;
   4589 	struct list *worklist = NULL;
   4590 	int auth = 1;
   4591 	sa_group_t group;
   4592 
   4593 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
   4594 		switch (c) {
   4595 		case 'a':
   4596 			all = 1;
   4597 			break;
   4598 		case 'n':
   4599 			dryrun++;
   4600 			break;
   4601 		case 'P':
   4602 			if (protocol != NULL) {
   4603 				(void) printf(gettext(
   4604 				    "Specifying multiple protocols "
   4605 				    "not supported: %s\n"), protocol);
   4606 				return (SA_SYNTAX_ERR);
   4607 			}
   4608 			protocol = optarg;
   4609 			if (!sa_valid_protocol(protocol)) {
   4610 				(void) printf(gettext(
   4611 				    "Invalid protocol specified: %s\n"),
   4612 				    protocol);
   4613 				return (SA_INVALID_PROTOCOL);
   4614 			}
   4615 			break;
   4616 		case 'v':
   4617 			verbose++;
   4618 			break;
   4619 		case 'h':
   4620 			/* optopt on valid arg isn't defined */
   4621 			optopt = c;
   4622 			/*FALLTHROUGH*/
   4623 		case '?':
   4624 		default:
   4625 			/*
   4626 			 * Since a bad option gets to here, sort it
   4627 			 * out and return a syntax error return value
   4628 			 * if necessary.
   4629 			 */
   4630 			switch (optopt) {
   4631 			default:
   4632 				ret = SA_SYNTAX_ERR;
   4633 				break;
   4634 			case 'h':
   4635 			case '?':
   4636 				(void) printf(gettext("usage: %s\n"),
   4637 				    sa_get_usage(USAGE_ENABLE));
   4638 				return (ret);
   4639 			}
   4640 		}
   4641 	}
   4642 
   4643 	if (optind == argc && !all) {
   4644 		(void) printf(gettext("usage: %s\n"),
   4645 		    sa_get_usage(USAGE_ENABLE));
   4646 		(void) printf(gettext("\tmust specify group\n"));
   4647 		return (SA_NO_SUCH_PATH);
   4648 	}
   4649 	if (!all) {
   4650 		while (optind < argc) {
   4651 			group = sa_get_group(handle, argv[optind]);
   4652 			if (group != NULL) {
   4653 				auth &= check_authorizations(argv[optind],
   4654 				    flags);
   4655 				state = sa_get_group_attr(group, "state");
   4656 				if (state != NULL &&
   4657 				    strcmp(state, "enabled") == 0) {
   4658 					/* already enabled */
   4659 					if (verbose)
   4660 						(void) printf(gettext(
   4661 						    "Group \"%s\" is already "
   4662 						    "enabled\n"),
   4663 						    argv[optind]);
   4664 					ret = SA_BUSY; /* already enabled */
   4665 				} else {
   4666 					worklist = add_list(worklist, group,
   4667 					    0, protocol);
   4668 					if (verbose)
   4669 						(void) printf(gettext(
   4670 						    "Enabling group \"%s\"\n"),
   4671 						    argv[optind]);
   4672 				}
   4673 				if (state != NULL)
   4674 					sa_free_attr_string(state);
   4675 			} else {
   4676 				ret = SA_NO_SUCH_GROUP;
   4677 			}
   4678 			optind++;
   4679 		}
   4680 	} else {
   4681 		for (group = sa_get_group(handle, NULL);
   4682 		    group != NULL;
   4683 		    group = sa_get_next_group(group)) {
   4684 			worklist = add_list(worklist, group, 0, protocol);
   4685 		}
   4686 	}
   4687 	if (!dryrun && ret == SA_OK)
   4688 		ret = enable_all_groups(handle, worklist, 1, 0, NULL, B_FALSE);
   4689 
   4690 	if (ret != SA_OK && ret != SA_BUSY)
   4691 		(void) printf(gettext("Could not enable group: %s\n"),
   4692 		    sa_errorstr(ret));
   4693 	if (ret == SA_BUSY)
   4694 		ret = SA_OK;
   4695 
   4696 	if (worklist != NULL)
   4697 		free_list(worklist);
   4698 	if (dryrun && ret == SA_OK && !auth && verbose) {
   4699 		(void) printf(gettext("Command would fail: %s\n"),
   4700 		    sa_errorstr(SA_NO_PERMISSION));
   4701 	}
   4702 	return (ret);
   4703 }
   4704 
   4705 /*
   4706  * disable_group(group, proto)
   4707  *
   4708  * Disable all the shares in the specified group.. This is a helper
   4709  * for disable_all_groups in order to simplify regular and subgroup
   4710  * (zfs) disabling. Group has already been checked for non-NULL.
   4711  */
   4712 
   4713 static int
   4714 disable_group(sa_group_t group, char *proto)
   4715 {
   4716 	sa_share_t share;
   4717 	int ret = SA_OK;
   4718 
   4719 	/*
   4720 	 * If the protocol isn't enabled, skip it and treat as
   4721 	 * successful.
   4722 	 */
   4723 	if (!has_protocol(group, proto))
   4724 		return (ret);
   4725 
   4726 	for (share = sa_get_share(group, NULL);
   4727 	    share != NULL && ret == SA_OK;
   4728 	    share = sa_get_next_share(share)) {
   4729 		ret = sa_disable_share(share, proto);
   4730 		if (ret == SA_NO_SUCH_PATH) {
   4731 			/*
   4732 			 * this is OK since the path is gone. we can't
   4733 			 * re-share it anyway so no error.
   4734 			 */
   4735 			ret = SA_OK;
   4736 		}
   4737 	}
   4738 	return (ret);
   4739 }
   4740 
   4741 /*
   4742  * disable_all_groups(work, setstate)
   4743  *
   4744  * helper function that disables the shares in the list of groups
   4745  * provided. It optionally marks the group as disabled. Used by both
   4746  * enable and start subcommands.
   4747  */
   4748 
   4749 static int
   4750 disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
   4751 {
   4752 	int ret = SA_OK;
   4753 	sa_group_t subgroup, group;
   4754 
   4755 	while (work != NULL && ret == SA_OK) {
   4756 		group = (sa_group_t)work->item;
   4757 		if (setstate)
   4758 			ret = sa_set_group_attr(group, "state", "disabled");
   4759 		if (ret == SA_OK) {
   4760 			char *name;
   4761 			name = sa_get_group_attr(group, "name");
   4762 			if (name != NULL && strcmp(name, "zfs") == 0) {
   4763 				/* need to get the sub-groups for stopping */
   4764 				for (subgroup = sa_get_sub_group(group);
   4765 				    subgroup != NULL;
   4766 				    subgroup = sa_get_next_group(subgroup)) {
   4767 					ret = disable_group(subgroup,
   4768 					    work->proto);
   4769 				}
   4770 			} else {
   4771 				ret = disable_group(group, work->proto);
   4772 			}
   4773 			if (name != NULL)
   4774 				sa_free_attr_string(name);
   4775 			/*
   4776 			 * We don't want to "disable" since it won't come
   4777 			 * up after a reboot.  The SMF framework should do
   4778 			 * the right thing. On enable we do want to do
   4779 			 * something.
   4780 			 */
   4781 		}
   4782 		work = work->next;
   4783 	}
   4784 	if (ret == SA_OK)
   4785 		ret = sa_update_config(handle);
   4786 	return (ret);
   4787 }
   4788 
   4789 /*
   4790  * sa_disable_group(flags, argc, argv)
   4791  *
   4792  * Implements the disable subcommand
   4793  */
   4794 
   4795 int
   4796 sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
   4797 {
   4798 	int verbose = 0;
   4799 	int dryrun = 0;
   4800 	int all = 0;
   4801 	int c;
   4802 	int ret = SA_OK;
   4803 	char *protocol = NULL;
   4804 	char *state;
   4805 	struct list *worklist = NULL;
   4806 	sa_group_t group;
   4807 	int auth = 1;
   4808 
   4809 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
   4810 		switch (c) {
   4811 		case 'a':
   4812 			all = 1;
   4813 			break;
   4814 		case 'n':
   4815 			dryrun++;
   4816 			break;
   4817 		case 'P':
   4818 			if (protocol != NULL) {
   4819 				(void) printf(gettext(
   4820 				    "Specifying multiple protocols "
   4821 				    "not supported: %s\n"), protocol);
   4822 				return (SA_SYNTAX_ERR);
   4823 			}
   4824 			protocol = optarg;
   4825 			if (!sa_valid_protocol(protocol)) {
   4826 				(void) printf(gettext(
   4827 				    "Invalid protocol specified: %s\n"),
   4828 				    protocol);
   4829 				return (SA_INVALID_PROTOCOL);
   4830 			}
   4831 			break;
   4832 		case 'v':
   4833 			verbose++;
   4834 			break;
   4835 		case 'h':
   4836 			/* optopt on valid arg isn't defined */
   4837 			optopt = c;
   4838 			/*FALLTHROUGH*/
   4839 		case '?':
   4840 		default:
   4841 			/*
   4842 			 * Since a bad option gets to here, sort it
   4843 			 * out and return a syntax error return value
   4844 			 * if necessary.
   4845 			 */
   4846 			switch (optopt) {
   4847 			default:
   4848 				ret = SA_SYNTAX_ERR;
   4849 				break;
   4850 			case 'h':
   4851 			case '?':
   4852 				break;
   4853 			}
   4854 			(void) printf(gettext("usage: %s\n"),
   4855 			    sa_get_usage(USAGE_DISABLE));
   4856 			return (ret);
   4857 		}
   4858 	}
   4859 
   4860 	if (optind == argc && !all) {
   4861 		(void) printf(gettext("usage: %s\n"),
   4862 		    sa_get_usage(USAGE_DISABLE));
   4863 		(void) printf(gettext("\tmust specify group\n"));
   4864 		return (SA_NO_SUCH_PATH);
   4865 	}
   4866 	if (!all) {
   4867 		while (optind < argc) {
   4868 			group = sa_get_group(handle, argv[optind]);
   4869 			if (group != NULL) {
   4870 				auth &= check_authorizations(argv[optind],
   4871 				    flags);
   4872 				state = sa_get_group_attr(group, "state");
   4873 				if (state == NULL ||
   4874 				    strcmp(state, "disabled") == 0) {
   4875 					/* already disabled */
   4876 					if (verbose)
   4877 						(void) printf(gettext(
   4878 						    "Group \"%s\" is "
   4879 						    "already disabled\n"),
   4880 						    argv[optind]);
   4881 					ret = SA_BUSY; /* already disabled */
   4882 				} else {
   4883 					worklist = add_list(worklist, group, 0,
   4884 					    protocol);
   4885 					if (verbose)
   4886 						(void) printf(gettext(
   4887 						    "Disabling group "
   4888 						    "\"%s\"\n"), argv[optind]);
   4889 				}
   4890 				if (state != NULL)
   4891 					sa_free_attr_string(state);
   4892 			} else {
   4893 				ret = SA_NO_SUCH_GROUP;
   4894 			}
   4895 			optind++;
   4896 		}
   4897 	} else {
   4898 		for (group = sa_get_group(handle, NULL);
   4899 		    group != NULL;
   4900 		    group = sa_get_next_group(group))
   4901 			worklist = add_list(worklist, group, 0, protocol);
   4902 	}
   4903 
   4904 	if (ret == SA_OK && !dryrun)
   4905 		ret = disable_all_groups(handle, worklist, 1);
   4906 	if (ret != SA_OK && ret != SA_BUSY)
   4907 		(void) printf(gettext("Could not disable group: %s\n"),
   4908 		    sa_errorstr(ret));
   4909 	if (ret == SA_BUSY)
   4910 		ret = SA_OK;
   4911 	if (worklist != NULL)
   4912 		free_list(worklist);
   4913 	if (dryrun && ret == SA_OK && !auth && verbose)
   4914 		(void) printf(gettext("Command would fail: %s\n"),
   4915 		    sa_errorstr(SA_NO_PERMISSION));
   4916 	return (ret);
   4917 }
   4918 
   4919 /*
   4920  * sa_start_group(flags, argc, argv)
   4921  *
   4922  * Implements the start command.
   4923  * This is similar to enable except it doesn't change the state
   4924  * of the group(s) and only enables shares if the group is already
   4925  * enabled.
   4926  */
   4927 
   4928 int
   4929 sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
   4930 {
   4931 	int verbose = 0;
   4932 	int all = 0;
   4933 	int c;
   4934 	int ret = SMF_EXIT_OK;
   4935 	char *protocol = NULL;
   4936 	char *state;
   4937 	struct list *worklist = NULL;
   4938 	sa_group_t group;
   4939 #ifdef lint
   4940 	flags = flags;
   4941 #endif
   4942 
   4943 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
   4944 		switch (c) {
   4945 		case 'a':
   4946 			all = 1;
   4947 			break;
   4948 		case 'P':
   4949 			if (protocol != NULL) {
   4950 				(void) printf(gettext(
   4951 				    "Specifying multiple protocols "
   4952 				    "not supported: %s\n"), protocol);
   4953 				return (SA_SYNTAX_ERR);
   4954 			}
   4955 			protocol = optarg;
   4956 			if (!sa_valid_protocol(protocol)) {
   4957 				(void) printf(gettext(
   4958 				    "Invalid protocol specified: %s\n"),
   4959 				    protocol);
   4960 				return (SA_INVALID_PROTOCOL);
   4961 			}
   4962 			break;
   4963 		case 'v':
   4964 			verbose++;
   4965 			break;
   4966 		case 'h':
   4967 			/* optopt on valid arg isn't defined */
   4968 			optopt = c;
   4969 			/*FALLTHROUGH*/
   4970 		case '?':
   4971 		default:
   4972 			/*
   4973 			 * Since a bad option gets to here, sort it
   4974 			 * out and return a syntax error return value
   4975 			 * if necessary.
   4976 			 */
   4977 			ret = SA_OK;
   4978 			switch (optopt) {
   4979 			default:
   4980 				ret = SA_SYNTAX_ERR;
   4981 				break;
   4982 			case 'h':
   4983 			case '?':
   4984 				break;
   4985 			}
   4986 			(void) printf(gettext("usage: %s\n"),
   4987 			    sa_get_usage(USAGE_START));
   4988 			return (ret);
   4989 		}
   4990 	}
   4991 
   4992 	if (optind == argc && !all) {
   4993 		(void) printf(gettext("usage: %s\n"),
   4994 		    sa_get_usage(USAGE_START));
   4995 		return (SMF_EXIT_ERR_FATAL);
   4996 	}
   4997 
   4998 	if (!all) {
   4999 		while (optind < argc) {
   5000 			group = sa_get_group(handle, argv[optind]);
   5001 			if (group != NULL) {
   5002 				state = sa_get_group_attr(group, "state");
   5003 				if (state == NULL ||
   5004 				    strcmp(state, "enabled") == 0) {
   5005 					worklist = add_list(worklist, group, 0,
   5006 					    protocol);
   5007 					if (verbose)
   5008 						(void) printf(gettext(
   5009 						    "Starting group \"%s\"\n"),
   5010 						    argv[optind]);
   5011 				} else {
   5012 					/*
   5013 					 * Determine if there are any
   5014 					 * protocols.  If there aren't any,
   5015 					 * then there isn't anything to do in
   5016 					 * any case so no error.
   5017 					 */
   5018 					if (sa_get_optionset(group,
   5019 					    protocol) != NULL) {
   5020 						ret = SMF_EXIT_OK;
   5021 					}
   5022 				}
   5023 				if (state != NULL)
   5024 					sa_free_attr_string(state);
   5025 			}
   5026 			optind++;
   5027 		}
   5028 	} else {
   5029 		for (group = sa_get_group(handle, NULL);
   5030 		    group != NULL;
   5031 		    group = sa_get_next_group(group)) {
   5032 			state = sa_get_group_attr(group, "state");
   5033 			if (state == NULL || strcmp(state, "enabled") == 0)
   5034 				worklist = add_list(worklist, group, 0,
   5035 				    protocol);
   5036 			if (state != NULL)
   5037 				sa_free_attr_string(state);
   5038 		}
   5039 	}
   5040 
   5041 	(void) enable_all_groups(handle, worklist, 0, 1, protocol, B_FALSE);
   5042 
   5043 	if (worklist != NULL)
   5044 		free_list(worklist);
   5045 	return (ret);
   5046 }
   5047 
   5048 /*
   5049  * sa_stop_group(flags, argc, argv)
   5050  *
   5051  * Implements the stop command.
   5052  * This is similar to disable except it doesn't change the state
   5053  * of the group(s) and only disables shares if the group is already
   5054  * enabled.
   5055  */
   5056 int
   5057 sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
   5058 {
   5059 	int verbose = 0;
   5060 	int all = 0;
   5061 	int c;
   5062 	int ret = SMF_EXIT_OK;
   5063 	char *protocol = NULL;
   5064 	char *state;
   5065 	struct list *worklist = NULL;
   5066 	sa_group_t group;
   5067 #ifdef lint
   5068 	flags = flags;
   5069 #endif
   5070 
   5071 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
   5072 		switch (c) {
   5073 		case 'a':
   5074 			all = 1;
   5075 			break;
   5076 		case 'P':
   5077 			if (protocol != NULL) {
   5078 				(void) printf(gettext(
   5079 				    "Specifying multiple protocols "
   5080 				    "not supported: %s\n"), protocol);
   5081 				return (SA_SYNTAX_ERR);
   5082 			}
   5083 			protocol = optarg;
   5084 			if (!sa_valid_protocol(protocol)) {
   5085 				(void) printf(gettext(
   5086 				    "Invalid protocol specified: %s\n"),
   5087 				    protocol);
   5088 				return (SA_INVALID_PROTOCOL);
   5089 			}
   5090 			break;
   5091 		case 'v':
   5092 			verbose++;
   5093 			break;
   5094 		case 'h':
   5095 			/* optopt on valid arg isn't defined */
   5096 			optopt = c;
   5097 			/*FALLTHROUGH*/
   5098 		case '?':
   5099 		default:
   5100 			/*
   5101 			 * Since a bad option gets to here, sort it
   5102 			 * out and return a syntax error return value
   5103 			 * if necessary.
   5104 			 */
   5105 			ret = SA_OK;
   5106 			switch (optopt) {
   5107 			default:
   5108 				ret = SA_SYNTAX_ERR;
   5109 				break;
   5110 			case 'h':
   5111 			case '?':
   5112 				break;
   5113 			}
   5114 			(void) printf(gettext("usage: %s\n"),
   5115 			    sa_get_usage(USAGE_STOP));
   5116 			return (ret);
   5117 		}
   5118 	}
   5119 
   5120 	if (optind == argc && !all) {
   5121 		(void) printf(gettext("usage: %s\n"),
   5122 		    sa_get_usage(USAGE_STOP));
   5123 		return (SMF_EXIT_ERR_FATAL);
   5124 	} else if (!all) {
   5125 		while (optind < argc) {
   5126 			group = sa_get_group(handle, argv[optind]);
   5127 			if (group != NULL) {
   5128 				state = sa_get_group_attr(group, "state");
   5129 				if (state == NULL ||
   5130 				    strcmp(state, "enabled") == 0) {
   5131 					worklist = add_list(worklist, group, 0,
   5132 					    protocol);
   5133 					if (verbose)
   5134 						(void) printf(gettext(
   5135 						    "Stopping group \"%s\"\n"),
   5136 						    argv[optind]);
   5137 				} else {
   5138 					ret = SMF_EXIT_OK;
   5139 				}
   5140 				if (state != NULL)
   5141 					sa_free_attr_string(state);
   5142 			}
   5143 			optind++;
   5144 		}
   5145 	} else {
   5146 		for (group = sa_get_group(handle, NULL);
   5147 		    group != NULL;
   5148 		    group = sa_get_next_group(group)) {
   5149 			state = sa_get_group_attr(group, "state");
   5150 			if (state == NULL || strcmp(state, "enabled") == 0)
   5151 				worklist = add_list(worklist, group, 0,
   5152 				    protocol);
   5153 			if (state != NULL)
   5154 				sa_free_attr_string(state);
   5155 		}
   5156 	}
   5157 	(void) disable_all_groups(handle, worklist, 0);
   5158 	ret = sa_update_config(handle);
   5159 
   5160 	if (worklist != NULL)
   5161 		free_list(worklist);
   5162 	return (ret);
   5163 }
   5164 
   5165 /*
   5166  * remove_all_options(share, proto)
   5167  *
   5168  * Removes all options on a share.
   5169  */
   5170 
   5171 static void
   5172 remove_all_options(sa_share_t share, char *proto)
   5173 {
   5174 	sa_optionset_t optionset;
   5175 	sa_security_t security;
   5176 	sa_security_t prevsec = NULL;
   5177 
   5178 	optionset = sa_get_optionset(share, proto);
   5179 	if (optionset != NULL)
   5180 		(void) sa_destroy_optionset(optionset);
   5181 	for (security = sa_get_security(share, NULL, NULL);
   5182 	    security != NULL;
   5183 	    security = sa_get_next_security(security)) {
   5184 		char *type;
   5185 		/*
   5186 		 * We walk through the list.  prevsec keeps the
   5187 		 * previous security so we can delete it without
   5188 		 * destroying the list.
   5189 		 */
   5190 		if (prevsec != NULL) {
   5191 			/* remove the previously seen security */
   5192 			(void) sa_destroy_security(prevsec);
   5193 			/* set to NULL so we don't try multiple times */
   5194 			prevsec = NULL;
   5195 		}
   5196 		type = sa_get_security_attr(security, "type");
   5197 		if (type != NULL) {
   5198 			/*
   5199 			 * if the security matches the specified protocol, we
   5200 			 * want to remove it. prevsec holds it until either
   5201 			 * the next pass or we fall out of the loop.
   5202 			 */
   5203 			if (strcmp(type, proto) == 0)
   5204 				prevsec = security;
   5205 			sa_free_attr_string(type);
   5206 		}
   5207 	}
   5208 	/* in case there is one left */
   5209 	if (prevsec != NULL)
   5210 		(void) sa_destroy_security(prevsec);
   5211 }
   5212 
   5213 
   5214 /*
   5215  * for legacy support, we need to handle the old syntax. This is what
   5216  * we get if sharemgr is called with the name "share" rather than
   5217  * sharemgr.
   5218  */
   5219 
   5220 static int
   5221 format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
   5222 {
   5223 	int err;
   5224 
   5225 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
   5226 	if (err > buffsize)
   5227 		return (-1);
   5228 	return (0);
   5229 }
   5230 
   5231 
   5232 /*
   5233  * check_legacy_cmd(proto, cmd)
   5234  *
   5235  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
   5236  * executable.
   5237  */
   5238 
   5239 static int
   5240 check_legacy_cmd(char *path)
   5241 {
   5242 	struct stat st;
   5243 	int ret = 0;
   5244 
   5245 	if (stat(path, &st) == 0) {
   5246 		if (S_ISREG(st.st_mode) &&
   5247 		    st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
   5248 			ret = 1;
   5249 	}
   5250 	return (ret);
   5251 }
   5252 
   5253 /*
   5254  * run_legacy_command(proto, cmd, argv)
   5255  *
   5256  * We know the command exists, so attempt to execute it with all the
   5257  * arguments. This implements full legacy share support for those
   5258  * protocols that don't have plugin providers.
   5259  */
   5260 
   5261 static int
   5262 run_legacy_command(char *path, char *argv[])
   5263 {
   5264 	int ret;
   5265 
   5266 	ret = execv(path, argv);
   5267 	if (ret < 0) {
   5268 		switch (errno) {
   5269 		case EACCES:
   5270 			ret = SA_NO_PERMISSION;
   5271 			break;
   5272 		default:
   5273 			ret = SA_SYSTEM_ERR;
   5274 			break;
   5275 		}
   5276 	}
   5277 	return (ret);
   5278 }
   5279 
   5280 /*
   5281  * out_share(out, group, proto)
   5282  *
   5283  * Display the share information in the format that the "share"
   5284  * command has traditionally used.
   5285  */
   5286 
   5287 static void
   5288 out_share(FILE *out, sa_group_t group, char *proto)
   5289 {
   5290 	sa_share_t share;
   5291 	char resfmt[128];
   5292 	char *defprop;
   5293 
   5294 	/*
   5295 	 * The original share command defaulted to displaying NFS
   5296 	 * shares or allowed a protocol to be specified. We want to
   5297 	 * skip those shares that are not the specified protocol.
   5298 	 */
   5299 	if (proto != NULL && sa_get_optionset(group, proto) == NULL)
   5300 		return;
   5301 
   5302 	if (proto == NULL)
   5303 		proto = "nfs";
   5304 
   5305 	/*
   5306 	 * get the default property string.  NFS uses "rw" but
   5307 	 * everything else will use "".
   5308 	 */
   5309 	if (proto != NULL && strcmp(proto, "nfs") != 0)
   5310 		defprop = "\"\"";
   5311 	else
   5312 		defprop = "rw";
   5313 
   5314 	for (share = sa_get_share(group, NULL);
   5315 	    share != NULL;
   5316 	    share = sa_get_next_share(share)) {
   5317 		char *path;
   5318 		char *type;
   5319 		char *resource;
   5320 		char *description;
   5321 		char *groupname;
   5322 		char *sharedstate;
   5323 		int shared = 1;
   5324 		char *soptions;
   5325 		char shareopts[MAXNAMLEN];
   5326 
   5327 		sharedstate = sa_get_share_attr(share, "shared");
   5328 		path = sa_get_share_attr(share, "path");
   5329 		type = sa_get_share_attr(share, "type");
   5330 		resource = get_resource(share);
   5331 		groupname = sa_get_group_attr(group, "name");
   5332 
   5333 		if (groupname != NULL && strcmp(groupname, "default") == 0) {
   5334 			sa_free_attr_string(groupname);
   5335 			groupname = NULL;
   5336 		}
   5337 		description = sa_get_share_description(share);
   5338 
   5339 		/*
   5340 		 * Want the sharetab version if it exists, defaulting
   5341 		 * to NFS if no protocol specified.
   5342 		 */
   5343 		(void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", proto);
   5344 		soptions = sa_get_share_attr(share, shareopts);
   5345 
   5346 		if (sharedstate == NULL)
   5347 			shared = 0;
   5348 
   5349 		if (soptions == NULL)
   5350 			soptions = sa_proto_legacy_format(proto, share, 1);
   5351 
   5352 		if (shared) {
   5353 			/* only active shares go here */
   5354 			(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
   5355 			    resource != NULL ? resource : "-",
   5356 			    groupname != NULL ? "@" : "",
   5357 			    groupname != NULL ? groupname : "");
   5358 			(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
   5359 			    resfmt, (path != NULL) ? path : "",
   5360 			    (soptions != NULL && strlen(soptions) > 0) ?
   5361 			    soptions : defprop,
   5362 			    (description != NULL) ? description : "");
   5363 		}
   5364 
   5365 		if (path != NULL)
   5366 			sa_free_attr_string(path);
   5367 		if (type != NULL)
   5368 			sa_free_attr_string(type);
   5369 		if (resource != NULL)
   5370 			sa_free_attr_string(resource);
   5371 		if (groupname != NULL)
   5372 			sa_free_attr_string(groupname);
   5373 		if (description != NULL)
   5374 			sa_free_share_description(description);
   5375 		if (sharedstate != NULL)
   5376 			sa_free_attr_string(sharedstate);
   5377 		if (soptions != NULL)
   5378 			sa_format_free(soptions);
   5379 	}
   5380 }
   5381 
   5382 /*
   5383  * output_legacy_file(out, proto)
   5384  *
   5385  * Walk all of the groups for the specified protocol and call
   5386  * out_share() to format and write in the format displayed by the
   5387  * "share" command with no arguments.
   5388  */
   5389 
   5390 static void
   5391 output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
   5392 {
   5393 	sa_group_t group;
   5394 
   5395 	for (group = sa_get_group(handle, NULL);
   5396 	    group != NULL;
   5397 	    group = sa_get_next_group(group)) {
   5398 		char *zfs;
   5399 
   5400 		/*
   5401 		 * Go through all the groups and ZFS
   5402 		 * sub-groups. out_share() will format the shares in
   5403 		 * the group appropriately.
   5404 		 */
   5405 
   5406 		zfs = sa_get_group_attr(group, "zfs");
   5407 		if (zfs != NULL) {
   5408 			sa_group_t zgroup;
   5409 			sa_free_attr_string(zfs);
   5410 			for (zgroup = sa_get_sub_group(group);
   5411 			    zgroup != NULL;
   5412 			    zgroup = sa_get_next_group(zgroup)) {
   5413 
   5414 				/* got a group, so display it */
   5415 				out_share(out, zgroup, proto);
   5416 			}
   5417 		} else {
   5418 			out_share(out, group, proto);
   5419 		}
   5420 	}
   5421 }
   5422 
   5423 int
   5424 sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
   5425 {
   5426 	char *protocol = "nfs";
   5427 	char *options = NULL;
   5428 	char *description = NULL;
   5429 	char *groupname = NULL;
   5430 	char *sharepath = NULL;
   5431 	char *resource = NULL;
   5432 	char *groupstatus = NULL;
   5433 	int persist = SA_SHARE_TRANSIENT;
   5434 	int argsused = 0;
   5435 	int c;
   5436 	int ret = SA_OK;
   5437 	int zfs = 0;
   5438 	int true_legacy = 0;
   5439 	int curtype = SA_SHARE_TRANSIENT;
   5440 	char cmd[MAXPATHLEN];
   5441 	sa_group_t group = NULL;
   5442 	sa_resource_t rsrc = NULL;
   5443 	sa_share_t share;
   5444 	char dir[MAXPATHLEN];
   5445 	uint64_t features;
   5446 #ifdef lint
   5447 	flags = flags;
   5448 #endif
   5449 
   5450 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
   5451 		switch (c) {
   5452 		case 'd':
   5453 			description = optarg;
   5454 			argsused++;
   5455 			break;
   5456 		case 'F':
   5457 			protocol = optarg;
   5458 			if (!sa_valid_protocol(protocol)) {
   5459 				if (format_legacy_path(cmd, MAXPATHLEN,
   5460 				    protocol, "share") == 0 &&
   5461 				    check_legacy_cmd(cmd)) {
   5462 					true_legacy++;
   5463 				} else {
   5464 					(void) fprintf(stderr, gettext(
   5465 					    "Invalid protocol specified: "
   5466 					    "%s\n"), protocol);
   5467 					return (SA_INVALID_PROTOCOL);
   5468 				}
   5469 			}
   5470 			break;
   5471 		case 'o':
   5472 			options = optarg;
   5473 			argsused++;
   5474 			break;
   5475 		case 'p':
   5476 			persist = SA_SHARE_PERMANENT;
   5477 			argsused++;
   5478 			break;
   5479 		case 'h':
   5480 			/* optopt on valid arg isn't defined */
   5481 			optopt = c;
   5482 			/*FALLTHROUGH*/
   5483 		case '?':
   5484 		default:
   5485 			/*
   5486 			 * Since a bad option gets to here, sort it
   5487 			 * out and return a syntax error return value
   5488 			 * if necessary.
   5489 			 */
   5490 			switch (optopt) {
   5491 			default:
   5492 				ret = SA_LEGACY_ERR;
   5493 				break;
   5494 			case 'h':
   5495 			case '?':
   5496 				break;
   5497 			}
   5498 			(void) fprintf(stderr, gettext("usage: %s\n"),
   5499 			    sa_get_usage(USAGE_SHARE));
   5500 			return (ret);
   5501 		}
   5502 	}
   5503 
   5504 	/* Have the info so construct what is needed */
   5505 	if (!argsused && optind == argc) {
   5506 		/* display current info in share format */
   5507 		(void) output_legacy_file(stdout, protocol, handle);
   5508 		return (ret);
   5509 	}
   5510 
   5511 	/* We are modifying the configuration */
   5512 	if (optind == argc) {
   5513 		(void) fprintf(stderr, gettext("usage: %s\n"),
   5514 		    sa_get_usage(USAGE_SHARE));
   5515 		return (SA_LEGACY_ERR);
   5516 	}
   5517 	if (true_legacy) {
   5518 		/* If still using legacy share/unshare, exec it */
   5519 		ret = run_legacy_command(cmd, argv);
   5520 		return (ret);
   5521 	}
   5522 
   5523 	sharepath = argv[optind++];
   5524 	if (optind < argc) {
   5525 		resource = argv[optind];
   5526 		groupname = strchr(resource, '@');
   5527 		if (groupname != NULL)
   5528 			*groupname++ = '\0';
   5529 	}
   5530 	if (realpath(sharepath, dir) == NULL)
   5531 		ret = SA_BAD_PATH;
   5532 	else
   5533 		sharepath = dir;
   5534 	if (ret == SA_OK)
   5535 		share = sa_find_share(handle, sharepath);
   5536 	else
   5537 		share = NULL;
   5538 
   5539 	features = sa_proto_get_featureset(protocol);
   5540 
   5541 	if (groupname != NULL) {
   5542 		ret = SA_NOT_ALLOWED;
   5543 	} else if (ret == SA_OK) {
   5544 		char *legacygroup;
   5545 		/*
   5546 		 * The legacy group is always present and zfs groups
   5547 		 * come and go.  zfs shares may be in sub-groups and
   5548 		 * the zfs share will already be in that group so it
   5549 		 * isn't an error. If the protocol is "smb", the group
   5550 		 * "smb" is used when "default" would otherwise be
   5551 		 * used.  "default" is NFS only and "smb" is SMB only.
   5552 		 */
   5553 		if (strcmp(protocol, "smb") == 0)
   5554 			legacygroup = "smb";
   5555 		else
   5556 			legacygroup = "default";
   5557 
   5558 		/*
   5559 		 * If the share exists (not NULL), then make sure it
   5560 		 * is one we want to handle by getting the parent
   5561 		 * group.
   5562 		 */
   5563 		if (share != NULL) {
   5564 			group = sa_get_parent_group(share);
   5565 		} else {
   5566 			group = sa_get_group(handle, legacygroup);
   5567 			if (group == NULL && strcmp(legacygroup, "smb") == 0) {
   5568 				/*
   5569 				 * This group may not exist, so create
   5570 				 * as necessary. It only contains the
   5571 				 * "smb" protocol.
   5572 				 */
   5573 				group = sa_create_group(handle, legacygroup,
   5574 				    &ret);
   5575 				if (group != NULL)
   5576 					(void) sa_create_optionset(group,
   5577 					    protocol);
   5578 			}
   5579 		}
   5580 
   5581 		if (group == NULL) {
   5582 			ret = SA_SYSTEM_ERR;
   5583 			goto err;
   5584 		}
   5585 
   5586 		groupstatus = group_status(group);
   5587 		if (share == NULL) {
   5588 			share = sa_add_share(group, sharepath,
   5589 			    persist, &ret);
   5590 			if (share == NULL &&
   5591 			    ret == SA_DUPLICATE_NAME) {
   5592 				/*
   5593 				 * Could be a ZFS path being started
   5594 				 */
   5595 				if (sa_zfs_is_shared(handle,
   5596 				    sharepath)) {
   5597 					ret = SA_OK;
   5598 					group = sa_get_group(handle,
   5599 					    "zfs");
   5600 					if (group == NULL) {
   5601 						/*
   5602 						 * This shouldn't
   5603 						 * happen.
   5604 						 */
   5605 						ret = SA_CONFIG_ERR;
   5606 					} else {
   5607 						share = sa_add_share(
   5608 						    group, sharepath,
   5609 						    persist, &ret);
   5610 					}
   5611 				}
   5612 			}
   5613 		} else {
   5614 			char *type;
   5615 			/*
   5616 			 * May want to change persist state, but the
   5617 			 * important thing is to change options. We
   5618 			 * need to change them regardless of the
   5619 			 * source.
   5620 			 */
   5621 
   5622 			if (sa_zfs_is_shared(handle, sharepath)) {
   5623 				zfs = 1;
   5624 			}
   5625 			remove_all_options(share, protocol);
   5626 			type = sa_get_share_attr(share, "type");
   5627 			if (type != NULL &&
   5628 			    strcmp(type, "transient") != 0) {
   5629 				curtype = SA_SHARE_PERMANENT;
   5630 			}
   5631 			if (type != NULL)
   5632 				sa_free_attr_string(type);
   5633 			if (curtype != persist) {
   5634 				(void) sa_set_share_attr(share, "type",
   5635 				    persist == SA_SHARE_PERMANENT ?
   5636 				    "persist" : "transient");
   5637 			}
   5638 		}
   5639 
   5640 		/*
   5641 		 * If there is a resource name, we may
   5642 		 * actually care about it if this is share for
   5643 		 * a protocol that uses resource level sharing
   5644 		 * (SMB). We need to find the resource and, if
   5645 		 * it exists, make sure it belongs to the
   5646 		 * current share. If it doesn't exist, attempt
   5647 		 * to create it.
   5648 		 */
   5649 
   5650 		if (ret == SA_OK && resource != NULL) {
   5651 			rsrc = sa_find_resource(handle, resource);
   5652 			if (rsrc != NULL) {
   5653 				if (share != sa_get_resource_parent(rsrc))
   5654 					ret = SA_DUPLICATE_NAME;
   5655 				} else {
   5656 					rsrc = sa_add_resource(share, resource,
   5657 					    persist, &ret);
   5658 				}
   5659 				if (features & SA_FEATURE_RESOURCE)
   5660 					share = rsrc;
   5661 			}
   5662 
   5663 			/* Have a group to hold this share path */
   5664 			if (ret == SA_OK && options != NULL &&
   5665 			    strlen(options) > 0) {
   5666 				ret = sa_parse_legacy_options(share,
   5667 				    options,
   5668 				    protocol);
   5669 			}
   5670 			if (!zfs) {
   5671 				/*
   5672 				 * ZFS shares never have a description
   5673 				 * and we can't store the values so
   5674 				 * don't try.
   5675 				 */
   5676 				if (ret == SA_OK && description != NULL)
   5677 					ret = sa_set_share_description(share,
   5678 					    description);
   5679 			}
   5680 			if (ret == SA_OK &&
   5681 			    strcmp(groupstatus, "enabled") == 0) {
   5682 				if (rsrc != share)
   5683 					ret = sa_enable_share(share, protocol);
   5684 				else
   5685 					ret = sa_enable_resource(rsrc,
   5686 					    protocol);
   5687 				if (ret == SA_OK &&
   5688 				    persist == SA_SHARE_PERMANENT) {
   5689 					(void) sa_update_legacy(share,
   5690 					    protocol);
   5691 				}
   5692 				if (ret == SA_OK)
   5693 					ret = sa_update_config(handle);
   5694 			}
   5695 	}
   5696 err:
   5697 	if (ret != SA_OK) {
   5698 		(void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
   5699 		    sharepath, sa_errorstr(ret));
   5700 		ret = SA_LEGACY_ERR;
   5701 	}
   5702 	return (ret);
   5703 }
   5704 
   5705 /*
   5706  * sa_legacy_unshare(flags, argc, argv)
   5707  *
   5708  * Implements the original unshare command.
   5709  */
   5710 int
   5711 sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
   5712 {
   5713 	char *protocol = "nfs"; /* for now */
   5714 	char *options = NULL;
   5715 	char *sharepath = NULL;
   5716 	int persist = SA_SHARE_TRANSIENT;
   5717 	int argsused = 0;
   5718 	int c;
   5719 	int ret = SA_OK;
   5720 	int true_legacy = 0;
   5721 	uint64_t features = 0;
   5722 	sa_resource_t resource = NULL;
   5723 	char cmd[MAXPATHLEN];
   5724 #ifdef lint
   5725 	flags = flags;
   5726 	options = options;
   5727 #endif
   5728 
   5729 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
   5730 		switch (c) {
   5731 		case 'F':
   5732 			protocol = optarg;
   5733 			if (!sa_valid_protocol(protocol)) {
   5734 				if (format_legacy_path(cmd, MAXPATHLEN,
   5735 				    protocol, "unshare") == 0 &&
   5736 				    check_legacy_cmd(cmd)) {
   5737 					true_legacy++;
   5738 				} else {
   5739 					(void) printf(gettext(
   5740 					    "Invalid file system name\n"));
   5741 					return (SA_INVALID_PROTOCOL);
   5742 				}
   5743 			}
   5744 			break;
   5745 		case 'o':
   5746 			options = optarg;
   5747 			argsused++;
   5748 			break;
   5749 		case 'p':
   5750 			persist = SA_SHARE_PERMANENT;
   5751 			argsused++;
   5752 			break;
   5753 		case 'h':
   5754 			/* optopt on valid arg isn't defined */
   5755 			optopt = c;
   5756 			/*FALLTHROUGH*/
   5757 		case '?':
   5758 		default:
   5759 			/*
   5760 			 * Since a bad option gets to here, sort it
   5761 			 * out and return a syntax error return value
   5762 			 * if necessary.
   5763 			 */
   5764 			switch (optopt) {
   5765 			default:
   5766 				ret = SA_LEGACY_ERR;
   5767 				break;
   5768 			case 'h':
   5769 			case '?':
   5770 				break;
   5771 			}
   5772 			(void) printf(gettext("usage: %s\n"),
   5773 			    sa_get_usage(USAGE_UNSHARE));
   5774 			return (ret);
   5775 		}
   5776 	}
   5777 
   5778 	/* Have the info so construct what is needed */
   5779 	if (optind == argc || (optind + 1) < argc || options != NULL) {
   5780 		ret = SA_SYNTAX_ERR;
   5781 	} else {
   5782 		sa_share_t share;
   5783 		char dir[MAXPATHLEN];
   5784 		if (true_legacy) {
   5785 			/* if still using legacy share/unshare, exec it */
   5786 			ret = run_legacy_command(cmd, argv);
   5787 			return (ret);
   5788 		}
   5789 		/*
   5790 		 * Find the path in the internal configuration. If it
   5791 		 * isn't found, attempt to resolve the path via
   5792 		 * realpath() and try again.
   5793 		 */
   5794 		sharepath = argv[optind++];
   5795 		share = sa_find_share(handle, sharepath);
   5796 		if (share == NULL) {
   5797 			if (realpath(sharepath, dir) == NULL) {
   5798 				ret = SA_NO_SUCH_PATH;
   5799 			} else {
   5800 				share = sa_find_share(handle, dir);
   5801 			}
   5802 		}
   5803 		if (share == NULL) {
   5804 			/* Could be a resource name so check that next */
   5805 			features = sa_proto_get_featureset(protocol);
   5806 			resource = sa_find_resource(handle, sharepath);
   5807 			if (resource != NULL) {
   5808 				share = sa_get_resource_parent(resource);
   5809 				if (features & SA_FEATURE_RESOURCE)
   5810 					(void) sa_disable_resource(resource,
   5811 					    protocol);
   5812 				if (persist == SA_SHARE_PERMANENT) {
   5813 					ret = sa_remove_resource(resource);
   5814 					if (ret == SA_OK)
   5815 						ret = sa_update_config(handle);
   5816 				}
   5817 				/*
   5818 				 * If we still have a resource on the
   5819 				 * share, we don't disable the share
   5820 				 * itself. IF there aren't anymore, we
   5821 				 * need to remove the share. The
   5822 				 * removal will be done in the next
   5823 				 * section if appropriate.
   5824 				 */
   5825 				resource = sa_get_share_resource(share, NULL);
   5826 				if (resource != NULL)
   5827 					share = NULL;
   5828 			} else if (ret == SA_OK) {
   5829 				/* Didn't find path and no  resource */
   5830 				ret = SA_BAD_PATH;
   5831 			}
   5832 		}
   5833 		if (share != NULL && resource == NULL) {
   5834 			ret = sa_disable_share(share, protocol);
   5835 			/*
   5836 			 * Errors are ok and removal should still occur. The
   5837 			 * legacy unshare is more forgiving of errors than the
   5838 			 * remove-share subcommand which may need the force
   5839 			 * flag set for some error conditions. That is, the
   5840 			 * "unshare" command will always unshare if it can
   5841 			 * while "remove-share" might require the force option.
   5842 			 */
   5843 			if (persist == SA_SHARE_PERMANENT) {
   5844 				ret = sa_remove_share(share);
   5845 				if (ret == SA_OK)
   5846 					ret = sa_update_config(handle);
   5847 			}
   5848 		} else if (ret == SA_OK && share == NULL && resource == NULL) {
   5849 			/*
   5850 			 * If both share and resource are NULL, then
   5851 			 * share not found. If one or the other was
   5852 			 * found or there was an earlier error, we
   5853 			 * assume it was handled earlier.
   5854 			 */
   5855 			ret = SA_NOT_SHARED;
   5856 		}
   5857 	}
   5858 	switch (ret) {
   5859 	default:
   5860 		(void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
   5861 		ret = SA_LEGACY_ERR;
   5862 		break;
   5863 	case SA_SYNTAX_ERR:
   5864 		(void) printf(gettext("usage: %s\n"),
   5865 		    sa_get_usage(USAGE_UNSHARE));
   5866 		break;
   5867 	case SA_OK:
   5868 		break;
   5869 	}
   5870 	return (ret);
   5871 }
   5872 
   5873 /*
   5874  * Common commands that implement the sub-commands used by all
   5875  * protocols. The entries are found via the lookup command
   5876  */
   5877 
   5878 static sa_command_t commands[] = {
   5879 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
   5880 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
   5881 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
   5882 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
   5883 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
   5884 	{"list", 0, sa_list, USAGE_LIST},
   5885 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
   5886 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
   5887 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
   5888 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
   5889 	{"show", 0, sa_show, USAGE_SHOW},
   5890 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
   5891 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
   5892 	    SVC_SET|SVC_ACTION},
   5893 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
   5894 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
   5895 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
   5896 	{NULL, 0, NULL, NULL}
   5897 };
   5898 
   5899 static char *
   5900 sa_get_usage(sa_usage_t index)
   5901 {
   5902 	char *ret = NULL;
   5903 	switch (index) {
   5904 	case USAGE_ADD_SHARE:
   5905 		ret = gettext("add-share [-nth] [-r resource-name] "
   5906 		    "[-d \"description text\"] -s sharepath group");
   5907 		break;
   5908 	case USAGE_CREATE:
   5909 		ret = gettext(
   5910 		    "create [-nvh] [-P proto [-p property=value]] group");
   5911 		break;
   5912 	case USAGE_DELETE:
   5913 		ret = gettext("delete [-nvh] [-P proto] [-f] group");
   5914 		break;
   5915 	case USAGE_DISABLE:
   5916 		ret = gettext("disable [-nvh] {-a | group ...}");
   5917 		break;
   5918 	case USAGE_ENABLE:
   5919 		ret = gettext("enable [-nvh] {-a | group ...}");
   5920 		break;
   5921 	case USAGE_LIST:
   5922 		ret = gettext("list [-vh] [-P proto]");
   5923 		break;
   5924 	case USAGE_MOVE_SHARE:
   5925 		ret = gettext(
   5926 		    "move-share [-nvh] -s sharepath destination-group");
   5927 		break;
   5928 	case USAGE_REMOVE_SHARE:
   5929 		ret = gettext(
   5930 		    "remove-share [-fnvh] {-s sharepath | -r resource} "
   5931 		    "group");
   5932 		break;
   5933 	case USAGE_SET:
   5934 		ret = gettext("set [-nvh] -P proto [-S optspace] "
   5935 		    "[-p property=value]* [-s sharepath] [-r resource]] "
   5936 		    "group");
   5937 		break;
   5938 	case USAGE_SET_SECURITY:
   5939 		ret = gettext("set-security [-nvh] -P proto -S security-type "
   5940 		    "[-p property=value]* group");
   5941 		break;
   5942 	case USAGE_SET_SHARE:
   5943 		ret = gettext("set-share [-nh] [-r resource] "
   5944 		    "[-d \"description text\"] -s sharepath group");
   5945 		break;
   5946 	case USAGE_SHOW:
   5947 		ret = gettext("show [-pvxh] [-P proto] [group ...]");
   5948 		break;
   5949 	case USAGE_SHARE:
   5950 		ret = gettext("share [-F fstype] [-p] [-o optionlist]"
   5951 		    "[-d description] [pathname [resourcename]]");
   5952 		break;
   5953 	case USAGE_START:
   5954 		ret = gettext("start [-vh] [-P proto] {-a | group ...}");
   5955 		break;
   5956 	case USAGE_STOP:
   5957 		ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
   5958 		break;
   5959 	case USAGE_UNSET:
   5960 		ret = gettext("unset [-nvh] -P proto [-S optspace] "
   5961 		    "[-p property]* group");
   5962 		break;
   5963 	case USAGE_UNSET_SECURITY:
   5964 		ret = gettext("unset-security [-nvh] -P proto "
   5965 		    "-S security-type [-p property]* group");
   5966 		break;
   5967 	case USAGE_UNSHARE:
   5968 		ret = gettext(
   5969 		    "unshare [-F fstype] [-p] [-o optionlist] sharepath");
   5970 		break;
   5971 	}
   5972 	return (ret);
   5973 }
   5974 
   5975 /*
   5976  * sa_lookup(cmd, proto)
   5977  *
   5978  * Lookup the sub-command. proto isn't currently used, but it may
   5979  * eventually provide a way to provide protocol specific sub-commands.
   5980  */
   5981 sa_command_t *
   5982 sa_lookup(char *cmd, char *proto)
   5983 {
   5984 	int i;
   5985 	size_t len;
   5986 #ifdef lint
   5987 	proto = proto;
   5988 #endif
   5989 
   5990 	len = strlen(cmd);
   5991 	for (i = 0; commands[i].cmdname != NULL; i++) {
   5992 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
   5993 			return (&commands[i]);
   5994 	}
   5995 	return (NULL);
   5996 }
   5997 
   5998 void
   5999 sub_command_help(char *proto)
   6000 {
   6001 	int i;
   6002 #ifdef lint
   6003 	proto = proto;
   6004 #endif
   6005 
   6006 	(void) printf(gettext("\tsub-commands:\n"));
   6007 	for (i = 0; commands[i].cmdname != NULL; i++) {
   6008 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
   6009 			(void) printf("\t%s\n",
   6010 			    sa_get_usage((sa_usage_t)commands[i].cmdidx));
   6011 	}
   6012 }
   6013