xref: /titanic_50/usr/src/cmd/dfs.cmds/sharemgr/commands.c (revision 5b7f77ad52bf657ba49d64d16f527e958d0fb820)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <getopt.h>
38 #include <utmpx.h>
39 #include <pwd.h>
40 #include <auth_attr.h>
41 #include <secdb.h>
42 #include <sys/param.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 
46 #include <libshare.h>
47 #include "sharemgr.h"
48 #include <libscf.h>
49 #include <libxml/tree.h>
50 #include <libintl.h>
51 #include <assert.h>
52 #include <iconv.h>
53 #include <langinfo.h>
54 #include <dirent.h>
55 
56 static char *sa_get_usage(sa_usage_t);
57 
58 /*
59  * Implementation of the common sub-commands supported by sharemgr.
60  * A number of helper functions are also included.
61  */
62 
63 /*
64  * has_protocol(group, proto)
65  *	If the group has an optionset with the specified protocol,
66  *	return true (1) otherwise false (0).
67  */
68 static int
69 has_protocol(sa_group_t group, char *protocol)
70 {
71 	sa_optionset_t optionset;
72 	int result = 0;
73 
74 	optionset = sa_get_optionset(group, protocol);
75 	if (optionset != NULL) {
76 		result++;
77 	}
78 	return (result);
79 }
80 
81 /*
82  * validresource(name)
83  *
84  * Check that name only has valid characters in it. The current valid
85  * set are the printable characters but not including:
86  *	" / \ [ ] : | < > + ; , ? * = \t
87  * Note that space is included and there is a maximum length.
88  */
89 static int
90 validresource(const char *name)
91 {
92 	const char *cp;
93 	size_t len;
94 
95 	if (name == NULL)
96 		return (B_FALSE);
97 
98 	len = strlen(name);
99 	if (len == 0 || len > SA_MAX_RESOURCE_NAME)
100 		return (B_FALSE);
101 
102 	if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
103 		return (B_FALSE);
104 	}
105 
106 	for (cp = name; *cp != '\0'; cp++)
107 		if (iscntrl(*cp))
108 			return (B_FALSE);
109 
110 	return (B_TRUE);
111 }
112 
113 /*
114  * conv_to_utf8(input)
115  *
116  * Convert the input string to utf8 from the current locale.  If the
117  * conversion fails, use the current locale, it is likely close
118  * enough. For example, the "C" locale is a subset of utf-8. The
119  * return value may be a new string or the original input string.
120  */
121 
122 static char *
123 conv_to_utf8(char *input)
124 {
125 	iconv_t cd;
126 	char *inval = input;
127 	char *output = input;
128 	char *outleft;
129 	char *curlocale;
130 	size_t bytesleft;
131 	size_t size;
132 	size_t osize;
133 	static int warned = 0;
134 
135 	curlocale = nl_langinfo(CODESET);
136 	if (curlocale == NULL)
137 		curlocale = "C";
138 	cd = iconv_open("UTF-8", curlocale);
139 	if (cd != NULL && cd != (iconv_t)-1) {
140 		size = strlen(input);
141 		/* Assume worst case of characters expanding to 4 bytes. */
142 		bytesleft = size * 4;
143 		output = calloc(bytesleft, 1);
144 		if (output != NULL) {
145 			outleft = output;
146 			/* inval can be modified on return */
147 			osize = iconv(cd, (const char **)&inval, &size,
148 			    &outleft, &bytesleft);
149 			if (osize == (size_t)-1 || size != 0) {
150 				free(output);
151 				output = input;
152 			}
153 		} else {
154 			/* Need to return something. */
155 			output = input;
156 		}
157 		(void) iconv_close(cd);
158 	} else {
159 		if (!warned)
160 			(void) fprintf(stderr,
161 			    gettext("Cannot convert to UTF-8 from %s\n"),
162 			    curlocale ? curlocale : gettext("unknown"));
163 		warned = 1;
164 	}
165 	return (output);
166 }
167 
168 /*
169  * conv_from(input)
170  *
171  * Convert the input string from utf8 to current locale.  If the
172  * conversion isn't supported, just use as is. The return value may be
173  * a new string or the original input string.
174  */
175 
176 static char *
177 conv_from_utf8(char *input)
178 {
179 	iconv_t cd;
180 	char *output = input;
181 	char *inval = input;
182 	char *outleft;
183 	char *curlocale;
184 	size_t bytesleft;
185 	size_t size;
186 	size_t osize;
187 	static int warned = 0;
188 
189 	curlocale = nl_langinfo(CODESET);
190 	if (curlocale == NULL)
191 		curlocale = "C";
192 	cd = iconv_open(curlocale, "UTF-8");
193 	if (cd != NULL && cd != (iconv_t)-1) {
194 		size = strlen(input);
195 		/* Assume worst case of characters expanding to 4 bytes. */
196 		bytesleft = size * 4;
197 		output = calloc(bytesleft, 1);
198 		if (output != NULL) {
199 			outleft = output;
200 			osize = iconv(cd, (const char **)&inval, &size,
201 			    &outleft, &bytesleft);
202 			if (osize == (size_t)-1 || size != 0)
203 				output = input;
204 		} else {
205 			/* Need to return something. */
206 			output = input;
207 		}
208 		(void) iconv_close(cd);
209 	} else {
210 		if (!warned)
211 			(void) fprintf(stderr,
212 			    gettext("Cannot convert to %s from UTF-8\n"),
213 			    curlocale ? curlocale : gettext("unknown"));
214 		warned = 1;
215 	}
216 	return (output);
217 }
218 
219 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 = NULL;
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 		else
3113 			ret = SA_NO_SUCH_RESOURCE;
3114 	}
3115 	if (share != NULL) {
3116 		sharegroup = sa_get_parent_group(share);
3117 		if (group != NULL && group != sharegroup) {
3118 			(void) printf(gettext("Group \"%s\" does not contain "
3119 			    "share %s\n"),
3120 			    argv[optind], sharepath);
3121 			ret = SA_BAD_PATH;
3122 		} else {
3123 			int delgroupname = 0;
3124 			if (groupname == NULL) {
3125 				groupname = sa_get_group_attr(sharegroup,
3126 				    "name");
3127 				delgroupname = 1;
3128 			}
3129 			if (groupname != NULL) {
3130 				auth = check_authorizations(groupname, flags);
3131 				if (delgroupname) {
3132 					sa_free_attr_string(groupname);
3133 					groupname = NULL;
3134 				}
3135 			} else {
3136 				ret = SA_NO_MEMORY;
3137 			}
3138 			if (rsrcname != NULL) {
3139 				resource = sa_find_resource(handle, rsrc);
3140 				if (!dryrun) {
3141 					if (newname != NULL &&
3142 					    resource != NULL)
3143 						ret = sa_rename_resource(
3144 						    resource, newrsrc);
3145 					else if (newname != NULL)
3146 						ret = SA_NO_SUCH_RESOURCE;
3147 					if (newname != NULL &&
3148 					    newname != newrsrc)
3149 						sa_free_attr_string(newrsrc);
3150 				}
3151 				if (rsrc != rsrcname)
3152 					sa_free_attr_string(rsrc);
3153 			}
3154 
3155 			/*
3156 			 * If the user has set a description, it will be
3157 			 * on the resource if -r was used otherwise it
3158 			 * must be on the share.
3159 			 */
3160 			if (ret == SA_OK && description != NULL) {
3161 				desc = conv_to_utf8(description);
3162 				if (resource != NULL)
3163 					ret = sa_set_resource_description(
3164 					    resource, desc);
3165 				else
3166 					ret = sa_set_share_description(share,
3167 					    desc);
3168 				if (desc != description)
3169 					sa_free_share_description(desc);
3170 			}
3171 		}
3172 		if (!dryrun && ret == SA_OK) {
3173 			if (resource != NULL)
3174 				(void) sa_enable_resource(resource, NULL);
3175 			ret = sa_update_config(handle);
3176 		}
3177 		switch (ret) {
3178 		case SA_DUPLICATE_NAME:
3179 			(void) printf(gettext("Resource name in use: %s\n"),
3180 			    rsrcname);
3181 			break;
3182 		default:
3183 			(void) printf(gettext("Could not set: %s\n"),
3184 			    sa_errorstr(ret));
3185 			break;
3186 		case SA_OK:
3187 			if (dryrun && !auth && verbose) {
3188 				(void) printf(gettext(
3189 				    "Command would fail: %s\n"),
3190 				    sa_errorstr(SA_NO_PERMISSION));
3191 			}
3192 			break;
3193 		}
3194 	} else {
3195 		switch (ret) {
3196 		case SA_NO_SUCH_RESOURCE:
3197 			(void) printf(gettext("Resource \"%s\" not found\n"),
3198 			    rsrcname);
3199 			break;
3200 		default:
3201 			if (sharepath != NULL) {
3202 				(void) printf(
3203 				    gettext("Share path \"%s\" not found\n"),
3204 				    sharepath);
3205 				ret = SA_NO_SUCH_PATH;
3206 			} else {
3207 				(void) printf(gettext("Set failed: %s\n"),
3208 				    sa_errorstr(ret));
3209 			}
3210 		}
3211 	}
3212 
3213 	return (ret);
3214 }
3215 
3216 /*
3217  * add_security(group, sectype, optlist, proto, *err)
3218  *
3219  * Helper function to add a security option (named optionset) to the
3220  * group.
3221  */
3222 
3223 static int
3224 add_security(sa_group_t group, char *sectype,
3225     struct options *optlist, char *proto, int *err)
3226 {
3227 	sa_security_t security;
3228 	int ret = SA_OK;
3229 	int result = 0;
3230 
3231 	sectype = sa_proto_space_alias(proto, sectype);
3232 	security = sa_get_security(group, sectype, proto);
3233 	if (security == NULL)
3234 		security = sa_create_security(group, sectype, proto);
3235 
3236 	if (sectype != NULL)
3237 		sa_free_attr_string(sectype);
3238 
3239 	if (security == NULL)
3240 		return (ret);
3241 
3242 	while (optlist != NULL) {
3243 		sa_property_t prop;
3244 		prop = sa_get_property(security, optlist->optname);
3245 		if (prop == NULL) {
3246 			/*
3247 			 * Add the property, but only if it is
3248 			 * a non-NULL or non-zero length value
3249 			 */
3250 			if (optlist->optvalue != NULL) {
3251 				prop = sa_create_property(optlist->optname,
3252 				    optlist->optvalue);
3253 				if (prop != NULL) {
3254 					ret = sa_valid_property(security,
3255 					    proto, prop);
3256 					if (ret != SA_OK) {
3257 						(void) sa_remove_property(prop);
3258 						(void) printf(gettext(
3259 						    "Could not add "
3260 						    "property %s: %s\n"),
3261 						    optlist->optname,
3262 						    sa_errorstr(ret));
3263 					}
3264 					if (ret == SA_OK) {
3265 						ret = sa_add_property(security,
3266 						    prop);
3267 						if (ret != SA_OK) {
3268 							(void) printf(gettext(
3269 							    "Could not add "
3270 							    "property (%s=%s):"
3271 							    " %s\n"),
3272 							    optlist->optname,
3273 							    optlist->optvalue,
3274 							    sa_errorstr(ret));
3275 						} else {
3276 							result = 1;
3277 						}
3278 					}
3279 				}
3280 			}
3281 		} else {
3282 			ret = sa_update_property(prop, optlist->optvalue);
3283 			result = 1; /* should check if really changed */
3284 		}
3285 		optlist = optlist->next;
3286 	}
3287 	/*
3288 	 * When done, properties may have all been removed but
3289 	 * we need to keep the security type itself until
3290 	 * explicitly removed.
3291 	 */
3292 	if (result)
3293 		ret = sa_commit_properties(security, 0);
3294 	*err = ret;
3295 	return (result);
3296 }
3297 
3298 /*
3299  * zfscheck(group, share)
3300  *
3301  * For the special case where a share was provided, make sure it is a
3302  * compatible path for a ZFS property change.  The only path
3303  * acceptable is the path that defines the zfs sub-group (dataset with
3304  * the sharenfs property set) and not one of the paths that inherited
3305  * the NFS properties. Returns SA_OK if it is usable and
3306  * SA_NOT_ALLOWED if it isn't.
3307  *
3308  * If group is not a ZFS group/subgroup, we assume OK since the check
3309  * on return will catch errors for those cases.  What we are looking
3310  * for here is that the group is ZFS and the share is not the defining
3311  * share.  All else is SA_OK.
3312  */
3313 
3314 static int
3315 zfscheck(sa_group_t group, sa_share_t share)
3316 {
3317 	int ret = SA_OK;
3318 	char *attr;
3319 
3320 	if (sa_group_is_zfs(group)) {
3321 		/*
3322 		 * The group is a ZFS group.  Does the share represent
3323 		 * the dataset that defined the group? It is only OK
3324 		 * if the attribute "subgroup" exists on the share and
3325 		 * has a value of "true".
3326 		 */
3327 
3328 		ret = SA_NOT_ALLOWED;
3329 		attr = sa_get_share_attr(share, "subgroup");
3330 		if (attr != NULL) {
3331 			if (strcmp(attr, "true") == 0)
3332 				ret = SA_OK;
3333 			sa_free_attr_string(attr);
3334 		}
3335 	}
3336 	return (ret);
3337 }
3338 
3339 /*
3340  * basic_set(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
3341  *
3342  * This function implements "set" when a name space (-S) is not
3343  * specified. It is a basic set. Options and other CLI parsing has
3344  * already been done.
3345  *
3346  * "rsrcname" is a "resource name". If it is non-NULL, it must match
3347  * the sharepath if present or group if present, otherwise it is used
3348  * to set options.
3349  *
3350  * Resource names may take options if the protocol supports it. If the
3351  * protocol doesn't support resource level options, rsrcname is just
3352  * an alias for the share.
3353  */
3354 
3355 static int
3356 basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
3357     char *protocol, char *sharepath, char *rsrcname, int dryrun)
3358 {
3359 	sa_group_t group;
3360 	int ret = SA_OK;
3361 	int change = 0;
3362 	struct list *worklist = NULL;
3363 
3364 	group = sa_get_group(handle, groupname);
3365 	if (group != NULL) {
3366 		sa_share_t share = NULL;
3367 		sa_resource_t resource = NULL;
3368 
3369 		/*
3370 		 * If there is a sharepath, make sure it belongs to
3371 		 * the group.
3372 		 */
3373 		if (sharepath != NULL) {
3374 			share = sa_get_share(group, sharepath);
3375 			if (share == NULL) {
3376 				(void) printf(gettext(
3377 				    "Share does not exist in group %s\n"),
3378 				    groupname, sharepath);
3379 				ret = SA_NO_SUCH_PATH;
3380 			} else {
3381 				/* if ZFS and OK, then only group */
3382 				ret = zfscheck(group, share);
3383 				if (ret == SA_OK &&
3384 				    sa_group_is_zfs(group))
3385 					share = NULL;
3386 				if (ret == SA_NOT_ALLOWED)
3387 					(void) printf(gettext(
3388 					    "Properties on ZFS group shares "
3389 					    "not supported: %s\n"), sharepath);
3390 			}
3391 		}
3392 
3393 		/*
3394 		 * If a resource name exists, make sure it belongs to
3395 		 * the share if present else it belongs to the
3396 		 * group. Also check the protocol to see if it
3397 		 * supports resource level properties or not. If not,
3398 		 * use share only.
3399 		 */
3400 		if (rsrcname != NULL) {
3401 			if (share != NULL) {
3402 				resource = sa_get_share_resource(share,
3403 				    rsrcname);
3404 				if (resource == NULL)
3405 					ret = SA_NO_SUCH_RESOURCE;
3406 			} else {
3407 				resource = sa_get_resource(group, rsrcname);
3408 				if (resource != NULL)
3409 					share = sa_get_resource_parent(
3410 					    resource);
3411 				else
3412 					ret = SA_NO_SUCH_RESOURCE;
3413 			}
3414 			if (ret == SA_OK && resource != NULL) {
3415 				uint64_t features;
3416 				/*
3417 				 * Check to see if the resource can take
3418 				 * properties. If so, stick the resource into
3419 				 * "share" so it will all just work.
3420 				 */
3421 				features = sa_proto_get_featureset(protocol);
3422 				if (features & SA_FEATURE_RESOURCE)
3423 					share = (sa_share_t)resource;
3424 			}
3425 		}
3426 
3427 		if (ret == SA_OK) {
3428 			/* group must exist */
3429 			ret = valid_options(optlist, protocol,
3430 			    share == NULL ? group : share, NULL);
3431 			if (ret == SA_OK && !dryrun) {
3432 				if (share != NULL)
3433 					change |= add_optionset(share, optlist,
3434 					    protocol, &ret);
3435 				else
3436 					change |= add_optionset(group, optlist,
3437 					    protocol, &ret);
3438 				if (ret == SA_OK && change)
3439 					worklist = add_list(worklist, group,
3440 					    share, protocol);
3441 			}
3442 		}
3443 		free_opt(optlist);
3444 	} else {
3445 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
3446 		ret = SA_NO_SUCH_GROUP;
3447 	}
3448 	/*
3449 	 * we have a group and potentially legal additions
3450 	 */
3451 
3452 	/*
3453 	 * Commit to configuration if not a dryrunp and properties
3454 	 * have changed.
3455 	 */
3456 	if (!dryrun && ret == SA_OK && change && worklist != NULL)
3457 		/* properties changed, so update all shares */
3458 		(void) enable_all_groups(handle, worklist, 0, 0, protocol,
3459 		    B_TRUE);
3460 
3461 	if (worklist != NULL)
3462 		free_list(worklist);
3463 	return (ret);
3464 }
3465 
3466 /*
3467  * space_set(groupname, optlist, protocol, sharepath, dryrun)
3468  *
3469  * This function implements "set" when a name space (-S) is
3470  * specified. It is a namespace set. Options and other CLI parsing has
3471  * already been done.
3472  */
3473 
3474 static int
3475 space_set(sa_handle_t handle, char *groupname, struct options *optlist,
3476     char *protocol, char *sharepath, int dryrun, char *sectype)
3477 {
3478 	sa_group_t group;
3479 	int ret = SA_OK;
3480 	int change = 0;
3481 	struct list *worklist = NULL;
3482 
3483 	/*
3484 	 * make sure protcol and sectype are valid
3485 	 */
3486 
3487 	if (sa_proto_valid_space(protocol, sectype) == 0) {
3488 		(void) printf(gettext("Option space \"%s\" not valid "
3489 		    "for protocol.\n"), sectype);
3490 		return (SA_INVALID_SECURITY);
3491 	}
3492 
3493 	group = sa_get_group(handle, groupname);
3494 	if (group != NULL) {
3495 		sa_share_t share = NULL;
3496 		if (sharepath != NULL) {
3497 			share = sa_get_share(group, sharepath);
3498 			if (share == NULL) {
3499 				(void) printf(gettext(
3500 				    "Share does not exist in group %s\n"),
3501 				    groupname, sharepath);
3502 				ret = SA_NO_SUCH_PATH;
3503 			} else {
3504 				/* if ZFS and OK, then only group */
3505 				ret = zfscheck(group, share);
3506 				if (ret == SA_OK &&
3507 				    sa_group_is_zfs(group))
3508 					share = NULL;
3509 				if (ret == SA_NOT_ALLOWED)
3510 					(void) printf(gettext(
3511 					    "Properties on ZFS group shares "
3512 					    "not supported: %s\n"), sharepath);
3513 			}
3514 		}
3515 		if (ret == SA_OK) {
3516 			/* group must exist */
3517 			ret = valid_options(optlist, protocol,
3518 			    share == NULL ? group : share, sectype);
3519 			if (ret == SA_OK && !dryrun) {
3520 				if (share != NULL)
3521 					change = add_security(share, sectype,
3522 					    optlist, protocol, &ret);
3523 				else
3524 					change = add_security(group, sectype,
3525 					    optlist, protocol, &ret);
3526 				if (ret != SA_OK)
3527 					(void) printf(gettext(
3528 					    "Could not set property: %s\n"),
3529 					    sa_errorstr(ret));
3530 			}
3531 			if (ret == SA_OK && change)
3532 				worklist = add_list(worklist, group, share,
3533 				    protocol);
3534 		}
3535 		free_opt(optlist);
3536 	} else {
3537 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
3538 		ret = SA_NO_SUCH_GROUP;
3539 	}
3540 
3541 	/*
3542 	 * We have a group and potentially legal additions.
3543 	 */
3544 
3545 	/* Commit to configuration if not a dryrun */
3546 	if (!dryrun && ret == 0) {
3547 		if (change && worklist != NULL) {
3548 			/* properties changed, so update all shares */
3549 			(void) enable_all_groups(handle, worklist, 0, 0,
3550 			    protocol, B_TRUE);
3551 		}
3552 		ret = sa_update_config(handle);
3553 	}
3554 	if (worklist != NULL)
3555 		free_list(worklist);
3556 	return (ret);
3557 }
3558 
3559 /*
3560  * sa_set(flags, argc, argv)
3561  *
3562  * Implements the set subcommand. It keys off of -S to determine which
3563  * set of operations to actually do.
3564  */
3565 
3566 int
3567 sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
3568 {
3569 	char *groupname;
3570 	int verbose = 0;
3571 	int dryrun = 0;
3572 	int c;
3573 	char *protocol = NULL;
3574 	int ret = SA_OK;
3575 	struct options *optlist = NULL;
3576 	char *rsrcname = NULL;
3577 	char *sharepath = NULL;
3578 	char *optset = NULL;
3579 	int auth;
3580 
3581 	while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
3582 		switch (c) {
3583 		case 'v':
3584 			verbose++;
3585 			break;
3586 		case 'n':
3587 			dryrun++;
3588 			break;
3589 		case 'P':
3590 			if (protocol != NULL) {
3591 				(void) printf(gettext(
3592 				    "Specifying multiple protocols "
3593 				    "not supported: %s\n"), protocol);
3594 				return (SA_SYNTAX_ERR);
3595 			}
3596 			protocol = optarg;
3597 			if (!sa_valid_protocol(protocol)) {
3598 				(void) printf(gettext(
3599 				    "Invalid protocol specified: %s\n"),
3600 				    protocol);
3601 				return (SA_INVALID_PROTOCOL);
3602 			}
3603 			break;
3604 		case 'p':
3605 			ret = add_opt(&optlist, optarg, 0);
3606 			switch (ret) {
3607 			case OPT_ADD_SYNTAX:
3608 				(void) printf(gettext("Property syntax error:"
3609 				    " %s\n"), optarg);
3610 				return (SA_SYNTAX_ERR);
3611 			case OPT_ADD_MEMORY:
3612 				(void) printf(gettext("No memory to set "
3613 				    "property: %s\n"), optarg);
3614 				return (SA_NO_MEMORY);
3615 			default:
3616 				break;
3617 			}
3618 			break;
3619 		case 'r':
3620 			if (rsrcname != NULL) {
3621 				(void) printf(gettext(
3622 				    "Setting multiple resource names not"
3623 				    " supported\n"));
3624 				return (SA_SYNTAX_ERR);
3625 			}
3626 			rsrcname = optarg;
3627 			break;
3628 		case 's':
3629 			if (sharepath != NULL) {
3630 				(void) printf(gettext(
3631 				    "Setting multiple shares not supported\n"));
3632 				return (SA_SYNTAX_ERR);
3633 			}
3634 			sharepath = optarg;
3635 			break;
3636 		case 'S':
3637 			if (optset != NULL) {
3638 				(void) printf(gettext(
3639 				    "Specifying multiple property "
3640 				    "spaces not supported: %s\n"), optset);
3641 				return (SA_SYNTAX_ERR);
3642 			}
3643 			optset = optarg;
3644 			break;
3645 		default:
3646 		case 'h':
3647 		case '?':
3648 			(void) printf(gettext("usage: %s\n"),
3649 			    sa_get_usage(USAGE_SET));
3650 			return (SA_OK);
3651 		}
3652 	}
3653 
3654 	if (optlist != NULL)
3655 		ret = chk_opt(optlist, optset != NULL, protocol);
3656 
3657 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
3658 	    protocol == NULL || ret != OPT_ADD_OK) {
3659 		char *sep = "\t";
3660 
3661 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
3662 		if (optind >= argc) {
3663 			(void) printf(gettext("%sgroup must be specified"),
3664 			    sep);
3665 			sep = ", ";
3666 		}
3667 		if (optlist == NULL) {
3668 			(void) printf(gettext("%sat least one property must be"
3669 			    " specified"), sep);
3670 			sep = ", ";
3671 		}
3672 		if (protocol == NULL) {
3673 			(void) printf(gettext("%sprotocol must be specified"),
3674 			    sep);
3675 			sep = ", ";
3676 		}
3677 		(void) printf("\n");
3678 		ret = SA_SYNTAX_ERR;
3679 	} else {
3680 		/*
3681 		 * Group already exists so we can proceed after a few
3682 		 * additional checks related to ZFS handling.
3683 		 */
3684 
3685 		groupname = argv[optind];
3686 		if (strcmp(groupname, "zfs") == 0) {
3687 			(void) printf(gettext("Changing properties for group "
3688 			    "\"zfs\" not allowed\n"));
3689 			return (SA_NOT_ALLOWED);
3690 		}
3691 
3692 		auth = check_authorizations(groupname, flags);
3693 		if (optset == NULL)
3694 			ret = basic_set(handle, groupname, optlist, protocol,
3695 			    sharepath, rsrcname, dryrun);
3696 		else
3697 			ret = space_set(handle, groupname, optlist, protocol,
3698 			    sharepath, dryrun, optset);
3699 		if (dryrun && ret == SA_OK && !auth && verbose) {
3700 			(void) printf(gettext("Command would fail: %s\n"),
3701 			    sa_errorstr(SA_NO_PERMISSION));
3702 		}
3703 	}
3704 	return (ret);
3705 }
3706 
3707 /*
3708  * remove_options(group, optlist, proto, *err)
3709  *
3710  * Helper function to actually remove options from a group after all
3711  * preprocessing is done.
3712  */
3713 
3714 static int
3715 remove_options(sa_group_t group, struct options *optlist,
3716     char *proto, int *err)
3717 {
3718 	struct options *cur;
3719 	sa_optionset_t optionset;
3720 	sa_property_t prop;
3721 	int change = 0;
3722 	int ret = SA_OK;
3723 
3724 	optionset = sa_get_optionset(group, proto);
3725 	if (optionset != NULL) {
3726 		for (cur = optlist; cur != NULL; cur = cur->next) {
3727 			prop = sa_get_property(optionset, cur->optname);
3728 			if (prop != NULL) {
3729 				ret = sa_remove_property(prop);
3730 				if (ret != SA_OK)
3731 					break;
3732 				change = 1;
3733 			}
3734 		}
3735 	}
3736 	if (ret == SA_OK && change)
3737 		ret = sa_commit_properties(optionset, 0);
3738 
3739 	if (err != NULL)
3740 		*err = ret;
3741 	return (change);
3742 }
3743 
3744 /*
3745  * valid_unset(group, optlist, proto)
3746  *
3747  * Sanity check the optlist to make sure they can be removed. Issue an
3748  * error if a property doesn't exist.
3749  */
3750 
3751 static int
3752 valid_unset(sa_group_t group, struct options *optlist, char *proto)
3753 {
3754 	struct options *cur;
3755 	sa_optionset_t optionset;
3756 	sa_property_t prop;
3757 	int ret = SA_OK;
3758 
3759 	optionset = sa_get_optionset(group, proto);
3760 	if (optionset != NULL) {
3761 		for (cur = optlist; cur != NULL; cur = cur->next) {
3762 			prop = sa_get_property(optionset, cur->optname);
3763 			if (prop == NULL) {
3764 				(void) printf(gettext(
3765 				    "Could not unset property %s: not set\n"),
3766 				    cur->optname);
3767 				ret = SA_NO_SUCH_PROP;
3768 			}
3769 		}
3770 	}
3771 	return (ret);
3772 }
3773 
3774 /*
3775  * valid_unset_security(group, optlist, proto)
3776  *
3777  * Sanity check the optlist to make sure they can be removed. Issue an
3778  * error if a property doesn't exist.
3779  */
3780 
3781 static int
3782 valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
3783     char *sectype)
3784 {
3785 	struct options *cur;
3786 	sa_security_t security;
3787 	sa_property_t prop;
3788 	int ret = SA_OK;
3789 	char *sec;
3790 
3791 	sec = sa_proto_space_alias(proto, sectype);
3792 	security = sa_get_security(group, sec, proto);
3793 	if (security != NULL) {
3794 		for (cur = optlist; cur != NULL; cur = cur->next) {
3795 			prop = sa_get_property(security, cur->optname);
3796 			if (prop == NULL) {
3797 				(void) printf(gettext(
3798 				    "Could not unset property %s: not set\n"),
3799 				    cur->optname);
3800 				ret = SA_NO_SUCH_PROP;
3801 			}
3802 		}
3803 	} else {
3804 		(void) printf(gettext(
3805 		    "Could not unset %s: space not defined\n"), sectype);
3806 		ret = SA_NO_SUCH_SECURITY;
3807 	}
3808 	if (sec != NULL)
3809 		sa_free_attr_string(sec);
3810 	return (ret);
3811 }
3812 
3813 /*
3814  * remove_security(group, optlist, proto)
3815  *
3816  * Remove the properties since they were checked as valid.
3817  */
3818 
3819 static int
3820 remove_security(sa_group_t group, char *sectype,
3821     struct options *optlist, char *proto, int *err)
3822 {
3823 	sa_security_t security;
3824 	int ret = SA_OK;
3825 	int change = 0;
3826 
3827 	sectype = sa_proto_space_alias(proto, sectype);
3828 	security = sa_get_security(group, sectype, proto);
3829 	if (sectype != NULL)
3830 		sa_free_attr_string(sectype);
3831 
3832 	if (security != NULL) {
3833 		while (optlist != NULL) {
3834 			sa_property_t prop;
3835 			prop = sa_get_property(security, optlist->optname);
3836 			if (prop != NULL) {
3837 				ret = sa_remove_property(prop);
3838 				if (ret != SA_OK)
3839 					break;
3840 				change = 1;
3841 			}
3842 			optlist = optlist->next;
3843 		}
3844 		/*
3845 		 * when done, properties may have all been removed but
3846 		 * we need to keep the security type itself until
3847 		 * explicitly removed.
3848 		 */
3849 		if (ret == SA_OK && change)
3850 			ret = sa_commit_properties(security, 0);
3851 	} else {
3852 		ret = SA_NO_SUCH_PROP;
3853 	}
3854 	if (err != NULL)
3855 		*err = ret;
3856 	return (change);
3857 }
3858 
3859 /*
3860  * basic_unset(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
3861  *
3862  * Unset non-named optionset properties.
3863  */
3864 
3865 static int
3866 basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
3867     char *protocol, char *sharepath, char *rsrcname, int dryrun)
3868 {
3869 	sa_group_t group;
3870 	int ret = SA_OK;
3871 	int change = 0;
3872 	struct list *worklist = NULL;
3873 	sa_share_t share = NULL;
3874 	sa_resource_t resource = NULL;
3875 
3876 	group = sa_get_group(handle, groupname);
3877 	if (group == NULL)
3878 		return (ret);
3879 
3880 	/*
3881 	 * If there is a sharepath, make sure it belongs to
3882 	 * the group.
3883 	 */
3884 	if (sharepath != NULL) {
3885 		share = sa_get_share(group, sharepath);
3886 		if (share == NULL) {
3887 			(void) printf(gettext(
3888 			    "Share does not exist in group %s\n"),
3889 			    groupname, sharepath);
3890 			ret = SA_NO_SUCH_PATH;
3891 		}
3892 	}
3893 	/*
3894 	 * If a resource name exists, make sure it belongs to
3895 	 * the share if present else it belongs to the
3896 	 * group. Also check the protocol to see if it
3897 	 * supports resource level properties or not. If not,
3898 	 * use share only.
3899 	 */
3900 	if (rsrcname != NULL) {
3901 		if (share != NULL) {
3902 			resource = sa_get_share_resource(share, rsrcname);
3903 			if (resource == NULL)
3904 				ret = SA_NO_SUCH_RESOURCE;
3905 		} else {
3906 			resource = sa_get_resource(group, rsrcname);
3907 			if (resource != NULL) {
3908 				share = sa_get_resource_parent(resource);
3909 			} else {
3910 				ret = SA_NO_SUCH_RESOURCE;
3911 			}
3912 		}
3913 		if (ret == SA_OK && resource != NULL) {
3914 			uint64_t features;
3915 			/*
3916 			 * Check to see if the resource can take
3917 			 * properties. If so, stick the resource into
3918 			 * "share" so it will all just work.
3919 			 */
3920 			features = sa_proto_get_featureset(protocol);
3921 			if (features & SA_FEATURE_RESOURCE)
3922 				share = (sa_share_t)resource;
3923 		}
3924 	}
3925 
3926 	if (ret == SA_OK) {
3927 		/* group must exist */
3928 		ret = valid_unset(share != NULL ? share : group,
3929 		    optlist, protocol);
3930 		if (ret == SA_OK && !dryrun) {
3931 			if (share != NULL) {
3932 				sa_optionset_t optionset;
3933 				sa_property_t prop;
3934 				change |= remove_options(share, optlist,
3935 				    protocol, &ret);
3936 				/*
3937 				 * If a share optionset is
3938 				 * empty, remove it.
3939 				 */
3940 				optionset = sa_get_optionset((sa_share_t)share,
3941 				    protocol);
3942 				if (optionset != NULL) {
3943 					prop = sa_get_property(optionset, NULL);
3944 					if (prop == NULL)
3945 						(void) sa_destroy_optionset(
3946 						    optionset);
3947 				}
3948 			} else {
3949 				change |= remove_options(group,
3950 				    optlist, protocol, &ret);
3951 			}
3952 			if (ret == SA_OK && change)
3953 				worklist = add_list(worklist, group, share,
3954 				    protocol);
3955 			if (ret != SA_OK)
3956 				(void) printf(gettext(
3957 				    "Could not remove properties: "
3958 				    "%s\n"), sa_errorstr(ret));
3959 		}
3960 	} else {
3961 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
3962 		ret = SA_NO_SUCH_GROUP;
3963 	}
3964 	free_opt(optlist);
3965 
3966 	/*
3967 	 * We have a group and potentially legal additions
3968 	 *
3969 	 * Commit to configuration if not a dryrun
3970 	 */
3971 	if (!dryrun && ret == SA_OK) {
3972 		if (change && worklist != NULL) {
3973 			/* properties changed, so update all shares */
3974 			(void) enable_all_groups(handle, worklist, 0, 0,
3975 			    protocol, B_TRUE);
3976 		}
3977 	}
3978 	if (worklist != NULL)
3979 		free_list(worklist);
3980 	return (ret);
3981 }
3982 
3983 /*
3984  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
3985  *
3986  * Unset named optionset properties.
3987  */
3988 static int
3989 space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
3990     char *protocol, char *sharepath, int dryrun, char *sectype)
3991 {
3992 	sa_group_t group;
3993 	int ret = SA_OK;
3994 	int change = 0;
3995 	struct list *worklist = NULL;
3996 	sa_share_t share = NULL;
3997 
3998 	group = sa_get_group(handle, groupname);
3999 	if (group == NULL) {
4000 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
4001 		return (SA_NO_SUCH_GROUP);
4002 	}
4003 	if (sharepath != NULL) {
4004 		share = sa_get_share(group, sharepath);
4005 		if (share == NULL) {
4006 			(void) printf(gettext(
4007 			    "Share does not exist in group %s\n"),
4008 			    groupname, sharepath);
4009 			return (SA_NO_SUCH_PATH);
4010 		}
4011 	}
4012 	ret = valid_unset_security(share != NULL ? share : group,
4013 	    optlist, protocol, sectype);
4014 
4015 	if (ret == SA_OK && !dryrun) {
4016 		if (optlist != NULL) {
4017 			if (share != NULL) {
4018 				sa_security_t optionset;
4019 				sa_property_t prop;
4020 				change = remove_security(share,
4021 				    sectype, optlist, protocol, &ret);
4022 
4023 				/* If a share security is empty, remove it */
4024 				optionset = sa_get_security((sa_group_t)share,
4025 				    sectype, protocol);
4026 				if (optionset != NULL) {
4027 					prop = sa_get_property(optionset,
4028 					    NULL);
4029 					if (prop == NULL)
4030 						ret = sa_destroy_security(
4031 						    optionset);
4032 				}
4033 			} else {
4034 				change = remove_security(group, sectype,
4035 				    optlist, protocol, &ret);
4036 			}
4037 		} else {
4038 			sa_security_t security;
4039 			char *sec;
4040 			sec = sa_proto_space_alias(protocol, sectype);
4041 			security = sa_get_security(group, sec, protocol);
4042 			if (sec != NULL)
4043 				sa_free_attr_string(sec);
4044 			if (security != NULL) {
4045 				ret = sa_destroy_security(security);
4046 				if (ret == SA_OK)
4047 					change = 1;
4048 			} else {
4049 				ret = SA_NO_SUCH_PROP;
4050 			}
4051 		}
4052 		if (ret != SA_OK)
4053 			(void) printf(gettext("Could not unset property: %s\n"),
4054 			    sa_errorstr(ret));
4055 	}
4056 
4057 	if (ret == SA_OK && change)
4058 		worklist = add_list(worklist, group, 0, protocol);
4059 
4060 	free_opt(optlist);
4061 	/*
4062 	 * We have a group and potentially legal additions
4063 	 */
4064 
4065 	/* Commit to configuration if not a dryrun */
4066 	if (!dryrun && ret == 0) {
4067 		/* properties changed, so update all shares */
4068 		if (change && worklist != NULL)
4069 			(void) enable_all_groups(handle, worklist, 0, 0,
4070 			    protocol, B_TRUE);
4071 		ret = sa_update_config(handle);
4072 	}
4073 	if (worklist != NULL)
4074 		free_list(worklist);
4075 	return (ret);
4076 }
4077 
4078 /*
4079  * sa_unset(flags, argc, argv)
4080  *
4081  * Implements the unset subcommand. Parsing done here and then basic
4082  * or space versions of the real code are called.
4083  */
4084 
4085 int
4086 sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
4087 {
4088 	char *groupname;
4089 	int verbose = 0;
4090 	int dryrun = 0;
4091 	int c;
4092 	char *protocol = NULL;
4093 	int ret = SA_OK;
4094 	struct options *optlist = NULL;
4095 	char *rsrcname = NULL;
4096 	char *sharepath = NULL;
4097 	char *optset = NULL;
4098 	int auth;
4099 
4100 	while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
4101 		switch (c) {
4102 		case 'v':
4103 			verbose++;
4104 			break;
4105 		case 'n':
4106 			dryrun++;
4107 			break;
4108 		case 'P':
4109 			if (protocol != NULL) {
4110 				(void) printf(gettext(
4111 				    "Specifying multiple protocols "
4112 				    "not supported: %s\n"), protocol);
4113 				return (SA_SYNTAX_ERR);
4114 			}
4115 			protocol = optarg;
4116 			if (!sa_valid_protocol(protocol)) {
4117 				(void) printf(gettext(
4118 				    "Invalid protocol specified: %s\n"),
4119 				    protocol);
4120 				return (SA_INVALID_PROTOCOL);
4121 			}
4122 			break;
4123 		case 'p':
4124 			ret = add_opt(&optlist, optarg, 1);
4125 			switch (ret) {
4126 			case OPT_ADD_SYNTAX:
4127 				(void) printf(gettext("Property syntax error "
4128 				    "for property %s\n"), optarg);
4129 				return (SA_SYNTAX_ERR);
4130 
4131 			case OPT_ADD_PROPERTY:
4132 				(void) printf(gettext("Properties need to be "
4133 				    "set with set command: %s\n"), optarg);
4134 				return (SA_SYNTAX_ERR);
4135 
4136 			default:
4137 				break;
4138 			}
4139 			break;
4140 		case 'r':
4141 			/*
4142 			 * Unset properties on resource if applicable or on
4143 			 * share if resource for this protocol doesn't use
4144 			 * resources.
4145 			 */
4146 			if (rsrcname != NULL) {
4147 				(void) printf(gettext(
4148 				    "Unsetting multiple resource "
4149 				    "names not supported\n"));
4150 				return (SA_SYNTAX_ERR);
4151 			}
4152 			rsrcname = optarg;
4153 			break;
4154 		case 's':
4155 			if (sharepath != NULL) {
4156 				(void) printf(gettext(
4157 				    "Adding multiple shares not supported\n"));
4158 				return (SA_SYNTAX_ERR);
4159 			}
4160 			sharepath = optarg;
4161 			break;
4162 		case 'S':
4163 			if (optset != NULL) {
4164 				(void) printf(gettext(
4165 				    "Specifying multiple property "
4166 				    "spaces not supported: %s\n"), optset);
4167 				return (SA_SYNTAX_ERR);
4168 			}
4169 			optset = optarg;
4170 			break;
4171 		default:
4172 		case 'h':
4173 		case '?':
4174 			(void) printf(gettext("usage: %s\n"),
4175 			    sa_get_usage(USAGE_UNSET));
4176 			return (SA_OK);
4177 		}
4178 	}
4179 
4180 	if (optlist != NULL)
4181 		ret = chk_opt(optlist, optset != NULL, protocol);
4182 
4183 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
4184 	    protocol == NULL) {
4185 		char *sep = "\t";
4186 		(void) printf(gettext("usage: %s\n"),
4187 		    sa_get_usage(USAGE_UNSET));
4188 		if (optind >= argc) {
4189 			(void) printf(gettext("%sgroup must be specified"),
4190 			    sep);
4191 			sep = ", ";
4192 		}
4193 		if (optlist == NULL) {
4194 			(void) printf(gettext("%sat least one property must "
4195 			    "be specified"), sep);
4196 			sep = ", ";
4197 		}
4198 		if (protocol == NULL) {
4199 			(void) printf(gettext("%sprotocol must be specified"),
4200 			    sep);
4201 			sep = ", ";
4202 		}
4203 		(void) printf("\n");
4204 		ret = SA_SYNTAX_ERR;
4205 	} else {
4206 
4207 		/*
4208 		 * If a group already exists, we can only add a new
4209 		 * protocol to it and not create a new one or add the
4210 		 * same protocol again.
4211 		 */
4212 
4213 		groupname = argv[optind];
4214 		auth = check_authorizations(groupname, flags);
4215 		if (optset == NULL)
4216 			ret = basic_unset(handle, groupname, optlist, protocol,
4217 			    sharepath, rsrcname, dryrun);
4218 		else
4219 			ret = space_unset(handle, groupname, optlist, protocol,
4220 			    sharepath, dryrun, optset);
4221 
4222 		if (dryrun && ret == SA_OK && !auth && verbose)
4223 			(void) printf(gettext("Command would fail: %s\n"),
4224 			    sa_errorstr(SA_NO_PERMISSION));
4225 	}
4226 	return (ret);
4227 }
4228 
4229 /*
4230  * sa_enable_group(flags, argc, argv)
4231  *
4232  * Implements the enable subcommand
4233  */
4234 
4235 int
4236 sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
4237 {
4238 	int verbose = 0;
4239 	int dryrun = 0;
4240 	int all = 0;
4241 	int c;
4242 	int ret = SA_OK;
4243 	char *protocol = NULL;
4244 	char *state;
4245 	struct list *worklist = NULL;
4246 	int auth = 1;
4247 	sa_group_t group;
4248 
4249 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
4250 		switch (c) {
4251 		case 'a':
4252 			all = 1;
4253 			break;
4254 		case 'n':
4255 			dryrun++;
4256 			break;
4257 		case 'P':
4258 			if (protocol != NULL) {
4259 				(void) printf(gettext(
4260 				    "Specifying multiple protocols "
4261 				    "not supported: %s\n"), protocol);
4262 				return (SA_SYNTAX_ERR);
4263 			}
4264 			protocol = optarg;
4265 			if (!sa_valid_protocol(protocol)) {
4266 				(void) printf(gettext(
4267 				    "Invalid protocol specified: %s\n"),
4268 				    protocol);
4269 				return (SA_INVALID_PROTOCOL);
4270 			}
4271 			break;
4272 		case 'v':
4273 			verbose++;
4274 			break;
4275 		default:
4276 		case 'h':
4277 		case '?':
4278 			(void) printf(gettext("usage: %s\n"),
4279 			    sa_get_usage(USAGE_ENABLE));
4280 			return (0);
4281 		}
4282 	}
4283 
4284 	if (optind == argc && !all) {
4285 		(void) printf(gettext("usage: %s\n"),
4286 		    sa_get_usage(USAGE_ENABLE));
4287 		(void) printf(gettext("\tmust specify group\n"));
4288 		return (SA_NO_SUCH_PATH);
4289 	}
4290 	if (!all) {
4291 		while (optind < argc) {
4292 			group = sa_get_group(handle, argv[optind]);
4293 			if (group != NULL) {
4294 				auth &= check_authorizations(argv[optind],
4295 				    flags);
4296 				state = sa_get_group_attr(group, "state");
4297 				if (state != NULL &&
4298 				    strcmp(state, "enabled") == 0) {
4299 					/* already enabled */
4300 					if (verbose)
4301 						(void) printf(gettext(
4302 						    "Group \"%s\" is already "
4303 						    "enabled\n"),
4304 						    argv[optind]);
4305 					ret = SA_BUSY; /* already enabled */
4306 				} else {
4307 					worklist = add_list(worklist, group,
4308 					    0, protocol);
4309 					if (verbose)
4310 						(void) printf(gettext(
4311 						    "Enabling group \"%s\"\n"),
4312 						    argv[optind]);
4313 				}
4314 				if (state != NULL)
4315 					sa_free_attr_string(state);
4316 			} else {
4317 				ret = SA_NO_SUCH_GROUP;
4318 			}
4319 			optind++;
4320 		}
4321 	} else {
4322 		for (group = sa_get_group(handle, NULL);
4323 		    group != NULL;
4324 		    group = sa_get_next_group(group)) {
4325 			worklist = add_list(worklist, group, 0, protocol);
4326 		}
4327 	}
4328 	if (!dryrun && ret == SA_OK)
4329 		ret = enable_all_groups(handle, worklist, 1, 0, NULL, B_FALSE);
4330 
4331 	if (ret != SA_OK && ret != SA_BUSY)
4332 		(void) printf(gettext("Could not enable group: %s\n"),
4333 		    sa_errorstr(ret));
4334 	if (ret == SA_BUSY)
4335 		ret = SA_OK;
4336 
4337 	if (worklist != NULL)
4338 		free_list(worklist);
4339 	if (dryrun && ret == SA_OK && !auth && verbose) {
4340 		(void) printf(gettext("Command would fail: %s\n"),
4341 		    sa_errorstr(SA_NO_PERMISSION));
4342 	}
4343 	return (ret);
4344 }
4345 
4346 /*
4347  * disable_group(group, proto)
4348  *
4349  * Disable all the shares in the specified group.. This is a helper
4350  * for disable_all_groups in order to simplify regular and subgroup
4351  * (zfs) disabling. Group has already been checked for non-NULL.
4352  */
4353 
4354 static int
4355 disable_group(sa_group_t group, char *proto)
4356 {
4357 	sa_share_t share;
4358 	int ret = SA_OK;
4359 
4360 	/*
4361 	 * If the protocol isn't enabled, skip it and treat as
4362 	 * successful.
4363 	 */
4364 	if (!has_protocol(group, proto))
4365 		return (ret);
4366 
4367 	for (share = sa_get_share(group, NULL);
4368 	    share != NULL && ret == SA_OK;
4369 	    share = sa_get_next_share(share)) {
4370 		ret = sa_disable_share(share, proto);
4371 		if (ret == SA_NO_SUCH_PATH) {
4372 			/*
4373 			 * this is OK since the path is gone. we can't
4374 			 * re-share it anyway so no error.
4375 			 */
4376 			ret = SA_OK;
4377 		}
4378 	}
4379 	return (ret);
4380 }
4381 
4382 /*
4383  * disable_all_groups(work, setstate)
4384  *
4385  * helper function that disables the shares in the list of groups
4386  * provided. It optionally marks the group as disabled. Used by both
4387  * enable and start subcommands.
4388  */
4389 
4390 static int
4391 disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
4392 {
4393 	int ret = SA_OK;
4394 	sa_group_t subgroup, group;
4395 
4396 	while (work != NULL && ret == SA_OK) {
4397 		group = (sa_group_t)work->item;
4398 		if (setstate)
4399 			ret = sa_set_group_attr(group, "state", "disabled");
4400 		if (ret == SA_OK) {
4401 			char *name;
4402 			name = sa_get_group_attr(group, "name");
4403 			if (name != NULL && strcmp(name, "zfs") == 0) {
4404 				/* need to get the sub-groups for stopping */
4405 				for (subgroup = sa_get_sub_group(group);
4406 				    subgroup != NULL;
4407 				    subgroup = sa_get_next_group(subgroup)) {
4408 					ret = disable_group(subgroup,
4409 					    work->proto);
4410 				}
4411 			} else {
4412 				ret = disable_group(group, work->proto);
4413 			}
4414 			/*
4415 			 * We don't want to "disable" since it won't come
4416 			 * up after a reboot.  The SMF framework should do
4417 			 * the right thing. On enable we do want to do
4418 			 * something.
4419 			 */
4420 		}
4421 		work = work->next;
4422 	}
4423 	if (ret == SA_OK)
4424 		ret = sa_update_config(handle);
4425 	return (ret);
4426 }
4427 
4428 /*
4429  * sa_disable_group(flags, argc, argv)
4430  *
4431  * Implements the disable subcommand
4432  */
4433 
4434 int
4435 sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
4436 {
4437 	int verbose = 0;
4438 	int dryrun = 0;
4439 	int all = 0;
4440 	int c;
4441 	int ret = SA_OK;
4442 	char *protocol = NULL;
4443 	char *state;
4444 	struct list *worklist = NULL;
4445 	sa_group_t group;
4446 	int auth = 1;
4447 
4448 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
4449 		switch (c) {
4450 		case 'a':
4451 			all = 1;
4452 			break;
4453 		case 'n':
4454 			dryrun++;
4455 			break;
4456 		case 'P':
4457 			if (protocol != NULL) {
4458 				(void) printf(gettext(
4459 				    "Specifying multiple protocols "
4460 				    "not supported: %s\n"), protocol);
4461 				return (SA_SYNTAX_ERR);
4462 			}
4463 			protocol = optarg;
4464 			if (!sa_valid_protocol(protocol)) {
4465 				(void) printf(gettext(
4466 				    "Invalid protocol specified: %s\n"),
4467 				    protocol);
4468 				return (SA_INVALID_PROTOCOL);
4469 			}
4470 			break;
4471 		case 'v':
4472 			verbose++;
4473 			break;
4474 		default:
4475 		case 'h':
4476 		case '?':
4477 			(void) printf(gettext("usage: %s\n"),
4478 			    sa_get_usage(USAGE_DISABLE));
4479 			return (0);
4480 		}
4481 	}
4482 
4483 	if (optind == argc && !all) {
4484 		(void) printf(gettext("usage: %s\n"),
4485 		    sa_get_usage(USAGE_DISABLE));
4486 		(void) printf(gettext("\tmust specify group\n"));
4487 		return (SA_NO_SUCH_PATH);
4488 	}
4489 	if (!all) {
4490 		while (optind < argc) {
4491 			group = sa_get_group(handle, argv[optind]);
4492 			if (group != NULL) {
4493 				auth &= check_authorizations(argv[optind],
4494 				    flags);
4495 				state = sa_get_group_attr(group, "state");
4496 				if (state == NULL ||
4497 				    strcmp(state, "disabled") == 0) {
4498 					/* already disabled */
4499 					if (verbose)
4500 						(void) printf(gettext(
4501 						    "Group \"%s\" is "
4502 						    "already disabled\n"),
4503 						    argv[optind]);
4504 					ret = SA_BUSY; /* already disabled */
4505 				} else {
4506 					worklist = add_list(worklist, group, 0,
4507 					    protocol);
4508 					if (verbose)
4509 						(void) printf(gettext(
4510 						    "Disabling group "
4511 						    "\"%s\"\n"), argv[optind]);
4512 				}
4513 				if (state != NULL)
4514 					sa_free_attr_string(state);
4515 			} else {
4516 				ret = SA_NO_SUCH_GROUP;
4517 			}
4518 			optind++;
4519 		}
4520 	} else {
4521 		for (group = sa_get_group(handle, NULL);
4522 		    group != NULL;
4523 		    group = sa_get_next_group(group))
4524 			worklist = add_list(worklist, group, 0, protocol);
4525 	}
4526 
4527 	if (ret == SA_OK && !dryrun)
4528 		ret = disable_all_groups(handle, worklist, 1);
4529 	if (ret != SA_OK && ret != SA_BUSY)
4530 		(void) printf(gettext("Could not disable group: %s\n"),
4531 		    sa_errorstr(ret));
4532 	if (ret == SA_BUSY)
4533 		ret = SA_OK;
4534 	if (worklist != NULL)
4535 		free_list(worklist);
4536 	if (dryrun && ret == SA_OK && !auth && verbose)
4537 		(void) printf(gettext("Command would fail: %s\n"),
4538 		    sa_errorstr(SA_NO_PERMISSION));
4539 	return (ret);
4540 }
4541 
4542 /*
4543  * sa_start_group(flags, argc, argv)
4544  *
4545  * Implements the start command.
4546  * This is similar to enable except it doesn't change the state
4547  * of the group(s) and only enables shares if the group is already
4548  * enabled.
4549  */
4550 
4551 int
4552 sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
4553 {
4554 	int verbose = 0;
4555 	int all = 0;
4556 	int c;
4557 	int ret = SMF_EXIT_OK;
4558 	char *protocol = NULL;
4559 	char *state;
4560 	struct list *worklist = NULL;
4561 	sa_group_t group;
4562 #ifdef lint
4563 	flags = flags;
4564 #endif
4565 
4566 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
4567 		switch (c) {
4568 		case 'a':
4569 			all = 1;
4570 			break;
4571 		case 'P':
4572 			if (protocol != NULL) {
4573 				(void) printf(gettext(
4574 				    "Specifying multiple protocols "
4575 				    "not supported: %s\n"), protocol);
4576 				return (SA_SYNTAX_ERR);
4577 			}
4578 			protocol = optarg;
4579 			if (!sa_valid_protocol(protocol)) {
4580 				(void) printf(gettext(
4581 				    "Invalid protocol specified: %s\n"),
4582 				    protocol);
4583 				return (SA_INVALID_PROTOCOL);
4584 			}
4585 			break;
4586 		case 'v':
4587 			verbose++;
4588 			break;
4589 		default:
4590 		case 'h':
4591 		case '?':
4592 			(void) printf(gettext("usage: %s\n"),
4593 			    sa_get_usage(USAGE_START));
4594 			return (SA_OK);
4595 		}
4596 	}
4597 
4598 	if (optind == argc && !all) {
4599 		(void) printf(gettext("usage: %s\n"),
4600 		    sa_get_usage(USAGE_START));
4601 		return (SMF_EXIT_ERR_FATAL);
4602 	}
4603 
4604 	if (!all) {
4605 		while (optind < argc) {
4606 			group = sa_get_group(handle, argv[optind]);
4607 			if (group != NULL) {
4608 				state = sa_get_group_attr(group, "state");
4609 				if (state == NULL ||
4610 				    strcmp(state, "enabled") == 0) {
4611 					worklist = add_list(worklist, group, 0,
4612 					    protocol);
4613 					if (verbose)
4614 						(void) printf(gettext(
4615 						    "Starting group \"%s\"\n"),
4616 						    argv[optind]);
4617 				} else {
4618 					/*
4619 					 * Determine if there are any
4620 					 * protocols.  If there aren't any,
4621 					 * then there isn't anything to do in
4622 					 * any case so no error.
4623 					 */
4624 					if (sa_get_optionset(group,
4625 					    protocol) != NULL) {
4626 						ret = SMF_EXIT_OK;
4627 					}
4628 				}
4629 				if (state != NULL)
4630 					sa_free_attr_string(state);
4631 			}
4632 			optind++;
4633 		}
4634 	} else {
4635 		for (group = sa_get_group(handle, NULL);
4636 		    group != NULL;
4637 		    group = sa_get_next_group(group)) {
4638 			state = sa_get_group_attr(group, "state");
4639 			if (state == NULL || strcmp(state, "enabled") == 0)
4640 				worklist = add_list(worklist, group, 0,
4641 				    protocol);
4642 			if (state != NULL)
4643 				sa_free_attr_string(state);
4644 		}
4645 	}
4646 
4647 	(void) enable_all_groups(handle, worklist, 0, 1, protocol, B_FALSE);
4648 
4649 	if (worklist != NULL)
4650 		free_list(worklist);
4651 	return (ret);
4652 }
4653 
4654 /*
4655  * sa_stop_group(flags, argc, argv)
4656  *
4657  * Implements the stop command.
4658  * This is similar to disable except it doesn't change the state
4659  * of the group(s) and only disables shares if the group is already
4660  * enabled.
4661  */
4662 int
4663 sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
4664 {
4665 	int verbose = 0;
4666 	int all = 0;
4667 	int c;
4668 	int ret = SMF_EXIT_OK;
4669 	char *protocol = NULL;
4670 	char *state;
4671 	struct list *worklist = NULL;
4672 	sa_group_t group;
4673 #ifdef lint
4674 	flags = flags;
4675 #endif
4676 
4677 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
4678 		switch (c) {
4679 		case 'a':
4680 			all = 1;
4681 			break;
4682 		case 'P':
4683 			if (protocol != NULL) {
4684 				(void) printf(gettext(
4685 				    "Specifying multiple protocols "
4686 				    "not supported: %s\n"), protocol);
4687 				return (SA_SYNTAX_ERR);
4688 			}
4689 			protocol = optarg;
4690 			if (!sa_valid_protocol(protocol)) {
4691 				(void) printf(gettext(
4692 				    "Invalid protocol specified: %s\n"),
4693 				    protocol);
4694 				return (SA_INVALID_PROTOCOL);
4695 			}
4696 			break;
4697 		case 'v':
4698 			verbose++;
4699 			break;
4700 		default:
4701 		case 'h':
4702 		case '?':
4703 			(void) printf(gettext("usage: %s\n"),
4704 			    sa_get_usage(USAGE_STOP));
4705 			return (0);
4706 		}
4707 	}
4708 
4709 	if (optind == argc && !all) {
4710 		(void) printf(gettext("usage: %s\n"),
4711 		    sa_get_usage(USAGE_STOP));
4712 		return (SMF_EXIT_ERR_FATAL);
4713 	} else if (!all) {
4714 		while (optind < argc) {
4715 			group = sa_get_group(handle, argv[optind]);
4716 			if (group != NULL) {
4717 				state = sa_get_group_attr(group, "state");
4718 				if (state == NULL ||
4719 				    strcmp(state, "enabled") == 0) {
4720 					worklist = add_list(worklist, group, 0,
4721 					    protocol);
4722 					if (verbose)
4723 						(void) printf(gettext(
4724 						    "Stopping group \"%s\"\n"),
4725 						    argv[optind]);
4726 				} else {
4727 					ret = SMF_EXIT_OK;
4728 				}
4729 				if (state != NULL)
4730 					sa_free_attr_string(state);
4731 			}
4732 			optind++;
4733 		}
4734 	} else {
4735 		for (group = sa_get_group(handle, NULL);
4736 		    group != NULL;
4737 		    group = sa_get_next_group(group)) {
4738 			state = sa_get_group_attr(group, "state");
4739 			if (state == NULL || strcmp(state, "enabled") == 0)
4740 				worklist = add_list(worklist, group, 0,
4741 				    protocol);
4742 			if (state != NULL)
4743 				sa_free_attr_string(state);
4744 		}
4745 	}
4746 	(void) disable_all_groups(handle, worklist, 0);
4747 	ret = sa_update_config(handle);
4748 
4749 	if (worklist != NULL)
4750 		free_list(worklist);
4751 	return (ret);
4752 }
4753 
4754 /*
4755  * remove_all_options(share, proto)
4756  *
4757  * Removes all options on a share.
4758  */
4759 
4760 static void
4761 remove_all_options(sa_share_t share, char *proto)
4762 {
4763 	sa_optionset_t optionset;
4764 	sa_security_t security;
4765 	sa_security_t prevsec = NULL;
4766 
4767 	optionset = sa_get_optionset(share, proto);
4768 	if (optionset != NULL)
4769 		(void) sa_destroy_optionset(optionset);
4770 	for (security = sa_get_security(share, NULL, NULL);
4771 	    security != NULL;
4772 	    security = sa_get_next_security(security)) {
4773 		char *type;
4774 		/*
4775 		 * We walk through the list.  prevsec keeps the
4776 		 * previous security so we can delete it without
4777 		 * destroying the list.
4778 		 */
4779 		if (prevsec != NULL) {
4780 			/* remove the previously seen security */
4781 			(void) sa_destroy_security(prevsec);
4782 			/* set to NULL so we don't try multiple times */
4783 			prevsec = NULL;
4784 		}
4785 		type = sa_get_security_attr(security, "type");
4786 		if (type != NULL) {
4787 			/*
4788 			 * if the security matches the specified protocol, we
4789 			 * want to remove it. prevsec holds it until either
4790 			 * the next pass or we fall out of the loop.
4791 			 */
4792 			if (strcmp(type, proto) == 0)
4793 				prevsec = security;
4794 			sa_free_attr_string(type);
4795 		}
4796 	}
4797 	/* in case there is one left */
4798 	if (prevsec != NULL)
4799 		(void) sa_destroy_security(prevsec);
4800 }
4801 
4802 
4803 /*
4804  * for legacy support, we need to handle the old syntax. This is what
4805  * we get if sharemgr is called with the name "share" rather than
4806  * sharemgr.
4807  */
4808 
4809 static int
4810 format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
4811 {
4812 	int err;
4813 
4814 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
4815 	if (err > buffsize)
4816 		return (-1);
4817 	return (0);
4818 }
4819 
4820 
4821 /*
4822  * check_legacy_cmd(proto, cmd)
4823  *
4824  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
4825  * executable.
4826  */
4827 
4828 static int
4829 check_legacy_cmd(char *path)
4830 {
4831 	struct stat st;
4832 	int ret = 0;
4833 
4834 	if (stat(path, &st) == 0) {
4835 		if (S_ISREG(st.st_mode) &&
4836 		    st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
4837 			ret = 1;
4838 	}
4839 	return (ret);
4840 }
4841 
4842 /*
4843  * run_legacy_command(proto, cmd, argv)
4844  *
4845  * We know the command exists, so attempt to execute it with all the
4846  * arguments. This implements full legacy share support for those
4847  * protocols that don't have plugin providers.
4848  */
4849 
4850 static int
4851 run_legacy_command(char *path, char *argv[])
4852 {
4853 	int ret;
4854 
4855 	ret = execv(path, argv);
4856 	if (ret < 0) {
4857 		switch (errno) {
4858 		case EACCES:
4859 			ret = SA_NO_PERMISSION;
4860 			break;
4861 		default:
4862 			ret = SA_SYSTEM_ERR;
4863 			break;
4864 		}
4865 	}
4866 	return (ret);
4867 }
4868 
4869 /*
4870  * out_share(out, group, proto)
4871  *
4872  * Display the share information in the format that the "share"
4873  * command has traditionally used.
4874  */
4875 
4876 static void
4877 out_share(FILE *out, sa_group_t group, char *proto)
4878 {
4879 	sa_share_t share;
4880 	char resfmt[128];
4881 	char *defprop;
4882 
4883 	/*
4884 	 * The original share command defaulted to displaying NFS
4885 	 * shares or allowed a protocol to be specified. We want to
4886 	 * skip those shares that are not the specified protocol.
4887 	 */
4888 	if (proto != NULL && sa_get_optionset(group, proto) == NULL)
4889 		return;
4890 
4891 	if (proto == NULL)
4892 		proto = "nfs";
4893 
4894 	/*
4895 	 * get the default property string.  NFS uses "rw" but
4896 	 * everything else will use "".
4897 	 */
4898 	if (proto != NULL && strcmp(proto, "nfs") != 0)
4899 		defprop = "\"\"";
4900 	else
4901 		defprop = "rw";
4902 
4903 	for (share = sa_get_share(group, NULL);
4904 	    share != NULL;
4905 	    share = sa_get_next_share(share)) {
4906 		char *path;
4907 		char *type;
4908 		char *resource;
4909 		char *description;
4910 		char *groupname;
4911 		char *sharedstate;
4912 		int shared = 1;
4913 		char *soptions;
4914 		char shareopts[MAXNAMLEN];
4915 
4916 		sharedstate = sa_get_share_attr(share, "shared");
4917 		path = sa_get_share_attr(share, "path");
4918 		type = sa_get_share_attr(share, "type");
4919 		resource = get_resource(share);
4920 		groupname = sa_get_group_attr(group, "name");
4921 
4922 		if (groupname != NULL && strcmp(groupname, "default") == 0) {
4923 			sa_free_attr_string(groupname);
4924 			groupname = NULL;
4925 		}
4926 		description = sa_get_share_description(share);
4927 
4928 		/*
4929 		 * Want the sharetab version if it exists, defaulting
4930 		 * to NFS if no protocol specified.
4931 		 */
4932 		(void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", proto);
4933 		soptions = sa_get_share_attr(share, shareopts);
4934 
4935 		if (sharedstate == NULL)
4936 			shared = 0;
4937 
4938 		if (soptions == NULL)
4939 			soptions = sa_proto_legacy_format(proto, share, 1);
4940 
4941 		if (shared) {
4942 			/* only active shares go here */
4943 			(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
4944 			    resource != NULL ? resource : "-",
4945 			    groupname != NULL ? "@" : "",
4946 			    groupname != NULL ? groupname : "");
4947 			(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
4948 			    resfmt, path,
4949 			    (soptions != NULL && strlen(soptions) > 0) ?
4950 			    soptions : defprop,
4951 			    (description != NULL) ? description : "");
4952 		}
4953 
4954 		if (path != NULL)
4955 			sa_free_attr_string(path);
4956 		if (type != NULL)
4957 			sa_free_attr_string(type);
4958 		if (resource != NULL)
4959 			sa_free_attr_string(resource);
4960 		if (groupname != NULL)
4961 			sa_free_attr_string(groupname);
4962 		if (description != NULL)
4963 			sa_free_share_description(description);
4964 		if (sharedstate != NULL)
4965 			sa_free_attr_string(sharedstate);
4966 		if (soptions != NULL)
4967 			sa_format_free(soptions);
4968 	}
4969 }
4970 
4971 /*
4972  * output_legacy_file(out, proto)
4973  *
4974  * Walk all of the groups for the specified protocol and call
4975  * out_share() to format and write in the format displayed by the
4976  * "share" command with no arguments.
4977  */
4978 
4979 static void
4980 output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
4981 {
4982 	sa_group_t group;
4983 
4984 	for (group = sa_get_group(handle, NULL);
4985 	    group != NULL;
4986 	    group = sa_get_next_group(group)) {
4987 		char *zfs;
4988 
4989 		/*
4990 		 * Go through all the groups and ZFS
4991 		 * sub-groups. out_share() will format the shares in
4992 		 * the group appropriately.
4993 		 */
4994 
4995 		zfs = sa_get_group_attr(group, "zfs");
4996 		if (zfs != NULL) {
4997 			sa_group_t zgroup;
4998 			sa_free_attr_string(zfs);
4999 			for (zgroup = sa_get_sub_group(group);
5000 			    zgroup != NULL;
5001 			    zgroup = sa_get_next_group(zgroup)) {
5002 
5003 				/* got a group, so display it */
5004 				out_share(out, zgroup, proto);
5005 			}
5006 		} else {
5007 			out_share(out, group, proto);
5008 		}
5009 	}
5010 }
5011 
5012 int
5013 sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
5014 {
5015 	char *protocol = "nfs";
5016 	char *options = NULL;
5017 	char *description = NULL;
5018 	char *groupname = NULL;
5019 	char *sharepath = NULL;
5020 	char *resource = NULL;
5021 	char *groupstatus = NULL;
5022 	int persist = SA_SHARE_TRANSIENT;
5023 	int argsused = 0;
5024 	int c;
5025 	int ret = SA_OK;
5026 	int zfs = 0;
5027 	int true_legacy = 0;
5028 	int curtype = SA_SHARE_TRANSIENT;
5029 	char cmd[MAXPATHLEN];
5030 	sa_group_t group = NULL;
5031 	sa_resource_t rsrc = NULL;
5032 	sa_share_t share;
5033 	char dir[MAXPATHLEN];
5034 	uint64_t features;
5035 #ifdef lint
5036 	flags = flags;
5037 #endif
5038 
5039 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
5040 		switch (c) {
5041 		case 'd':
5042 			description = optarg;
5043 			argsused++;
5044 			break;
5045 		case 'F':
5046 			protocol = optarg;
5047 			if (!sa_valid_protocol(protocol)) {
5048 				if (format_legacy_path(cmd, MAXPATHLEN,
5049 				    protocol, "share") == 0 &&
5050 				    check_legacy_cmd(cmd)) {
5051 					true_legacy++;
5052 				} else {
5053 					(void) fprintf(stderr, gettext(
5054 					    "Invalid protocol specified: "
5055 					    "%s\n"), protocol);
5056 					return (SA_INVALID_PROTOCOL);
5057 				}
5058 			}
5059 			break;
5060 		case 'o':
5061 			options = optarg;
5062 			argsused++;
5063 			break;
5064 		case 'p':
5065 			persist = SA_SHARE_PERMANENT;
5066 			argsused++;
5067 			break;
5068 		case 'h':
5069 		case '?':
5070 		default:
5071 			(void) fprintf(stderr, gettext("usage: %s\n"),
5072 			    sa_get_usage(USAGE_SHARE));
5073 			return (SA_OK);
5074 		}
5075 	}
5076 
5077 	/* Have the info so construct what is needed */
5078 	if (!argsused && optind == argc) {
5079 		/* display current info in share format */
5080 		(void) output_legacy_file(stdout, protocol, handle);
5081 		return (ret);
5082 	}
5083 
5084 	/* We are modifying the configuration */
5085 	if (optind == argc) {
5086 		(void) fprintf(stderr, gettext("usage: %s\n"),
5087 		    sa_get_usage(USAGE_SHARE));
5088 		return (SA_LEGACY_ERR);
5089 	}
5090 	if (true_legacy) {
5091 		/* If still using legacy share/unshare, exec it */
5092 		ret = run_legacy_command(cmd, argv);
5093 		return (ret);
5094 	}
5095 
5096 	sharepath = argv[optind++];
5097 	if (optind < argc) {
5098 		resource = argv[optind];
5099 		groupname = strchr(resource, '@');
5100 		if (groupname != NULL)
5101 			*groupname++ = '\0';
5102 	}
5103 	if (realpath(sharepath, dir) == NULL)
5104 		ret = SA_BAD_PATH;
5105 	else
5106 		sharepath = dir;
5107 	if (ret == SA_OK)
5108 		share = sa_find_share(handle, sharepath);
5109 	else
5110 		share = NULL;
5111 
5112 	features = sa_proto_get_featureset(protocol);
5113 
5114 	if (groupname != NULL) {
5115 		ret = SA_NOT_ALLOWED;
5116 	} else if (ret == SA_OK) {
5117 		char *legacygroup;
5118 		/*
5119 		 * The legacy group is always present and zfs groups
5120 		 * come and go.  zfs shares may be in sub-groups and
5121 		 * the zfs share will already be in that group so it
5122 		 * isn't an error. If the protocol is "smb", the group
5123 		 * "smb" is used when "default" would otherwise be
5124 		 * used.  "default" is NFS only and "smb" is SMB only.
5125 		 */
5126 		if (strcmp(protocol, "smb") == 0)
5127 			legacygroup = "smb";
5128 		else
5129 			legacygroup = "default";
5130 
5131 		/*
5132 		 * If the share exists (not NULL), then make sure it
5133 		 * is one we want to handle by getting the parent
5134 		 * group.
5135 		 */
5136 		if (share != NULL) {
5137 			group = sa_get_parent_group(share);
5138 		} else {
5139 			group = sa_get_group(handle, legacygroup);
5140 			if (group == NULL && strcmp(legacygroup, "smb") == 0) {
5141 				/*
5142 				 * This group may not exist, so create
5143 				 * as necessary. It only contains the
5144 				 * "smb" protocol.
5145 				 */
5146 				group = sa_create_group(handle, legacygroup,
5147 				    &ret);
5148 				if (group != NULL)
5149 					(void) sa_create_optionset(group,
5150 					    protocol);
5151 			}
5152 		}
5153 
5154 		if (group == NULL) {
5155 			ret = SA_SYSTEM_ERR;
5156 			goto err;
5157 		}
5158 
5159 		groupstatus = group_status(group);
5160 		if (share == NULL) {
5161 			share = sa_add_share(group, sharepath,
5162 			    persist, &ret);
5163 			if (share == NULL &&
5164 			    ret == SA_DUPLICATE_NAME) {
5165 				/*
5166 				 * Could be a ZFS path being started
5167 				 */
5168 				if (sa_zfs_is_shared(handle,
5169 				    sharepath)) {
5170 					ret = SA_OK;
5171 					group = sa_get_group(handle,
5172 					    "zfs");
5173 					if (group == NULL) {
5174 						/*
5175 						 * This shouldn't
5176 						 * happen.
5177 						 */
5178 						ret = SA_CONFIG_ERR;
5179 					} else {
5180 						share = sa_add_share(
5181 						    group, sharepath,
5182 						    persist, &ret);
5183 					}
5184 				}
5185 			}
5186 		} else {
5187 			char *type;
5188 			/*
5189 			 * May want to change persist state, but the
5190 			 * important thing is to change options. We
5191 			 * need to change them regardless of the
5192 			 * source.
5193 			 */
5194 
5195 			if (sa_zfs_is_shared(handle, sharepath)) {
5196 				zfs = 1;
5197 			}
5198 			remove_all_options(share, protocol);
5199 			type = sa_get_share_attr(share, "type");
5200 			if (type != NULL &&
5201 			    strcmp(type, "transient") != 0) {
5202 				curtype = SA_SHARE_PERMANENT;
5203 			}
5204 			if (type != NULL)
5205 				sa_free_attr_string(type);
5206 			if (curtype != persist) {
5207 				(void) sa_set_share_attr(share, "type",
5208 				    persist == SA_SHARE_PERMANENT ?
5209 				    "persist" : "transient");
5210 			}
5211 		}
5212 
5213 		/*
5214 		 * If there is a resource name, we may
5215 		 * actually care about it if this is share for
5216 		 * a protocol that uses resource level sharing
5217 		 * (SMB). We need to find the resource and, if
5218 		 * it exists, make sure it belongs to the
5219 		 * current share. If it doesn't exist, attempt
5220 		 * to create it.
5221 		 */
5222 
5223 		if (ret == SA_OK && resource != NULL) {
5224 			rsrc = sa_find_resource(handle, resource);
5225 			if (rsrc != NULL) {
5226 				if (share != sa_get_resource_parent(rsrc))
5227 					ret = SA_DUPLICATE_NAME;
5228 				} else {
5229 					rsrc = sa_add_resource(share, resource,
5230 					    persist, &ret);
5231 				}
5232 				if (features & SA_FEATURE_RESOURCE)
5233 					share = rsrc;
5234 			}
5235 
5236 			/* Have a group to hold this share path */
5237 			if (ret == SA_OK && options != NULL &&
5238 			    strlen(options) > 0) {
5239 				ret = sa_parse_legacy_options(share,
5240 				    options,
5241 				    protocol);
5242 			}
5243 			if (!zfs) {
5244 				/*
5245 				 * ZFS shares never have a description
5246 				 * and we can't store the values so
5247 				 * don't try.
5248 				 */
5249 				if (ret == SA_OK && description != NULL)
5250 					ret = sa_set_share_description(share,
5251 					    description);
5252 			}
5253 			if (ret == SA_OK &&
5254 			    strcmp(groupstatus, "enabled") == 0) {
5255 				if (rsrc != share)
5256 					ret = sa_enable_share(share, protocol);
5257 				else
5258 					ret = sa_enable_resource(rsrc,
5259 					    protocol);
5260 				if (ret == SA_OK &&
5261 				    persist == SA_SHARE_PERMANENT) {
5262 					(void) sa_update_legacy(share,
5263 					    protocol);
5264 				}
5265 				if (ret == SA_OK)
5266 					ret = sa_update_config(handle);
5267 			}
5268 	}
5269 err:
5270 	if (ret != SA_OK) {
5271 		(void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
5272 		    sharepath, sa_errorstr(ret));
5273 		ret = SA_LEGACY_ERR;
5274 	}
5275 	return (ret);
5276 }
5277 
5278 /*
5279  * sa_legacy_unshare(flags, argc, argv)
5280  *
5281  * Implements the original unshare command.
5282  */
5283 int
5284 sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
5285 {
5286 	char *protocol = "nfs"; /* for now */
5287 	char *options = NULL;
5288 	char *sharepath = NULL;
5289 	int persist = SA_SHARE_TRANSIENT;
5290 	int argsused = 0;
5291 	int c;
5292 	int ret = SA_OK;
5293 	int true_legacy = 0;
5294 	uint64_t features = 0;
5295 	sa_resource_t resource = NULL;
5296 	char cmd[MAXPATHLEN];
5297 #ifdef lint
5298 	flags = flags;
5299 	options = options;
5300 #endif
5301 
5302 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
5303 		switch (c) {
5304 		case 'h':
5305 		case '?':
5306 			break;
5307 		case 'F':
5308 			protocol = optarg;
5309 			if (!sa_valid_protocol(protocol)) {
5310 				if (format_legacy_path(cmd, MAXPATHLEN,
5311 				    protocol, "unshare") == 0 &&
5312 				    check_legacy_cmd(cmd)) {
5313 					true_legacy++;
5314 				} else {
5315 					(void) printf(gettext(
5316 					    "Invalid file system name\n"));
5317 					return (SA_INVALID_PROTOCOL);
5318 				}
5319 			}
5320 			break;
5321 		case 'o':
5322 			options = optarg;
5323 			argsused++;
5324 			break;
5325 		case 'p':
5326 			persist = SA_SHARE_PERMANENT;
5327 			argsused++;
5328 			break;
5329 		default:
5330 			(void) printf(gettext("usage: %s\n"),
5331 			    sa_get_usage(USAGE_UNSHARE));
5332 			return (SA_OK);
5333 		}
5334 	}
5335 
5336 	/* Have the info so construct what is needed */
5337 	if (optind == argc || (optind + 1) < argc || options != NULL) {
5338 		ret = SA_SYNTAX_ERR;
5339 	} else {
5340 		sa_share_t share;
5341 		char dir[MAXPATHLEN];
5342 		if (true_legacy) {
5343 			/* if still using legacy share/unshare, exec it */
5344 			ret = run_legacy_command(cmd, argv);
5345 			return (ret);
5346 		}
5347 		/*
5348 		 * Find the path in the internal configuration. If it
5349 		 * isn't found, attempt to resolve the path via
5350 		 * realpath() and try again.
5351 		 */
5352 		sharepath = argv[optind++];
5353 		share = sa_find_share(handle, sharepath);
5354 		if (share == NULL) {
5355 			if (realpath(sharepath, dir) == NULL) {
5356 				ret = SA_NO_SUCH_PATH;
5357 			} else {
5358 				share = sa_find_share(handle, dir);
5359 			}
5360 		}
5361 		if (share == NULL) {
5362 			/* Could be a resource name so check that next */
5363 			features = sa_proto_get_featureset(protocol);
5364 			resource = sa_find_resource(handle, sharepath);
5365 			if (resource != NULL) {
5366 				share = sa_get_resource_parent(resource);
5367 				if (features & SA_FEATURE_RESOURCE)
5368 					(void) sa_disable_resource(resource,
5369 					    protocol);
5370 				if (persist == SA_SHARE_PERMANENT) {
5371 					ret = sa_remove_resource(resource);
5372 					if (ret == SA_OK)
5373 						ret = sa_update_config(handle);
5374 				}
5375 				/*
5376 				 * If we still have a resource on the
5377 				 * share, we don't disable the share
5378 				 * itself. IF there aren't anymore, we
5379 				 * need to remove the share. The
5380 				 * removal will be done in the next
5381 				 * section if appropriate.
5382 				 */
5383 				resource = sa_get_share_resource(share, NULL);
5384 				if (resource != NULL)
5385 					share = NULL;
5386 			} else if (ret == SA_OK) {
5387 				/* Didn't find path and no  resource */
5388 				ret = SA_BAD_PATH;
5389 			}
5390 		}
5391 		if (share != NULL && resource == NULL) {
5392 			ret = sa_disable_share(share, protocol);
5393 			/*
5394 			 * Errors are ok and removal should still occur. The
5395 			 * legacy unshare is more forgiving of errors than the
5396 			 * remove-share subcommand which may need the force
5397 			 * flag set for some error conditions. That is, the
5398 			 * "unshare" command will always unshare if it can
5399 			 * while "remove-share" might require the force option.
5400 			 */
5401 			if (persist == SA_SHARE_PERMANENT) {
5402 				ret = sa_remove_share(share);
5403 				if (ret == SA_OK)
5404 					ret = sa_update_config(handle);
5405 			}
5406 		} else if (ret == SA_OK && share == NULL && resource == NULL) {
5407 			/*
5408 			 * If both share and resource are NULL, then
5409 			 * share not found. If one or the other was
5410 			 * found or there was an earlier error, we
5411 			 * assume it was handled earlier.
5412 			 */
5413 			ret = SA_NOT_SHARED;
5414 		}
5415 	}
5416 	switch (ret) {
5417 	default:
5418 		(void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
5419 		ret = SA_LEGACY_ERR;
5420 		break;
5421 	case SA_SYNTAX_ERR:
5422 		(void) printf(gettext("usage: %s\n"),
5423 		    sa_get_usage(USAGE_UNSHARE));
5424 		break;
5425 	case SA_OK:
5426 		break;
5427 	}
5428 	return (ret);
5429 }
5430 
5431 /*
5432  * Common commands that implement the sub-commands used by all
5433  * protocols. The entries are found via the lookup command
5434  */
5435 
5436 static sa_command_t commands[] = {
5437 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
5438 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
5439 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
5440 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
5441 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
5442 	{"list", 0, sa_list, USAGE_LIST},
5443 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
5444 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
5445 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
5446 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
5447 	{"show", 0, sa_show, USAGE_SHOW},
5448 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
5449 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
5450 	    SVC_SET|SVC_ACTION},
5451 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
5452 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
5453 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
5454 	{NULL, 0, NULL, NULL}
5455 };
5456 
5457 static char *
5458 sa_get_usage(sa_usage_t index)
5459 {
5460 	char *ret = NULL;
5461 	switch (index) {
5462 	case USAGE_ADD_SHARE:
5463 		ret = gettext("add-share [-nth] [-r resource-name] "
5464 		    "[-d \"description text\"] -s sharepath group");
5465 		break;
5466 	case USAGE_CREATE:
5467 		ret = gettext(
5468 		    "create [-nvh] [-P proto [-p property=value]] group");
5469 		break;
5470 	case USAGE_DELETE:
5471 		ret = gettext("delete [-nvh] [-P proto] [-f] group");
5472 		break;
5473 	case USAGE_DISABLE:
5474 		ret = gettext("disable [-nvh] {-a | group ...}");
5475 		break;
5476 	case USAGE_ENABLE:
5477 		ret = gettext("enable [-nvh] {-a | group ...}");
5478 		break;
5479 	case USAGE_LIST:
5480 		ret = gettext("list [-vh] [-P proto]");
5481 		break;
5482 	case USAGE_MOVE_SHARE:
5483 		ret = gettext(
5484 		    "move-share [-nvh] -s sharepath destination-group");
5485 		break;
5486 	case USAGE_REMOVE_SHARE:
5487 		ret = gettext(
5488 		    "remove-share [-fnvh] {-s sharepath | -r resource} "
5489 		    "group");
5490 		break;
5491 	case USAGE_SET:
5492 		ret = gettext("set [-nvh] -P proto [-S optspace] "
5493 		    "[-p property=value]* [-s sharepath] [-r resource]] "
5494 		    "group");
5495 		break;
5496 	case USAGE_SET_SECURITY:
5497 		ret = gettext("set-security [-nvh] -P proto -S security-type "
5498 		    "[-p property=value]* group");
5499 		break;
5500 	case USAGE_SET_SHARE:
5501 		ret = gettext("set-share [-nh] [-r resource] "
5502 		    "[-d \"description text\"] -s sharepath group");
5503 		break;
5504 	case USAGE_SHOW:
5505 		ret = gettext("show [-pvxh] [-P proto] [group ...]");
5506 		break;
5507 	case USAGE_SHARE:
5508 		ret = gettext("share [-F fstype] [-p] [-o optionlist]"
5509 		    "[-d description] [pathname [resourcename]]");
5510 		break;
5511 	case USAGE_START:
5512 		ret = gettext("start [-vh] [-P proto] {-a | group ...}");
5513 		break;
5514 	case USAGE_STOP:
5515 		ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
5516 		break;
5517 	case USAGE_UNSET:
5518 		ret = gettext("unset [-nvh] -P proto [-S optspace] "
5519 		    "[-p property]* group");
5520 		break;
5521 	case USAGE_UNSET_SECURITY:
5522 		ret = gettext("unset-security [-nvh] -P proto "
5523 		    "-S security-type [-p property]* group");
5524 		break;
5525 	case USAGE_UNSHARE:
5526 		ret = gettext(
5527 		    "unshare [-F fstype] [-p] [-o optionlist] sharepath");
5528 		break;
5529 	}
5530 	return (ret);
5531 }
5532 
5533 /*
5534  * sa_lookup(cmd, proto)
5535  *
5536  * Lookup the sub-command. proto isn't currently used, but it may
5537  * eventually provide a way to provide protocol specific sub-commands.
5538  */
5539 sa_command_t *
5540 sa_lookup(char *cmd, char *proto)
5541 {
5542 	int i;
5543 	size_t len;
5544 #ifdef lint
5545 	proto = proto;
5546 #endif
5547 
5548 	len = strlen(cmd);
5549 	for (i = 0; commands[i].cmdname != NULL; i++) {
5550 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
5551 			return (&commands[i]);
5552 	}
5553 	return (NULL);
5554 }
5555 
5556 void
5557 sub_command_help(char *proto)
5558 {
5559 	int i;
5560 #ifdef lint
5561 	proto = proto;
5562 #endif
5563 
5564 	(void) printf(gettext("\tsub-commands:\n"));
5565 	for (i = 0; commands[i].cmdname != NULL; i++) {
5566 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
5567 			(void) printf("\t%s\n",
5568 			    sa_get_usage((sa_usage_t)commands[i].cmdidx));
5569 	}
5570 }
5571