1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <unistd.h>
37 #include <getopt.h>
38 #include <utmpx.h>
39 #include <pwd.h>
40 #include <auth_attr.h>
41 #include <secdb.h>
42 #include <sys/param.h>
43 #include <sys/stat.h>
44 #include <errno.h>
45 
46 #include <libshare.h>
47 #include "sharemgr.h"
48 #include <libscf.h>
49 #include <libxml/tree.h>
50 #include <libintl.h>
51 
52 static char *sa_get_usage(sa_usage_t);
53 
54 /*
55  * Implementation of the common sub-commands supported by sharemgr.
56  * A number of helper functions are also included.
57  */
58 
59 /*
60  * has_protocol(group, proto)
61  *	If the group has an optionset with the specified protocol,
62  *	return true (1) otherwise false (0).
63  */
64 static int
65 has_protocol(sa_group_t group, char *protocol)
66 {
67 	sa_optionset_t optionset;
68 	int result = 0;
69 
70 	optionset = sa_get_optionset(group, protocol);
71 	if (optionset != NULL) {
72 		result++;
73 	}
74 	return (result);
75 }
76 
77 /*
78  * add_list(list, item)
79  *	Adds a new list member that points to item to the list.
80  *	If list is NULL, it starts a new list.  The function returns
81  *	the first member of the list.
82  */
83 struct list *
84 add_list(struct list *listp, void *item, void *data)
85 {
86 	struct list *new, *tmp;
87 
88 	new = malloc(sizeof (struct list));
89 	if (new != NULL) {
90 		new->next = NULL;
91 		new->item = item;
92 		new->itemdata = data;
93 	} else {
94 		return (listp);
95 	}
96 
97 	if (listp == NULL)
98 		return (new);
99 
100 	for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
101 		/* get to end of list */
102 	}
103 	tmp->next = new;
104 	return (listp);
105 }
106 
107 /*
108  * free_list(list)
109  *	Given a list, free all the members of the list;
110  */
111 static void
112 free_list(struct list *listp)
113 {
114 	struct list *tmp;
115 	while (listp != NULL) {
116 		tmp = listp;
117 		listp = listp->next;
118 		free(tmp);
119 	}
120 }
121 
122 /*
123  * check_authorization(instname, which)
124  *
125  * Checks to see if the specific type of authorization in which is
126  * enabled for the user in this SMF service instance.
127  */
128 
129 static int
130 check_authorization(char *instname, int which)
131 {
132 	scf_handle_t *handle = NULL;
133 	scf_simple_prop_t *prop = NULL;
134 	char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
135 	char *authstr = NULL;
136 	ssize_t numauths;
137 	int ret = B_TRUE;
138 	uid_t uid;
139 	struct passwd *pw = NULL;
140 
141 	uid = getuid();
142 	pw = getpwuid(uid);
143 	if (pw == NULL) {
144 		ret = B_FALSE;
145 	} else {
146 		/*
147 		 * Since names are restricted to SA_MAX_NAME_LEN won't
148 		 * overflow.
149 		 */
150 		(void) snprintf(svcstring, sizeof (svcstring), "%s:%s",
151 		    SA_SVC_FMRI_BASE, instname);
152 		handle = scf_handle_create(SCF_VERSION);
153 		if (handle != NULL) {
154 			if (scf_handle_bind(handle) == 0) {
155 				switch (which) {
156 				case SVC_SET:
157 					prop = scf_simple_prop_get(handle,
158 					    svcstring, "general",
159 					    SVC_AUTH_VALUE);
160 					break;
161 				case SVC_ACTION:
162 					prop = scf_simple_prop_get(handle,
163 					    svcstring, "general",
164 					    SVC_AUTH_ACTION);
165 					break;
166 				}
167 			}
168 		}
169 	}
170 	/* make sure we have an authorization string property */
171 	if (prop != NULL) {
172 		int i;
173 		numauths = scf_simple_prop_numvalues(prop);
174 		for (ret = 0, i = 0; i < numauths; i++) {
175 			authstr = scf_simple_prop_next_astring(prop);
176 			if (authstr != NULL) {
177 				/* check if this user has one of the strings */
178 				if (chkauthattr(authstr, pw->pw_name)) {
179 					ret = 1;
180 					break;
181 				}
182 			}
183 		}
184 		endauthattr();
185 		scf_simple_prop_free(prop);
186 	} else {
187 		/* no authorization string defined */
188 		ret = 0;
189 	}
190 	if (handle != NULL)
191 		scf_handle_destroy(handle);
192 	return (ret);
193 }
194 
195 /*
196  * check_authorizations(instname, flags)
197  *
198  * check all the needed authorizations for the user in this service
199  * instance. Return value of 1(true) or 0(false) indicates whether
200  * there are authorizations for the user or not.
201  */
202 
203 static int
204 check_authorizations(char *instname, int flags)
205 {
206 	int ret1 = 0;
207 	int ret2 = 0;
208 	int ret;
209 
210 	if (flags & SVC_SET)
211 		ret1 = check_authorization(instname, SVC_SET);
212 	if (flags & SVC_ACTION)
213 		ret2 = check_authorization(instname, SVC_ACTION);
214 	switch (flags) {
215 	case SVC_ACTION:
216 		ret = ret2;
217 		break;
218 	case SVC_SET:
219 		ret = ret1;
220 		break;
221 	case SVC_ACTION|SVC_SET:
222 		ret = ret1 & ret2;
223 		break;
224 	default:
225 		/* if not flags set, we assume we don't need authorizations */
226 		ret = 1;
227 	}
228 	return (ret);
229 }
230 
231 /*
232  * enable_group(group, updateproto)
233  *
234  * enable all the shares in the specified group. This is a helper for
235  * enable_all_groups in order to simplify regular and subgroup (zfs)
236  * disabling. Group has already been checked for non-NULL.
237  */
238 
239 static void
240 enable_group(sa_group_t group, char *updateproto)
241 {
242 	sa_share_t share;
243 
244 	for (share = sa_get_share(group, NULL);
245 	    share != NULL;
246 	    share = sa_get_next_share(share)) {
247 		if (updateproto != NULL)
248 			(void) sa_update_legacy(share, updateproto);
249 		(void) sa_enable_share(share, NULL);
250 	}
251 }
252 
253 /*
254  * isenabled(group)
255  *
256  * Returns B_TRUE if the group is enabled or B_FALSE if it isn't.
257  * Moved to separate function to reduce clutter in the code.
258  */
259 
260 static int
261 isenabled(sa_group_t group)
262 {
263 	char *state;
264 	int ret = B_FALSE;
265 
266 	if (group != NULL) {
267 		state = sa_get_group_attr(group, "state");
268 		if (state != NULL) {
269 			if (strcmp(state, "enabled") == 0)
270 				ret = B_TRUE;
271 			sa_free_attr_string(state);
272 		}
273 	}
274 	return (ret);
275 }
276 
277 /*
278  * enable_all_groups(list, setstate, online, updateproto)
279  *	Given a list of groups, enable each one found.  If updateproto
280  *	is not NULL, then update all the shares for the protocol that
281  *	was passed in.
282  */
283 static int
284 enable_all_groups(sa_handle_t handle, struct list *work, int setstate,
285 	int online, char *updateproto)
286 {
287 	int ret;
288 	char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
289 	char *state;
290 	char *name;
291 	char *zfs = NULL;
292 	sa_group_t group;
293 	sa_group_t subgroup;
294 
295 	for (ret = SA_OK; work != NULL;	work = work->next) {
296 		group = (sa_group_t)work->item;
297 
298 		/*
299 		 * If setstate == TRUE, then make sure to set
300 		 * enabled. This needs to be done here in order for
301 		 * the isenabled check to succeed on a newly enabled
302 		 * group.
303 		 */
304 		if (setstate == B_TRUE) {
305 			ret = sa_set_group_attr(group, "state",	"enabled");
306 			if (ret != SA_OK)
307 				break;
308 		}
309 
310 		/*
311 		 * Check to see if group is enabled. If it isn't, skip
312 		 * the rest.  We don't want shares starting if the
313 		 * group is disabled. The properties may have been
314 		 * updated, but there won't be a change until the
315 		 * group is enabled.
316 		 */
317 		if (!isenabled(group))
318 			continue;
319 
320 		/* if itemdata != NULL then a single share */
321 		if (work->itemdata != NULL) {
322 			ret = sa_enable_share((sa_share_t)work->itemdata, NULL);
323 		}
324 		if (ret != SA_OK)
325 			break;
326 
327 		/* if itemdata == NULL then the whole group */
328 		if (work->itemdata == NULL) {
329 			zfs = sa_get_group_attr(group, "zfs");
330 			/*
331 			 * if the share is managed by ZFS, don't
332 			 * update any of the protocols since ZFS is
333 			 * handling this.  updateproto will contain
334 			 * the name of the protocol that we want to
335 			 * update legacy files for.
336 			 */
337 			enable_group(group, zfs == NULL ? updateproto : NULL);
338 			for (subgroup = sa_get_sub_group(group);
339 			    subgroup != NULL;
340 			    subgroup = sa_get_next_group(subgroup)) {
341 				/* never update legacy for ZFS subgroups */
342 				enable_group(subgroup, NULL);
343 			}
344 		}
345 		if (online) {
346 			zfs = sa_get_group_attr(group, "zfs");
347 			name = sa_get_group_attr(group, "name");
348 			if (name != NULL) {
349 				if (zfs == NULL) {
350 					(void) snprintf(instance,
351 					    sizeof (instance), "%s:%s",
352 					    SA_SVC_FMRI_BASE, name);
353 					state = smf_get_state(instance);
354 					if (state == NULL ||
355 					    strcmp(state, "online") != 0) {
356 						(void) smf_enable_instance(
357 						    instance, 0);
358 						free(state);
359 					}
360 				} else {
361 					sa_free_attr_string(zfs);
362 					zfs = NULL;
363 				}
364 				if (name != NULL)
365 					sa_free_attr_string(name);
366 			}
367 		}
368 	}
369 	if (ret == SA_OK) {
370 		ret = sa_update_config(handle);
371 	}
372 	return (ret);
373 }
374 
375 /*
376  * chk_opt(optlistp, security, proto)
377  *
378  * Do a sanity check on the optlist provided for the protocol.  This
379  * is a syntax check and verification that the property is either a
380  * general or specific to a names optionset.
381  */
382 
383 static int
384 chk_opt(struct options *optlistp, int security, char *proto)
385 {
386 	struct options *optlist;
387 	char *sep = "";
388 	int notfirst = 0;
389 	int ret;
390 
391 	for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
392 		char *optname;
393 
394 		optname = optlist->optname;
395 		ret = OPT_ADD_OK;
396 		/* extract property/value pair */
397 		if (sa_is_security(optname, proto)) {
398 			if (!security)
399 				ret = OPT_ADD_SECURITY;
400 		} else {
401 			if (security)
402 				ret = OPT_ADD_PROPERTY;
403 		}
404 		if (ret != OPT_ADD_OK) {
405 			if (notfirst == 0)
406 				(void) printf(
407 				    gettext("Property syntax error: "));
408 			switch (ret) {
409 			case OPT_ADD_SYNTAX:
410 				(void) printf(gettext("%ssyntax error: %s"),
411 				    sep, optname);
412 				sep = ", ";
413 				break;
414 			case OPT_ADD_SECURITY:
415 				(void) printf(gettext("%s%s requires -S"),
416 				    optname, sep);
417 				sep = ", ";
418 				break;
419 			case OPT_ADD_PROPERTY:
420 				(void) printf(
421 				    gettext("%s%s not supported with -S"),
422 				    optname, sep);
423 				sep = ", ";
424 				break;
425 			}
426 			notfirst++;
427 		}
428 	}
429 	if (notfirst) {
430 		(void) printf("\n");
431 		ret = SA_SYNTAX_ERR;
432 	}
433 	return (ret);
434 }
435 
436 /*
437  * free_opt(optlist)
438  *	Free the specified option list.
439  */
440 static void
441 free_opt(struct options *optlist)
442 {
443 	struct options *nextopt;
444 	while (optlist != NULL) {
445 		nextopt = optlist->next;
446 		free(optlist);
447 		optlist = nextopt;
448 	}
449 }
450 
451 /*
452  * check property list for valid properties
453  * A null value is a remove which is always valid.
454  */
455 static int
456 valid_options(struct options *optlist, char *proto, void *object, char *sec)
457 {
458 	int ret = SA_OK;
459 	struct options *cur;
460 	sa_property_t prop;
461 	sa_optionset_t parent = NULL;
462 
463 	if (object != NULL) {
464 		if (sec == NULL)
465 			parent = sa_get_optionset(object, proto);
466 		else
467 			parent = sa_get_security(object, sec, proto);
468 	}
469 
470 	for (cur = optlist; cur != NULL; cur = cur->next) {
471 		if (cur->optvalue == NULL)
472 			continue;
473 		prop = sa_create_property(cur->optname, cur->optvalue);
474 		if (prop == NULL)
475 			ret = SA_NO_MEMORY;
476 		if (ret != SA_OK ||
477 		    (ret = sa_valid_property(parent, proto, prop)) != SA_OK) {
478 			(void) printf(
479 			    gettext("Could not add property %s: %s\n"),
480 			    cur->optname, sa_errorstr(ret));
481 		}
482 		(void) sa_remove_property(prop);
483 	}
484 	return (ret);
485 }
486 
487 /*
488  * add_optionset(group, optlist, protocol, *err)
489  *	Add the options in optlist to an optionset and then add the optionset
490  *	to the group.
491  *
492  *	The return value indicates if there was a "change" while errors are
493  *	returned via the *err parameters.
494  */
495 static int
496 add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
497 {
498 	sa_optionset_t optionset;
499 	int ret = SA_OK;
500 	int result = 0;
501 
502 	optionset = sa_get_optionset(group, proto);
503 	if (optionset == NULL) {
504 		optionset = sa_create_optionset(group, proto);
505 		result = 1; /* adding a protocol is a change */
506 	}
507 	if (optionset == NULL) {
508 		ret = SA_NO_MEMORY;
509 		goto out;
510 	}
511 	while (optlist != NULL) {
512 		sa_property_t prop;
513 		prop = sa_get_property(optionset, optlist->optname);
514 		if (prop == NULL) {
515 			/*
516 			 * add the property, but only if it is
517 			 * a non-NULL or non-zero length value
518 			 */
519 			if (optlist->optvalue != NULL) {
520 				prop = sa_create_property(optlist->optname,
521 				    optlist->optvalue);
522 				if (prop != NULL) {
523 					ret = sa_valid_property(optionset,
524 					    proto, prop);
525 					if (ret != SA_OK) {
526 						(void) sa_remove_property(prop);
527 						(void) printf(gettext("Could "
528 						    "not add property "
529 						    "%s: %s\n"),
530 						    optlist->optname,
531 						    sa_errorstr(ret));
532 					}
533 				}
534 				if (ret == SA_OK) {
535 					ret = sa_add_property(optionset, prop);
536 					if (ret != SA_OK) {
537 						(void) printf(gettext(
538 						    "Could not add property "
539 						    "%s: %s\n"),
540 						    optlist->optname,
541 						    sa_errorstr(ret));
542 					} else {
543 						/* there was a change */
544 						result = 1;
545 					}
546 				}
547 			}
548 		} else {
549 			ret = sa_update_property(prop, optlist->optvalue);
550 			/* should check to see if value changed */
551 			if (ret != SA_OK) {
552 				(void) printf(gettext("Could not update "
553 				    "property %s: %s\n"), optlist->optname,
554 				    sa_errorstr(ret));
555 			} else {
556 				result = 1;
557 			}
558 		}
559 		optlist = optlist->next;
560 	}
561 	ret = sa_commit_properties(optionset, 0);
562 
563 out:
564 	if (err != NULL)
565 		*err = ret;
566 	return (result);
567 }
568 
569 /*
570  * sa_create(flags, argc, argv)
571  *	create a new group
572  *	this may or may not have a protocol associated with it.
573  *	No protocol means "all" protocols in this case.
574  */
575 static int
576 sa_create(sa_handle_t handle, int flags, int argc, char *argv[])
577 {
578 	char *groupname;
579 
580 	sa_group_t group;
581 	int verbose = 0;
582 	int dryrun = 0;
583 	int c;
584 	char *protocol = NULL;
585 	int ret = SA_OK;
586 	struct options *optlist = NULL;
587 	int err = 0;
588 	int auth;
589 
590 	while ((c = getopt(argc, argv, "?hvnP:p:")) != EOF) {
591 		switch (c) {
592 		case 'v':
593 			verbose++;
594 			break;
595 		case 'n':
596 			dryrun++;
597 			break;
598 		case 'P':
599 			protocol = optarg;
600 			if (sa_valid_protocol(protocol))
601 				break;
602 			(void) printf(gettext(
603 			    "Invalid protocol specified: %s\n"), protocol);
604 			return (SA_INVALID_PROTOCOL);
605 			break;
606 		case 'p':
607 			ret = add_opt(&optlist, optarg, 0);
608 			switch (ret) {
609 			case OPT_ADD_SYNTAX:
610 				(void) printf(gettext(
611 				    "Property syntax error for property: %s\n"),
612 				    optarg);
613 				return (SA_SYNTAX_ERR);
614 			case OPT_ADD_SECURITY:
615 				(void) printf(gettext(
616 				    "Security properties need "
617 				    "to be set with set-security: %s\n"),
618 				    optarg);
619 				return (SA_SYNTAX_ERR);
620 			default:
621 				break;
622 			}
623 			break;
624 		default:
625 		case 'h':
626 		case '?':
627 			(void) printf(gettext("usage: %s\n"),
628 			    sa_get_usage(USAGE_CREATE));
629 			return (0);
630 		}
631 	}
632 
633 	if (optind >= argc) {
634 		(void) printf(gettext("usage: %s\n"),
635 		    sa_get_usage(USAGE_CREATE));
636 		(void) printf(gettext("\tgroup must be specified.\n"));
637 		return (SA_BAD_PATH);
638 	}
639 
640 	if ((optind + 1) < argc) {
641 		(void) printf(gettext("usage: %s\n"),
642 		    sa_get_usage(USAGE_CREATE));
643 		(void) printf(gettext("\textraneous group(s) at end\n"));
644 		return (SA_SYNTAX_ERR);
645 	}
646 
647 	if (protocol == NULL && optlist != NULL) {
648 		/* lookup default protocol */
649 		(void) printf(gettext("usage: %s\n"),
650 		    sa_get_usage(USAGE_CREATE));
651 		(void) printf(gettext("\tprotocol must be specified "
652 		    "with properties\n"));
653 		return (SA_INVALID_PROTOCOL);
654 	}
655 
656 	if (optlist != NULL)
657 		ret = chk_opt(optlist, 0, protocol);
658 	if (ret == OPT_ADD_SECURITY) {
659 		(void) printf(gettext("Security properties not "
660 		    "supported with create\n"));
661 		return (SA_SYNTAX_ERR);
662 	}
663 
664 	/*
665 	 * If a group already exists, we can only add a new protocol
666 	 * to it and not create a new one or add the same protocol
667 	 * again.
668 	 */
669 
670 	groupname = argv[optind];
671 
672 	auth = check_authorizations(groupname, flags);
673 
674 	group = sa_get_group(handle, groupname);
675 	if (group != NULL) {
676 		/* group exists so must be a protocol add */
677 		if (protocol != NULL) {
678 			if (has_protocol(group, protocol)) {
679 				(void) printf(gettext(
680 				    "Group \"%s\" already exists"
681 				    " with protocol %s\n"), groupname,
682 				    protocol);
683 				ret = SA_DUPLICATE_NAME;
684 			}
685 		} else {
686 			/* must add new protocol */
687 			(void) printf(gettext(
688 			    "Group already exists and no protocol "
689 			    "specified.\n"));
690 			ret = SA_DUPLICATE_NAME;
691 		}
692 	} else {
693 		/*
694 		 * is it a valid name? Must comply with SMF instance
695 		 * name restrictions.
696 		 */
697 		if (!sa_valid_group_name(groupname)) {
698 			ret = SA_INVALID_NAME;
699 			(void) printf(gettext("Invalid group name: %s\n"),
700 			    groupname);
701 		}
702 	}
703 	if (ret == SA_OK) {
704 		/* check protocol vs optlist */
705 		if (optlist != NULL) {
706 			/* check options, if any, for validity */
707 			ret = valid_options(optlist, protocol, group, NULL);
708 		}
709 	}
710 	if (ret == SA_OK && !dryrun) {
711 		if (group == NULL) {
712 			group = sa_create_group(handle, (char *)groupname,
713 			    &err);
714 		}
715 		if (group != NULL) {
716 			sa_optionset_t optionset;
717 			if (optlist != NULL) {
718 				(void) add_optionset(group, optlist, protocol,
719 				    &ret);
720 			} else if (protocol != NULL) {
721 				optionset = sa_create_optionset(group,
722 				    protocol);
723 				if (optionset == NULL)
724 					ret = SA_NO_MEMORY;
725 			} else if (protocol == NULL) {
726 				char **protolist;
727 				int numprotos, i;
728 				numprotos = sa_get_protocols(&protolist);
729 				for (i = 0; i < numprotos; i++) {
730 					optionset = sa_create_optionset(group,
731 					    protolist[i]);
732 				}
733 				if (protolist != NULL)
734 					free(protolist);
735 			}
736 			/*
737 			 * We have a group and legal additions
738 			 */
739 			if (ret == SA_OK) {
740 				/*
741 				 * Commit to configuration for protocols that
742 				 * need to do block updates. For NFS, this
743 				 * doesn't do anything but it will be run for
744 				 * all protocols that implement the
745 				 * appropriate plugin.
746 				 */
747 				ret = sa_update_config(handle);
748 			} else {
749 				if (group != NULL)
750 					(void) sa_remove_group(group);
751 			}
752 		} else {
753 			ret = err;
754 			(void) printf(gettext("Could not create group: %s\n"),
755 			    sa_errorstr(ret));
756 		}
757 	}
758 	if (dryrun && ret == SA_OK && !auth && verbose) {
759 		(void) printf(gettext("Command would fail: %s\n"),
760 		    sa_errorstr(SA_NO_PERMISSION));
761 		ret = SA_NO_PERMISSION;
762 	}
763 	free_opt(optlist);
764 	return (ret);
765 }
766 
767 /*
768  * group_status(group)
769  *
770  * return the current status (enabled/disabled) of the group.
771  */
772 
773 static char *
774 group_status(sa_group_t group)
775 {
776 	char *state;
777 	int enabled = 0;
778 
779 	state = sa_get_group_attr(group, "state");
780 	if (state != NULL) {
781 		if (strcmp(state, "enabled") == 0) {
782 			enabled = 1;
783 		}
784 		sa_free_attr_string(state);
785 	}
786 	return (enabled ? "enabled" : "disabled");
787 }
788 
789 /*
790  * sa_delete(flags, argc, argv)
791  *
792  *	Delete a group.
793  */
794 
795 static int
796 sa_delete(sa_handle_t handle, int flags, int argc, char *argv[])
797 {
798 	char *groupname;
799 	sa_group_t group;
800 	sa_share_t share;
801 	int verbose = 0;
802 	int dryrun = 0;
803 	int force = 0;
804 	int c;
805 	char *protocol = NULL;
806 	char *sectype = NULL;
807 	int ret = SA_OK;
808 	int auth;
809 
810 	while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
811 		switch (c) {
812 		case 'v':
813 			verbose++;
814 			break;
815 		case 'n':
816 			dryrun++;
817 			break;
818 		case 'P':
819 			protocol = optarg;
820 			if (!sa_valid_protocol(protocol)) {
821 				(void) printf(gettext("Invalid protocol "
822 				    "specified: %s\n"),   protocol);
823 				return (SA_INVALID_PROTOCOL);
824 			}
825 			break;
826 		case 'S':
827 			sectype = optarg;
828 			break;
829 		case 'f':
830 			force++;
831 			break;
832 		default:
833 		case 'h':
834 		case '?':
835 			(void) printf(gettext("usage: %s\n"),
836 			    sa_get_usage(USAGE_DELETE));
837 			return (0);
838 		}
839 	}
840 
841 	if (optind >= argc) {
842 		(void) printf(gettext("usage: %s\n"),
843 		    sa_get_usage(USAGE_DELETE));
844 		(void) printf(gettext("\tgroup must be specified.\n"));
845 		return (SA_SYNTAX_ERR);
846 	}
847 
848 	if ((optind + 1) < argc) {
849 		(void) printf(gettext("usage: %s\n"),
850 		    sa_get_usage(USAGE_DELETE));
851 		(void) printf(gettext("\textraneous group(s) at end\n"));
852 		return (SA_SYNTAX_ERR);
853 	}
854 
855 	if (sectype != NULL && protocol == NULL) {
856 		(void) printf(gettext("usage: %s\n"),
857 		    sa_get_usage(USAGE_DELETE));
858 		(void) printf(gettext("\tsecurity requires protocol to be "
859 		    "specified.\n"));
860 		return (SA_SYNTAX_ERR);
861 	}
862 
863 	/*
864 	 * Determine if the group already exists since it must in
865 	 * order to be removed.
866 	 *
867 	 * We can delete when:
868 	 *
869 	 *	- group is empty
870 	 *	- force flag is set
871 	 *	- if protocol specified, only delete the protocol
872 	 */
873 
874 	groupname = argv[optind];
875 	group = sa_get_group(handle, groupname);
876 	if (group == NULL) {
877 		ret = SA_NO_SUCH_GROUP;
878 		goto done;
879 	}
880 	auth = check_authorizations(groupname, flags);
881 	if (protocol == NULL) {
882 		share = sa_get_share(group, NULL);
883 		if (share != NULL)
884 			ret = SA_BUSY;
885 		if (share == NULL || (share != NULL && force == 1)) {
886 			ret = SA_OK;
887 			if (!dryrun) {
888 				while (share != NULL) {
889 					sa_share_t next_share;
890 					next_share = sa_get_next_share(share);
891 					/*
892 					 * need to do the disable of
893 					 * each share, but don't
894 					 * actually do anything on a
895 					 * dryrun.
896 					 */
897 					ret = sa_disable_share(share, NULL);
898 					ret = sa_remove_share(share);
899 					share = next_share;
900 				}
901 				ret = sa_remove_group(group);
902 			}
903 		}
904 		/* Commit to configuration if not a dryrun */
905 		if (!dryrun && ret == SA_OK) {
906 			ret = sa_update_config(handle);
907 		}
908 	} else {
909 		/* a protocol delete */
910 		sa_optionset_t optionset;
911 		sa_security_t security;
912 			if (sectype != NULL) {
913 			/* only delete specified security */
914 			security = sa_get_security(group, sectype, protocol);
915 			if (security != NULL && !dryrun)
916 				ret = sa_destroy_security(security);
917 			else
918 				ret = SA_INVALID_PROTOCOL;
919 		} else {
920 			optionset = sa_get_optionset(group, protocol);
921 			if (optionset != NULL && !dryrun) {
922 				/*
923 				 * have an optionset with
924 				 * protocol to delete
925 				 */
926 				ret = sa_destroy_optionset(optionset);
927 				/*
928 				 * Now find all security sets
929 				 * for the protocol and remove
930 				 * them. Don't remove other
931 				 * protocols.
932 				 */
933 				for (security =
934 				    sa_get_security(group, NULL, NULL);
935 				    ret == SA_OK && security != NULL;
936 				    security = sa_get_next_security(security)) {
937 					char *secprot;
938 					secprot = sa_get_security_attr(security,
939 					    "type");
940 					if (secprot != NULL &&
941 					    strcmp(secprot, protocol) == 0)
942 						ret = sa_destroy_security(
943 						    security);
944 					if (secprot != NULL)
945 						sa_free_attr_string(secprot);
946 				}
947 			} else {
948 				if (!dryrun)
949 					ret = SA_INVALID_PROTOCOL;
950 			}
951 		}
952 	}
953 
954 done:
955 	if (ret != SA_OK) {
956 		(void) printf(gettext("Could not delete group: %s\n"),
957 		    sa_errorstr(ret));
958 	} else if (dryrun && !auth && verbose) {
959 		(void) printf(gettext("Command would fail: %s\n"),
960 		    sa_errorstr(SA_NO_PERMISSION));
961 	}
962 	return (ret);
963 }
964 
965 /*
966  * strndupr(*buff, str, buffsize)
967  *
968  * used with small strings to duplicate and possibly increase the
969  * buffer size of a string.
970  */
971 static char *
972 strndupr(char *buff, char *str, int *buffsize)
973 {
974 	int limit;
975 	char *orig_buff = buff;
976 
977 	if (buff == NULL) {
978 		buff = (char *)malloc(64);
979 		if (buff == NULL)
980 			return (NULL);
981 		*buffsize = 64;
982 		buff[0] = '\0';
983 	}
984 	limit = strlen(buff) + strlen(str) + 1;
985 	if (limit > *buffsize) {
986 		limit = *buffsize = *buffsize + ((limit / 64) + 64);
987 		buff = realloc(buff, limit);
988 	}
989 	if (buff != NULL) {
990 		(void) strcat(buff, str);
991 	} else {
992 		/* if it fails, fail it hard */
993 		if (orig_buff != NULL)
994 			free(orig_buff);
995 	}
996 	return (buff);
997 }
998 
999 /*
1000  * group_proto(group)
1001  *
1002  * return a string of all the protocols (space separated) associated
1003  * with this group.
1004  */
1005 
1006 static char *
1007 group_proto(sa_group_t group)
1008 {
1009 	sa_optionset_t optionset;
1010 	char *proto;
1011 	char *buff = NULL;
1012 	int buffsize = 0;
1013 	int addspace = 0;
1014 	/*
1015 	 * get the protocol list by finding the optionsets on this
1016 	 * group and extracting the type value. The initial call to
1017 	 * strndupr() initailizes buff.
1018 	 */
1019 	buff = strndupr(buff, "", &buffsize);
1020 	if (buff != NULL) {
1021 		for (optionset = sa_get_optionset(group, NULL);
1022 		    optionset != NULL && buff != NULL;
1023 		    optionset = sa_get_next_optionset(optionset)) {
1024 			/*
1025 			 * extract out the protocol type from this optionset
1026 			 * and append it to the buffer "buff". strndupr() will
1027 			 * reallocate space as necessay.
1028 			 */
1029 			proto = sa_get_optionset_attr(optionset, "type");
1030 			if (proto != NULL) {
1031 				if (addspace++)
1032 					buff = strndupr(buff, " ", &buffsize);
1033 				buff = strndupr(buff, proto, &buffsize);
1034 				sa_free_attr_string(proto);
1035 			}
1036 		}
1037 	}
1038 	return (buff);
1039 }
1040 
1041 /*
1042  * sa_list(flags, argc, argv)
1043  *
1044  * implements the "list" subcommand to list groups and optionally
1045  * their state and protocols.
1046  */
1047 
1048 /*ARGSUSED*/
1049 static int
1050 sa_list(sa_handle_t handle, int flags, int argc, char *argv[])
1051 {
1052 	sa_group_t group;
1053 	int verbose = 0;
1054 	int c;
1055 	char *protocol = NULL;
1056 
1057 	while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
1058 		switch (c) {
1059 		case 'v':
1060 			verbose++;
1061 			break;
1062 		case 'P':
1063 			protocol = optarg;
1064 			if (!sa_valid_protocol(protocol)) {
1065 				(void) printf(gettext(
1066 				    "Invalid protocol specified: %s\n"),
1067 				    protocol);
1068 				return (SA_INVALID_PROTOCOL);
1069 			}
1070 			break;
1071 		default:
1072 		case 'h':
1073 		case '?':
1074 			(void) printf(gettext("usage: %s\n"),
1075 			    sa_get_usage(USAGE_LIST));
1076 			return (0);
1077 		}
1078 	}
1079 
1080 	for (group = sa_get_group(handle, NULL);
1081 	    group != NULL;
1082 	    group = sa_get_next_group(group)) {
1083 		char *name;
1084 		char *proto;
1085 		if (protocol == NULL || has_protocol(group, protocol)) {
1086 			name = sa_get_group_attr(group, "name");
1087 			if (name != NULL && (verbose > 1 || name[0] != '#')) {
1088 				(void) printf("%s", (char *)name);
1089 				if (verbose) {
1090 					/*
1091 					 * Need the list of protocols
1092 					 * and current status once
1093 					 * available. We do want to
1094 					 * translate the
1095 					 * enabled/disabled text here.
1096 					 */
1097 					(void) printf("\t%s", isenabled(group) ?
1098 					    gettext("enabled") :
1099 					    gettext("disabled"));
1100 					proto = group_proto(group);
1101 					if (proto != NULL) {
1102 						(void) printf("\t%s",
1103 						    (char *)proto);
1104 						free(proto);
1105 					}
1106 				}
1107 				(void) printf("\n");
1108 			}
1109 			if (name != NULL)
1110 				sa_free_attr_string(name);
1111 		}
1112 	}
1113 	return (0);
1114 }
1115 
1116 /*
1117  * out_properties(optionset, proto, sec)
1118  *
1119  * Format the properties and encode the protocol and optional named
1120  * optionset into the string.
1121  *
1122  * format is protocol[:name]=(property-list)
1123  */
1124 
1125 static void
1126 out_properties(sa_optionset_t optionset, char *proto, char *sec)
1127 {
1128 	char *type;
1129 	char *value;
1130 	int spacer;
1131 	sa_property_t prop;
1132 
1133 	if (sec == NULL)
1134 		(void) printf(" %s=(", proto ? proto : gettext("all"));
1135 	else
1136 		(void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
1137 
1138 	for (spacer = 0, prop = sa_get_property(optionset, NULL);
1139 	    prop != NULL;
1140 	    prop = sa_get_next_property(prop)) {
1141 
1142 		/*
1143 		 * extract the property name/value and output with
1144 		 * appropriate spacing. I.e. no prefixed space the
1145 		 * first time through but a space on subsequent
1146 		 * properties.
1147 		 */
1148 		type = sa_get_property_attr(prop, "type");
1149 		value = sa_get_property_attr(prop, "value");
1150 		if (type != NULL) {
1151 			(void) printf("%s%s=", spacer ? " " : "",	type);
1152 			spacer = 1;
1153 			if (value != NULL)
1154 				(void) printf("\"%s\"", value);
1155 			else
1156 				(void) printf("\"\"");
1157 		}
1158 		if (type != NULL)
1159 			sa_free_attr_string(type);
1160 		if (value != NULL)
1161 			sa_free_attr_string(value);
1162 	}
1163 	(void) printf(")");
1164 }
1165 
1166 /*
1167  * show_properties(group, protocol, prefix)
1168  *
1169  * print the properties for a group. If protocol is NULL, do all
1170  * protocols otherwise only the specified protocol. All security
1171  * (named groups specific to the protocol) are included.
1172  *
1173  * The "prefix" is always applied. The caller knows whether it wants
1174  * some type of prefix string (white space) or not.  Once the prefix
1175  * has been output, it is reduced to the zero length string for the
1176  * remainder of the property output.
1177  */
1178 
1179 static void
1180 show_properties(sa_group_t group, char *protocol, char *prefix)
1181 {
1182 	sa_optionset_t optionset;
1183 	sa_security_t security;
1184 	char *value;
1185 	char *secvalue;
1186 
1187 	if (protocol != NULL) {
1188 		optionset = sa_get_optionset(group, protocol);
1189 		if (optionset != NULL) {
1190 			(void) printf("%s", prefix);
1191 			prefix = "";
1192 			out_properties(optionset, protocol, NULL);
1193 		}
1194 		security = sa_get_security(group, protocol, NULL);
1195 		if (security != NULL) {
1196 			(void) printf("%s", prefix);
1197 			prefix = "";
1198 			out_properties(security, protocol, NULL);
1199 		}
1200 	} else {
1201 		for (optionset = sa_get_optionset(group, protocol);
1202 		    optionset != NULL;
1203 		    optionset = sa_get_next_optionset(optionset)) {
1204 
1205 			value = sa_get_optionset_attr(optionset, "type");
1206 			(void) printf("%s", prefix);
1207 			prefix = "";
1208 			out_properties(optionset, value, 0);
1209 			if (value != NULL)
1210 				sa_free_attr_string(value);
1211 		}
1212 		for (security = sa_get_security(group, NULL, protocol);
1213 		    security != NULL;
1214 		    security = sa_get_next_security(security)) {
1215 
1216 			value = sa_get_security_attr(security, "type");
1217 			secvalue = sa_get_security_attr(security, "sectype");
1218 			(void) printf("%s", prefix);
1219 			prefix = "";
1220 			out_properties(security, value, secvalue);
1221 			if (value != NULL)
1222 				sa_free_attr_string(value);
1223 			if (secvalue != NULL)
1224 				sa_free_attr_string(secvalue);
1225 		}
1226 	}
1227 }
1228 
1229 /*
1230  * show_group(group, verbose, properties, proto, subgroup)
1231  *
1232  * helper function to show the contents of a group.
1233  */
1234 
1235 static void
1236 show_group(sa_group_t group, int verbose, int properties, char *proto,
1237 		char *subgroup)
1238 {
1239 	sa_share_t share;
1240 	char *groupname;
1241 	char *sharepath;
1242 	char *resource;
1243 	char *description;
1244 	char *type;
1245 	char *zfs = NULL;
1246 	int iszfs = 0;
1247 
1248 	groupname = sa_get_group_attr(group, "name");
1249 	if (groupname != NULL) {
1250 		if (proto != NULL && !has_protocol(group, proto)) {
1251 			sa_free_attr_string(groupname);
1252 			return;
1253 		}
1254 		/*
1255 		 * check to see if the group is managed by ZFS. If
1256 		 * there is an attribute, then it is. A non-NULL zfs
1257 		 * variable will trigger the different way to display
1258 		 * and will remove the transient property indicator
1259 		 * from the output.
1260 		 */
1261 		zfs = sa_get_group_attr(group, "zfs");
1262 		if (zfs != NULL) {
1263 			iszfs = 1;
1264 			sa_free_attr_string(zfs);
1265 		}
1266 		share = sa_get_share(group, NULL);
1267 		if (subgroup == NULL)
1268 			(void) printf("%s", groupname);
1269 		else
1270 			(void) printf("    %s/%s", subgroup, groupname);
1271 		if (properties)
1272 			show_properties(group, proto, "");
1273 		(void) printf("\n");
1274 		if (strcmp(groupname, "zfs") == 0) {
1275 			sa_group_t zgroup;
1276 
1277 			for (zgroup = sa_get_sub_group(group);
1278 			    zgroup != NULL;
1279 			    zgroup = sa_get_next_group(zgroup)) {
1280 				show_group(zgroup, verbose, properties, proto,
1281 				    "zfs");
1282 			}
1283 			sa_free_attr_string(groupname);
1284 			return;
1285 		}
1286 		/*
1287 		 * Have a group, so list the contents. Resource and
1288 		 * description are only listed if verbose is set.
1289 		 */
1290 		for (share = sa_get_share(group, NULL);
1291 		    share != NULL;
1292 		    share = sa_get_next_share(share)) {
1293 			sharepath = sa_get_share_attr(share, "path");
1294 			if (sharepath != NULL) {
1295 				if (verbose) {
1296 					resource = sa_get_share_attr(share,
1297 					    "resource");
1298 					description =
1299 					    sa_get_share_description(share);
1300 					type = sa_get_share_attr(share,
1301 					    "type");
1302 					if (type != NULL && !iszfs &&
1303 					    strcmp(type, "transient") == 0)
1304 						(void) printf("\t* ");
1305 					else
1306 						(void) printf("\t  ");
1307 					if (resource != NULL &&
1308 					    strlen(resource) > 0) {
1309 						(void) printf("%s=%s",
1310 						    resource, sharepath);
1311 					} else {
1312 						(void) printf("%s", sharepath);
1313 					}
1314 					if (resource != NULL)
1315 						sa_free_attr_string(resource);
1316 					if (properties)
1317 						show_properties(share, NULL,
1318 						    "\t");
1319 					if (description != NULL) {
1320 						if (strlen(description) > 0) {
1321 							(void) printf(
1322 							    "\t\"%s\"",
1323 							    description);
1324 						}
1325 						sa_free_share_description(
1326 						    description);
1327 					}
1328 					if (type != NULL)
1329 						sa_free_attr_string(type);
1330 				} else {
1331 					(void) printf("\t%s", sharepath);
1332 					if (properties)
1333 						show_properties(share, NULL,
1334 						    "\t");
1335 				}
1336 				(void) printf("\n");
1337 				sa_free_attr_string(sharepath);
1338 			}
1339 		}
1340 	}
1341 	if (groupname != NULL) {
1342 		sa_free_attr_string(groupname);
1343 	}
1344 }
1345 
1346 /*
1347  * show_group_xml_init()
1348  *
1349  * Create an XML document that will be used to display config info via
1350  * XML format.
1351  */
1352 
1353 xmlDocPtr
1354 show_group_xml_init()
1355 {
1356 	xmlDocPtr doc;
1357 	xmlNodePtr root;
1358 
1359 	doc = xmlNewDoc((xmlChar *)"1.0");
1360 	if (doc != NULL) {
1361 		root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
1362 		if (root != NULL)
1363 			xmlDocSetRootElement(doc, root);
1364 	}
1365 	return (doc);
1366 }
1367 
1368 /*
1369  * show_group_xml(doc, group)
1370  *
1371  * Copy the group info into the XML doc.
1372  */
1373 
1374 static void
1375 show_group_xml(xmlDocPtr doc, sa_group_t group)
1376 {
1377 	xmlNodePtr node;
1378 	xmlNodePtr root;
1379 
1380 	root = xmlDocGetRootElement(doc);
1381 	node = xmlCopyNode((xmlNodePtr)group, 1);
1382 	if (node != NULL && root != NULL) {
1383 		xmlAddChild(root, node);
1384 		/*
1385 		 * In the future, we may have interally used tags that
1386 		 * should not appear in the XML output. Remove
1387 		 * anything we don't want to show here.
1388 		 */
1389 	}
1390 }
1391 
1392 /*
1393  * sa_show(flags, argc, argv)
1394  *
1395  * Implements the show subcommand.
1396  */
1397 
1398 /*ARGSUSED*/
1399 int
1400 sa_show(sa_handle_t handle, int flags, int argc, char *argv[])
1401 {
1402 	sa_group_t group;
1403 	int verbose = 0;
1404 	int properties = 0;
1405 	int c;
1406 	int ret = SA_OK;
1407 	char *protocol = NULL;
1408 	int xml = 0;
1409 	xmlDocPtr doc;
1410 
1411 	while ((c = getopt(argc, argv, "?hvP:px")) !=	EOF) {
1412 		switch (c) {
1413 		case 'v':
1414 			verbose++;
1415 			break;
1416 		case 'p':
1417 			properties++;
1418 			break;
1419 		case 'P':
1420 			protocol = optarg;
1421 			if (!sa_valid_protocol(protocol)) {
1422 				(void) printf(gettext(
1423 				    "Invalid protocol specified: %s\n"),
1424 				    protocol);
1425 				return (SA_INVALID_PROTOCOL);
1426 			}
1427 			break;
1428 		case 'x':
1429 			xml++;
1430 			break;
1431 		default:
1432 		case 'h':
1433 		case '?':
1434 			(void) printf(gettext("usage: %s\n"),
1435 			    sa_get_usage(USAGE_SHOW));
1436 			return (0);
1437 		}
1438 	}
1439 
1440 	if (xml) {
1441 		doc = show_group_xml_init();
1442 		if (doc == NULL)
1443 			ret = SA_NO_MEMORY;
1444 	}
1445 
1446 	if (optind == argc) {
1447 		/* No group specified so go through them all */
1448 		for (group = sa_get_group(handle, NULL);
1449 		    group != NULL;
1450 		    group = sa_get_next_group(group)) {
1451 			/*
1452 			 * Have a group so check if one we want and then list
1453 			 * contents with appropriate options.
1454 			 */
1455 			if (xml)
1456 				show_group_xml(doc, group);
1457 			else
1458 				show_group(group, verbose, properties, protocol,
1459 				    NULL);
1460 		}
1461 	} else {
1462 		/* Have a specified list of groups */
1463 		for (; optind < argc; optind++) {
1464 			group = sa_get_group(handle, argv[optind]);
1465 			if (group != NULL) {
1466 				if (xml)
1467 					show_group_xml(doc, group);
1468 				else
1469 					show_group(group, verbose, properties,
1470 					    protocol, NULL);
1471 			} else {
1472 				(void) printf(gettext("%s: not found\n"),
1473 				    argv[optind]);
1474 				ret = SA_NO_SUCH_GROUP;
1475 			}
1476 		}
1477 	}
1478 	if (xml && ret == SA_OK) {
1479 		xmlDocFormatDump(stdout, doc, 1);
1480 		xmlFreeDoc(doc);
1481 	}
1482 	return (ret);
1483 
1484 }
1485 
1486 /*
1487  * enable_share(group, share, update_legacy)
1488  *
1489  * helper function to enable a share if the group is enabled.
1490  */
1491 
1492 static int
1493 enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
1494 		int update_legacy)
1495 {
1496 	char *value;
1497 	int enabled;
1498 	sa_optionset_t optionset;
1499 	int ret = SA_OK;
1500 	char *zfs = NULL;
1501 	int iszfs = 0;
1502 
1503 	/*
1504 	 * need to enable this share if the group is enabled but not
1505 	 * otherwise. The enable is also done on each protocol
1506 	 * represented in the group.
1507 	 */
1508 	value = sa_get_group_attr(group, "state");
1509 	enabled = value != NULL && strcmp(value, "enabled") == 0;
1510 	if (value != NULL)
1511 		sa_free_attr_string(value);
1512 	/* remove legacy config if necessary */
1513 	if (update_legacy)
1514 		ret = sa_delete_legacy(share);
1515 	zfs = sa_get_group_attr(group, "zfs");
1516 	if (zfs != NULL) {
1517 		iszfs++;
1518 		sa_free_attr_string(zfs);
1519 	}
1520 
1521 	/*
1522 	 * Step through each optionset at the group level and
1523 	 * enable the share based on the protocol type. This
1524 	 * works because protocols must be set on the group
1525 	 * for the protocol to be enabled.
1526 	 */
1527 	for (optionset = sa_get_optionset(group, NULL);
1528 	    optionset != NULL && ret == SA_OK;
1529 	    optionset = sa_get_next_optionset(optionset)) {
1530 		value = sa_get_optionset_attr(optionset, "type");
1531 		if (value != NULL) {
1532 			if (enabled)
1533 				ret = sa_enable_share(share, value);
1534 			if (update_legacy && !iszfs)
1535 				(void) sa_update_legacy(share, value);
1536 			sa_free_attr_string(value);
1537 		}
1538 	}
1539 	if (ret == SA_OK)
1540 		(void) sa_update_config(handle);
1541 	return (ret);
1542 }
1543 
1544 /*
1545  * sa_addshare(flags, argc, argv)
1546  *
1547  * implements add-share subcommand.
1548  */
1549 
1550 int
1551 sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[])
1552 {
1553 	int verbose = 0;
1554 	int dryrun = 0;
1555 	int c;
1556 	int ret = SA_OK;
1557 	sa_group_t group;
1558 	sa_share_t share;
1559 	char *sharepath = NULL;
1560 	char *description = NULL;
1561 	char *resource = NULL;
1562 	int persist = SA_SHARE_PERMANENT; /* default to persist */
1563 	int auth;
1564 	char dir[MAXPATHLEN];
1565 
1566 	while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
1567 		switch (c) {
1568 		case 'n':
1569 			dryrun++;
1570 			break;
1571 		case 'v':
1572 			verbose++;
1573 			break;
1574 		case 'd':
1575 			description = optarg;
1576 			break;
1577 		case 'r':
1578 			resource = optarg;
1579 			break;
1580 		case 's':
1581 			/*
1582 			 * Save share path into group. Currently limit
1583 			 * to one share per command.
1584 			 */
1585 			if (sharepath != NULL) {
1586 				(void) printf(gettext(
1587 				    "Adding multiple shares not supported\n"));
1588 				return (1);
1589 			}
1590 			sharepath = optarg;
1591 			break;
1592 		case 't':
1593 			persist = SA_SHARE_TRANSIENT;
1594 			break;
1595 		default:
1596 		case 'h':
1597 		case '?':
1598 			(void) printf(gettext("usage: %s\n"),
1599 			    sa_get_usage(USAGE_ADD_SHARE));
1600 			return (0);
1601 		}
1602 	}
1603 
1604 	if (optind >= argc) {
1605 		(void) printf(gettext("usage: %s\n"),
1606 		    sa_get_usage(USAGE_ADD_SHARE));
1607 		if (dryrun || sharepath != NULL || description != NULL ||
1608 		    resource != NULL || verbose || persist) {
1609 			(void) printf(gettext("\tgroup must be specified\n"));
1610 			ret = SA_NO_SUCH_GROUP;
1611 		} else {
1612 			ret = SA_OK;
1613 		}
1614 	} else {
1615 		if (sharepath == NULL) {
1616 			(void) printf(gettext("usage: %s\n"),
1617 			    sa_get_usage(USAGE_ADD_SHARE));
1618 			(void) printf(gettext(
1619 			    "\t-s sharepath must be specified\n"));
1620 			return (SA_BAD_PATH);
1621 		}
1622 		if (realpath(sharepath, dir) == NULL) {
1623 			(void) printf(gettext(
1624 			    "Path is not valid: %s\n"), sharepath);
1625 			return (SA_BAD_PATH);
1626 		} else {
1627 			sharepath = dir;
1628 		}
1629 
1630 		/* Check for valid syntax */
1631 		if (resource != NULL && strpbrk(resource, " \t/") != NULL) {
1632 			(void) printf(gettext("usage: %s\n"),
1633 			    sa_get_usage(USAGE_ADD_SHARE));
1634 			(void) printf(gettext(
1635 			    "\tresource must not contain white"
1636 			    "space or '/' characters\n"));
1637 			return (SA_BAD_PATH);
1638 		}
1639 		group = sa_get_group(handle, argv[optind]);
1640 		if (group == NULL) {
1641 			(void) printf(gettext("Group \"%s\" not found\n"),
1642 			    argv[optind]);
1643 			return (SA_NO_SUCH_GROUP);
1644 		}
1645 		auth = check_authorizations(argv[optind],  flags);
1646 		share = sa_find_share(handle, sharepath);
1647 		if (share != NULL) {
1648 			group = sa_get_parent_group(share);
1649 			if (group != NULL) {
1650 				char *groupname;
1651 				groupname = sa_get_group_attr(
1652 				    group, "name");
1653 				if (groupname != NULL) {
1654 					(void) printf(gettext(
1655 					    "Share path already "
1656 					    "shared in group "
1657 					    "\"%s\": %s\n"),
1658 					    groupname, sharepath);
1659 					sa_free_attr_string(groupname);
1660 				} else {
1661 					(void) printf(gettext(
1662 					    "Share path already"
1663 					    "shared: %s\n"),
1664 					    groupname, sharepath);
1665 				}
1666 			} else {
1667 				(void) printf(gettext(
1668 				    "Share path %s already shared\n"),
1669 				    sharepath);
1670 			}
1671 			return (SA_DUPLICATE_NAME);
1672 		} else {
1673 			/*
1674 			 * Need to check that resource name is
1675 			 * unique at some point. Path checking
1676 			 * should use the "normal" rules which
1677 			 * don't check the repository.
1678 			 */
1679 			if (dryrun)
1680 				ret = sa_check_path(group, sharepath,
1681 				    SA_CHECK_NORMAL);
1682 			else
1683 				share = sa_add_share(group, sharepath,
1684 				    persist, &ret);
1685 			if (!dryrun && share == NULL) {
1686 				(void) printf(gettext(
1687 				    "Could not add share: %s\n"),
1688 				    sa_errorstr(ret));
1689 			} else {
1690 				if (!dryrun && ret == SA_OK) {
1691 					if (resource != NULL &&
1692 					    strpbrk(resource, " \t/") == NULL) {
1693 						ret = sa_set_share_attr(share,
1694 						    "resource", resource);
1695 					}
1696 					if (ret == SA_OK &&
1697 					    description != NULL) {
1698 						ret = sa_set_share_description(
1699 						    share, description);
1700 					}
1701 					if (ret == SA_OK) {
1702 						/* Now enable the share(s) */
1703 						ret = enable_share(handle,
1704 						    group, share, 1);
1705 						ret = sa_update_config(handle);
1706 					}
1707 					switch (ret) {
1708 					case SA_DUPLICATE_NAME:
1709 						(void) printf(gettext(
1710 						    "Resource name in"
1711 						    "use: %s\n"), resource);
1712 						break;
1713 					default:
1714 						(void) printf(
1715 						    gettext("Could not set "
1716 						    "attribute: %s\n"),
1717 						    sa_errorstr(ret));
1718 						break;
1719 					case SA_OK:
1720 						break;
1721 					}
1722 				} else if (dryrun && ret == SA_OK && !auth &&
1723 				    verbose) {
1724 					(void) printf(gettext(
1725 					    "Command would fail: %s\n"),
1726 					    sa_errorstr(SA_NO_PERMISSION));
1727 					ret = SA_NO_PERMISSION;
1728 				}
1729 			}
1730 		}
1731 	}
1732 	return (ret);
1733 }
1734 
1735 /*
1736  * sa_moveshare(flags, argc, argv)
1737  *
1738  * implements move-share subcommand.
1739  */
1740 
1741 int
1742 sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[])
1743 {
1744 	int verbose = 0;
1745 	int dryrun = 0;
1746 	int c;
1747 	int ret = SA_OK;
1748 	sa_group_t group;
1749 	sa_share_t share;
1750 	char *sharepath = NULL;
1751 	int authsrc = 0, authdst = 0;
1752 
1753 	while ((c = getopt(argc, argv, "?hvns:")) != EOF) {
1754 		switch (c) {
1755 		case 'n':
1756 			dryrun++;
1757 			break;
1758 		case 'v':
1759 			verbose++;
1760 			break;
1761 		case 's':
1762 			/*
1763 			 * Remove share path from group. Currently limit
1764 			 * to one share per command.
1765 			 */
1766 			if (sharepath != NULL) {
1767 				(void) printf(gettext("Moving multiple shares"
1768 				    "not supported\n"));
1769 				return (SA_BAD_PATH);
1770 			}
1771 			sharepath = optarg;
1772 			break;
1773 		default:
1774 		case 'h':
1775 		case '?':
1776 			(void) printf(gettext("usage: %s\n"),
1777 			    sa_get_usage(USAGE_MOVE_SHARE));
1778 			return (0);
1779 		}
1780 	}
1781 
1782 	if (optind >= argc || sharepath == NULL) {
1783 			(void) printf(gettext("usage: %s\n"),
1784 			    sa_get_usage(USAGE_MOVE_SHARE));
1785 			if (dryrun || verbose || sharepath != NULL) {
1786 				(void) printf(gettext(
1787 				    "\tgroup must be specified\n"));
1788 				ret = SA_NO_SUCH_GROUP;
1789 			} else {
1790 				if (sharepath == NULL) {
1791 					ret = SA_SYNTAX_ERR;
1792 					(void) printf(gettext(
1793 					    "\tsharepath must be specified\n"));
1794 				} else {
1795 					ret = SA_OK;
1796 				}
1797 			}
1798 	} else {
1799 		sa_group_t parent;
1800 		char *zfsold;
1801 		char *zfsnew;
1802 
1803 		if (sharepath == NULL) {
1804 			(void) printf(gettext(
1805 			    "sharepath must be specified with the -s "
1806 			    "option\n"));
1807 			return (SA_BAD_PATH);
1808 		}
1809 		group = sa_get_group(handle, argv[optind]);
1810 		if (group == NULL) {
1811 			(void) printf(gettext("Group \"%s\" not found\n"),
1812 			    argv[optind]);
1813 			return (SA_NO_SUCH_GROUP);
1814 		}
1815 		share = sa_find_share(handle, sharepath);
1816 		authdst = check_authorizations(argv[optind], flags);
1817 		if (share == NULL) {
1818 			(void) printf(gettext("Share not found: %s\n"),
1819 			    sharepath);
1820 			return (SA_NO_SUCH_PATH);
1821 		}
1822 
1823 		parent = sa_get_parent_group(share);
1824 		if (parent != NULL) {
1825 			char *pname;
1826 			pname = sa_get_group_attr(parent, "name");
1827 			if (pname != NULL) {
1828 				authsrc = check_authorizations(pname, flags);
1829 				sa_free_attr_string(pname);
1830 			}
1831 			zfsold = sa_get_group_attr(parent, "zfs");
1832 			zfsnew = sa_get_group_attr(group, "zfs");
1833 			if ((zfsold != NULL && zfsnew == NULL) ||
1834 			    (zfsold == NULL && zfsnew != NULL)) {
1835 				ret = SA_NOT_ALLOWED;
1836 			}
1837 			if (zfsold != NULL)
1838 				sa_free_attr_string(zfsold);
1839 			if (zfsnew != NULL)
1840 				sa_free_attr_string(zfsnew);
1841 		}
1842 		if (!dryrun && ret == SA_OK)
1843 			ret = sa_move_share(group, share);
1844 
1845 		if (ret == SA_OK && parent != group && !dryrun) {
1846 			char *oldstate;
1847 			ret = sa_update_config(handle);
1848 			/*
1849 			 * Note that the share may need to be
1850 			 * "unshared" if the new group is
1851 			 * disabled and the old was enabled or
1852 			 * it may need to be share to update
1853 			 * if the new group is enabled.
1854 			 */
1855 			oldstate = sa_get_group_attr(parent, "state");
1856 
1857 			/* enable_share determines what to do */
1858 			if (strcmp(oldstate, "enabled") == 0) {
1859 				(void) sa_disable_share(share, NULL);
1860 			}
1861 			(void) enable_share(handle, group, share, 1);
1862 			if (oldstate != NULL)
1863 				sa_free_attr_string(oldstate);
1864 		}
1865 
1866 		if (ret != SA_OK)
1867 			(void) printf(gettext("Could not move share: %s\n"),
1868 			    sa_errorstr(ret));
1869 
1870 		if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
1871 		    verbose) {
1872 			(void) printf(gettext("Command would fail: %s\n"),
1873 			    sa_errorstr(SA_NO_PERMISSION));
1874 		}
1875 	}
1876 	return (ret);
1877 }
1878 
1879 /*
1880  * sa_removeshare(flags, argc, argv)
1881  *
1882  * implements remove-share subcommand.
1883  */
1884 
1885 int
1886 sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[])
1887 {
1888 	int verbose = 0;
1889 	int dryrun = 0;
1890 	int force = 0;
1891 	int c;
1892 	int ret = SA_OK;
1893 	sa_group_t group;
1894 	sa_share_t share;
1895 	char *sharepath = NULL;
1896 	char dir[MAXPATHLEN];
1897 	int auth;
1898 
1899 	while ((c = getopt(argc, argv, "?hfns:v")) != EOF) {
1900 		switch (c) {
1901 		case 'n':
1902 			dryrun++;
1903 			break;
1904 		case 'v':
1905 			verbose++;
1906 			break;
1907 		case 'f':
1908 			force++;
1909 			break;
1910 		case 's':
1911 			/*
1912 			 * Remove share path from group. Currently limit
1913 			 * to one share per command.
1914 			 */
1915 			if (sharepath != NULL) {
1916 				(void) printf(gettext(
1917 				    "Removing multiple shares not "
1918 				    "supported\n"));
1919 				return (SA_SYNTAX_ERR);
1920 			}
1921 			sharepath = optarg;
1922 			break;
1923 		default:
1924 		case 'h':
1925 		case '?':
1926 			(void) printf(gettext("usage: %s\n"),
1927 			    sa_get_usage(USAGE_REMOVE_SHARE));
1928 			return (0);
1929 		}
1930 	}
1931 
1932 	if (optind >= argc || sharepath == NULL) {
1933 		if (sharepath == NULL) {
1934 			(void) printf(gettext("usage: %s\n"),
1935 			    sa_get_usage(USAGE_REMOVE_SHARE));
1936 			(void) printf(gettext(
1937 			    "\t-s sharepath must be specified\n"));
1938 			ret = SA_BAD_PATH;
1939 		} else {
1940 			ret = SA_OK;
1941 		}
1942 	}
1943 	if (ret != SA_OK) {
1944 		return (ret);
1945 	}
1946 
1947 	if (optind < argc) {
1948 		if ((optind + 1) < argc) {
1949 			(void) printf(gettext("Extraneous group(s) at end of "
1950 			    "command\n"));
1951 			ret = SA_SYNTAX_ERR;
1952 		} else {
1953 			group = sa_get_group(handle, argv[optind]);
1954 			if (group == NULL) {
1955 				(void) printf(gettext(
1956 				    "Group \"%s\" not found\n"), argv[optind]);
1957 				ret = SA_NO_SUCH_GROUP;
1958 			}
1959 		}
1960 	} else {
1961 		group = NULL;
1962 	}
1963 
1964 	/*
1965 	 * Lookup the path in the internal configuration. Care
1966 	 * must be taken to handle the case where the
1967 	 * underlying path has been removed since we need to
1968 	 * be able to deal with that as well.
1969 	 */
1970 	if (ret == SA_OK) {
1971 		if (group != NULL)
1972 			share = sa_get_share(group, sharepath);
1973 		else
1974 			share = sa_find_share(handle, sharepath);
1975 		/*
1976 		 * If we didn't find the share with the provided path,
1977 		 * it may be a symlink so attempt to resolve it using
1978 		 * realpath and try again. Realpath will resolve any
1979 		 * symlinks and place them in "dir". Note that
1980 		 * sharepath is only used for the lookup the first
1981 		 * time and later for error messages. dir will be used
1982 		 * on the second attempt. Once a share is found, all
1983 		 * operations are based off of the share variable.
1984 		 */
1985 		if (share == NULL) {
1986 			if (realpath(sharepath, dir) == NULL) {
1987 				ret = SA_BAD_PATH;
1988 				(void) printf(gettext(
1989 				    "Path is not valid: %s\n"), sharepath);
1990 			} else {
1991 				if (group != NULL)
1992 					share = sa_get_share(group, dir);
1993 				else
1994 					share = sa_find_share(handle, dir);
1995 			}
1996 		}
1997 	}
1998 
1999 	/*
2000 	 * If there hasn't been an error, there was likely a
2001 	 * path found. If not, give the appropriate error
2002 	 * message and set the return error. If it was found,
2003 	 * then disable the share and then remove it from the
2004 	 * configuration.
2005 	 */
2006 	if (ret != SA_OK) {
2007 		return (ret);
2008 	}
2009 	if (share == NULL) {
2010 		if (group != NULL)
2011 			(void) printf(gettext("Share not found in group %s:"
2012 			    " %s\n"), argv[optind], sharepath);
2013 		else
2014 			(void) printf(gettext("Share not found: %s\n"),
2015 			    sharepath);
2016 			ret = SA_NO_SUCH_PATH;
2017 	} else {
2018 		if (group == NULL)
2019 			group = sa_get_parent_group(share);
2020 		if (!dryrun) {
2021 			if (ret == SA_OK) {
2022 				ret = sa_disable_share(share, NULL);
2023 				/*
2024 				 * We don't care if it fails since it
2025 				 * could be disabled already. Some
2026 				 * unexpected errors could occur that
2027 				 * prevent removal, so also check for
2028 				 * force being set.
2029 				 */
2030 				if (ret == SA_OK || ret == SA_NO_SUCH_PATH ||
2031 				    ret == SA_NOT_SUPPORTED ||
2032 				    ret == SA_SYSTEM_ERR || force) {
2033 					ret = sa_remove_share(share);
2034 				}
2035 				if (ret == SA_OK)
2036 					ret = sa_update_config(handle);
2037 			}
2038 			if (ret != SA_OK)
2039 				(void) printf(gettext(
2040 				    "Could not remove share: %s\n"),
2041 				    sa_errorstr(ret));
2042 
2043 		} else if (ret == SA_OK) {
2044 			char *pname;
2045 			pname = sa_get_group_attr(group, "name");
2046 			if (pname != NULL) {
2047 				auth = check_authorizations(pname, flags);
2048 				sa_free_attr_string(pname);
2049 			}
2050 			if (!auth && verbose) {
2051 				(void) printf(gettext(
2052 				    "Command would fail: %s\n"),
2053 				    sa_errorstr(SA_NO_PERMISSION));
2054 			}
2055 		}
2056 	}
2057 	return (ret);
2058 }
2059 
2060 /*
2061  * sa_set_share(flags, argc, argv)
2062  *
2063  * implements set-share subcommand.
2064  */
2065 
2066 int
2067 sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[])
2068 {
2069 	int dryrun = 0;
2070 	int c;
2071 	int ret = SA_OK;
2072 	sa_group_t group, sharegroup;
2073 	sa_share_t share;
2074 	char *sharepath = NULL;
2075 	char *description = NULL;
2076 	char *resource = NULL;
2077 	int auth;
2078 	int verbose = 0;
2079 	char *groupname;
2080 
2081 	while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
2082 		switch (c) {
2083 		case 'n':
2084 			dryrun++;
2085 			break;
2086 		case 'd':
2087 			description = optarg;
2088 			break;
2089 		case 'r':
2090 			resource = optarg;
2091 			break;
2092 		case 'v':
2093 			verbose++;
2094 			break;
2095 		case 's':
2096 			/*
2097 			 * Save share path into group. Currently limit
2098 			 * to one share per command.
2099 			 */
2100 			if (sharepath != NULL) {
2101 				(void) printf(gettext(
2102 				    "Updating multiple shares not "
2103 				    "supported\n"));
2104 				return (SA_BAD_PATH);
2105 			}
2106 			sharepath = optarg;
2107 			break;
2108 		default:
2109 		case 'h':
2110 		case '?':
2111 			(void) printf(gettext("usage: %s\n"),
2112 			    sa_get_usage(USAGE_SET_SHARE));
2113 			return (SA_OK);
2114 		}
2115 	}
2116 
2117 	if (optind >= argc || sharepath == NULL) {
2118 		if (sharepath == NULL) {
2119 			(void) printf(gettext("usage: %s\n"),
2120 			    sa_get_usage(USAGE_SET_SHARE));
2121 			(void) printf(gettext("\tgroup must be specified\n"));
2122 			ret = SA_BAD_PATH;
2123 		} else {
2124 			ret = SA_OK;
2125 		}
2126 	}
2127 	if ((optind + 1) < argc) {
2128 		(void) printf(gettext("usage: %s\n"),
2129 		    sa_get_usage(USAGE_SET_SHARE));
2130 		(void) printf(gettext("\tExtraneous group(s) at end\n"));
2131 		ret = SA_SYNTAX_ERR;
2132 	}
2133 
2134 	if (ret != SA_OK)
2135 		return (ret);
2136 
2137 	if (optind < argc) {
2138 		groupname = argv[optind];
2139 		group = sa_get_group(handle, groupname);
2140 	} else {
2141 		group = NULL;
2142 		groupname = NULL;
2143 	}
2144 	share = sa_find_share(handle, sharepath);
2145 	if (share == NULL) {
2146 		(void) printf(gettext("Share path \"%s\" not found\n"),
2147 		    sharepath);
2148 		return (SA_NO_SUCH_PATH);
2149 	}
2150 	sharegroup = sa_get_parent_group(share);
2151 	if (group != NULL && group != sharegroup) {
2152 		(void) printf(gettext("Group \"%s\" does not contain "
2153 		    "share %s\n"), argv[optind], sharepath);
2154 		ret = SA_BAD_PATH;
2155 	} else {
2156 		int delgroupname = 0;
2157 		if (groupname == NULL) {
2158 			groupname = sa_get_group_attr(sharegroup, "name");
2159 			delgroupname = 1;
2160 		}
2161 		if (groupname != NULL) {
2162 			auth = check_authorizations(groupname, flags);
2163 			if (delgroupname) {
2164 				sa_free_attr_string(groupname);
2165 				groupname = NULL;
2166 			}
2167 		} else {
2168 			ret = SA_NO_MEMORY;
2169 		}
2170 		if (resource != NULL) {
2171 			if (strpbrk(resource, " \t/") == NULL) {
2172 				if (!dryrun) {
2173 					ret = sa_set_share_attr(share,
2174 					    "resource", resource);
2175 				} else {
2176 					sa_share_t resshare;
2177 					resshare = sa_get_resource(sharegroup,
2178 					    resource);
2179 					if (resshare != NULL &&
2180 					    resshare != share)
2181 						ret = SA_DUPLICATE_NAME;
2182 				}
2183 			} else {
2184 				ret = SA_BAD_PATH;
2185 				(void) printf(gettext("Resource must not "
2186 				    "contain white space or '/'\n"));
2187 			}
2188 		}
2189 		if (ret == SA_OK && description != NULL)
2190 			ret = sa_set_share_description(share, description);
2191 	}
2192 	if (!dryrun && ret == SA_OK)
2193 		ret = sa_update_config(handle);
2194 
2195 	switch (ret) {
2196 	case SA_DUPLICATE_NAME:
2197 		(void) printf(gettext("Resource name in use: %s\n"), resource);
2198 		break;
2199 	default:
2200 		(void) printf(gettext("Could not set attribute: %s\n"),
2201 		    sa_errorstr(ret));
2202 		break;
2203 	case SA_OK:
2204 		if (dryrun && !auth && verbose)
2205 			(void) printf(gettext("Command would fail: %s\n"),
2206 			    sa_errorstr(SA_NO_PERMISSION));
2207 		break;
2208 	}
2209 
2210 	return (ret);
2211 }
2212 
2213 /*
2214  * add_security(group, sectype, optlist, proto, *err)
2215  *
2216  * Helper function to add a security option (named optionset) to the
2217  * group.
2218  */
2219 
2220 static int
2221 add_security(sa_group_t group, char *sectype,
2222 		struct options *optlist, char *proto, int *err)
2223 {
2224 	sa_security_t security;
2225 	int ret = SA_OK;
2226 	int result = 0;
2227 
2228 	sectype = sa_proto_space_alias(proto, sectype);
2229 	security = sa_get_security(group, sectype, proto);
2230 	if (security == NULL)
2231 		security = sa_create_security(group, sectype, proto);
2232 
2233 	if (sectype != NULL)
2234 		sa_free_attr_string(sectype);
2235 
2236 	if (security == NULL)
2237 		return (ret);
2238 
2239 	while (optlist != NULL) {
2240 		sa_property_t prop;
2241 		prop = sa_get_property(security, optlist->optname);
2242 		if (prop == NULL) {
2243 			/*
2244 			 * Add the property, but only if it is
2245 			 * a non-NULL or non-zero length value
2246 			 */
2247 			if (optlist->optvalue != NULL) {
2248 				prop = sa_create_property(optlist->optname,
2249 				    optlist->optvalue);
2250 				if (prop != NULL) {
2251 					ret = sa_valid_property(security, proto,
2252 					    prop);
2253 					if (ret != SA_OK) {
2254 						(void) sa_remove_property(prop);
2255 						(void) printf(gettext(
2256 						    "Could not add "
2257 						    "property %s: %s\n"),
2258 						    optlist->optname,
2259 						    sa_errorstr(ret));
2260 					}
2261 					if (ret == SA_OK) {
2262 						ret = sa_add_property(security,
2263 						    prop);
2264 						if (ret != SA_OK) {
2265 							(void) printf(gettext(
2266 							    "Could not add "
2267 							    "property (%s=%s): "
2268 							    "%s\n"),
2269 							    optlist->optname,
2270 							    optlist->optvalue,
2271 							    sa_errorstr(ret));
2272 						} else {
2273 							result = 1;
2274 						}
2275 					}
2276 				}
2277 			}
2278 		} else {
2279 			ret = sa_update_property(prop, optlist->optvalue);
2280 			result = 1; /* should check if really changed */
2281 		}
2282 		optlist = optlist->next;
2283 	}
2284 	/*
2285 	 * When done, properties may have all been removed but
2286 	 * we need to keep the security type itself until
2287 	 * explicitly removed.
2288 	 */
2289 	if (result)
2290 		ret = sa_commit_properties(security, 0);
2291 	*err = ret;
2292 	return (result);
2293 }
2294 
2295 /*
2296  * zfscheck(group, share)
2297  *
2298  * For the special case where a share was provided, make sure it is a
2299  * compatible path for a ZFS property change.  The only path
2300  * acceptable is the path that defines the zfs sub-group (dataset with
2301  * the sharenfs property set) and not one of the paths that inherited
2302  * the NFS properties. Returns SA_OK if it is usable and
2303  * SA_NOT_ALLOWED if it isn't.
2304  *
2305  * If group is not a ZFS group/subgroup, we assume OK since the check
2306  * on return will catch errors for those cases.  What we are looking
2307  * for here is that the group is ZFS and the share is not the defining
2308  * share.  All else is SA_OK.
2309  */
2310 
2311 static int
2312 zfscheck(sa_group_t group, sa_share_t share)
2313 {
2314 	int ret = SA_OK;
2315 	char *attr;
2316 
2317 	if (sa_group_is_zfs(group)) {
2318 		/*
2319 		 * The group is a ZFS group.  Does the share represent
2320 		 * the dataset that defined the group? It is only OK
2321 		 * if the attribute "subgroup" exists on the share and
2322 		 * has a value of "true".
2323 		 */
2324 
2325 		ret = SA_NOT_ALLOWED;
2326 		attr = sa_get_share_attr(share, "subgroup");
2327 		if (attr != NULL) {
2328 			if (strcmp(attr, "true") == 0)
2329 				ret = SA_OK;
2330 			sa_free_attr_string(attr);
2331 		}
2332 	}
2333 	return (ret);
2334 }
2335 
2336 /*
2337  * basic_set(groupname, optlist, protocol, sharepath, dryrun)
2338  *
2339  * This function implements "set" when a name space (-S) is not
2340  * specified. It is a basic set. Options and other CLI parsing has
2341  * already been done.
2342  */
2343 
2344 static int
2345 basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
2346 		char *protocol,	char *sharepath, int dryrun)
2347 {
2348 	sa_group_t group;
2349 	int ret = SA_OK;
2350 	int change = 0;
2351 	struct list *worklist = NULL;
2352 
2353 	group = sa_get_group(handle, groupname);
2354 	if (group != NULL) {
2355 		sa_share_t share = NULL;
2356 		if (sharepath != NULL) {
2357 			share = sa_get_share(group, sharepath);
2358 			if (share == NULL) {
2359 				(void) printf(gettext(
2360 				    "Share does not exist in group %s\n"),
2361 				    groupname, sharepath);
2362 				ret = SA_NO_SUCH_PATH;
2363 			} else {
2364 				/* if ZFS and OK, then only group */
2365 				ret = zfscheck(group, share);
2366 				if (ret == SA_OK &&
2367 				    sa_group_is_zfs(group))
2368 					share = NULL;
2369 				if (ret == SA_NOT_ALLOWED)
2370 					(void) printf(gettext(
2371 					    "Properties on ZFS group shares "
2372 					    "not supported: %s\n"), sharepath);
2373 			}
2374 		}
2375 		if (ret == SA_OK) {
2376 			/* group must exist */
2377 			ret = valid_options(optlist, protocol,
2378 			    share == NULL ? group : share, NULL);
2379 			if (ret == SA_OK && !dryrun) {
2380 				if (share != NULL)
2381 					change |= add_optionset(share, optlist,
2382 					    protocol, &ret);
2383 				else
2384 					change |= add_optionset(group, optlist,
2385 					    protocol, &ret);
2386 				if (ret == SA_OK && change)
2387 					worklist = add_list(worklist, group,
2388 					    share);
2389 			}
2390 		}
2391 		free_opt(optlist);
2392 	} else {
2393 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2394 		ret = SA_NO_SUCH_GROUP;
2395 	}
2396 	/*
2397 	 * we have a group and potentially legal additions
2398 	 */
2399 
2400 	/*
2401 	 * Commit to configuration if not a dryrunp and properties
2402 	 * have changed.
2403 	 */
2404 	if (!dryrun && ret == SA_OK && change && worklist != NULL)
2405 		/* properties changed, so update all shares */
2406 		(void) enable_all_groups(handle, worklist, 0, 0, protocol);
2407 
2408 	if (worklist != NULL)
2409 		free_list(worklist);
2410 	return (ret);
2411 }
2412 
2413 /*
2414  * space_set(groupname, optlist, protocol, sharepath, dryrun)
2415  *
2416  * This function implements "set" when a name space (-S) is
2417  * specified. It is a namespace set. Options and other CLI parsing has
2418  * already been done.
2419  */
2420 
2421 static int
2422 space_set(sa_handle_t handle, char *groupname, struct options *optlist,
2423 		char *protocol,	char *sharepath, int dryrun, char *sectype)
2424 {
2425 	sa_group_t group;
2426 	int ret = SA_OK;
2427 	int change = 0;
2428 	struct list *worklist = NULL;
2429 
2430 	/*
2431 	 * make sure protcol and sectype are valid
2432 	 */
2433 
2434 	if (sa_proto_valid_space(protocol, sectype) == 0) {
2435 		(void) printf(gettext("Option space \"%s\" not valid "
2436 		    "for protocol.\n"), sectype);
2437 		return (SA_INVALID_SECURITY);
2438 	}
2439 
2440 	group = sa_get_group(handle, groupname);
2441 	if (group != NULL) {
2442 		sa_share_t share = NULL;
2443 		if (sharepath != NULL) {
2444 			share = sa_get_share(group, sharepath);
2445 			if (share == NULL) {
2446 				(void) printf(gettext(
2447 				    "Share does not exist in group %s\n"),
2448 				    groupname, sharepath);
2449 				ret = SA_NO_SUCH_PATH;
2450 			} else {
2451 				/* if ZFS and OK, then only group */
2452 				ret = zfscheck(group, share);
2453 				if (ret == SA_OK &&
2454 				    sa_group_is_zfs(group))
2455 					share = NULL;
2456 				if (ret == SA_NOT_ALLOWED)
2457 					(void) printf(gettext(
2458 					    "Properties on ZFS group shares "
2459 					    "not supported: %s\n"), sharepath);
2460 			}
2461 		}
2462 		if (ret == SA_OK) {
2463 			/* group must exist */
2464 			ret = valid_options(optlist, protocol,
2465 			    share == NULL ? group : share, sectype);
2466 			if (ret == SA_OK && !dryrun) {
2467 				if (share != NULL)
2468 					change = add_security(share, sectype,
2469 					    optlist, protocol, &ret);
2470 				else
2471 					change = add_security(group, sectype,
2472 					    optlist, protocol, &ret);
2473 				if (ret != SA_OK)
2474 					(void) printf(gettext(
2475 					    "Could not set property: %s\n"),
2476 					    sa_errorstr(ret));
2477 			}
2478 			if (ret == SA_OK && change)
2479 				worklist = add_list(worklist, group, share);
2480 		}
2481 		free_opt(optlist);
2482 	} else {
2483 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2484 		ret = SA_NO_SUCH_GROUP;
2485 	}
2486 	/*
2487 	 * we have a group and potentially legal additions
2488 	 */
2489 
2490 	/* Commit to configuration if not a dryrun */
2491 	if (!dryrun && ret == 0) {
2492 		if (change && worklist != NULL) {
2493 			/* properties changed, so update all shares */
2494 			(void) enable_all_groups(handle, worklist, 0, 0,
2495 			    protocol);
2496 		}
2497 		ret = sa_update_config(handle);
2498 	}
2499 	if (worklist != NULL)
2500 		free_list(worklist);
2501 	return (ret);
2502 }
2503 
2504 /*
2505  * sa_set(flags, argc, argv)
2506  *
2507  * Implements the set subcommand. It keys off of -S to determine which
2508  * set of operations to actually do.
2509  */
2510 
2511 int
2512 sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
2513 {
2514 	char *groupname;
2515 	int verbose = 0;
2516 	int dryrun = 0;
2517 	int c;
2518 	char *protocol = NULL;
2519 	int ret = SA_OK;
2520 	struct options *optlist = NULL;
2521 	char *sharepath = NULL;
2522 	char *optset = NULL;
2523 	int auth;
2524 
2525 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
2526 		switch (c) {
2527 		case 'v':
2528 			verbose++;
2529 			break;
2530 		case 'n':
2531 			dryrun++;
2532 			break;
2533 		case 'P':
2534 			protocol = optarg;
2535 			if (!sa_valid_protocol(protocol)) {
2536 				(void) printf(gettext(
2537 				    "Invalid protocol specified: %s\n"),
2538 				    protocol);
2539 				return (SA_INVALID_PROTOCOL);
2540 			}
2541 			break;
2542 		case 'p':
2543 			ret = add_opt(&optlist, optarg, 0);
2544 			switch (ret) {
2545 			case OPT_ADD_SYNTAX:
2546 				(void) printf(gettext("Property syntax error:"
2547 				    " %s\n"), optarg);
2548 				return (SA_SYNTAX_ERR);
2549 			case OPT_ADD_MEMORY:
2550 				(void) printf(gettext("No memory to set "
2551 				    "property: %s\n"), optarg);
2552 				return (SA_NO_MEMORY);
2553 			default:
2554 				break;
2555 			}
2556 			break;
2557 		case 's':
2558 			sharepath = optarg;
2559 			break;
2560 		case 'S':
2561 			optset = optarg;
2562 			break;
2563 		default:
2564 		case 'h':
2565 		case '?':
2566 			(void) printf(gettext("usage: %s\n"),
2567 			    sa_get_usage(USAGE_SET));
2568 			return (SA_OK);
2569 		}
2570 	}
2571 
2572 	if (optlist != NULL)
2573 		ret = chk_opt(optlist, optset != NULL, protocol);
2574 
2575 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
2576 	    protocol == NULL || ret != OPT_ADD_OK) {
2577 		char *sep = "\t";
2578 
2579 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
2580 		if (optind >= argc) {
2581 			(void) printf(gettext("%sgroup must be specified"),
2582 			    sep);
2583 			sep = ", ";
2584 		}
2585 		if (optlist == NULL) {
2586 			(void) printf(gettext("%sat least one property must be"
2587 			    " specified"), sep);
2588 			sep = ", ";
2589 		}
2590 		if (protocol == NULL) {
2591 			(void) printf(gettext("%sprotocol must be specified"),
2592 			    sep);
2593 			sep = ", ";
2594 		}
2595 		(void) printf("\n");
2596 		ret = SA_SYNTAX_ERR;
2597 	} else {
2598 		/*
2599 		 * Group already exists so we can proceed after a few
2600 		 * additional checks related to ZFS handling.
2601 		 */
2602 
2603 		groupname = argv[optind];
2604 		if (strcmp(groupname, "zfs") == 0) {
2605 			(void) printf(gettext("Changing properties for group "
2606 			    "\"zfs\" not allowed\n"));
2607 			return (SA_NOT_ALLOWED);
2608 		}
2609 
2610 		auth = check_authorizations(groupname, flags);
2611 		if (optset == NULL)
2612 			ret = basic_set(handle, groupname, optlist, protocol,
2613 			    sharepath, dryrun);
2614 		else
2615 			ret = space_set(handle, groupname, optlist, protocol,
2616 			    sharepath, dryrun, optset);
2617 		if (dryrun && ret == SA_OK && !auth && verbose) {
2618 			(void) printf(gettext("Command would fail: %s\n"),
2619 			    sa_errorstr(SA_NO_PERMISSION));
2620 		}
2621 	}
2622 	return (ret);
2623 }
2624 
2625 /*
2626  * remove_options(group, optlist, proto, *err)
2627  *
2628  * Helper function to actually remove options from a group after all
2629  * preprocessing is done.
2630  */
2631 
2632 static int
2633 remove_options(sa_group_t group, struct options *optlist,
2634 		char *proto, int *err)
2635 {
2636 	struct options *cur;
2637 	sa_optionset_t optionset;
2638 	sa_property_t prop;
2639 	int change = 0;
2640 	int ret = SA_OK;
2641 
2642 	optionset = sa_get_optionset(group, proto);
2643 	if (optionset != NULL) {
2644 		for (cur = optlist; cur != NULL; cur = cur->next) {
2645 			prop = sa_get_property(optionset, cur->optname);
2646 			if (prop != NULL) {
2647 				ret = sa_remove_property(prop);
2648 				if (ret != SA_OK)
2649 					break;
2650 				change = 1;
2651 			}
2652 		}
2653 	}
2654 	if (ret == SA_OK && change)
2655 		ret = sa_commit_properties(optionset, 0);
2656 
2657 	if (err != NULL)
2658 		*err = ret;
2659 	return (change);
2660 }
2661 
2662 /*
2663  * valid_unset(group, optlist, proto)
2664  *
2665  * Sanity check the optlist to make sure they can be removed. Issue an
2666  * error if a property doesn't exist.
2667  */
2668 
2669 static int
2670 valid_unset(sa_group_t group, struct options *optlist, char *proto)
2671 {
2672 	struct options *cur;
2673 	sa_optionset_t optionset;
2674 	sa_property_t prop;
2675 	int ret = SA_OK;
2676 
2677 	optionset = sa_get_optionset(group, proto);
2678 	if (optionset != NULL) {
2679 		for (cur = optlist; cur != NULL; cur = cur->next) {
2680 			prop = sa_get_property(optionset, cur->optname);
2681 			if (prop == NULL) {
2682 				(void) printf(gettext(
2683 				    "Could not unset property %s: not set\n"),
2684 				    cur->optname);
2685 				ret = SA_NO_SUCH_PROP;
2686 			}
2687 		}
2688 	}
2689 	return (ret);
2690 }
2691 
2692 /*
2693  * valid_unset_security(group, optlist, proto)
2694  *
2695  * Sanity check the optlist to make sure they can be removed. Issue an
2696  * error if a property doesn't exist.
2697  */
2698 
2699 static int
2700 valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
2701 	    char *sectype)
2702 {
2703 	struct options *cur;
2704 	sa_security_t security;
2705 	sa_property_t prop;
2706 	int ret = SA_OK;
2707 	char *sec;
2708 
2709 	sec = sa_proto_space_alias(proto, sectype);
2710 	security = sa_get_security(group, sec, proto);
2711 	if (security != NULL) {
2712 		for (cur = optlist; cur != NULL; cur = cur->next) {
2713 			prop = sa_get_property(security, cur->optname);
2714 			if (prop == NULL) {
2715 				(void) printf(gettext(
2716 				    "Could not unset property %s: not set\n"),
2717 				    cur->optname);
2718 				ret = SA_NO_SUCH_PROP;
2719 			}
2720 		}
2721 	} else {
2722 		(void) printf(gettext(
2723 		    "Could not unset %s: space not defined\n"), sectype);
2724 		ret = SA_NO_SUCH_SECURITY;
2725 	}
2726 	if (sec != NULL)
2727 		sa_free_attr_string(sec);
2728 	return (ret);
2729 }
2730 
2731 /*
2732  * remove_security(group, optlist, proto)
2733  *
2734  * Remove the properties since they were checked as valid.
2735  */
2736 
2737 static int
2738 remove_security(sa_group_t group, char *sectype,
2739 		struct options *optlist, char *proto, int *err)
2740 {
2741 	sa_security_t security;
2742 	int ret = SA_OK;
2743 	int change = 0;
2744 
2745 	sectype = sa_proto_space_alias(proto, sectype);
2746 	security = sa_get_security(group, sectype, proto);
2747 	if (sectype != NULL)
2748 		sa_free_attr_string(sectype);
2749 
2750 	if (security != NULL) {
2751 		while (optlist != NULL) {
2752 			sa_property_t prop;
2753 			prop = sa_get_property(security, optlist->optname);
2754 			if (prop != NULL) {
2755 				ret = sa_remove_property(prop);
2756 				if (ret != SA_OK)
2757 					break;
2758 				change = 1;
2759 			}
2760 			optlist = optlist->next;
2761 		}
2762 		/*
2763 		 * when done, properties may have all been removed but
2764 		 * we need to keep the security type itself until
2765 		 * explicitly removed.
2766 		 */
2767 		if (ret == SA_OK && change)
2768 			ret = sa_commit_properties(security, 0);
2769 	} else {
2770 		ret = SA_NO_SUCH_PROP;
2771 	}
2772 	if (err != NULL)
2773 		*err = ret;
2774 	return (change);
2775 }
2776 
2777 /*
2778  * basic_unset(groupname, optlist, protocol, sharepath, dryrun)
2779  *
2780  * Unset non-named optionset properties.
2781  */
2782 
2783 static int
2784 basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
2785 		char *protocol,	char *sharepath, int dryrun)
2786 {
2787 	sa_group_t group;
2788 	int ret = SA_OK;
2789 	int change = 0;
2790 	struct list *worklist = NULL;
2791 	sa_share_t share = NULL;
2792 
2793 	group = sa_get_group(handle, groupname);
2794 	if (group == NULL)
2795 		return (ret);
2796 
2797 	if (sharepath != NULL) {
2798 		share = sa_get_share(group, sharepath);
2799 		if (share == NULL) {
2800 			(void) printf(gettext(
2801 			    "Share does not exist in group %s\n"),
2802 			    groupname, sharepath);
2803 			ret = SA_NO_SUCH_PATH;
2804 		}
2805 	}
2806 	if (ret == SA_OK) {
2807 		/* group must exist */
2808 		ret = valid_unset(share != NULL ? share : group,
2809 		    optlist, protocol);
2810 		if (ret == SA_OK && !dryrun) {
2811 			if (share != NULL) {
2812 				sa_optionset_t optionset;
2813 				sa_property_t prop;
2814 				change |= remove_options(share, optlist,
2815 				    protocol, &ret);
2816 				/*
2817 				 * If a share optionset is
2818 				 * empty, remove it.
2819 				 */
2820 				optionset = sa_get_optionset((sa_share_t)share,
2821 				    protocol);
2822 				if (optionset != NULL) {
2823 					prop = sa_get_property(optionset, NULL);
2824 					if (prop == NULL)
2825 						(void) sa_destroy_optionset(
2826 						    optionset);
2827 				}
2828 			} else {
2829 				change |= remove_options(group,
2830 				    optlist, protocol, &ret);
2831 			}
2832 			if (ret == SA_OK && change)
2833 				worklist = add_list(worklist, group,
2834 				    share);
2835 			if (ret != SA_OK)
2836 				(void) printf(gettext(
2837 				    "Could not remove properties: "
2838 				    "%s\n"), sa_errorstr(ret));
2839 		}
2840 	} else {
2841 		(void) printf(gettext("Group \"%s\" not found\n"),
2842 		    groupname);
2843 		ret = SA_NO_SUCH_GROUP;
2844 	}
2845 	free_opt(optlist);
2846 
2847 	/*
2848 	 * We have a group and potentially legal additions
2849 	 *
2850 	 * Commit to configuration if not a dryrun
2851 	 */
2852 	if (!dryrun && ret == SA_OK) {
2853 		if (change && worklist != NULL) {
2854 			/* properties changed, so update all shares */
2855 			(void) enable_all_groups(handle, worklist, 0, 0,
2856 			    protocol);
2857 		}
2858 	}
2859 	if (worklist != NULL)
2860 		free_list(worklist);
2861 	return (ret);
2862 }
2863 
2864 /*
2865  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
2866  *
2867  * Unset named optionset properties.
2868  */
2869 static int
2870 space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
2871 		char *protocol, char *sharepath, int dryrun, char *sectype)
2872 {
2873 	sa_group_t group;
2874 	int ret = SA_OK;
2875 	int change = 0;
2876 	struct list *worklist = NULL;
2877 	sa_share_t share = NULL;
2878 
2879 	group = sa_get_group(handle, groupname);
2880 	if (group == NULL) {
2881 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2882 		return (SA_NO_SUCH_GROUP);
2883 	}
2884 	if (sharepath != NULL) {
2885 		share = sa_get_share(group, sharepath);
2886 		if (share == NULL) {
2887 			(void) printf(gettext(
2888 			    "Share does not exist in group %s\n"),
2889 			    groupname, sharepath);
2890 			return (SA_NO_SUCH_PATH);
2891 		}
2892 	}
2893 	ret = valid_unset_security(share != NULL ? share : group, optlist,
2894 	    protocol, sectype);
2895 
2896 	if (ret == SA_OK && !dryrun) {
2897 		if (optlist != NULL) {
2898 			if (share != NULL) {
2899 				sa_security_t optionset;
2900 				sa_property_t prop;
2901 				change = remove_security(share,
2902 				    sectype, optlist, protocol, &ret);
2903 
2904 				/* If a share security is empty, remove it */
2905 				optionset = sa_get_security((sa_group_t)share,
2906 				    sectype, protocol);
2907 				if (optionset != NULL) {
2908 					prop = sa_get_property(optionset,
2909 					    NULL);
2910 					if (prop == NULL)
2911 						ret = sa_destroy_security(
2912 						    optionset);
2913 				}
2914 			} else {
2915 				change = remove_security(group, sectype,
2916 				    optlist, protocol, &ret);
2917 			}
2918 		} else {
2919 			sa_security_t security;
2920 			char *sec;
2921 			sec = sa_proto_space_alias(protocol, sectype);
2922 			security = sa_get_security(group, sec, protocol);
2923 			if (sec != NULL)
2924 				sa_free_attr_string(sec);
2925 			if (security != NULL) {
2926 				ret = sa_destroy_security(security);
2927 				if (ret == SA_OK)
2928 					change = 1;
2929 			} else {
2930 				ret = SA_NO_SUCH_PROP;
2931 			}
2932 		}
2933 		if (ret != SA_OK)
2934 			(void) printf(gettext("Could not unset property: %s\n"),
2935 			    sa_errorstr(ret));
2936 	}
2937 
2938 	if (ret == SA_OK && change)
2939 		worklist = add_list(worklist, group, 0);
2940 
2941 	free_opt(optlist);
2942 	/*
2943 	 * We have a group and potentially legal additions
2944 	 */
2945 
2946 	/* Commit to configuration if not a dryrun */
2947 	if (!dryrun && ret == 0) {
2948 		/* properties changed, so update all shares */
2949 		if (change && worklist != NULL)
2950 			(void) enable_all_groups(handle, worklist, 0, 0,
2951 			    protocol);
2952 		ret = sa_update_config(handle);
2953 	}
2954 	if (worklist != NULL)
2955 		free_list(worklist);
2956 	return (ret);
2957 }
2958 
2959 /*
2960  * sa_unset(flags, argc, argv)
2961  *
2962  * Implements the unset subcommand. Parsing done here and then basic
2963  * or space versions of the real code are called.
2964  */
2965 
2966 int
2967 sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
2968 {
2969 	char *groupname;
2970 	int verbose = 0;
2971 	int dryrun = 0;
2972 	int c;
2973 	char *protocol = NULL;
2974 	int ret = SA_OK;
2975 	struct options *optlist = NULL;
2976 	char *sharepath = NULL;
2977 	char *optset = NULL;
2978 	int auth;
2979 
2980 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
2981 		switch (c) {
2982 		case 'v':
2983 			verbose++;
2984 			break;
2985 		case 'n':
2986 			dryrun++;
2987 			break;
2988 		case 'P':
2989 			protocol = optarg;
2990 			if (!sa_valid_protocol(protocol)) {
2991 				(void) printf(gettext(
2992 				    "Invalid protocol specified: %s\n"),
2993 				    protocol);
2994 				return (SA_INVALID_PROTOCOL);
2995 			}
2996 			break;
2997 		case 'p':
2998 			ret = add_opt(&optlist, optarg, 1);
2999 			switch (ret) {
3000 			case OPT_ADD_SYNTAX:
3001 				(void) printf(gettext("Property syntax error "
3002 				    "for property %s\n"), optarg);
3003 				return (SA_SYNTAX_ERR);
3004 
3005 			case OPT_ADD_PROPERTY:
3006 				(void) printf(gettext("Properties need to be "
3007 				    "set with set command: %s\n"), optarg);
3008 				return (SA_SYNTAX_ERR);
3009 
3010 			default:
3011 				break;
3012 			}
3013 			break;
3014 		case 's':
3015 			sharepath = optarg;
3016 			break;
3017 		case 'S':
3018 			optset = optarg;
3019 			break;
3020 		default:
3021 		case 'h':
3022 		case '?':
3023 			(void) printf(gettext("usage: %s\n"),
3024 			    sa_get_usage(USAGE_UNSET));
3025 			return (SA_OK);
3026 		}
3027 	}
3028 
3029 	if (optlist != NULL)
3030 		ret = chk_opt(optlist, optset != NULL, protocol);
3031 
3032 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
3033 	    protocol == NULL) {
3034 		char *sep = "\t";
3035 		(void) printf(gettext("usage: %s\n"),
3036 		    sa_get_usage(USAGE_UNSET));
3037 		if (optind >= argc) {
3038 			(void) printf(gettext("%sgroup must be specified"),
3039 			    sep);
3040 			sep = ", ";
3041 		}
3042 		if (optlist == NULL) {
3043 			(void) printf(gettext("%sat least one property must "
3044 			    "be specified"), sep);
3045 			sep = ", ";
3046 		}
3047 		if (protocol == NULL) {
3048 			(void) printf(gettext("%sprotocol must be specified"),
3049 			    sep);
3050 			sep = ", ";
3051 		}
3052 		(void) printf("\n");
3053 		ret = SA_SYNTAX_ERR;
3054 	} else {
3055 
3056 		/*
3057 		 * If a group already exists, we can only add a new
3058 		 * protocol to it and not create a new one or add the
3059 		 * same protocol again.
3060 		 */
3061 
3062 		groupname = argv[optind];
3063 		auth = check_authorizations(groupname, flags);
3064 		if (optset == NULL)
3065 			ret = basic_unset(handle, groupname, optlist, protocol,
3066 			    sharepath, dryrun);
3067 		else
3068 			ret = space_unset(handle, groupname, optlist, protocol,
3069 			    sharepath, dryrun, optset);
3070 
3071 		if (dryrun && ret == SA_OK && !auth && verbose)
3072 			(void) printf(gettext("Command would fail: %s\n"),
3073 			    sa_errorstr(SA_NO_PERMISSION));
3074 	}
3075 	return (ret);
3076 }
3077 
3078 /*
3079  * sa_enable_group(flags, argc, argv)
3080  *
3081  * Implements the enable subcommand
3082  */
3083 
3084 int
3085 sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
3086 {
3087 	int verbose = 0;
3088 	int dryrun = 0;
3089 	int all = 0;
3090 	int c;
3091 	int ret = SA_OK;
3092 	char *protocol = NULL;
3093 	char *state;
3094 	struct list *worklist = NULL;
3095 	int auth = 1;
3096 	sa_group_t group;
3097 
3098 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
3099 		switch (c) {
3100 		case 'a':
3101 			all = 1;
3102 			break;
3103 		case 'n':
3104 			dryrun++;
3105 			break;
3106 		case 'P':
3107 			protocol = optarg;
3108 			if (!sa_valid_protocol(protocol)) {
3109 				(void) printf(gettext(
3110 				    "Invalid protocol specified: %s\n"),
3111 				    protocol);
3112 				return (SA_INVALID_PROTOCOL);
3113 			}
3114 			break;
3115 		case 'v':
3116 			verbose++;
3117 			break;
3118 		default:
3119 		case 'h':
3120 		case '?':
3121 			(void) printf(gettext("usage: %s\n"),
3122 			    sa_get_usage(USAGE_ENABLE));
3123 			return (0);
3124 		}
3125 	}
3126 
3127 	if (optind == argc && !all) {
3128 		(void) printf(gettext("usage: %s\n"),
3129 		    sa_get_usage(USAGE_ENABLE));
3130 		(void) printf(gettext("\tmust specify group\n"));
3131 		return (SA_NO_SUCH_PATH);
3132 	}
3133 	if (!all) {
3134 		while (optind < argc) {
3135 			group = sa_get_group(handle, argv[optind]);
3136 			if (group != NULL) {
3137 				auth &= check_authorizations(argv[optind],
3138 				    flags);
3139 				state = sa_get_group_attr(group, "state");
3140 				if (state != NULL &&
3141 				    strcmp(state, "enabled") == 0) {
3142 					/* already enabled */
3143 					if (verbose)
3144 						(void) printf(gettext(
3145 						    "Group \"%s\" is already "
3146 						    "enabled\n"),
3147 						    argv[optind]);
3148 					ret = SA_BUSY; /* already enabled */
3149 				} else {
3150 					worklist = add_list(worklist, group,
3151 					    0);
3152 					if (verbose)
3153 						(void) printf(gettext(
3154 						    "Enabling group \"%s\"\n"),
3155 						    argv[optind]);
3156 				}
3157 				if (state != NULL)
3158 					sa_free_attr_string(state);
3159 			} else {
3160 				ret = SA_NO_SUCH_GROUP;
3161 			}
3162 			optind++;
3163 		}
3164 	} else {
3165 		for (group = sa_get_group(handle, NULL);
3166 		    group != NULL;
3167 		    group = sa_get_next_group(group)) {
3168 			worklist = add_list(worklist, group, 0);
3169 		}
3170 	}
3171 	if (!dryrun && ret == SA_OK)
3172 		ret = enable_all_groups(handle, worklist, 1, 0, NULL);
3173 
3174 	if (ret != SA_OK && ret != SA_BUSY)
3175 		(void) printf(gettext("Could not enable group: %s\n"),
3176 		    sa_errorstr(ret));
3177 	if (ret == SA_BUSY)
3178 		ret = SA_OK;
3179 
3180 	if (worklist != NULL)
3181 		free_list(worklist);
3182 	if (dryrun && ret == SA_OK && !auth && verbose) {
3183 		(void) printf(gettext("Command would fail: %s\n"),
3184 		    sa_errorstr(SA_NO_PERMISSION));
3185 	}
3186 	return (ret);
3187 }
3188 
3189 /*
3190  * disable_group(group, setstate)
3191  *
3192  * Disable all the shares in the specified group honoring the setstate
3193  * argument. This is a helper for disable_all_groups in order to
3194  * simplify regular and subgroup (zfs) disabling. Group has already
3195  * been checked for non-NULL.
3196  */
3197 
3198 static int
3199 disable_group(sa_group_t group)
3200 {
3201 	sa_share_t share;
3202 	int ret = SA_OK;
3203 
3204 	for (share = sa_get_share(group, NULL);
3205 	    share != NULL && ret == SA_OK;
3206 	    share = sa_get_next_share(share)) {
3207 		ret = sa_disable_share(share, NULL);
3208 		if (ret == SA_NO_SUCH_PATH) {
3209 			/*
3210 			 * this is OK since the path is gone. we can't
3211 			 * re-share it anyway so no error.
3212 			 */
3213 			ret = SA_OK;
3214 		}
3215 	}
3216 	return (ret);
3217 }
3218 
3219 
3220 /*
3221  * disable_all_groups(work, setstate)
3222  *
3223  * helper function that disables the shares in the list of groups
3224  * provided. It optionally marks the group as disabled. Used by both
3225  * enable and start subcommands.
3226  */
3227 
3228 static int
3229 disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
3230 {
3231 	int ret = SA_OK;
3232 	sa_group_t subgroup, group;
3233 
3234 	while (work != NULL && ret == SA_OK) {
3235 		group = (sa_group_t)work->item;
3236 		if (setstate)
3237 			ret = sa_set_group_attr(group, "state", "disabled");
3238 		if (ret == SA_OK) {
3239 			char *name;
3240 			name = sa_get_group_attr(group, "name");
3241 			if (name != NULL && strcmp(name, "zfs") == 0) {
3242 				/* need to get the sub-groups for stopping */
3243 				for (subgroup = sa_get_sub_group(group);
3244 				    subgroup != NULL;
3245 				    subgroup = sa_get_next_group(subgroup)) {
3246 					ret = disable_group(subgroup);
3247 				}
3248 			} else {
3249 				ret = disable_group(group);
3250 			}
3251 			/*
3252 			 * We don't want to "disable" since it won't come
3253 			 * up after a reboot.  The SMF framework should do
3254 			 * the right thing. On enable we do want to do
3255 			 * something.
3256 			 */
3257 		}
3258 		work = work->next;
3259 	}
3260 	if (ret == SA_OK)
3261 		ret = sa_update_config(handle);
3262 	return (ret);
3263 }
3264 
3265 /*
3266  * sa_disable_group(flags, argc, argv)
3267  *
3268  * Implements the disable subcommand
3269  */
3270 
3271 int
3272 sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
3273 {
3274 	int verbose = 0;
3275 	int dryrun = 0;
3276 	int all = 0;
3277 	int c;
3278 	int ret = SA_OK;
3279 	char *protocol;
3280 	char *state;
3281 	struct list *worklist = NULL;
3282 	sa_group_t group;
3283 	int auth = 1;
3284 
3285 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
3286 		switch (c) {
3287 		case 'a':
3288 			all = 1;
3289 			break;
3290 		case 'n':
3291 			dryrun++;
3292 			break;
3293 		case 'P':
3294 			protocol = optarg;
3295 			if (!sa_valid_protocol(protocol)) {
3296 				(void) printf(gettext(
3297 				    "Invalid protocol specified: %s\n"),
3298 				    protocol);
3299 				return (SA_INVALID_PROTOCOL);
3300 			}
3301 			break;
3302 		case 'v':
3303 			verbose++;
3304 			break;
3305 		default:
3306 		case 'h':
3307 		case '?':
3308 			(void) printf(gettext("usage: %s\n"),
3309 			    sa_get_usage(USAGE_DISABLE));
3310 			return (0);
3311 		}
3312 	}
3313 
3314 	if (optind == argc && !all) {
3315 		(void) printf(gettext("usage: %s\n"),
3316 		    sa_get_usage(USAGE_DISABLE));
3317 		(void) printf(gettext("\tmust specify group\n"));
3318 		return (SA_NO_SUCH_PATH);
3319 	}
3320 	if (!all) {
3321 		while (optind < argc) {
3322 			group = sa_get_group(handle, argv[optind]);
3323 			if (group != NULL) {
3324 				auth &= check_authorizations(argv[optind],
3325 				    flags);
3326 				state = sa_get_group_attr(group, "state");
3327 				if (state == NULL ||
3328 				    strcmp(state, "disabled") == 0) {
3329 					/* already disabled */
3330 					if (verbose)
3331 						(void) printf(gettext(
3332 						    "Group \"%s\" is "
3333 						    "already disabled\n"),
3334 						    argv[optind]);
3335 					ret = SA_BUSY; /* already disable */
3336 				} else {
3337 					worklist = add_list(worklist, group, 0);
3338 					if (verbose)
3339 						(void) printf(gettext(
3340 						    "Disabling group "
3341 						    "\"%s\"\n"), argv[optind]);
3342 				}
3343 				if (state != NULL)
3344 					sa_free_attr_string(state);
3345 			} else {
3346 				ret = SA_NO_SUCH_GROUP;
3347 			}
3348 			optind++;
3349 		}
3350 	} else {
3351 		for (group = sa_get_group(handle, NULL);
3352 		    group != NULL;
3353 		    group = sa_get_next_group(group))
3354 			worklist = add_list(worklist, group, 0);
3355 	}
3356 
3357 	if (ret == SA_OK && !dryrun)
3358 		ret = disable_all_groups(handle, worklist, 1);
3359 	if (ret != SA_OK && ret != SA_BUSY)
3360 		(void) printf(gettext("Could not disable group: %s\n"),
3361 		    sa_errorstr(ret));
3362 	if (ret == SA_BUSY)
3363 		ret = SA_OK;
3364 	if (worklist != NULL)
3365 		free_list(worklist);
3366 	if (dryrun && ret == SA_OK && !auth && verbose)
3367 		(void) printf(gettext("Command would fail: %s\n"),
3368 		    sa_errorstr(SA_NO_PERMISSION));
3369 	return (ret);
3370 }
3371 
3372 /*
3373  * sa_start_group(flags, argc, argv)
3374  *
3375  * Implements the start command.
3376  * This is similar to enable except it doesn't change the state
3377  * of the group(s) and only enables shares if the group is already
3378  * enabled.
3379  */
3380 /*ARGSUSED*/
3381 int
3382 sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
3383 {
3384 	int verbose = 0;
3385 	int all = 0;
3386 	int c;
3387 	int ret = SMF_EXIT_OK;
3388 	char *protocol = NULL;
3389 	char *state;
3390 	struct list *worklist = NULL;
3391 	sa_group_t group;
3392 
3393 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
3394 		switch (c) {
3395 		case 'a':
3396 			all = 1;
3397 			break;
3398 		case 'P':
3399 			protocol = optarg;
3400 			if (!sa_valid_protocol(protocol)) {
3401 				(void) printf(gettext(
3402 				    "Invalid protocol specified: %s\n"),
3403 				    protocol);
3404 				return (SA_INVALID_PROTOCOL);
3405 			}
3406 			break;
3407 		case 'v':
3408 			verbose++;
3409 			break;
3410 		default:
3411 		case 'h':
3412 		case '?':
3413 			(void) printf(gettext("usage: %s\n"),
3414 			    sa_get_usage(USAGE_START));
3415 			return (SA_OK);
3416 		}
3417 	}
3418 
3419 	if (optind == argc && !all) {
3420 		(void) printf(gettext("usage: %s\n"),
3421 		    sa_get_usage(USAGE_START));
3422 		return (SMF_EXIT_ERR_FATAL);
3423 	}
3424 
3425 	if (!all) {
3426 		while (optind < argc) {
3427 			group = sa_get_group(handle, argv[optind]);
3428 			if (group != NULL) {
3429 				state = sa_get_group_attr(group, "state");
3430 				if (state == NULL ||
3431 				    strcmp(state, "enabled") == 0) {
3432 					worklist = add_list(worklist, group, 0);
3433 					if (verbose)
3434 						(void) printf(gettext(
3435 						    "Starting group \"%s\"\n"),
3436 						    argv[optind]);
3437 				} else {
3438 					/*
3439 					 * Determine if there are any
3440 					 * protocols.  if there aren't any,
3441 					 * then there isn't anything to do in
3442 					 * any case so no error.
3443 					 */
3444 					if (sa_get_optionset(group,
3445 					    protocol) != NULL) {
3446 						ret = SMF_EXIT_OK;
3447 					}
3448 				}
3449 				if (state != NULL)
3450 					sa_free_attr_string(state);
3451 			}
3452 			optind++;
3453 		}
3454 	} else {
3455 		for (group = sa_get_group(handle, NULL); group != NULL;
3456 		    group = sa_get_next_group(group)) {
3457 			state = sa_get_group_attr(group, "state");
3458 			if (state == NULL || strcmp(state, "enabled") == 0)
3459 				worklist = add_list(worklist, group, 0);
3460 			if (state != NULL)
3461 				sa_free_attr_string(state);
3462 		}
3463 	}
3464 
3465 	(void) enable_all_groups(handle, worklist, 0, 1, NULL);
3466 
3467 	if (worklist != NULL)
3468 		free_list(worklist);
3469 	return (ret);
3470 }
3471 
3472 /*
3473  * sa_stop_group(flags, argc, argv)
3474  *
3475  * Implements the stop command.
3476  * This is similar to disable except it doesn't change the state
3477  * of the group(s) and only disables shares if the group is already
3478  * enabled.
3479  */
3480 /*ARGSUSED*/
3481 int
3482 sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
3483 {
3484 	int verbose = 0;
3485 	int all = 0;
3486 	int c;
3487 	int ret = SMF_EXIT_OK;
3488 	char *protocol = NULL;
3489 	char *state;
3490 	struct list *worklist = NULL;
3491 	sa_group_t group;
3492 
3493 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
3494 		switch (c) {
3495 		case 'a':
3496 			all = 1;
3497 			break;
3498 		case 'P':
3499 			protocol = optarg;
3500 			if (!sa_valid_protocol(protocol)) {
3501 				(void) printf(gettext(
3502 				    "Invalid protocol specified: %s\n"),
3503 				    protocol);
3504 				return (SA_INVALID_PROTOCOL);
3505 			}
3506 			break;
3507 		case 'v':
3508 			verbose++;
3509 			break;
3510 		default:
3511 		case 'h':
3512 		case '?':
3513 			(void) printf(gettext("usage: %s\n"),
3514 			    sa_get_usage(USAGE_STOP));
3515 			return (0);
3516 		}
3517 	}
3518 
3519 	if (optind == argc && !all) {
3520 		(void) printf(gettext("usage: %s\n"),
3521 		    sa_get_usage(USAGE_STOP));
3522 		return (SMF_EXIT_ERR_FATAL);
3523 	} else if (!all) {
3524 		while (optind < argc) {
3525 			group = sa_get_group(handle, argv[optind]);
3526 			if (group != NULL) {
3527 				state = sa_get_group_attr(group, "state");
3528 				if (state == NULL ||
3529 				    strcmp(state, "enabled") == 0) {
3530 					worklist = add_list(worklist, group, 0);
3531 					if (verbose)
3532 						(void) printf(gettext(
3533 						    "Stopping group \"%s\"\n"),
3534 						    argv[optind]);
3535 				} else {
3536 					ret = SMF_EXIT_OK;
3537 				}
3538 				if (state != NULL)
3539 					sa_free_attr_string(state);
3540 			}
3541 			optind++;
3542 		}
3543 	} else {
3544 		for (group = sa_get_group(handle, NULL); group != NULL;
3545 		    group = sa_get_next_group(group)) {
3546 			state = sa_get_group_attr(group, "state");
3547 			if (state == NULL || strcmp(state, "enabled") == 0)
3548 				worklist = add_list(worklist, group, 0);
3549 			if (state != NULL)
3550 				sa_free_attr_string(state);
3551 		}
3552 	}
3553 
3554 	(void) disable_all_groups(handle, worklist, 0);
3555 	ret = sa_update_config(handle);
3556 
3557 	if (worklist != NULL)
3558 		free_list(worklist);
3559 	return (ret);
3560 }
3561 
3562 /*
3563  * remove_all_options(share, proto)
3564  *
3565  * Removes all options on a share.
3566  */
3567 
3568 static void
3569 remove_all_options(sa_share_t share, char *proto)
3570 {
3571 	sa_optionset_t optionset;
3572 	sa_security_t security;
3573 	sa_security_t prevsec = NULL;
3574 
3575 	optionset = sa_get_optionset(share, proto);
3576 	if (optionset != NULL)
3577 		(void) sa_destroy_optionset(optionset);
3578 	for (security = sa_get_security(share, NULL, NULL);
3579 	    security != NULL;
3580 	    security = sa_get_next_security(security)) {
3581 		char *type;
3582 		/*
3583 		 * We walk through the list.  prevsec keeps the
3584 		 * previous security so we can delete it without
3585 		 * destroying the list.
3586 		 */
3587 		if (prevsec != NULL) {
3588 			/* remove the previously seen security */
3589 			(void) sa_destroy_security(prevsec);
3590 			/* set to NULL so we don't try multiple times */
3591 			prevsec = NULL;
3592 		}
3593 		type = sa_get_security_attr(security, "type");
3594 		if (type != NULL) {
3595 			/*
3596 			 * if the security matches the specified protocol, we
3597 			 * want to remove it. prevsec holds it until either
3598 			 * the next pass or we fall out of the loop.
3599 			 */
3600 			if (strcmp(type, proto) == 0)
3601 				prevsec = security;
3602 			sa_free_attr_string(type);
3603 		}
3604 	}
3605 	/* in case there is one left */
3606 	if (prevsec != NULL)
3607 		(void) sa_destroy_security(prevsec);
3608 }
3609 
3610 
3611 /*
3612  * for legacy support, we need to handle the old syntax. This is what
3613  * we get if sharemgr is called with the name "share" rather than
3614  * sharemgr.
3615  */
3616 
3617 static int
3618 format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
3619 {
3620 	int err;
3621 
3622 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
3623 	if (err > buffsize)
3624 		return (-1);
3625 	return (0);
3626 }
3627 
3628 
3629 /*
3630  * check_legacy_cmd(proto, cmd)
3631  *
3632  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
3633  * executable.
3634  */
3635 
3636 static int
3637 check_legacy_cmd(char *path)
3638 {
3639 	struct stat st;
3640 	int ret = 0;
3641 
3642 	if (stat(path, &st) == 0) {
3643 		if (S_ISREG(st.st_mode) &&
3644 		    st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
3645 			ret = 1;
3646 	}
3647 	return (ret);
3648 }
3649 
3650 /*
3651  * run_legacy_command(proto, cmd, argv)
3652  *
3653  * We know the command exists, so attempt to execute it with all the
3654  * arguments. This implements full legacy share support for those
3655  * protocols that don't have plugin providers.
3656  */
3657 
3658 static int
3659 run_legacy_command(char *path, char *argv[])
3660 {
3661 	int ret;
3662 
3663 	ret = execv(path, argv);
3664 	if (ret < 0) {
3665 		switch (errno) {
3666 		case EACCES:
3667 			ret = SA_NO_PERMISSION;
3668 			break;
3669 		default:
3670 			ret = SA_SYSTEM_ERR;
3671 			break;
3672 		}
3673 	}
3674 	return (ret);
3675 }
3676 
3677 /*
3678  * out_share(out, group, proto)
3679  *
3680  * Display the share information in the format that the "share"
3681  * command has traditionally used.
3682  */
3683 
3684 static void
3685 out_share(FILE *out, sa_group_t group, char *proto)
3686 {
3687 	sa_share_t share;
3688 	char resfmt[128];
3689 
3690 	for (share = sa_get_share(group, NULL);
3691 	    share != NULL;
3692 	    share = sa_get_next_share(share)) {
3693 		char *path;
3694 		char *type;
3695 		char *resource;
3696 		char *description;
3697 		char *groupname;
3698 		char *sharedstate;
3699 		int shared = 1;
3700 		char *soptions;
3701 
3702 		sharedstate = sa_get_share_attr(share, "shared");
3703 		path = sa_get_share_attr(share, "path");
3704 		type = sa_get_share_attr(share, "type");
3705 		resource = sa_get_share_attr(share, "resource");
3706 		groupname = sa_get_group_attr(group, "name");
3707 
3708 		if (groupname != NULL && strcmp(groupname, "default") == 0) {
3709 			sa_free_attr_string(groupname);
3710 			groupname = NULL;
3711 		}
3712 		description = sa_get_share_description(share);
3713 
3714 		/* Want the sharetab version if it exists */
3715 		soptions = sa_get_share_attr(share, "shareopts");
3716 
3717 		if (sharedstate == NULL)
3718 			shared = 0;
3719 
3720 		if (soptions == NULL)
3721 			soptions = sa_proto_legacy_format(proto, share, 1);
3722 
3723 		if (shared) {
3724 			/* only active shares go here */
3725 			(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
3726 			    resource != NULL ? resource : "-",
3727 			    groupname != NULL ? "@" : "",
3728 			    groupname != NULL ? groupname : "");
3729 			(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
3730 			    resfmt, path,
3731 			    (soptions != NULL && strlen(soptions) > 0) ?
3732 			    soptions : "rw",
3733 			    (description != NULL) ? description : "");
3734 		}
3735 
3736 		if (path != NULL)
3737 			sa_free_attr_string(path);
3738 		if (type != NULL)
3739 			sa_free_attr_string(type);
3740 		if (resource != NULL)
3741 			sa_free_attr_string(resource);
3742 		if (groupname != NULL)
3743 			sa_free_attr_string(groupname);
3744 		if (description != NULL)
3745 			sa_free_share_description(description);
3746 		if (sharedstate != NULL)
3747 			sa_free_attr_string(sharedstate);
3748 		if (soptions != NULL)
3749 			sa_format_free(soptions);
3750 	}
3751 }
3752 
3753 /*
3754  * output_legacy_file(out, proto)
3755  *
3756  * Walk all of the groups for the specified protocol and call
3757  * out_share() to format and write in the format displayed by the
3758  * "share" command with no arguments.
3759  */
3760 
3761 static void
3762 output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
3763 {
3764 	sa_group_t group;
3765 
3766 	for (group = sa_get_group(handle, NULL); group != NULL;
3767 	    group = sa_get_next_group(group)) {
3768 		char *options;
3769 		char *zfs;
3770 
3771 		/*
3772 		 * Get default options preformated, being careful to
3773 		 * handle legacy shares differently from new style
3774 		 * shares. Legacy share have options on the share.
3775 		 */
3776 
3777 		zfs = sa_get_group_attr(group, "zfs");
3778 		if (zfs != NULL) {
3779 			sa_group_t zgroup;
3780 			sa_free_attr_string(zfs);
3781 			options = sa_proto_legacy_format(proto, group, 1);
3782 			for (zgroup = sa_get_sub_group(group);
3783 			    zgroup != NULL;
3784 			    zgroup = sa_get_next_group(zgroup)) {
3785 
3786 				/* got a group, so display it */
3787 				out_share(out, zgroup, proto);
3788 			}
3789 		} else {
3790 			options = sa_proto_legacy_format(proto, group, 1);
3791 			out_share(out, group, proto);
3792 		}
3793 		if (options != NULL)
3794 			free(options);
3795 	}
3796 }
3797 
3798 /*ARGSUSED*/
3799 int
3800 sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
3801 {
3802 	char *protocol = "nfs";
3803 	char *options = NULL;
3804 	char *description = NULL;
3805 	char *groupname = NULL;
3806 	char *sharepath = NULL;
3807 	char *resource = NULL;
3808 	char *groupstatus = NULL;
3809 	int persist = SA_SHARE_TRANSIENT;
3810 	int argsused = 0;
3811 	int c;
3812 	int ret = SA_OK;
3813 	int zfs = 0;
3814 	int true_legacy = 0;
3815 	int curtype = SA_SHARE_TRANSIENT;
3816 	char cmd[MAXPATHLEN];
3817 	sa_group_t group = NULL;
3818 	sa_share_t share;
3819 	char dir[MAXPATHLEN];
3820 
3821 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
3822 		switch (c) {
3823 		case 'd':
3824 			description = optarg;
3825 			argsused++;
3826 			break;
3827 		case 'F':
3828 			protocol = optarg;
3829 			if (!sa_valid_protocol(protocol)) {
3830 				if (format_legacy_path(cmd, MAXPATHLEN,
3831 				    protocol, "share") == 0 &&
3832 				    check_legacy_cmd(cmd)) {
3833 					true_legacy++;
3834 				} else {
3835 					(void) fprintf(stderr, gettext(
3836 					    "Invalid protocol specified: "
3837 					    "%s\n"), protocol);
3838 					return (SA_INVALID_PROTOCOL);
3839 				}
3840 			}
3841 			break;
3842 		case 'o':
3843 			options = optarg;
3844 			argsused++;
3845 			break;
3846 		case 'p':
3847 			persist = SA_SHARE_PERMANENT;
3848 			argsused++;
3849 			break;
3850 		case 'h':
3851 		case '?':
3852 		default:
3853 			(void) fprintf(stderr, gettext("usage: %s\n"),
3854 			    sa_get_usage(USAGE_SHARE));
3855 			return (SA_OK);
3856 		}
3857 	}
3858 
3859 	/* Have the info so construct what is needed */
3860 	if (!argsused && optind == argc) {
3861 		/* display current info in share format */
3862 		(void) output_legacy_file(stdout, "nfs", handle);
3863 		return (ret);
3864 	}
3865 
3866 	/* We are modifying the configuration */
3867 	if (optind == argc) {
3868 		(void) fprintf(stderr, gettext("usage: %s\n"),
3869 		    sa_get_usage(USAGE_SHARE));
3870 		return (SA_LEGACY_ERR);
3871 	}
3872 	if (true_legacy) {
3873 		/* If still using legacy share/unshare, exec it */
3874 		ret = run_legacy_command(cmd, argv);
3875 		return (ret);
3876 	}
3877 
3878 	sharepath = argv[optind++];
3879 	if (optind < argc) {
3880 		resource = argv[optind];
3881 		groupname = strchr(resource, '@');
3882 		if (groupname != NULL)
3883 			*groupname++ = '\0';
3884 	}
3885 	if (realpath(sharepath, dir) == NULL)
3886 		ret = SA_BAD_PATH;
3887 	else
3888 		sharepath = dir;
3889 	if (ret == SA_OK)
3890 		share = sa_find_share(handle, sharepath);
3891 	else
3892 		share = NULL;
3893 
3894 	if (groupname != NULL) {
3895 		ret = SA_NOT_ALLOWED;
3896 	} else if (ret == SA_OK) {
3897 		char *legacygroup = "default";
3898 		/*
3899 		 * The legacy group is always present and zfs groups
3900 		 * come and go.  zfs shares may be in sub-groups and
3901 		 * the zfs share will already be in that group so it
3902 		 * isn't an error.
3903 		 */
3904 		/*
3905 		 * If the share exists (not NULL), then make sure it
3906 		 * is one we want to handle by getting the parent
3907 		 * group.
3908 		 */
3909 		if (share != NULL)
3910 			group = sa_get_parent_group(share);
3911 		else
3912 			group = sa_get_group(handle, legacygroup);
3913 
3914 		if (group != NULL) {
3915 			groupstatus = group_status(group);
3916 			if (share == NULL) {
3917 				share = sa_add_share(group, sharepath,
3918 				    persist, &ret);
3919 				if (share == NULL &&
3920 				    ret == SA_DUPLICATE_NAME) {
3921 					/*
3922 					 * Could be a ZFS path being started
3923 					 */
3924 					if (sa_zfs_is_shared(handle,
3925 					    sharepath)) {
3926 						ret = SA_OK;
3927 						group = sa_get_group(handle,
3928 						    "zfs");
3929 						if (group == NULL) {
3930 							/*
3931 							 * This shouldn't
3932 							 * happen.
3933 							 */
3934 							ret = SA_CONFIG_ERR;
3935 						} else {
3936 							share = sa_add_share(
3937 							    group, sharepath,
3938 							    persist, &ret);
3939 						}
3940 					}
3941 				}
3942 			} else {
3943 				char *type;
3944 				/*
3945 				 * May want to change persist state, but the
3946 				 * important thing is to change options. We
3947 				 * need to change them regardless of the
3948 				 * source.
3949 				 */
3950 				if (sa_zfs_is_shared(handle, sharepath)) {
3951 					zfs = 1;
3952 				}
3953 				remove_all_options(share, protocol);
3954 				type = sa_get_share_attr(share, "type");
3955 				if (type != NULL &&
3956 				    strcmp(type, "transient") != 0) {
3957 					curtype = SA_SHARE_PERMANENT;
3958 				}
3959 				if (type != NULL)
3960 					sa_free_attr_string(type);
3961 				if (curtype != persist) {
3962 					(void) sa_set_share_attr(share, "type",
3963 					    persist == SA_SHARE_PERMANENT ?
3964 					    "persist" : "transient");
3965 				}
3966 			}
3967 			/* Have a group to hold this share path */
3968 			if (ret == SA_OK && options != NULL &&
3969 			    strlen(options) > 0) {
3970 				ret = sa_parse_legacy_options(share,
3971 				    options,
3972 				    protocol);
3973 			}
3974 			if (!zfs) {
3975 				/*
3976 				 * ZFS shares never have resource or
3977 				 * description and we can't store the values
3978 				 * so don't try.
3979 				 */
3980 				if (ret == SA_OK && description != NULL)
3981 					ret = sa_set_share_description(share,
3982 					    description);
3983 				if (ret == SA_OK && resource != NULL)
3984 					ret = sa_set_share_attr(share,
3985 					    "resource", resource);
3986 			}
3987 			if (ret == SA_OK) {
3988 				if (strcmp(groupstatus, "enabled") == 0)
3989 					ret = sa_enable_share(share, protocol);
3990 				if (ret == SA_OK &&
3991 				    persist == SA_SHARE_PERMANENT) {
3992 					(void) sa_update_legacy(share,
3993 					    protocol);
3994 				}
3995 				if (ret == SA_OK)
3996 					ret = sa_update_config(handle);
3997 			}
3998 		} else {
3999 			ret = SA_SYSTEM_ERR;
4000 		}
4001 	}
4002 	if (ret != SA_OK) {
4003 		(void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
4004 		    sharepath, sa_errorstr(ret));
4005 		ret = SA_LEGACY_ERR;
4006 
4007 	}
4008 	return (ret);
4009 }
4010 
4011 /*
4012  * sa_legacy_unshare(flags, argc, argv)
4013  *
4014  * Implements the original unshare command.
4015  */
4016 /*ARGSUSED*/
4017 int
4018 sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
4019 {
4020 	char *protocol = "nfs"; /* for now */
4021 	char *options = NULL;
4022 	char *sharepath = NULL;
4023 	int persist = SA_SHARE_TRANSIENT;
4024 	int argsused = 0;
4025 	int c;
4026 	int ret = SA_OK;
4027 	int true_legacy = 0;
4028 	char cmd[MAXPATHLEN];
4029 
4030 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
4031 		switch (c) {
4032 		case 'h':
4033 		case '?':
4034 			break;
4035 		case 'F':
4036 			protocol = optarg;
4037 			if (!sa_valid_protocol(protocol)) {
4038 				if (format_legacy_path(cmd, MAXPATHLEN,
4039 				    protocol, "unshare") == 0 &&
4040 				    check_legacy_cmd(cmd)) {
4041 					true_legacy++;
4042 				} else {
4043 					(void) printf(gettext(
4044 					    "Invalid file system name\n"));
4045 					return (SA_INVALID_PROTOCOL);
4046 				}
4047 			}
4048 			break;
4049 		case 'o':
4050 			options = optarg;
4051 			argsused++;
4052 			break;
4053 		case 'p':
4054 			persist = SA_SHARE_PERMANENT;
4055 			argsused++;
4056 			break;
4057 		default:
4058 			(void) printf(gettext("usage: %s\n"),
4059 			    sa_get_usage(USAGE_UNSHARE));
4060 			return (SA_OK);
4061 		}
4062 	}
4063 
4064 	/* Have the info so construct what is needed */
4065 	if (optind == argc || (optind + 1) < argc || options != NULL) {
4066 		ret = SA_SYNTAX_ERR;
4067 	} else {
4068 		sa_share_t share;
4069 		char dir[MAXPATHLEN];
4070 		if (true_legacy) {
4071 			/* if still using legacy share/unshare, exec it */
4072 			ret = run_legacy_command(cmd, argv);
4073 			return (ret);
4074 		}
4075 		/*
4076 		 * Find the path in the internal configuration. If it
4077 		 * isn't found, attempt to resolve the path via
4078 		 * realpath() and try again.
4079 		 */
4080 		sharepath = argv[optind++];
4081 		share = sa_find_share(handle, sharepath);
4082 		if (share == NULL) {
4083 			if (realpath(sharepath, dir) == NULL) {
4084 				ret = SA_NO_SUCH_PATH;
4085 			} else {
4086 				share = sa_find_share(handle, dir);
4087 			}
4088 		}
4089 		if (share != NULL) {
4090 			ret = sa_disable_share(share, protocol);
4091 			/*
4092 			 * Errors are ok and removal should still occur. The
4093 			 * legacy unshare is more forgiving of errors than the
4094 			 * remove-share subcommand which may need the force
4095 			 * flag set for some error conditions. That is, the
4096 			 * "unshare" command will always unshare if it can
4097 			 * while "remove-share" might require the force option.
4098 			 */
4099 			if (persist == SA_SHARE_PERMANENT) {
4100 				ret = sa_remove_share(share);
4101 				if (ret == SA_OK)
4102 					ret = sa_update_config(handle);
4103 			}
4104 		} else {
4105 			ret = SA_NOT_SHARED;
4106 		}
4107 	}
4108 	switch (ret) {
4109 	default:
4110 		(void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
4111 		ret = SA_LEGACY_ERR;
4112 		break;
4113 	case SA_SYNTAX_ERR:
4114 		(void) printf(gettext("usage: %s\n"),
4115 		    sa_get_usage(USAGE_UNSHARE));
4116 		break;
4117 	case SA_OK:
4118 		break;
4119 	}
4120 	return (ret);
4121 }
4122 
4123 /*
4124  * Common commands that implement the sub-commands used by all
4125  * protcols. The entries are found via the lookup command
4126  */
4127 
4128 static sa_command_t commands[] = {
4129 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
4130 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
4131 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
4132 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
4133 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
4134 	{"list", 0, sa_list, USAGE_LIST},
4135 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
4136 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
4137 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
4138 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
4139 	{"show", 0, sa_show, USAGE_SHOW},
4140 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
4141 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
4142 		SVC_SET|SVC_ACTION},
4143 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
4144 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
4145 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
4146 	{NULL, 0, NULL, NULL}
4147 };
4148 
4149 static char *
4150 sa_get_usage(sa_usage_t index)
4151 {
4152 	char *ret = NULL;
4153 	switch (index) {
4154 	case USAGE_ADD_SHARE:
4155 		ret = gettext("add-share [-nth] [-r resource-name] "
4156 		    "[-d \"description text\"] -s sharepath group");
4157 		break;
4158 	case USAGE_CREATE:
4159 		ret = gettext(
4160 		    "create [-nvh] [-P proto [-p property=value]] group");
4161 		break;
4162 	case USAGE_DELETE:
4163 		ret = gettext("delete [-nvh] [-P proto] [-f] group");
4164 		break;
4165 	case USAGE_DISABLE:
4166 		ret = gettext("disable [-nvh] {-a | group ...}");
4167 		break;
4168 	case USAGE_ENABLE:
4169 		ret = gettext("enable [-nvh] {-a | group ...}");
4170 		break;
4171 	case USAGE_LIST:
4172 		ret = gettext("list [-vh] [-P proto]");
4173 		break;
4174 	case USAGE_MOVE_SHARE:
4175 		ret = gettext(
4176 		    "move-share [-nvh] -s sharepath destination-group");
4177 		break;
4178 	case USAGE_REMOVE_SHARE:
4179 		ret = gettext("remove-share [-fnvh] -s sharepath group");
4180 		break;
4181 	case USAGE_SET:
4182 		ret = gettext("set [-nvh] -P proto [-S optspace] "
4183 		    "[-p property=value]* [-s sharepath] group");
4184 		break;
4185 	case USAGE_SET_SECURITY:
4186 		ret = gettext("set-security [-nvh] -P proto -S security-type "
4187 		    "[-p property=value]* group");
4188 		break;
4189 	case USAGE_SET_SHARE:
4190 		ret = gettext("set-share [-nh] [-r resource] "
4191 		    "[-d \"description text\"] -s sharepath group");
4192 		break;
4193 	case USAGE_SHOW:
4194 		ret = gettext("show [-pvxh] [-P proto] [group ...]");
4195 		break;
4196 	case USAGE_SHARE:
4197 		ret = gettext("share [-F fstype] [-p] [-o optionlist]"
4198 		    "[-d description] [pathname [resourcename]]");
4199 		break;
4200 	case USAGE_START:
4201 		ret = gettext("start [-vh] [-P proto] {-a | group ...}");
4202 		break;
4203 	case USAGE_STOP:
4204 		ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
4205 		break;
4206 	case USAGE_UNSET:
4207 		ret = gettext("unset [-nvh] -P proto [-S optspace] "
4208 		    "[-p property]* group");
4209 		break;
4210 	case USAGE_UNSET_SECURITY:
4211 		ret = gettext("unset-security [-nvh] -P proto -S security-type"
4212 		    " [-p property]* group");
4213 		break;
4214 	case USAGE_UNSHARE:
4215 		ret = gettext(
4216 		    "unshare [-F fstype] [-p] sharepath");
4217 		break;
4218 	}
4219 	return (ret);
4220 }
4221 
4222 /*
4223  * sa_lookup(cmd, proto)
4224  *
4225  * Lookup the sub-command. proto isn't currently used, but it may
4226  * eventually provide a way to provide protocol specific sub-commands.
4227  */
4228 /*ARGSUSED*/
4229 sa_command_t *
4230 sa_lookup(char *cmd, char *proto)
4231 {
4232 	int i;
4233 	size_t len;
4234 
4235 	len = strlen(cmd);
4236 	for (i = 0; commands[i].cmdname != NULL; i++) {
4237 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
4238 			return (&commands[i]);
4239 	}
4240 	return (NULL);
4241 }
4242 
4243 /*ARGSUSED*/
4244 void
4245 sub_command_help(char *proto)
4246 {
4247 	int i;
4248 
4249 	(void) printf(gettext("\tsub-commands:\n"));
4250 	for (i = 0; commands[i].cmdname != NULL; i++) {
4251 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
4252 			(void) printf("\t%s\n",
4253 			    sa_get_usage((sa_usage_t)commands[i].cmdidx));
4254 	}
4255 }
4256