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