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