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