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