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