xref: /illumos-gate/usr/src/cmd/dfs.cmds/sharemgr/commands.c (revision 7247f8883be6bcac5fe4735b6f87f873387dbbef)
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  * basic_set(groupname, optlist, protocol, sharepath, dryrun)
2297  *
2298  * This function implements "set" when a name space (-S) is not
2299  * specified. It is a basic set. Options and other CLI parsing has
2300  * already been done.
2301  */
2302 
2303 static int
2304 basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
2305 		char *protocol,	char *sharepath, int dryrun)
2306 {
2307 	sa_group_t group;
2308 	int ret = SA_OK;
2309 	int change = 0;
2310 	struct list *worklist = NULL;
2311 
2312 	group = sa_get_group(handle, groupname);
2313 	if (group != NULL) {
2314 		sa_share_t share = NULL;
2315 		if (sharepath != NULL) {
2316 			share = sa_get_share(group, sharepath);
2317 			if (share == NULL) {
2318 				(void) printf(gettext(
2319 				    "Share does not exist in group %s\n"),
2320 				    groupname, sharepath);
2321 				ret = SA_NO_SUCH_PATH;
2322 			}
2323 		}
2324 		if (ret == SA_OK) {
2325 			/* group must exist */
2326 			ret = valid_options(optlist, protocol,
2327 			    share == NULL ? group : share, NULL);
2328 			if (ret == SA_OK && !dryrun) {
2329 				if (share != NULL)
2330 					change |= add_optionset(share, optlist,
2331 					    protocol, &ret);
2332 				else
2333 					change |= add_optionset(group, optlist,
2334 					    protocol, &ret);
2335 				if (ret == SA_OK && change)
2336 					worklist = add_list(worklist, group,
2337 					    share);
2338 			}
2339 		}
2340 		free_opt(optlist);
2341 	} else {
2342 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2343 		ret = SA_NO_SUCH_GROUP;
2344 	}
2345 	/*
2346 	 * we have a group and potentially legal additions
2347 	 */
2348 
2349 	/*
2350 	 * Commit to configuration if not a dryrunp and properties
2351 	 * have changed.
2352 	 */
2353 	if (!dryrun && ret == SA_OK && change && worklist != NULL)
2354 		/* properties changed, so update all shares */
2355 		(void) enable_all_groups(handle, worklist, 0, 0, protocol);
2356 
2357 	if (worklist != NULL)
2358 		free_list(worklist);
2359 	return (ret);
2360 }
2361 
2362 /*
2363  * space_set(groupname, optlist, protocol, sharepath, dryrun)
2364  *
2365  * This function implements "set" when a name space (-S) is
2366  * specified. It is a namespace set. Options and other CLI parsing has
2367  * already been done.
2368  */
2369 
2370 static int
2371 space_set(sa_handle_t handle, char *groupname, struct options *optlist,
2372 		char *protocol,	char *sharepath, int dryrun, char *sectype)
2373 {
2374 	sa_group_t group;
2375 	int ret = SA_OK;
2376 	int change = 0;
2377 	struct list *worklist = NULL;
2378 
2379 	/*
2380 	 * make sure protcol and sectype are valid
2381 	 */
2382 
2383 	if (sa_proto_valid_space(protocol, sectype) == 0) {
2384 		(void) printf(gettext("Option space \"%s\" not valid "
2385 		    "for protocol.\n"), sectype);
2386 		return (SA_INVALID_SECURITY);
2387 	}
2388 
2389 	group = sa_get_group(handle, groupname);
2390 	if (group != NULL) {
2391 		sa_share_t share = NULL;
2392 		if (sharepath != NULL) {
2393 			share = sa_get_share(group, sharepath);
2394 			if (share == NULL) {
2395 				(void) printf(gettext(
2396 				    "Share does not exist in group %s\n"),
2397 				    groupname, sharepath);
2398 				ret = SA_NO_SUCH_PATH;
2399 			}
2400 		}
2401 		if (ret == SA_OK) {
2402 			/* group must exist */
2403 			ret = valid_options(optlist, protocol,
2404 			    share == NULL ? group : share, sectype);
2405 			if (ret == SA_OK && !dryrun) {
2406 				if (share != NULL)
2407 					change = add_security(share, sectype,
2408 					    optlist, protocol, &ret);
2409 				else
2410 					change = add_security(group, sectype,
2411 					    optlist, protocol, &ret);
2412 				if (ret != SA_OK)
2413 					(void) printf(gettext(
2414 					    "Could not set property: %s\n"),
2415 					    sa_errorstr(ret));
2416 			}
2417 			if (ret == SA_OK && change)
2418 				worklist = add_list(worklist, group, share);
2419 		}
2420 		free_opt(optlist);
2421 	} else {
2422 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2423 		ret = SA_NO_SUCH_GROUP;
2424 	}
2425 	/*
2426 	 * we have a group and potentially legal additions
2427 	 */
2428 
2429 	/* Commit to configuration if not a dryrun */
2430 	if (!dryrun && ret == 0) {
2431 		if (change && worklist != NULL) {
2432 			/* properties changed, so update all shares */
2433 			(void) enable_all_groups(handle, worklist, 0, 0,
2434 			    protocol);
2435 		}
2436 		ret = sa_update_config(handle);
2437 	}
2438 	if (worklist != NULL)
2439 		free_list(worklist);
2440 	return (ret);
2441 }
2442 
2443 /*
2444  * sa_set(flags, argc, argv)
2445  *
2446  * Implements the set subcommand. It keys off of -S to determine which
2447  * set of operations to actually do.
2448  */
2449 
2450 int
2451 sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
2452 {
2453 	char *groupname;
2454 	int verbose = 0;
2455 	int dryrun = 0;
2456 	int c;
2457 	char *protocol = NULL;
2458 	int ret = SA_OK;
2459 	struct options *optlist = NULL;
2460 	char *sharepath = NULL;
2461 	char *optset = NULL;
2462 	int auth;
2463 
2464 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
2465 		switch (c) {
2466 		case 'v':
2467 			verbose++;
2468 			break;
2469 		case 'n':
2470 			dryrun++;
2471 			break;
2472 		case 'P':
2473 			protocol = optarg;
2474 			if (!sa_valid_protocol(protocol)) {
2475 				(void) printf(gettext(
2476 				    "Invalid protocol specified: %s\n"),
2477 				    protocol);
2478 				return (SA_INVALID_PROTOCOL);
2479 			}
2480 			break;
2481 		case 'p':
2482 			ret = add_opt(&optlist, optarg, 0);
2483 			switch (ret) {
2484 			case OPT_ADD_SYNTAX:
2485 				(void) printf(gettext("Property syntax error:"
2486 				    " %s\n"), optarg);
2487 				return (SA_SYNTAX_ERR);
2488 			case OPT_ADD_MEMORY:
2489 				(void) printf(gettext("No memory to set "
2490 				    "property: %s\n"), optarg);
2491 				return (SA_NO_MEMORY);
2492 			default:
2493 				break;
2494 			}
2495 			break;
2496 		case 's':
2497 			sharepath = optarg;
2498 			break;
2499 		case 'S':
2500 			optset = optarg;
2501 			break;
2502 		default:
2503 		case 'h':
2504 		case '?':
2505 			(void) printf(gettext("usage: %s\n"),
2506 			    sa_get_usage(USAGE_SET));
2507 			return (SA_OK);
2508 		}
2509 	}
2510 
2511 	if (optlist != NULL)
2512 		ret = chk_opt(optlist, optset != NULL, protocol);
2513 
2514 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
2515 	    protocol == NULL || ret != OPT_ADD_OK) {
2516 		char *sep = "\t";
2517 
2518 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
2519 		if (optind >= argc) {
2520 			(void) printf(gettext("%sgroup must be specified"),
2521 			    sep);
2522 			sep = ", ";
2523 		}
2524 		if (optlist == NULL) {
2525 			(void) printf(gettext("%sat least one property must be"
2526 			    " specified"), sep);
2527 			sep = ", ";
2528 		}
2529 		if (protocol == NULL) {
2530 			(void) printf(gettext("%sprotocol must be specified"),
2531 			    sep);
2532 			sep = ", ";
2533 		}
2534 		(void) printf("\n");
2535 		ret = SA_SYNTAX_ERR;
2536 	} else {
2537 		/*
2538 		 * If a group already exists, we can only add a new
2539 		 * protocol to it and not create a new one or add the
2540 		 * same protocol again.
2541 		 */
2542 
2543 		groupname = argv[optind];
2544 		auth = check_authorizations(groupname, flags);
2545 		if (optset == NULL)
2546 			ret = basic_set(handle, groupname, optlist, protocol,
2547 			    sharepath, dryrun);
2548 		else
2549 			ret = space_set(handle, groupname, optlist, protocol,
2550 			    sharepath, dryrun, optset);
2551 		if (dryrun && ret == SA_OK && !auth && verbose) {
2552 			(void) printf(gettext("Command would fail: %s\n"),
2553 			    sa_errorstr(SA_NO_PERMISSION));
2554 		}
2555 	}
2556 	return (ret);
2557 }
2558 
2559 /*
2560  * remove_options(group, optlist, proto, *err)
2561  *
2562  * Helper function to actually remove options from a group after all
2563  * preprocessing is done.
2564  */
2565 
2566 static int
2567 remove_options(sa_group_t group, struct options *optlist,
2568 		char *proto, int *err)
2569 {
2570 	struct options *cur;
2571 	sa_optionset_t optionset;
2572 	sa_property_t prop;
2573 	int change = 0;
2574 	int ret = SA_OK;
2575 
2576 	optionset = sa_get_optionset(group, proto);
2577 	if (optionset != NULL) {
2578 		for (cur = optlist; cur != NULL; cur = cur->next) {
2579 			prop = sa_get_property(optionset, cur->optname);
2580 			if (prop != NULL) {
2581 				ret = sa_remove_property(prop);
2582 				if (ret != SA_OK)
2583 					break;
2584 				change = 1;
2585 			}
2586 		}
2587 	}
2588 	if (ret == SA_OK && change)
2589 		ret = sa_commit_properties(optionset, 0);
2590 
2591 	if (err != NULL)
2592 		*err = ret;
2593 	return (change);
2594 }
2595 
2596 /*
2597  * valid_unset(group, optlist, proto)
2598  *
2599  * Sanity check the optlist to make sure they can be removed. Issue an
2600  * error if a property doesn't exist.
2601  */
2602 
2603 static int
2604 valid_unset(sa_group_t group, struct options *optlist, char *proto)
2605 {
2606 	struct options *cur;
2607 	sa_optionset_t optionset;
2608 	sa_property_t prop;
2609 	int ret = SA_OK;
2610 
2611 	optionset = sa_get_optionset(group, proto);
2612 	if (optionset != NULL) {
2613 		for (cur = optlist; cur != NULL; cur = cur->next) {
2614 			prop = sa_get_property(optionset, cur->optname);
2615 			if (prop == NULL) {
2616 				(void) printf(gettext(
2617 				    "Could not unset property %s: not set\n"),
2618 				    cur->optname);
2619 				ret = SA_NO_SUCH_PROP;
2620 			}
2621 		}
2622 	}
2623 	return (ret);
2624 }
2625 
2626 /*
2627  * valid_unset_security(group, optlist, proto)
2628  *
2629  * Sanity check the optlist to make sure they can be removed. Issue an
2630  * error if a property doesn't exist.
2631  */
2632 
2633 static int
2634 valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
2635 	    char *sectype)
2636 {
2637 	struct options *cur;
2638 	sa_security_t security;
2639 	sa_property_t prop;
2640 	int ret = SA_OK;
2641 	char *sec;
2642 
2643 	sec = sa_proto_space_alias(proto, sectype);
2644 	security = sa_get_security(group, sec, proto);
2645 	if (security != NULL) {
2646 		for (cur = optlist; cur != NULL; cur = cur->next) {
2647 			prop = sa_get_property(security, cur->optname);
2648 			if (prop == NULL) {
2649 				(void) printf(gettext(
2650 				    "Could not unset property %s: not set\n"),
2651 				    cur->optname);
2652 				ret = SA_NO_SUCH_PROP;
2653 			}
2654 		}
2655 	} else {
2656 		(void) printf(gettext(
2657 		    "Could not unset %s: space not defined\n"), sectype);
2658 		ret = SA_NO_SUCH_SECURITY;
2659 	}
2660 	if (sec != NULL)
2661 		sa_free_attr_string(sec);
2662 	return (ret);
2663 }
2664 
2665 /*
2666  * remove_security(group, optlist, proto)
2667  *
2668  * Remove the properties since they were checked as valid.
2669  */
2670 
2671 static int
2672 remove_security(sa_group_t group, char *sectype,
2673 		struct options *optlist, char *proto, int *err)
2674 {
2675 	sa_security_t security;
2676 	int ret = SA_OK;
2677 	int change = 0;
2678 
2679 	sectype = sa_proto_space_alias(proto, sectype);
2680 	security = sa_get_security(group, sectype, proto);
2681 	if (sectype != NULL)
2682 		sa_free_attr_string(sectype);
2683 
2684 	if (security != NULL) {
2685 		while (optlist != NULL) {
2686 			sa_property_t prop;
2687 			prop = sa_get_property(security, optlist->optname);
2688 			if (prop != NULL) {
2689 				ret = sa_remove_property(prop);
2690 				if (ret != SA_OK)
2691 					break;
2692 				change = 1;
2693 			}
2694 			optlist = optlist->next;
2695 		}
2696 		/*
2697 		 * when done, properties may have all been removed but
2698 		 * we need to keep the security type itself until
2699 		 * explicitly removed.
2700 		 */
2701 		if (ret == SA_OK && change)
2702 			ret = sa_commit_properties(security, 0);
2703 	} else {
2704 		ret = SA_NO_SUCH_PROP;
2705 	}
2706 	if (err != NULL)
2707 		*err = ret;
2708 	return (change);
2709 }
2710 
2711 /*
2712  * basic_unset(groupname, optlist, protocol, sharepath, dryrun)
2713  *
2714  * Unset non-named optionset properties.
2715  */
2716 
2717 static int
2718 basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
2719 		char *protocol,	char *sharepath, int dryrun)
2720 {
2721 	sa_group_t group;
2722 	int ret = SA_OK;
2723 	int change = 0;
2724 	struct list *worklist = NULL;
2725 	sa_share_t share = NULL;
2726 
2727 	group = sa_get_group(handle, groupname);
2728 	if (group == NULL)
2729 		return (ret);
2730 
2731 	if (sharepath != NULL) {
2732 		share = sa_get_share(group, sharepath);
2733 		if (share == NULL) {
2734 			(void) printf(gettext(
2735 			    "Share does not exist in group %s\n"),
2736 			    groupname, sharepath);
2737 			ret = SA_NO_SUCH_PATH;
2738 		}
2739 	}
2740 	if (ret == SA_OK) {
2741 		/* group must exist */
2742 		ret = valid_unset(share != NULL ? share : group,
2743 		    optlist, protocol);
2744 		if (ret == SA_OK && !dryrun) {
2745 			if (share != NULL) {
2746 				sa_optionset_t optionset;
2747 				sa_property_t prop;
2748 				change |= remove_options(share, optlist,
2749 				    protocol, &ret);
2750 				/*
2751 				 * If a share optionset is
2752 				 * empty, remove it.
2753 				 */
2754 				optionset = sa_get_optionset((sa_share_t)share,
2755 				    protocol);
2756 				if (optionset != NULL) {
2757 					prop = sa_get_property(optionset, NULL);
2758 					if (prop == NULL)
2759 						(void) sa_destroy_optionset(
2760 						    optionset);
2761 				}
2762 			} else {
2763 				change |= remove_options(group,
2764 				    optlist, protocol, &ret);
2765 			}
2766 			if (ret == SA_OK && change)
2767 				worklist = add_list(worklist, group,
2768 				    share);
2769 			if (ret != SA_OK)
2770 				(void) printf(gettext(
2771 				    "Could not remove properties: "
2772 				    "%s\n"), sa_errorstr(ret));
2773 		}
2774 	} else {
2775 		(void) printf(gettext("Group \"%s\" not found\n"),
2776 		    groupname);
2777 		ret = SA_NO_SUCH_GROUP;
2778 	}
2779 	free_opt(optlist);
2780 
2781 	/*
2782 	 * We have a group and potentially legal additions
2783 	 *
2784 	 * Commit to configuration if not a dryrun
2785 	 */
2786 	if (!dryrun && ret == SA_OK) {
2787 		if (change && worklist != NULL) {
2788 			/* properties changed, so update all shares */
2789 			(void) enable_all_groups(handle, worklist, 0, 0,
2790 			    protocol);
2791 		}
2792 	}
2793 	if (worklist != NULL)
2794 		free_list(worklist);
2795 	return (ret);
2796 }
2797 
2798 /*
2799  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
2800  *
2801  * Unset named optionset properties.
2802  */
2803 static int
2804 space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
2805 		char *protocol, char *sharepath, int dryrun, char *sectype)
2806 {
2807 	sa_group_t group;
2808 	int ret = SA_OK;
2809 	int change = 0;
2810 	struct list *worklist = NULL;
2811 	sa_share_t share = NULL;
2812 
2813 	group = sa_get_group(handle, groupname);
2814 	if (group == NULL) {
2815 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2816 		return (SA_NO_SUCH_GROUP);
2817 	}
2818 	if (sharepath != NULL) {
2819 		share = sa_get_share(group, sharepath);
2820 		if (share == NULL) {
2821 			(void) printf(gettext(
2822 			    "Share does not exist in group %s\n"),
2823 			    groupname, sharepath);
2824 			return (SA_NO_SUCH_PATH);
2825 		}
2826 	}
2827 	ret = valid_unset_security(share != NULL ? share : group, optlist,
2828 	    protocol, sectype);
2829 
2830 	if (ret == SA_OK && !dryrun) {
2831 		if (optlist != NULL) {
2832 			if (share != NULL) {
2833 				sa_security_t optionset;
2834 				sa_property_t prop;
2835 				change = remove_security(share,
2836 				    sectype, optlist, protocol, &ret);
2837 
2838 				/* If a share security is empty, remove it */
2839 				optionset = sa_get_security((sa_group_t)share,
2840 				    sectype, protocol);
2841 				if (optionset != NULL) {
2842 					prop = sa_get_property(optionset,
2843 					    NULL);
2844 					if (prop == NULL)
2845 						ret = sa_destroy_security(
2846 						    optionset);
2847 				}
2848 			} else {
2849 				change = remove_security(group, sectype,
2850 				    optlist, protocol, &ret);
2851 			}
2852 		} else {
2853 			sa_security_t security;
2854 			char *sec;
2855 			sec = sa_proto_space_alias(protocol, sectype);
2856 			security = sa_get_security(group, sec, protocol);
2857 			if (sec != NULL)
2858 				sa_free_attr_string(sec);
2859 			if (security != NULL) {
2860 				ret = sa_destroy_security(security);
2861 				if (ret == SA_OK)
2862 					change = 1;
2863 			} else {
2864 				ret = SA_NO_SUCH_PROP;
2865 			}
2866 		}
2867 		if (ret != SA_OK)
2868 			(void) printf(gettext("Could not unset property: %s\n"),
2869 			    sa_errorstr(ret));
2870 	}
2871 
2872 	if (ret == SA_OK && change)
2873 		worklist = add_list(worklist, group, 0);
2874 
2875 	free_opt(optlist);
2876 	/*
2877 	 * We have a group and potentially legal additions
2878 	 */
2879 
2880 	/* Commit to configuration if not a dryrun */
2881 	if (!dryrun && ret == 0) {
2882 		/* properties changed, so update all shares */
2883 		if (change && worklist != NULL)
2884 			(void) enable_all_groups(handle, worklist, 0, 0,
2885 			    protocol);
2886 		ret = sa_update_config(handle);
2887 	}
2888 	if (worklist != NULL)
2889 		free_list(worklist);
2890 	return (ret);
2891 }
2892 
2893 /*
2894  * sa_unset(flags, argc, argv)
2895  *
2896  * Implements the unset subcommand. Parsing done here and then basic
2897  * or space versions of the real code are called.
2898  */
2899 
2900 int
2901 sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
2902 {
2903 	char *groupname;
2904 	int verbose = 0;
2905 	int dryrun = 0;
2906 	int c;
2907 	char *protocol = NULL;
2908 	int ret = SA_OK;
2909 	struct options *optlist = NULL;
2910 	char *sharepath = NULL;
2911 	char *optset = NULL;
2912 	int auth;
2913 
2914 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
2915 		switch (c) {
2916 		case 'v':
2917 			verbose++;
2918 			break;
2919 		case 'n':
2920 			dryrun++;
2921 			break;
2922 		case 'P':
2923 			protocol = optarg;
2924 			if (!sa_valid_protocol(protocol)) {
2925 				(void) printf(gettext(
2926 				    "Invalid protocol specified: %s\n"),
2927 				    protocol);
2928 				return (SA_INVALID_PROTOCOL);
2929 			}
2930 			break;
2931 		case 'p':
2932 			ret = add_opt(&optlist, optarg, 1);
2933 			switch (ret) {
2934 			case OPT_ADD_SYNTAX:
2935 				(void) printf(gettext("Property syntax error "
2936 				    "for property %s\n"), optarg);
2937 				return (SA_SYNTAX_ERR);
2938 
2939 			case OPT_ADD_PROPERTY:
2940 				(void) printf(gettext("Properties need to be "
2941 				    "set with set command: %s\n"), optarg);
2942 				return (SA_SYNTAX_ERR);
2943 
2944 			default:
2945 				break;
2946 			}
2947 			break;
2948 		case 's':
2949 			sharepath = optarg;
2950 			break;
2951 		case 'S':
2952 			optset = optarg;
2953 			break;
2954 		default:
2955 		case 'h':
2956 		case '?':
2957 			(void) printf(gettext("usage: %s\n"),
2958 			    sa_get_usage(USAGE_UNSET));
2959 			return (SA_OK);
2960 		}
2961 	}
2962 
2963 	if (optlist != NULL)
2964 		ret = chk_opt(optlist, optset != NULL, protocol);
2965 
2966 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
2967 	    protocol == NULL) {
2968 		char *sep = "\t";
2969 		(void) printf(gettext("usage: %s\n"),
2970 		    sa_get_usage(USAGE_UNSET));
2971 		if (optind >= argc) {
2972 			(void) printf(gettext("%sgroup must be specified"),
2973 			    sep);
2974 			sep = ", ";
2975 		}
2976 		if (optlist == NULL) {
2977 			(void) printf(gettext("%sat least one property must "
2978 			    "be specified"), sep);
2979 			sep = ", ";
2980 		}
2981 		if (protocol == NULL) {
2982 			(void) printf(gettext("%sprotocol must be specified"),
2983 			    sep);
2984 			sep = ", ";
2985 		}
2986 		(void) printf("\n");
2987 		ret = SA_SYNTAX_ERR;
2988 	} else {
2989 
2990 		/*
2991 		 * If a group already exists, we can only add a new
2992 		 * protocol to it and not create a new one or add the
2993 		 * same protocol again.
2994 		 */
2995 
2996 		groupname = argv[optind];
2997 		auth = check_authorizations(groupname, flags);
2998 		if (optset == NULL)
2999 			ret = basic_unset(handle, groupname, optlist, protocol,
3000 			    sharepath, dryrun);
3001 		else
3002 			ret = space_unset(handle, groupname, optlist, protocol,
3003 			    sharepath, dryrun, optset);
3004 
3005 		if (dryrun && ret == SA_OK && !auth && verbose)
3006 			(void) printf(gettext("Command would fail: %s\n"),
3007 			    sa_errorstr(SA_NO_PERMISSION));
3008 	}
3009 	return (ret);
3010 }
3011 
3012 /*
3013  * sa_enable_group(flags, argc, argv)
3014  *
3015  * Implements the enable subcommand
3016  */
3017 
3018 int
3019 sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
3020 {
3021 	int verbose = 0;
3022 	int dryrun = 0;
3023 	int all = 0;
3024 	int c;
3025 	int ret = SA_OK;
3026 	char *protocol = NULL;
3027 	char *state;
3028 	struct list *worklist = NULL;
3029 	int auth = 1;
3030 	sa_group_t group;
3031 
3032 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
3033 		switch (c) {
3034 		case 'a':
3035 			all = 1;
3036 			break;
3037 		case 'n':
3038 			dryrun++;
3039 			break;
3040 		case 'P':
3041 			protocol = optarg;
3042 			if (!sa_valid_protocol(protocol)) {
3043 				(void) printf(gettext(
3044 				    "Invalid protocol specified: %s\n"),
3045 				    protocol);
3046 				return (SA_INVALID_PROTOCOL);
3047 			}
3048 			break;
3049 		case 'v':
3050 			verbose++;
3051 			break;
3052 		default:
3053 		case 'h':
3054 		case '?':
3055 			(void) printf(gettext("usage: %s\n"),
3056 			    sa_get_usage(USAGE_ENABLE));
3057 			return (0);
3058 		}
3059 	}
3060 
3061 	if (optind == argc && !all) {
3062 		(void) printf(gettext("usage: %s\n"),
3063 		    sa_get_usage(USAGE_ENABLE));
3064 		(void) printf(gettext("\tmust specify group\n"));
3065 		return (SA_NO_SUCH_PATH);
3066 	}
3067 	if (!all) {
3068 		while (optind < argc) {
3069 			group = sa_get_group(handle, argv[optind]);
3070 			if (group != NULL) {
3071 				auth &= check_authorizations(argv[optind],
3072 				    flags);
3073 				state = sa_get_group_attr(group, "state");
3074 				if (state != NULL &&
3075 				    strcmp(state, "enabled") == 0) {
3076 					/* already enabled */
3077 					if (verbose)
3078 						(void) printf(gettext(
3079 						    "Group \"%s\" is already "
3080 						    "enabled\n"),
3081 						    argv[optind]);
3082 					ret = SA_BUSY; /* already enabled */
3083 				} else {
3084 					worklist = add_list(worklist, group,
3085 					    0);
3086 					if (verbose)
3087 						(void) printf(gettext(
3088 						    "Enabling group \"%s\"\n"),
3089 						    argv[optind]);
3090 				}
3091 				if (state != NULL)
3092 					sa_free_attr_string(state);
3093 			} else {
3094 				ret = SA_NO_SUCH_GROUP;
3095 			}
3096 			optind++;
3097 		}
3098 	} else {
3099 		for (group = sa_get_group(handle, NULL);
3100 		    group != NULL;
3101 		    group = sa_get_next_group(group)) {
3102 			worklist = add_list(worklist, group, 0);
3103 		}
3104 	}
3105 	if (!dryrun && ret == SA_OK)
3106 		ret = enable_all_groups(handle, worklist, 1, 0, NULL);
3107 
3108 	if (ret != SA_OK && ret != SA_BUSY)
3109 		(void) printf(gettext("Could not enable group: %s\n"),
3110 		    sa_errorstr(ret));
3111 	if (ret == SA_BUSY)
3112 		ret = SA_OK;
3113 
3114 	if (worklist != NULL)
3115 		free_list(worklist);
3116 	if (dryrun && ret == SA_OK && !auth && verbose) {
3117 		(void) printf(gettext("Command would fail: %s\n"),
3118 		    sa_errorstr(SA_NO_PERMISSION));
3119 	}
3120 	return (ret);
3121 }
3122 
3123 /*
3124  * disable_group(group, setstate)
3125  *
3126  * Disable all the shares in the specified group honoring the setstate
3127  * argument. This is a helper for disable_all_groups in order to
3128  * simplify regular and subgroup (zfs) disabling. Group has already
3129  * been checked for non-NULL.
3130  */
3131 
3132 static int
3133 disable_group(sa_group_t group)
3134 {
3135 	sa_share_t share;
3136 	int ret = SA_OK;
3137 
3138 	for (share = sa_get_share(group, NULL);
3139 	    share != NULL && ret == SA_OK;
3140 	    share = sa_get_next_share(share)) {
3141 		ret = sa_disable_share(share, NULL);
3142 		if (ret == SA_NO_SUCH_PATH) {
3143 			/*
3144 			 * this is OK since the path is gone. we can't
3145 			 * re-share it anyway so no error.
3146 			 */
3147 			ret = SA_OK;
3148 		}
3149 	}
3150 	return (ret);
3151 }
3152 
3153 
3154 /*
3155  * disable_all_groups(work, setstate)
3156  *
3157  * helper function that disables the shares in the list of groups
3158  * provided. It optionally marks the group as disabled. Used by both
3159  * enable and start subcommands.
3160  */
3161 
3162 static int
3163 disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
3164 {
3165 	int ret = SA_OK;
3166 	sa_group_t subgroup, group;
3167 
3168 	while (work != NULL && ret == SA_OK) {
3169 		group = (sa_group_t)work->item;
3170 		if (setstate)
3171 			ret = sa_set_group_attr(group, "state", "disabled");
3172 		if (ret == SA_OK) {
3173 			char *name;
3174 			name = sa_get_group_attr(group, "name");
3175 			if (name != NULL && strcmp(name, "zfs") == 0) {
3176 				/* need to get the sub-groups for stopping */
3177 				for (subgroup = sa_get_sub_group(group);
3178 				    subgroup != NULL;
3179 				    subgroup = sa_get_next_group(subgroup)) {
3180 					ret = disable_group(subgroup);
3181 				}
3182 			} else {
3183 				ret = disable_group(group);
3184 			}
3185 			/*
3186 			 * We don't want to "disable" since it won't come
3187 			 * up after a reboot.  The SMF framework should do
3188 			 * the right thing. On enable we do want to do
3189 			 * something.
3190 			 */
3191 		}
3192 		work = work->next;
3193 	}
3194 	if (ret == SA_OK)
3195 		ret = sa_update_config(handle);
3196 	return (ret);
3197 }
3198 
3199 /*
3200  * sa_disable_group(flags, argc, argv)
3201  *
3202  * Implements the disable subcommand
3203  */
3204 
3205 int
3206 sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
3207 {
3208 	int verbose = 0;
3209 	int dryrun = 0;
3210 	int all = 0;
3211 	int c;
3212 	int ret = SA_OK;
3213 	char *protocol;
3214 	char *state;
3215 	struct list *worklist = NULL;
3216 	sa_group_t group;
3217 	int auth = 1;
3218 
3219 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
3220 		switch (c) {
3221 		case 'a':
3222 			all = 1;
3223 			break;
3224 		case 'n':
3225 			dryrun++;
3226 			break;
3227 		case 'P':
3228 			protocol = optarg;
3229 			if (!sa_valid_protocol(protocol)) {
3230 				(void) printf(gettext(
3231 				    "Invalid protocol specified: %s\n"),
3232 				    protocol);
3233 				return (SA_INVALID_PROTOCOL);
3234 			}
3235 			break;
3236 		case 'v':
3237 			verbose++;
3238 			break;
3239 		default:
3240 		case 'h':
3241 		case '?':
3242 			(void) printf(gettext("usage: %s\n"),
3243 			    sa_get_usage(USAGE_DISABLE));
3244 			return (0);
3245 		}
3246 	}
3247 
3248 	if (optind == argc && !all) {
3249 		(void) printf(gettext("usage: %s\n"),
3250 		    sa_get_usage(USAGE_DISABLE));
3251 		(void) printf(gettext("\tmust specify group\n"));
3252 		return (SA_NO_SUCH_PATH);
3253 	}
3254 	if (!all) {
3255 		while (optind < argc) {
3256 			group = sa_get_group(handle, argv[optind]);
3257 			if (group != NULL) {
3258 				auth &= check_authorizations(argv[optind],
3259 				    flags);
3260 				state = sa_get_group_attr(group, "state");
3261 				if (state == NULL ||
3262 				    strcmp(state, "disabled") == 0) {
3263 					/* already disabled */
3264 					if (verbose)
3265 						(void) printf(gettext(
3266 						    "Group \"%s\" is "
3267 						    "already disabled\n"),
3268 						    argv[optind]);
3269 					ret = SA_BUSY; /* already disable */
3270 				} else {
3271 					worklist = add_list(worklist, group, 0);
3272 					if (verbose)
3273 						(void) printf(gettext(
3274 						    "Disabling group "
3275 						    "\"%s\"\n"), argv[optind]);
3276 				}
3277 				if (state != NULL)
3278 					sa_free_attr_string(state);
3279 			} else {
3280 				ret = SA_NO_SUCH_GROUP;
3281 			}
3282 			optind++;
3283 		}
3284 	} else {
3285 		for (group = sa_get_group(handle, NULL);
3286 		    group != NULL;
3287 		    group = sa_get_next_group(group))
3288 			worklist = add_list(worklist, group, 0);
3289 	}
3290 
3291 	if (ret == SA_OK && !dryrun)
3292 		ret = disable_all_groups(handle, worklist, 1);
3293 	if (ret != SA_OK && ret != SA_BUSY)
3294 		(void) printf(gettext("Could not disable group: %s\n"),
3295 		    sa_errorstr(ret));
3296 	if (ret == SA_BUSY)
3297 		ret = SA_OK;
3298 	if (worklist != NULL)
3299 		free_list(worklist);
3300 	if (dryrun && ret == SA_OK && !auth && verbose)
3301 		(void) printf(gettext("Command would fail: %s\n"),
3302 		    sa_errorstr(SA_NO_PERMISSION));
3303 	return (ret);
3304 }
3305 
3306 /*
3307  * sa_start_group(flags, argc, argv)
3308  *
3309  * Implements the start command.
3310  * This is similar to enable except it doesn't change the state
3311  * of the group(s) and only enables shares if the group is already
3312  * enabled.
3313  */
3314 /*ARGSUSED*/
3315 int
3316 sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
3317 {
3318 	int verbose = 0;
3319 	int all = 0;
3320 	int c;
3321 	int ret = SMF_EXIT_OK;
3322 	char *protocol = NULL;
3323 	char *state;
3324 	struct list *worklist = NULL;
3325 	sa_group_t group;
3326 
3327 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
3328 		switch (c) {
3329 		case 'a':
3330 			all = 1;
3331 			break;
3332 		case 'P':
3333 			protocol = optarg;
3334 			if (!sa_valid_protocol(protocol)) {
3335 				(void) printf(gettext(
3336 				    "Invalid protocol specified: %s\n"),
3337 				    protocol);
3338 				return (SA_INVALID_PROTOCOL);
3339 			}
3340 			break;
3341 		case 'v':
3342 			verbose++;
3343 			break;
3344 		default:
3345 		case 'h':
3346 		case '?':
3347 			(void) printf(gettext("usage: %s\n"),
3348 			    sa_get_usage(USAGE_START));
3349 			return (SA_OK);
3350 		}
3351 	}
3352 
3353 	if (optind == argc && !all) {
3354 		(void) printf(gettext("usage: %s\n"),
3355 		    sa_get_usage(USAGE_START));
3356 		return (SMF_EXIT_ERR_FATAL);
3357 	}
3358 
3359 	if (!all) {
3360 		while (optind < argc) {
3361 			group = sa_get_group(handle, argv[optind]);
3362 			if (group != NULL) {
3363 				state = sa_get_group_attr(group, "state");
3364 				if (state == NULL ||
3365 				    strcmp(state, "enabled") == 0) {
3366 					worklist = add_list(worklist, group, 0);
3367 					if (verbose)
3368 						(void) printf(gettext(
3369 						    "Starting group \"%s\"\n"),
3370 						    argv[optind]);
3371 				} else {
3372 					/*
3373 					 * Determine if there are any
3374 					 * protocols.  if there aren't any,
3375 					 * then there isn't anything to do in
3376 					 * any case so no error.
3377 					 */
3378 					if (sa_get_optionset(group,
3379 					    protocol) != NULL) {
3380 						ret = SMF_EXIT_OK;
3381 					}
3382 				}
3383 				if (state != NULL)
3384 					sa_free_attr_string(state);
3385 			}
3386 			optind++;
3387 		}
3388 	} else {
3389 		for (group = sa_get_group(handle, NULL); group != NULL;
3390 		    group = sa_get_next_group(group)) {
3391 			state = sa_get_group_attr(group, "state");
3392 			if (state == NULL || strcmp(state, "enabled") == 0)
3393 				worklist = add_list(worklist, group, 0);
3394 			if (state != NULL)
3395 				sa_free_attr_string(state);
3396 		}
3397 	}
3398 
3399 	(void) enable_all_groups(handle, worklist, 0, 1, NULL);
3400 
3401 	if (worklist != NULL)
3402 		free_list(worklist);
3403 	return (ret);
3404 }
3405 
3406 /*
3407  * sa_stop_group(flags, argc, argv)
3408  *
3409  * Implements the stop command.
3410  * This is similar to disable except it doesn't change the state
3411  * of the group(s) and only disables shares if the group is already
3412  * enabled.
3413  */
3414 /*ARGSUSED*/
3415 int
3416 sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
3417 {
3418 	int verbose = 0;
3419 	int all = 0;
3420 	int c;
3421 	int ret = SMF_EXIT_OK;
3422 	char *protocol = NULL;
3423 	char *state;
3424 	struct list *worklist = NULL;
3425 	sa_group_t group;
3426 
3427 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
3428 		switch (c) {
3429 		case 'a':
3430 			all = 1;
3431 			break;
3432 		case 'P':
3433 			protocol = optarg;
3434 			if (!sa_valid_protocol(protocol)) {
3435 				(void) printf(gettext(
3436 				    "Invalid protocol specified: %s\n"),
3437 				    protocol);
3438 				return (SA_INVALID_PROTOCOL);
3439 			}
3440 			break;
3441 		case 'v':
3442 			verbose++;
3443 			break;
3444 		default:
3445 		case 'h':
3446 		case '?':
3447 			(void) printf(gettext("usage: %s\n"),
3448 			    sa_get_usage(USAGE_STOP));
3449 			return (0);
3450 		}
3451 	}
3452 
3453 	if (optind == argc && !all) {
3454 		(void) printf(gettext("usage: %s\n"),
3455 		    sa_get_usage(USAGE_STOP));
3456 		return (SMF_EXIT_ERR_FATAL);
3457 	} else if (!all) {
3458 		while (optind < argc) {
3459 			group = sa_get_group(handle, argv[optind]);
3460 			if (group != NULL) {
3461 				state = sa_get_group_attr(group, "state");
3462 				if (state == NULL ||
3463 				    strcmp(state, "enabled") == 0) {
3464 					worklist = add_list(worklist, group, 0);
3465 					if (verbose)
3466 						(void) printf(gettext(
3467 						    "Stopping group \"%s\"\n"),
3468 						    argv[optind]);
3469 				} else {
3470 					ret = SMF_EXIT_OK;
3471 				}
3472 				if (state != NULL)
3473 					sa_free_attr_string(state);
3474 			}
3475 			optind++;
3476 		}
3477 	} else {
3478 		for (group = sa_get_group(handle, NULL); group != NULL;
3479 		    group = sa_get_next_group(group)) {
3480 			state = sa_get_group_attr(group, "state");
3481 			if (state == NULL || strcmp(state, "enabled") == 0)
3482 				worklist = add_list(worklist, group, 0);
3483 			if (state != NULL)
3484 				sa_free_attr_string(state);
3485 		}
3486 	}
3487 
3488 	(void) disable_all_groups(handle, worklist, 0);
3489 	ret = sa_update_config(handle);
3490 
3491 	if (worklist != NULL)
3492 		free_list(worklist);
3493 	return (ret);
3494 }
3495 
3496 /*
3497  * remove_all_options(share, proto)
3498  *
3499  * Removes all options on a share.
3500  */
3501 
3502 static void
3503 remove_all_options(sa_share_t share, char *proto)
3504 {
3505 	sa_optionset_t optionset;
3506 	sa_security_t security;
3507 	sa_security_t prevsec = NULL;
3508 
3509 	optionset = sa_get_optionset(share, proto);
3510 	if (optionset != NULL)
3511 		(void) sa_destroy_optionset(optionset);
3512 	for (security = sa_get_security(share, NULL, NULL);
3513 	    security != NULL;
3514 	    security = sa_get_next_security(security)) {
3515 		char *type;
3516 		/*
3517 		 * We walk through the list.  prevsec keeps the
3518 		 * previous security so we can delete it without
3519 		 * destroying the list.
3520 		 */
3521 		if (prevsec != NULL) {
3522 			/* remove the previously seen security */
3523 			(void) sa_destroy_security(prevsec);
3524 			/* set to NULL so we don't try multiple times */
3525 			prevsec = NULL;
3526 		}
3527 		type = sa_get_security_attr(security, "type");
3528 		if (type != NULL) {
3529 			/*
3530 			 * if the security matches the specified protocol, we
3531 			 * want to remove it. prevsec holds it until either
3532 			 * the next pass or we fall out of the loop.
3533 			 */
3534 			if (strcmp(type, proto) == 0)
3535 				prevsec = security;
3536 			sa_free_attr_string(type);
3537 		}
3538 	}
3539 	/* in case there is one left */
3540 	if (prevsec != NULL)
3541 		(void) sa_destroy_security(prevsec);
3542 }
3543 
3544 
3545 /*
3546  * for legacy support, we need to handle the old syntax. This is what
3547  * we get if sharemgr is called with the name "share" rather than
3548  * sharemgr.
3549  */
3550 
3551 static int
3552 format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
3553 {
3554 	int err;
3555 
3556 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
3557 	if (err > buffsize)
3558 		return (-1);
3559 	return (0);
3560 }
3561 
3562 
3563 /*
3564  * check_legacy_cmd(proto, cmd)
3565  *
3566  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
3567  * executable.
3568  */
3569 
3570 static int
3571 check_legacy_cmd(char *path)
3572 {
3573 	struct stat st;
3574 	int ret = 0;
3575 
3576 	if (stat(path, &st) == 0) {
3577 		if (S_ISREG(st.st_mode) &&
3578 		    st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
3579 			ret = 1;
3580 	}
3581 	return (ret);
3582 }
3583 
3584 /*
3585  * run_legacy_command(proto, cmd, argv)
3586  *
3587  * We know the command exists, so attempt to execute it with all the
3588  * arguments. This implements full legacy share support for those
3589  * protocols that don't have plugin providers.
3590  */
3591 
3592 static int
3593 run_legacy_command(char *path, char *argv[])
3594 {
3595 	int ret;
3596 
3597 	ret = execv(path, argv);
3598 	if (ret < 0) {
3599 		switch (errno) {
3600 		case EACCES:
3601 			ret = SA_NO_PERMISSION;
3602 			break;
3603 		default:
3604 			ret = SA_SYSTEM_ERR;
3605 			break;
3606 		}
3607 	}
3608 	return (ret);
3609 }
3610 
3611 /*
3612  * out_share(out, group, proto)
3613  *
3614  * Display the share information in the format that the "share"
3615  * command has traditionally used.
3616  */
3617 
3618 static void
3619 out_share(FILE *out, sa_group_t group, char *proto)
3620 {
3621 	sa_share_t share;
3622 	char resfmt[128];
3623 
3624 	for (share = sa_get_share(group, NULL);
3625 	    share != NULL;
3626 	    share = sa_get_next_share(share)) {
3627 		char *path;
3628 		char *type;
3629 		char *resource;
3630 		char *description;
3631 		char *groupname;
3632 		char *sharedstate;
3633 		int shared = 1;
3634 		char *soptions;
3635 
3636 		sharedstate = sa_get_share_attr(share, "shared");
3637 		path = sa_get_share_attr(share, "path");
3638 		type = sa_get_share_attr(share, "type");
3639 		resource = sa_get_share_attr(share, "resource");
3640 		groupname = sa_get_group_attr(group, "name");
3641 
3642 		if (groupname != NULL && strcmp(groupname, "default") == 0) {
3643 			sa_free_attr_string(groupname);
3644 			groupname = NULL;
3645 		}
3646 		description = sa_get_share_description(share);
3647 
3648 		/* Want the sharetab version if it exists */
3649 		soptions = sa_get_share_attr(share, "shareopts");
3650 
3651 		if (sharedstate == NULL)
3652 			shared = 0;
3653 
3654 		if (soptions == NULL)
3655 			soptions = sa_proto_legacy_format(proto, share, 1);
3656 
3657 		if (shared) {
3658 			/* only active shares go here */
3659 			(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
3660 			    resource != NULL ? resource : "-",
3661 			    groupname != NULL ? "@" : "",
3662 			    groupname != NULL ? groupname : "");
3663 			(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
3664 			    resfmt, path,
3665 			    (soptions != NULL && strlen(soptions) > 0) ?
3666 			    soptions : "rw",
3667 			    (description != NULL) ? description : "");
3668 		}
3669 
3670 		if (path != NULL)
3671 			sa_free_attr_string(path);
3672 		if (type != NULL)
3673 			sa_free_attr_string(type);
3674 		if (resource != NULL)
3675 			sa_free_attr_string(resource);
3676 		if (groupname != NULL)
3677 			sa_free_attr_string(groupname);
3678 		if (description != NULL)
3679 			sa_free_share_description(description);
3680 		if (sharedstate != NULL)
3681 			sa_free_attr_string(sharedstate);
3682 		if (soptions != NULL)
3683 			sa_format_free(soptions);
3684 	}
3685 }
3686 
3687 /*
3688  * output_legacy_file(out, proto)
3689  *
3690  * Walk all of the groups for the specified protocol and call
3691  * out_share() to format and write in the format displayed by the
3692  * "share" command with no arguments.
3693  */
3694 
3695 static void
3696 output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
3697 {
3698 	sa_group_t group;
3699 
3700 	for (group = sa_get_group(handle, NULL); group != NULL;
3701 	    group = sa_get_next_group(group)) {
3702 		char *options;
3703 		char *zfs;
3704 
3705 		/*
3706 		 * Get default options preformated, being careful to
3707 		 * handle legacy shares differently from new style
3708 		 * shares. Legacy share have options on the share.
3709 		 */
3710 
3711 		zfs = sa_get_group_attr(group, "zfs");
3712 		if (zfs != NULL) {
3713 			sa_group_t zgroup;
3714 			sa_free_attr_string(zfs);
3715 			options = sa_proto_legacy_format(proto, group, 1);
3716 			for (zgroup = sa_get_sub_group(group);
3717 			    zgroup != NULL;
3718 			    zgroup = sa_get_next_group(zgroup)) {
3719 
3720 				/* got a group, so display it */
3721 				out_share(out, zgroup, proto);
3722 			}
3723 		} else {
3724 			options = sa_proto_legacy_format(proto, group, 1);
3725 			out_share(out, group, proto);
3726 		}
3727 		if (options != NULL)
3728 			free(options);
3729 	}
3730 }
3731 
3732 /*ARGSUSED*/
3733 int
3734 sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
3735 {
3736 	char *protocol = "nfs";
3737 	char *options = NULL;
3738 	char *description = NULL;
3739 	char *groupname = NULL;
3740 	char *sharepath = NULL;
3741 	char *resource = NULL;
3742 	char *groupstatus = NULL;
3743 	int persist = SA_SHARE_TRANSIENT;
3744 	int argsused = 0;
3745 	int c;
3746 	int ret = SA_OK;
3747 	int zfs = 0;
3748 	int true_legacy = 0;
3749 	int curtype = SA_SHARE_TRANSIENT;
3750 	char cmd[MAXPATHLEN];
3751 	sa_group_t group = NULL;
3752 	sa_share_t share;
3753 	char dir[MAXPATHLEN];
3754 
3755 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
3756 		switch (c) {
3757 		case 'd':
3758 			description = optarg;
3759 			argsused++;
3760 			break;
3761 		case 'F':
3762 			protocol = optarg;
3763 			if (!sa_valid_protocol(protocol)) {
3764 				if (format_legacy_path(cmd, MAXPATHLEN,
3765 				    protocol, "share") == 0 &&
3766 				    check_legacy_cmd(cmd)) {
3767 					true_legacy++;
3768 				} else {
3769 					(void) fprintf(stderr, gettext(
3770 					    "Invalid protocol specified: "
3771 					    "%s\n"), protocol);
3772 					return (SA_INVALID_PROTOCOL);
3773 				}
3774 			}
3775 			break;
3776 		case 'o':
3777 			options = optarg;
3778 			argsused++;
3779 			break;
3780 		case 'p':
3781 			persist = SA_SHARE_PERMANENT;
3782 			argsused++;
3783 			break;
3784 		case 'h':
3785 		case '?':
3786 		default:
3787 			(void) fprintf(stderr, gettext("usage: %s\n"),
3788 			    sa_get_usage(USAGE_SHARE));
3789 			return (SA_OK);
3790 		}
3791 	}
3792 
3793 	/* Have the info so construct what is needed */
3794 	if (!argsused && optind == argc) {
3795 		/* display current info in share format */
3796 		(void) output_legacy_file(stdout, "nfs", handle);
3797 		return (ret);
3798 	}
3799 
3800 	/* We are modifying the configuration */
3801 	if (optind == argc) {
3802 		(void) fprintf(stderr, gettext("usage: %s\n"),
3803 		    sa_get_usage(USAGE_SHARE));
3804 		return (SA_LEGACY_ERR);
3805 	}
3806 	if (true_legacy) {
3807 		/* If still using legacy share/unshare, exec it */
3808 		ret = run_legacy_command(cmd, argv);
3809 		return (ret);
3810 	}
3811 
3812 	sharepath = argv[optind++];
3813 	if (optind < argc) {
3814 		resource = argv[optind];
3815 		groupname = strchr(resource, '@');
3816 		if (groupname != NULL)
3817 			*groupname++ = '\0';
3818 	}
3819 	if (realpath(sharepath, dir) == NULL)
3820 		ret = SA_BAD_PATH;
3821 	else
3822 		sharepath = dir;
3823 	if (ret == SA_OK)
3824 		share = sa_find_share(handle, sharepath);
3825 	else
3826 		share = NULL;
3827 
3828 	if (groupname != NULL) {
3829 		ret = SA_NOT_ALLOWED;
3830 	} else if (ret == SA_OK) {
3831 		char *legacygroup = "default";
3832 		/*
3833 		 * The legacy group is always present and zfs groups
3834 		 * come and go.  zfs shares may be in sub-groups and
3835 		 * the zfs share will already be in that group so it
3836 		 * isn't an error.
3837 		 */
3838 		/*
3839 		 * If the share exists (not NULL), then make sure it
3840 		 * is one we want to handle by getting the parent
3841 		 * group.
3842 		 */
3843 		if (share != NULL)
3844 			group = sa_get_parent_group(share);
3845 		else
3846 			group = sa_get_group(handle, legacygroup);
3847 
3848 		if (group != NULL) {
3849 			groupstatus = group_status(group);
3850 			if (share == NULL) {
3851 				share = sa_add_share(group, sharepath,
3852 				    persist, &ret);
3853 				if (share == NULL &&
3854 				    ret == SA_DUPLICATE_NAME) {
3855 					/*
3856 					 * Could be a ZFS path being started
3857 					 */
3858 					if (sa_zfs_is_shared(handle,
3859 					    sharepath)) {
3860 						ret = SA_OK;
3861 						group = sa_get_group(handle,
3862 						    "zfs");
3863 						if (group == NULL) {
3864 							/*
3865 							 * This shouldn't
3866 							 * happen.
3867 							 */
3868 							ret = SA_CONFIG_ERR;
3869 						} else {
3870 							share = sa_add_share(
3871 							    group, sharepath,
3872 							    persist, &ret);
3873 						}
3874 					}
3875 				}
3876 			} else {
3877 				char *type;
3878 				/*
3879 				 * May want to change persist state, but the
3880 				 * important thing is to change options. We
3881 				 * need to change them regardless of the
3882 				 * source.
3883 				 */
3884 				if (sa_zfs_is_shared(handle, sharepath)) {
3885 					zfs = 1;
3886 				}
3887 				remove_all_options(share, protocol);
3888 				type = sa_get_share_attr(share, "type");
3889 				if (type != NULL &&
3890 				    strcmp(type, "transient") != 0) {
3891 					curtype = SA_SHARE_PERMANENT;
3892 				}
3893 				if (type != NULL)
3894 					sa_free_attr_string(type);
3895 				if (curtype != persist) {
3896 					(void) sa_set_share_attr(share, "type",
3897 					    persist == SA_SHARE_PERMANENT ?
3898 					    "persist" : "transient");
3899 				}
3900 			}
3901 			/* Have a group to hold this share path */
3902 			if (ret == SA_OK && options != NULL &&
3903 			    strlen(options) > 0) {
3904 				ret = sa_parse_legacy_options(share,
3905 				    options,
3906 				    protocol);
3907 			}
3908 			if (!zfs) {
3909 				/*
3910 				 * ZFS shares never have resource or
3911 				 * description and we can't store the values
3912 				 * so don't try.
3913 				 */
3914 				if (ret == SA_OK && description != NULL)
3915 					ret = sa_set_share_description(share,
3916 					    description);
3917 				if (ret == SA_OK && resource != NULL)
3918 					ret = sa_set_share_attr(share,
3919 					    "resource", resource);
3920 			}
3921 			if (ret == SA_OK) {
3922 				if (strcmp(groupstatus, "enabled") == 0)
3923 					ret = sa_enable_share(share, protocol);
3924 				if (ret == SA_OK &&
3925 				    persist == SA_SHARE_PERMANENT) {
3926 					(void) sa_update_legacy(share,
3927 					    protocol);
3928 				}
3929 				if (ret == SA_OK)
3930 					ret = sa_update_config(handle);
3931 			}
3932 		} else {
3933 			ret = SA_SYSTEM_ERR;
3934 		}
3935 	}
3936 	if (ret != SA_OK) {
3937 		(void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
3938 		    sharepath, sa_errorstr(ret));
3939 		ret = SA_LEGACY_ERR;
3940 
3941 	}
3942 	return (ret);
3943 }
3944 
3945 /*
3946  * sa_legacy_unshare(flags, argc, argv)
3947  *
3948  * Implements the original unshare command.
3949  */
3950 /*ARGSUSED*/
3951 int
3952 sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
3953 {
3954 	char *protocol = "nfs"; /* for now */
3955 	char *options = NULL;
3956 	char *sharepath = NULL;
3957 	int persist = SA_SHARE_TRANSIENT;
3958 	int argsused = 0;
3959 	int c;
3960 	int ret = SA_OK;
3961 	int true_legacy = 0;
3962 	char cmd[MAXPATHLEN];
3963 
3964 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
3965 		switch (c) {
3966 		case 'h':
3967 		case '?':
3968 			break;
3969 		case 'F':
3970 			protocol = optarg;
3971 			if (!sa_valid_protocol(protocol)) {
3972 				if (format_legacy_path(cmd, MAXPATHLEN,
3973 				    protocol, "unshare") == 0 &&
3974 				    check_legacy_cmd(cmd)) {
3975 					true_legacy++;
3976 				} else {
3977 					(void) printf(gettext(
3978 					    "Invalid file system name\n"));
3979 					return (SA_INVALID_PROTOCOL);
3980 				}
3981 			}
3982 			break;
3983 		case 'o':
3984 			options = optarg;
3985 			argsused++;
3986 			break;
3987 		case 'p':
3988 			persist = SA_SHARE_PERMANENT;
3989 			argsused++;
3990 			break;
3991 		default:
3992 			(void) printf(gettext("usage: %s\n"),
3993 			    sa_get_usage(USAGE_UNSHARE));
3994 			return (SA_OK);
3995 		}
3996 	}
3997 
3998 	/* Have the info so construct what is needed */
3999 	if (optind == argc || (optind + 1) < argc || options != NULL) {
4000 		ret = SA_SYNTAX_ERR;
4001 	} else {
4002 		sa_share_t share;
4003 		char dir[MAXPATHLEN];
4004 		if (true_legacy) {
4005 			/* if still using legacy share/unshare, exec it */
4006 			ret = run_legacy_command(cmd, argv);
4007 			return (ret);
4008 		}
4009 		/*
4010 		 * Find the path in the internal configuration. If it
4011 		 * isn't found, attempt to resolve the path via
4012 		 * realpath() and try again.
4013 		 */
4014 		sharepath = argv[optind++];
4015 		share = sa_find_share(handle, sharepath);
4016 		if (share == NULL) {
4017 			if (realpath(sharepath, dir) == NULL) {
4018 				ret = SA_NO_SUCH_PATH;
4019 			} else {
4020 				share = sa_find_share(handle, dir);
4021 			}
4022 		}
4023 		if (share != NULL) {
4024 			ret = sa_disable_share(share, protocol);
4025 			/*
4026 			 * Errors are ok and removal should still occur. The
4027 			 * legacy unshare is more forgiving of errors than the
4028 			 * remove-share subcommand which may need the force
4029 			 * flag set for some error conditions. That is, the
4030 			 * "unshare" command will always unshare if it can
4031 			 * while "remove-share" might require the force option.
4032 			 */
4033 			if (persist == SA_SHARE_PERMANENT) {
4034 				ret = sa_remove_share(share);
4035 				if (ret == SA_OK)
4036 					ret = sa_update_config(handle);
4037 			}
4038 		} else {
4039 			ret = SA_NOT_SHARED;
4040 		}
4041 	}
4042 	switch (ret) {
4043 	default:
4044 		(void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
4045 		ret = SA_LEGACY_ERR;
4046 		break;
4047 	case SA_SYNTAX_ERR:
4048 		(void) printf(gettext("usage: %s\n"),
4049 		    sa_get_usage(USAGE_UNSHARE));
4050 		break;
4051 	case SA_OK:
4052 		break;
4053 	}
4054 	return (ret);
4055 }
4056 
4057 /*
4058  * Common commands that implement the sub-commands used by all
4059  * protcols. The entries are found via the lookup command
4060  */
4061 
4062 static sa_command_t commands[] = {
4063 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
4064 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
4065 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
4066 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
4067 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
4068 	{"list", 0, sa_list, USAGE_LIST},
4069 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
4070 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
4071 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
4072 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
4073 	{"show", 0, sa_show, USAGE_SHOW},
4074 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
4075 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
4076 		SVC_SET|SVC_ACTION},
4077 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
4078 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
4079 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
4080 	{NULL, 0, NULL, NULL}
4081 };
4082 
4083 static char *
4084 sa_get_usage(sa_usage_t index)
4085 {
4086 	char *ret = NULL;
4087 	switch (index) {
4088 	case USAGE_ADD_SHARE:
4089 		ret = gettext("add-share [-nth] [-r resource-name] "
4090 		    "[-d \"description text\"] -s sharepath group");
4091 		break;
4092 	case USAGE_CREATE:
4093 		ret = gettext(
4094 		    "create [-nvh] [-P proto [-p property=value]] group");
4095 		break;
4096 	case USAGE_DELETE:
4097 		ret = gettext("delete [-nvh] [-P proto] [-f] group");
4098 		break;
4099 	case USAGE_DISABLE:
4100 		ret = gettext("disable [-nvh] {-a | group ...}");
4101 		break;
4102 	case USAGE_ENABLE:
4103 		ret = gettext("enable [-nvh] {-a | group ...}");
4104 		break;
4105 	case USAGE_LIST:
4106 		ret = gettext("list [-vh] [-P proto]");
4107 		break;
4108 	case USAGE_MOVE_SHARE:
4109 		ret = gettext(
4110 		    "move-share [-nvh] -s sharepath destination-group");
4111 		break;
4112 	case USAGE_REMOVE_SHARE:
4113 		ret = gettext("remove-share [-fnvh] -s sharepath group");
4114 		break;
4115 	case USAGE_SET:
4116 		ret = gettext("set [-nvh] -P proto [-S optspace] "
4117 		    "[-p property=value]* [-s sharepath] group");
4118 		break;
4119 	case USAGE_SET_SECURITY:
4120 		ret = gettext("set-security [-nvh] -P proto -S security-type "
4121 		    "[-p property=value]* group");
4122 		break;
4123 	case USAGE_SET_SHARE:
4124 		ret = gettext("set-share [-nh] [-r resource] "
4125 		    "[-d \"description text\"] -s sharepath group");
4126 		break;
4127 	case USAGE_SHOW:
4128 		ret = gettext("show [-pvxh] [-P proto] [group ...]");
4129 		break;
4130 	case USAGE_SHARE:
4131 		ret = gettext("share [-F fstype] [-p] [-o optionlist]"
4132 		    "[-d description] [pathname [resourcename]]");
4133 		break;
4134 	case USAGE_START:
4135 		ret = gettext("start [-vh] [-P proto] {-a | group ...}");
4136 		break;
4137 	case USAGE_STOP:
4138 		ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
4139 		break;
4140 	case USAGE_UNSET:
4141 		ret = gettext("unset [-nvh] -P proto [-S optspace] "
4142 		    "[-p property]* group");
4143 		break;
4144 	case USAGE_UNSET_SECURITY:
4145 		ret = gettext("unset-security [-nvh] -P proto -S security-type"
4146 		    " [-p property]* group");
4147 		break;
4148 	case USAGE_UNSHARE:
4149 		ret = gettext(
4150 		    "unshare [-F fstype] [-p] sharepath");
4151 		break;
4152 	}
4153 	return (ret);
4154 }
4155 
4156 /*
4157  * sa_lookup(cmd, proto)
4158  *
4159  * Lookup the sub-command. proto isn't currently used, but it may
4160  * eventually provide a way to provide protocol specific sub-commands.
4161  */
4162 /*ARGSUSED*/
4163 sa_command_t *
4164 sa_lookup(char *cmd, char *proto)
4165 {
4166 	int i;
4167 	size_t len;
4168 
4169 	len = strlen(cmd);
4170 	for (i = 0; commands[i].cmdname != NULL; i++) {
4171 		if (strncmp(cmd, commands[i].cmdname, len) == 0)
4172 			return (&commands[i]);
4173 	}
4174 	return (NULL);
4175 }
4176 
4177 /*ARGSUSED*/
4178 void
4179 sub_command_help(char *proto)
4180 {
4181 	int i;
4182 
4183 	(void) printf(gettext("\tsub-commands:\n"));
4184 	for (i = 0; commands[i].cmdname != NULL; i++) {
4185 		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
4186 			(void) printf("\t%s\n",
4187 			    sa_get_usage((sa_usage_t)commands[i].cmdidx));
4188 	}
4189 }
4190