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 2009 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 			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 		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 		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