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