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