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