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