xref: /titanic_50/usr/src/cmd/dfs.cmds/sharemgr/commands.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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 2006 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(struct list *work, int setstate, int online,
259 	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();
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(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(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((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();
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(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(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();
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(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(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(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(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(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_group_t group, sa_share_t share, int update_legacy)
1396 {
1397 	char *value;
1398 	int enabled;
1399 	sa_optionset_t optionset;
1400 	int ret = SA_OK;
1401 	char *zfs = NULL;
1402 	int iszfs = 0;
1403 
1404 	/*
1405 	 * need to enable this share if the group is enabled but not
1406 	 * otherwise. The enable is also done on each protocol
1407 	 * represented in the group.
1408 	 */
1409 	value = sa_get_group_attr(group, "state");
1410 	enabled = value != NULL && strcmp(value, "enabled") == 0;
1411 	if (value != NULL)
1412 	    sa_free_attr_string(value);
1413 	/* remove legacy config if necessary */
1414 	if (update_legacy)
1415 	    ret = sa_delete_legacy(share);
1416 	zfs = sa_get_group_attr(group, "zfs");
1417 	if (zfs != NULL) {
1418 	    iszfs++;
1419 	    sa_free_attr_string(zfs);
1420 	}
1421 
1422 	/*
1423 	 * Step through each optionset at the group level and
1424 	 * enable the share based on the protocol type. This
1425 	 * works because protocols must be set on the group
1426 	 * for the protocol to be enabled.
1427 	 */
1428 	for (optionset = sa_get_optionset(group, NULL);
1429 	    optionset != NULL && ret == SA_OK;
1430 	    optionset = sa_get_next_optionset(optionset)) {
1431 	    value = sa_get_optionset_attr(optionset, "type");
1432 	    if (value != NULL) {
1433 		if (enabled)
1434 		    ret = sa_enable_share(share, value);
1435 		if (update_legacy && !iszfs)
1436 		    (void) sa_update_legacy(share, value);
1437 		sa_free_attr_string(value);
1438 	    }
1439 	}
1440 	if (ret == SA_OK)
1441 	    (void) sa_update_config();
1442 	return (ret);
1443 }
1444 
1445 /*
1446  * sa_addshare(flags, argc, argv)
1447  *
1448  * implements add-share subcommand.
1449  */
1450 
1451 int
1452 sa_addshare(int flags, int argc, char *argv[])
1453 {
1454 	int verbose = 0;
1455 	int dryrun = 0;
1456 	int c;
1457 	int ret = SA_OK;
1458 	sa_group_t group;
1459 	sa_share_t share;
1460 	char *sharepath = NULL;
1461 	char *description = NULL;
1462 	char *resource = NULL;
1463 	int persist = SA_SHARE_PERMANENT; /* default to persist */
1464 	int auth;
1465 	char dir[MAXPATHLEN];
1466 
1467 	while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
1468 	    switch (c) {
1469 	    case 'n':
1470 		dryrun++;
1471 		break;
1472 	    case 'v':
1473 		verbose++;
1474 		break;
1475 	    case 'd':
1476 		description = optarg;
1477 		break;
1478 	    case 'r':
1479 		resource = optarg;
1480 		break;
1481 	    case 's':
1482 		/*
1483 		 * save share path into group. Currently limit
1484 		 * to one share per command.
1485 		 */
1486 		if (sharepath != NULL) {
1487 		    (void) printf(gettext("Adding multiple shares not"
1488 				    "supported\n"));
1489 		    return (1);
1490 		}
1491 		sharepath = optarg;
1492 		break;
1493 	    case 't':
1494 		persist = SA_SHARE_TRANSIENT;
1495 		break;
1496 	    default:
1497 	    case 'h':
1498 	    case '?':
1499 		(void) printf(gettext("usage: %s\n"),
1500 				sa_get_usage(USAGE_ADD_SHARE));
1501 		return (0);
1502 	    }
1503 	}
1504 
1505 	if (optind >= argc) {
1506 	    (void) printf(gettext("usage: %s\n"),
1507 				sa_get_usage(USAGE_ADD_SHARE));
1508 	    if (dryrun || sharepath != NULL || description != NULL ||
1509 		resource != NULL || verbose || persist) {
1510 		(void) printf(gettext("\tgroup must be specified\n"));
1511 		ret = SA_NO_SUCH_GROUP;
1512 	    } else {
1513 		ret = SA_OK;
1514 	    }
1515 	} else {
1516 	    if (sharepath == NULL) {
1517 		(void) printf(gettext("usage: %s\n"),
1518 				sa_get_usage(USAGE_ADD_SHARE));
1519 		(void) printf(gettext("\t-s sharepath must be specified\n"));
1520 		ret = SA_BAD_PATH;
1521 	    }
1522 	    if (ret == SA_OK) {
1523 		if (realpath(sharepath, dir) == NULL) {
1524 		    ret = SA_BAD_PATH;
1525 		    (void) printf(gettext("Path is not valid: %s\n"),
1526 					sharepath);
1527 		} else {
1528 		    sharepath = dir;
1529 		}
1530 	    }
1531 	    if (ret == SA_OK && resource != NULL) {
1532 		/* check for valid syntax */
1533 		if (strpbrk(resource, " \t/") != NULL) {
1534 		    (void) printf(gettext("usage: %s\n"),
1535 				sa_get_usage(USAGE_ADD_SHARE));
1536 		    (void) printf(gettext("\tresource must not contain white"
1537 				    "space or '/' characters\n"));
1538 		    ret = SA_BAD_PATH;
1539 		}
1540 	    }
1541 	    if (ret == SA_OK) {
1542 		group = sa_get_group(argv[optind]);
1543 		if (group != NULL) {
1544 		    auth = check_authorizations(argv[optind], flags);
1545 		    share = sa_find_share(sharepath);
1546 		    if (share != NULL) {
1547 			group = sa_get_parent_group(share);
1548 			if (group != NULL) {
1549 			    char *groupname;
1550 			    groupname = sa_get_group_attr(group, "name");
1551 			    if (groupname != NULL) {
1552 				(void) printf(gettext("Share path already "
1553 							"shared in group "
1554 							"\"%s\": %s\n"),
1555 						groupname, sharepath);
1556 				sa_free_attr_string(groupname);
1557 			    } else {
1558 				(void) printf(gettext("Share path already"
1559 							"shared: %s\n"),
1560 						groupname, sharepath);
1561 			    }
1562 			} else {
1563 			    (void) printf(gettext("Share path %s already "
1564 							"shared\n"),
1565 				    sharepath);
1566 			}
1567 			ret = SA_DUPLICATE_NAME;
1568 		    } else {
1569 			/*
1570 			 * need to check that resource name is unique
1571 			 * at some point.
1572 			 */
1573 			if (dryrun)
1574 			    ret = sa_check_path(group, sharepath);
1575 			else
1576 			    share = sa_add_share(group, sharepath,
1577 							persist, &ret);
1578 			if (!dryrun && share == NULL) {
1579 				(void) printf(gettext("Could not add share: "
1580 							"%s\n"),
1581 					sa_errorstr(ret));
1582 			} else {
1583 			    if (!dryrun && ret == SA_OK) {
1584 				if (resource != NULL) {
1585 				    if (strpbrk(resource, " \t/") == NULL) {
1586 					ret = sa_set_share_attr(share,
1587 								"resource",
1588 								resource);
1589 				    }
1590 				}
1591 				if (ret == SA_OK && description != NULL) {
1592 				    ret = sa_set_share_description(share,
1593 							    description);
1594 				}
1595 				if (ret == SA_OK) {
1596 				    /* now enable the share(s) */
1597 				    ret = enable_share(group, share, 1);
1598 				    ret = sa_update_config();
1599 				}
1600 				switch (ret) {
1601 				case SA_DUPLICATE_NAME:
1602 				    (void) printf(gettext("Resource name in"
1603 						    "use: %s\n"),
1604 					    resource);
1605 				    break;
1606 				default:
1607 				    (void) printf(gettext("Could not set "
1608 						    "attribute: %s\n"),
1609 					    sa_errorstr(ret));
1610 				    break;
1611 				case SA_OK:
1612 				    break;
1613 				}
1614 			    } else if (dryrun && ret == SA_OK &&
1615 					!auth && verbose) {
1616 				(void) printf(gettext("Command would fail: "
1617 							"%s\n"),
1618 					sa_errorstr(SA_NO_PERMISSION));
1619 				ret = SA_NO_PERMISSION;
1620 			    }
1621 			}
1622 		    }
1623 		} else {
1624 		    (void) printf(gettext("Group \"%s\" not found\n"),
1625 					argv[optind]);
1626 		    ret = SA_NO_SUCH_GROUP;
1627 		}
1628 	    }
1629 	}
1630 	return (ret);
1631 }
1632 
1633 /*
1634  * sa_moveshare(flags, argc, argv)
1635  *
1636  * implements move-share subcommand.
1637  */
1638 
1639 int
1640 sa_moveshare(int flags, int argc, char *argv[])
1641 {
1642 	int verbose = 0;
1643 	int dryrun = 0;
1644 	int c;
1645 	int ret = SA_OK;
1646 	sa_group_t group;
1647 	sa_share_t share;
1648 	char *sharepath = NULL;
1649 	int authsrc = 0, authdst = 0;
1650 
1651 	while ((c = getopt(argc, argv, "?hvns:")) != EOF) {
1652 	    switch (c) {
1653 	    case 'n':
1654 		dryrun++;
1655 		break;
1656 	    case 'v':
1657 		verbose++;
1658 		break;
1659 	    case 's':
1660 		/*
1661 		 * remove share path from group. Currently limit
1662 		 * to one share per command.
1663 		 */
1664 		if (sharepath != NULL) {
1665 		    (void) printf(gettext("Moving multiple shares not"
1666 				    "supported\n"));
1667 		    return (SA_BAD_PATH);
1668 		}
1669 		sharepath = optarg;
1670 		break;
1671 	    default:
1672 	    case 'h':
1673 	    case '?':
1674 		(void) printf(gettext("usage: %s\n"),
1675 				sa_get_usage(USAGE_MOVE_SHARE));
1676 		return (0);
1677 	    }
1678 	}
1679 
1680 	if (optind >= argc || sharepath == NULL) {
1681 			(void) printf(gettext("usage: %s\n"),
1682 				sa_get_usage(USAGE_MOVE_SHARE));
1683 	    if (dryrun || verbose || sharepath != NULL) {
1684 		(void) printf(gettext("\tgroup must be specified\n"));
1685 		ret = SA_NO_SUCH_GROUP;
1686 	    } else {
1687 		if (sharepath == NULL) {
1688 		    ret = SA_SYNTAX_ERR;
1689 		    (void) printf(gettext("\tsharepath must be specified\n"));
1690 		} else
1691 		    ret = SA_OK;
1692 	    }
1693 	} else {
1694 	    if (sharepath == NULL) {
1695 		(void) printf(gettext("sharepath must be specified with "
1696 				"the -s option\n"));
1697 		ret = SA_BAD_PATH;
1698 	    } else {
1699 		group = sa_get_group(argv[optind]);
1700 		if (group != NULL) {
1701 		    share = sa_find_share(sharepath);
1702 		    authdst = check_authorizations(argv[optind], flags);
1703 		    if (share == NULL) {
1704 			(void) printf(gettext("Share not found: %s\n"),
1705 					sharepath);
1706 			ret = SA_NO_SUCH_PATH;
1707 		    } else {
1708 			sa_group_t parent;
1709 			char *zfsold;
1710 			char *zfsnew;
1711 
1712 			parent = sa_get_parent_group(share);
1713 			if (parent != NULL) {
1714 			    char *pname;
1715 			    pname = sa_get_group_attr(parent, "name");
1716 			    if (pname != NULL) {
1717 				authsrc = check_authorizations(pname, flags);
1718 				sa_free_attr_string(pname);
1719 			    }
1720 			    zfsold = sa_get_group_attr(parent, "zfs");
1721 			    zfsnew = sa_get_group_attr(group, "zfs");
1722 			    if ((zfsold != NULL && zfsnew == NULL) ||
1723 				(zfsold == NULL && zfsnew != NULL)) {
1724 				ret = SA_NOT_ALLOWED;
1725 			    }
1726 			    if (zfsold != NULL)
1727 				sa_free_attr_string(zfsold);
1728 			    if (zfsnew != NULL)
1729 				sa_free_attr_string(zfsnew);
1730 			}
1731 			if (!dryrun && ret == SA_OK) {
1732 			    ret = sa_move_share(group, share);
1733 			}
1734 			if (ret == SA_OK && parent != group && !dryrun) {
1735 			    char *oldstate;
1736 			    ret = sa_update_config();
1737 				/*
1738 				 * note that the share may need to be
1739 				 * "unshared" if the new group is
1740 				 * disabled and the old was enabled or
1741 				 * it may need to be share to update
1742 				 * if the new group is enabled.
1743 				 */
1744 			    oldstate = sa_get_group_attr(parent, "state");
1745 			    /* enable_share determines what to do */
1746 			    if (strcmp(oldstate, "enabled") == 0) {
1747 				(void) sa_disable_share(share, NULL);
1748 			    }
1749 			    (void) enable_share(group, share, 1);
1750 			    if (oldstate != NULL)
1751 				sa_free_attr_string(oldstate);
1752 			}
1753 			if (ret != SA_OK) {
1754 			    (void) printf(gettext("Could not move share: %s\n"),
1755 				    sa_errorstr(ret));
1756 			}
1757 			if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
1758 			    verbose) {
1759 			    (void) printf(gettext("Command would fail: %s\n"),
1760 					sa_errorstr(SA_NO_PERMISSION));
1761 			}
1762 		    }
1763 		} else {
1764 		    (void) printf(gettext("Group \"%s\" not found\n"),
1765 					argv[optind]);
1766 		    ret = SA_NO_SUCH_GROUP;
1767 		}
1768 	    }
1769 	}
1770 	return (ret);
1771 }
1772 
1773 /*
1774  * sa_removeshare(flags, argc, argv)
1775  *
1776  * implements remove-share subcommand.
1777  */
1778 
1779 int
1780 sa_removeshare(int flags, int argc, char *argv[])
1781 {
1782 	int verbose = 0;
1783 	int dryrun = 0;
1784 	int force = 0;
1785 	int c;
1786 	int ret = SA_OK;
1787 	sa_group_t group;
1788 	sa_share_t share;
1789 	char *sharepath = NULL;
1790 	char dir[MAXPATHLEN];
1791 	int auth;
1792 
1793 	while ((c = getopt(argc, argv, "?hfns:v")) != EOF) {
1794 	    switch (c) {
1795 	    case 'n':
1796 		dryrun++;
1797 		break;
1798 	    case 'v':
1799 		verbose++;
1800 		break;
1801 	    case 'f':
1802 		force++;
1803 		break;
1804 	    case 's':
1805 		/*
1806 		 * remove share path from group. Currently limit
1807 		 * to one share per command.
1808 		 */
1809 		if (sharepath != NULL) {
1810 		    (void) printf(gettext("Removing multiple shares not"
1811 				    "supported\n"));
1812 		    return (SA_SYNTAX_ERR);
1813 		}
1814 		sharepath = optarg;
1815 		break;
1816 	    default:
1817 	    case 'h':
1818 	    case '?':
1819 		(void) printf(gettext("usage: %s\n"),
1820 				sa_get_usage(USAGE_REMOVE_SHARE));
1821 		return (0);
1822 	    }
1823 	}
1824 
1825 	if (optind >= argc || sharepath == NULL) {
1826 	    if (sharepath == NULL) {
1827 			(void) printf(gettext("usage: %s\n"),
1828 				sa_get_usage(USAGE_REMOVE_SHARE));
1829 		(void) printf(gettext("\t-s sharepath must be specified\n"));
1830 		ret = SA_BAD_PATH;
1831 	    } else {
1832 		ret = SA_OK;
1833 	    }
1834 	}
1835 	if (ret == SA_OK) {
1836 	    if (optind < argc) {
1837 		if ((optind + 1) < argc) {
1838 		    (void) printf(gettext("Extraneous group(s) at end of "
1839 						"command\n"));
1840 		    ret = SA_SYNTAX_ERR;
1841 		} else {
1842 		    group = sa_get_group(argv[optind]);
1843 		    if (group == NULL) {
1844 			(void) printf(gettext("Group \"%s\" not found\n"),
1845 					argv[optind]);
1846 			ret = SA_NO_SUCH_GROUP;
1847 		    }
1848 		}
1849 	    } else {
1850 		group = NULL;
1851 	    }
1852 	    if (ret == SA_OK) {
1853 		if (realpath(sharepath, dir) == NULL) {
1854 		    ret = SA_BAD_PATH;
1855 		    (void) printf(gettext("Path is not valid: %s\n"),
1856 					sharepath);
1857 		} else {
1858 		    sharepath = dir;
1859 		}
1860 	    }
1861 	    if (ret == SA_OK) {
1862 		if (group != NULL)
1863 		    share = sa_get_share(group, sharepath);
1864 		else
1865 		    share = sa_find_share(sharepath);
1866 		if (share == NULL) {
1867 		    if (group != NULL)
1868 			(void) printf(gettext("Share not found in group %s:"
1869 						"%s\n"),
1870 					argv[optind], sharepath);
1871 		    else
1872 			(void) printf(gettext("Share not found: %s\n"),
1873 					sharepath);
1874 		    ret = SA_NO_SUCH_PATH;
1875 		} else {
1876 		    if (group == NULL)
1877 			group = sa_get_parent_group(share);
1878 		    if (!dryrun) {
1879 			if (ret == SA_OK) {
1880 			    ret = sa_disable_share(share, NULL);
1881 				/*
1882 				 * we don't care if it fails since it
1883 				 * could be disabled already.
1884 				 */
1885 			    if (ret == SA_OK || ret == SA_NO_SUCH_PATH ||
1886 				ret == SA_NOT_SUPPORTED) {
1887 				ret = sa_remove_share(share);
1888 			    }
1889 			    if (ret == SA_OK)
1890 				ret = sa_update_config();
1891 			}
1892 			if (ret != SA_OK) {
1893 			    (void) printf(gettext("Could not remove share:"
1894 							" %s\n"),
1895 					sa_errorstr(ret));
1896 			}
1897 		    } else if (ret == SA_OK) {
1898 			char *pname;
1899 			pname = sa_get_group_attr(group, "name");
1900 			if (pname != NULL) {
1901 			    auth = check_authorizations(pname, flags);
1902 			    sa_free_attr_string(pname);
1903 			}
1904 			if (!auth && verbose) {
1905 			    (void) printf(gettext("Command would fail: %s\n"),
1906 					sa_errorstr(SA_NO_PERMISSION));
1907 			}
1908 		    }
1909 		}
1910 	    }
1911 	}
1912 	return (ret);
1913 }
1914 
1915 /*
1916  * sa_set_share(flags, argc, argv)
1917  *
1918  * implements set-share subcommand.
1919  */
1920 
1921 int
1922 sa_set_share(int flags, int argc, char *argv[])
1923 {
1924 	int dryrun = 0;
1925 	int c;
1926 	int ret = SA_OK;
1927 	sa_group_t group, sharegroup;
1928 	sa_share_t share;
1929 	char *sharepath = NULL;
1930 	char *description = NULL;
1931 	char *resource = NULL;
1932 	int auth;
1933 	int verbose = 0;
1934 
1935 	while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
1936 	    switch (c) {
1937 	    case 'n':
1938 		dryrun++;
1939 		break;
1940 	    case 'd':
1941 		description = optarg;
1942 		break;
1943 	    case 'r':
1944 		resource = optarg;
1945 		break;
1946 	    case 'v':
1947 		verbose++;
1948 		break;
1949 	    case 's':
1950 		/*
1951 		 * save share path into group. Currently limit
1952 		 * to one share per command.
1953 		 */
1954 		if (sharepath != NULL) {
1955 		    (void) printf(gettext("Updating multiple shares not"
1956 				    "supported\n"));
1957 		    return (SA_BAD_PATH);
1958 		}
1959 		sharepath = optarg;
1960 		break;
1961 	    default:
1962 	    case 'h':
1963 	    case '?':
1964 		(void) printf(gettext("usage: %s\n"),
1965 				sa_get_usage(USAGE_SET_SHARE));
1966 		return (SA_OK);
1967 	    }
1968 	}
1969 	if (optind >= argc || sharepath == NULL) {
1970 	    if (sharepath == NULL) {
1971 		(void) printf(gettext("usage: %s\n"),
1972 				sa_get_usage(USAGE_SET_SHARE));
1973 		(void) printf(gettext("\tgroup must be specified\n"));
1974 	    ret = SA_BAD_PATH;
1975 	    } else {
1976 		ret = SA_OK;
1977 	    }
1978 	}
1979 	if ((optind + 1) < argc) {
1980 	    (void) printf(gettext("usage: %s\n"),
1981 				sa_get_usage(USAGE_SET_SHARE));
1982 	    (void) printf(gettext("\tExtraneous group(s) at end\n"));
1983 	    ret = SA_SYNTAX_ERR;
1984 	}
1985 	if (ret == SA_OK) {
1986 	    char *groupname;
1987 	    if (optind < argc) {
1988 		groupname = argv[optind];
1989 		group = sa_get_group(groupname);
1990 	    } else {
1991 		group = NULL;
1992 		groupname = NULL;
1993 	    }
1994 	    share = sa_find_share(sharepath);
1995 	    if (share != NULL) {
1996 		sharegroup = sa_get_parent_group(share);
1997 		if (group != NULL && group != sharegroup) {
1998 		    (void) printf(gettext("Group \"%s\" does not contain "
1999 						"share %s\n"),
2000 			    argv[optind], sharepath);
2001 		    ret = SA_BAD_PATH;
2002 		} else {
2003 		    int delgroupname = 0;
2004 		    if (groupname == NULL) {
2005 			groupname = sa_get_group_attr(sharegroup, "name");
2006 			delgroupname = 1;
2007 		    }
2008 		    if (groupname != NULL) {
2009 			auth = check_authorizations(groupname, flags);
2010 			if (delgroupname) {
2011 			    sa_free_attr_string(groupname);
2012 			    groupname = NULL;
2013 			}
2014 		    } else {
2015 			ret = SA_NO_MEMORY;
2016 		    }
2017 		    if (resource != NULL) {
2018 			if (strpbrk(resource, " \t/") == NULL) {
2019 			    if (!dryrun) {
2020 				ret = sa_set_share_attr(share, "resource",
2021 						    resource);
2022 			    } else {
2023 				sa_share_t resshare;
2024 				resshare = sa_get_resource(sharegroup,
2025 							    resource);
2026 				if (resshare != NULL && resshare != share)
2027 				    ret = SA_DUPLICATE_NAME;
2028 			    }
2029 			} else {
2030 			    ret = SA_BAD_PATH;
2031 			    (void) printf(gettext("Resource must not contain "
2032 						"white space or '/'\n"));
2033 			}
2034 		    }
2035 		    if (ret == SA_OK && description != NULL) {
2036 			ret = sa_set_share_description(share, description);
2037 		    }
2038 		}
2039 		if (!dryrun && ret == SA_OK) {
2040 		    ret = sa_update_config();
2041 		}
2042 		switch (ret) {
2043 		case SA_DUPLICATE_NAME:
2044 		    (void) printf(gettext("Resource name in use: %s\n"),
2045 					resource);
2046 		    break;
2047 		default:
2048 		    (void) printf(gettext("Could not set attribute: %s\n"),
2049 			    sa_errorstr(ret));
2050 		    break;
2051 		case SA_OK:
2052 		    if (dryrun && !auth && verbose) {
2053 			(void) printf(gettext("Command would fail: %s\n"),
2054 				sa_errorstr(SA_NO_PERMISSION));
2055 		    }
2056 		    break;
2057 		}
2058 	    } else {
2059 		(void) printf(gettext("Share path \"%s\" not found\n"),
2060 				sharepath);
2061 		ret = SA_NO_SUCH_PATH;
2062 	    }
2063 	}
2064 	return (ret);
2065 }
2066 
2067 /*
2068  * add_security(group, sectype, optlist, proto, *err)
2069  *
2070  * Helper function to add a security option (named optionset) to the
2071  * group.
2072  */
2073 
2074 static int
2075 add_security(sa_group_t group, char *sectype,
2076 		struct options *optlist, char *proto, int *err)
2077 {
2078 	sa_security_t security;
2079 	int ret = SA_OK;
2080 	int result = 0;
2081 
2082 	sectype = sa_proto_space_alias(proto, sectype);
2083 	security = sa_get_security(group, sectype, proto);
2084 	if (security == NULL) {
2085 	    security = sa_create_security(group, sectype, proto);
2086 	}
2087 	if (sectype != NULL)
2088 	    sa_free_attr_string(sectype);
2089 	if (security != NULL) {
2090 	    while (optlist != NULL) {
2091 		sa_property_t prop;
2092 		prop = sa_get_property(security, optlist->optname);
2093 		if (prop == NULL) {
2094 			/*
2095 			 * add the property, but only if it is
2096 			 * a non-NULL or non-zero length value
2097 			 */
2098 		    if (optlist->optvalue != NULL) {
2099 			prop = sa_create_property(optlist->optname,
2100 							optlist->optvalue);
2101 			if (prop != NULL) {
2102 			    ret = sa_valid_property(security, proto, prop);
2103 			    if (ret != SA_OK) {
2104 				(void) sa_remove_property(prop);
2105 				(void) printf(gettext("Could not add "
2106 							"property %s: %s\n"),
2107 							optlist->optname,
2108 						sa_errorstr(ret));
2109 			    }
2110 			    if (ret == SA_OK) {
2111 				ret = sa_add_property(security, prop);
2112 				if (ret != SA_OK) {
2113 				    (void) printf(gettext("Could not add "
2114 						    "property (%s=%s): %s\n"),
2115 						optlist->optname,
2116 						optlist->optvalue,
2117 						sa_errorstr(ret));
2118 				} else {
2119 				    result = 1;
2120 				}
2121 			    }
2122 			}
2123 		    }
2124 		} else {
2125 		    ret = sa_update_property(prop, optlist->optvalue);
2126 		    result = 1; /* should check if really changed */
2127 		}
2128 		optlist = optlist->next;
2129 	    }
2130 		/*
2131 		 * when done, properties may have all been removed but
2132 		 * we need to keep the security type itself until
2133 		 * explicitly removed.
2134 		 */
2135 	    if (result)
2136 		ret = sa_commit_properties(security, 0);
2137 	}
2138 	*err = ret;
2139 	return (result);
2140 }
2141 
2142 /*
2143  * basic_set(groupname, optlist, protocol, sharepath, dryrun)
2144  *
2145  * This function implements "set" when a name space (-S) is not
2146  * specified. It is a basic set. Options and other CLI parsing has
2147  * already been done.
2148  */
2149 
2150 static int
2151 basic_set(char *groupname, struct options *optlist, char *protocol,
2152 		char *sharepath, int dryrun)
2153 {
2154 	sa_group_t group;
2155 	int ret = SA_OK;
2156 	int change = 0;
2157 	struct list *worklist = NULL;
2158 
2159 	group = sa_get_group(groupname);
2160 	if (group != NULL) {
2161 	    sa_share_t share = NULL;
2162 	    if (sharepath != NULL) {
2163 		share = sa_get_share(group, sharepath);
2164 		if (share == NULL) {
2165 		    (void) printf(gettext("Share does not exist in group %s\n"),
2166 				groupname, sharepath);
2167 		    ret = SA_NO_SUCH_PATH;
2168 		}
2169 	    }
2170 	    if (ret == SA_OK) {
2171 		/* group must exist */
2172 		ret = valid_options(optlist, protocol,
2173 				    share == NULL ? group : share, NULL);
2174 		if (ret == SA_OK && !dryrun) {
2175 		    if (share != NULL)
2176 			change |= add_optionset(share, optlist, protocol,
2177 						&ret);
2178 		    else
2179 			change |= add_optionset(group, optlist, protocol,
2180 						&ret);
2181 		    if (ret == SA_OK && change) {
2182 			worklist = add_list(worklist, group, share);
2183 		    }
2184 		}
2185 	    }
2186 	    free_opt(optlist);
2187 	} else {
2188 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2189 		ret = SA_NO_SUCH_GROUP;
2190 	}
2191 	/*
2192 	 * we have a group and potentially legal additions
2193 	 */
2194 
2195 	/* commit to configuration if not a dryrun */
2196 	if (!dryrun && ret == SA_OK) {
2197 	    if (change && worklist != NULL) {
2198 		/* properties changed, so update all shares */
2199 		(void) enable_all_groups(worklist, 0, 0, protocol);
2200 	    }
2201 	}
2202 	if (worklist != NULL)
2203 	    free_list(worklist);
2204 	return (ret);
2205 }
2206 
2207 /*
2208  * space_set(groupname, optlist, protocol, sharepath, dryrun)
2209  *
2210  * This function implements "set" when a name space (-S) is
2211  * specified. It is a namespace set. Options and other CLI parsing has
2212  * already been done.
2213  */
2214 
2215 static int
2216 space_set(char *groupname, struct options *optlist, char *protocol,
2217 		char *sharepath, int dryrun, char *sectype)
2218 {
2219 	sa_group_t group;
2220 	int ret = SA_OK;
2221 	int change = 0;
2222 	struct list *worklist = NULL;
2223 
2224 	/*
2225 	 * make sure protcol and sectype are valid
2226 	 */
2227 
2228 	if (sa_proto_valid_space(protocol, sectype) == 0) {
2229 	    (void) printf(gettext("Option space \"%s\" not valid "
2230 					"for protocol.\n"),
2231 				sectype);
2232 	    return (SA_INVALID_SECURITY);
2233 	}
2234 
2235 	group = sa_get_group(groupname);
2236 	if (group != NULL) {
2237 	    sa_share_t share = NULL;
2238 	    if (sharepath != NULL) {
2239 		share = sa_get_share(group, sharepath);
2240 		if (share == NULL) {
2241 		    (void) printf(gettext("Share does not exist in group %s\n"),
2242 				groupname, sharepath);
2243 		    ret = SA_NO_SUCH_PATH;
2244 		}
2245 	    }
2246 	    if (ret == SA_OK) {
2247 		/* group must exist */
2248 		ret = valid_options(optlist, protocol,
2249 				    share == NULL ? group : share, sectype);
2250 		if (ret == SA_OK && !dryrun) {
2251 		    if (share != NULL)
2252 			change = add_security(share, sectype, optlist,
2253 						protocol,
2254 						&ret);
2255 		    else
2256 			change = add_security(group, sectype, optlist,
2257 						protocol,
2258 						&ret);
2259 		    if (ret != SA_OK)
2260 			(void) printf(gettext("Could not set property: %s\n"),
2261 				sa_errorstr(ret));
2262 		}
2263 		if (ret == SA_OK && change)
2264 		    worklist = add_list(worklist, group, share);
2265 	    }
2266 	    free_opt(optlist);
2267 	} else {
2268 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2269 		ret = SA_NO_SUCH_GROUP;
2270 	}
2271 	/*
2272 	 * we have a group and potentially legal additions
2273 	 */
2274 
2275 	/* commit to configuration if not a dryrun */
2276 	if (!dryrun && ret == 0) {
2277 	    if (change && worklist != NULL) {
2278 		/* properties changed, so update all shares */
2279 		(void) enable_all_groups(worklist, 0, 0, protocol);
2280 	    }
2281 	    ret = sa_update_config();
2282 	}
2283 	if (worklist != NULL)
2284 	    free_list(worklist);
2285 	return (ret);
2286 }
2287 
2288 /*
2289  * sa_set(flags, argc, argv)
2290  *
2291  * Implements the set subcommand. It keys off of -S to determine which
2292  * set of operations to actually do.
2293  */
2294 
2295 int
2296 sa_set(int flags, int argc, char *argv[])
2297 {
2298 	char *groupname;
2299 	int verbose = 0;
2300 	int dryrun = 0;
2301 	int c;
2302 	char *protocol = NULL;
2303 	int ret = SA_OK;
2304 	struct options *optlist = NULL;
2305 	char *sharepath = NULL;
2306 	char *optset = NULL;
2307 	int auth;
2308 
2309 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
2310 	    switch (c) {
2311 	    case 'v':
2312 		verbose++;
2313 		break;
2314 	    case 'n':
2315 		dryrun++;
2316 		break;
2317 	    case 'P':
2318 		protocol = optarg;
2319 		if (!sa_valid_protocol(protocol)) {
2320 		    (void) printf(gettext("Invalid protocol specified:"
2321 				    "%s\n"),
2322 					protocol);
2323 		    return (SA_INVALID_PROTOCOL);
2324 		}
2325 		break;
2326 	    case 'p':
2327 		ret = add_opt(&optlist, optarg, 0);
2328 		switch (ret) {
2329 		case OPT_ADD_SYNTAX:
2330 		    (void) printf(gettext("Property syntax error: %s\n"),
2331 					optarg);
2332 		    return (SA_SYNTAX_ERR);
2333 		case OPT_ADD_MEMORY:
2334 		    (void) printf(gettext("No memory to set property: %s\n"),
2335 					optarg);
2336 		    return (SA_NO_MEMORY);
2337 		default:
2338 		    break;
2339 		}
2340 		break;
2341 	    case 's':
2342 		sharepath = optarg;
2343 		break;
2344 	    case 'S':
2345 		optset = optarg;
2346 		break;
2347 	    default:
2348 	    case 'h':
2349 	    case '?':
2350 		(void) printf(gettext("usage: %s\n"),
2351 				sa_get_usage(USAGE_SET));
2352 		return (SA_OK);
2353 	    }
2354 	}
2355 
2356 	if (optlist != NULL)
2357 	    ret = chk_opt(optlist, optset != NULL, protocol);
2358 
2359 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
2360 	    protocol == NULL ||
2361 	    ret != OPT_ADD_OK) {
2362 	    char *sep = "\t";
2363 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
2364 	    if (optind >= argc) {
2365 		(void) printf(gettext("%sgroup must be specified"), sep);
2366 		sep = ", ";
2367 	    }
2368 	    if (optlist == NULL) {
2369 		(void) printf(gettext("%sat least one property must be"
2370 				" specified"), sep);
2371 		sep = ", ";
2372 	    }
2373 	    if (protocol == NULL) {
2374 		(void) printf(gettext("%sprotocol must be specified"), sep);
2375 		sep = ", ";
2376 	    }
2377 	    (void) printf("\n");
2378 	    ret = SA_SYNTAX_ERR;
2379 	} else {
2380 		/*
2381 		 * if a group already exists, we can only add a new
2382 		 * protocol to it and not create a new one or add the
2383 		 * same protocol again.
2384 		 */
2385 
2386 	    groupname = argv[optind];
2387 	    auth = check_authorizations(groupname, flags);
2388 	    if (optset == NULL)
2389 		ret = basic_set(groupname, optlist, protocol,
2390 				sharepath, dryrun);
2391 	    else
2392 		ret = space_set(groupname, optlist, protocol,
2393 				sharepath, dryrun, optset);
2394 	    if (dryrun && ret == SA_OK && !auth && verbose) {
2395 		(void) printf(gettext("Command would fail: %s\n"),
2396 			sa_errorstr(SA_NO_PERMISSION));
2397 	    }
2398 	}
2399 	return (ret);
2400 }
2401 
2402 /*
2403  * remove_options(group, optlist, proto, *err)
2404  *
2405  * helper function to actually remove options from a group after all
2406  * preprocessing is done.
2407  */
2408 
2409 static int
2410 remove_options(sa_group_t group, struct options *optlist,
2411 		char *proto, int *err)
2412 {
2413 	struct options *cur;
2414 	sa_optionset_t optionset;
2415 	sa_property_t prop;
2416 	int change = 0;
2417 	int ret = SA_OK;
2418 
2419 	optionset = sa_get_optionset(group, proto);
2420 	if (optionset != NULL) {
2421 	    for (cur = optlist; cur != NULL; cur = cur->next) {
2422 		prop = sa_get_property(optionset, cur->optname);
2423 		if (prop != NULL) {
2424 		    ret = sa_remove_property(prop);
2425 		    if (ret != SA_OK)
2426 			break;
2427 		    change = 1;
2428 		}
2429 	    }
2430 	}
2431 	if (ret == SA_OK && change)
2432 	    ret = sa_commit_properties(optionset, 0);
2433 
2434 	if (err != NULL)
2435 	    *err = ret;
2436 	return (change);
2437 }
2438 
2439 /*
2440  * valid_unset(group, optlist, proto)
2441  *
2442  * Sanity check the optlist to make sure they can be removed. Issue an
2443  * error if a property doesn't exist.
2444  */
2445 
2446 static int
2447 valid_unset(sa_group_t group, struct options *optlist, char *proto)
2448 {
2449 	struct options *cur;
2450 	sa_optionset_t optionset;
2451 	sa_property_t prop;
2452 	int ret = SA_OK;
2453 
2454 	optionset = sa_get_optionset(group, proto);
2455 	if (optionset != NULL) {
2456 	    for (cur = optlist; cur != NULL; cur = cur->next) {
2457 		prop = sa_get_property(optionset, cur->optname);
2458 		if (prop == NULL) {
2459 		    (void) printf(gettext("Could not unset property %s:"
2460 						" not set\n"),
2461 			    cur->optname);
2462 		    ret = SA_NO_SUCH_PROP;
2463 		}
2464 	    }
2465 	}
2466 	return (ret);
2467 }
2468 
2469 /*
2470  * valid_unset_security(group, optlist, proto)
2471  *
2472  * Sanity check the optlist to make sure they can be removed. Issue an
2473  * error if a property doesn't exist.
2474  */
2475 
2476 static int
2477 valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
2478 	    char *sectype)
2479 {
2480 	struct options *cur;
2481 	sa_security_t security;
2482 	sa_property_t prop;
2483 	int ret = SA_OK;
2484 	char *sec;
2485 
2486 	sec = sa_proto_space_alias(proto, sectype);
2487 	security = sa_get_security(group, sec, proto);
2488 	if (security != NULL) {
2489 	    for (cur = optlist; cur != NULL; cur = cur->next) {
2490 		prop = sa_get_property(security, cur->optname);
2491 		if (prop == NULL) {
2492 		    (void) printf(gettext("Could not unset property %s:"
2493 						" not set\n"),
2494 					cur->optname);
2495 		    ret = SA_NO_SUCH_PROP;
2496 		}
2497 	    }
2498 	} else {
2499 	    (void) printf(gettext("Could not unset %s: space not defined\n"),
2500 			    sectype);
2501 	    ret = SA_NO_SUCH_SECURITY;
2502 	}
2503 	if (sec != NULL)
2504 	    sa_free_attr_string(sec);
2505 	return (ret);
2506 }
2507 
2508 /*
2509  * remove_security(group, optlist, proto)
2510  *
2511  * Remove the properties since they were checked as valid.
2512  */
2513 
2514 static int
2515 remove_security(sa_group_t group, char *sectype,
2516 		struct options *optlist, char *proto, int *err)
2517 {
2518 	sa_security_t security;
2519 	int ret = SA_OK;
2520 	int change = 0;
2521 
2522 	sectype = sa_proto_space_alias(proto, sectype);
2523 	security = sa_get_security(group, sectype, proto);
2524 	if (sectype != NULL)
2525 	    sa_free_attr_string(sectype);
2526 
2527 	if (security != NULL) {
2528 	    while (optlist != NULL) {
2529 		sa_property_t prop;
2530 		prop = sa_get_property(security, optlist->optname);
2531 		if (prop != NULL) {
2532 		    ret = sa_remove_property(prop);
2533 		    if (ret != SA_OK)
2534 			break;
2535 		    change = 1;
2536 		}
2537 		optlist = optlist->next;
2538 	    }
2539 		/*
2540 		 * when done, properties may have all been removed but
2541 		 * we need to keep the security type itself until
2542 		 * explicitly removed.
2543 		 */
2544 	    if (ret == SA_OK && change)
2545 		ret = sa_commit_properties(security, 0);
2546 	} else {
2547 	    ret = SA_NO_SUCH_PROP;
2548 	}
2549 	if (err != NULL)
2550 	    *err = ret;
2551 	return (change);
2552 }
2553 
2554 /*
2555  * basic_unset(groupname, optlist, protocol, sharepath, dryrun)
2556  *
2557  * unset non-named optionset properties.
2558  */
2559 
2560 static int
2561 basic_unset(char *groupname, struct options *optlist, char *protocol,
2562 		char *sharepath, int dryrun)
2563 {
2564 	sa_group_t group;
2565 	int ret = SA_OK;
2566 	int change = 0;
2567 	struct list *worklist = NULL;
2568 
2569 	group = sa_get_group(groupname);
2570 	if (group != NULL) {
2571 	    sa_share_t share = NULL;
2572 	    if (sharepath != NULL) {
2573 		share = sa_get_share(group, sharepath);
2574 		if (share == NULL) {
2575 		    (void) printf(gettext("Share does not exist in group %s\n"),
2576 				groupname, sharepath);
2577 		    ret = SA_NO_SUCH_PATH;
2578 		}
2579 	    }
2580 	    if (ret == SA_OK) {
2581 		/* group must exist */
2582 		ret = valid_unset(share != NULL ? share : group,
2583 					optlist, protocol);
2584 		if (ret == SA_OK && !dryrun) {
2585 		    if (share != NULL) {
2586 			sa_optionset_t optionset;
2587 			sa_property_t prop;
2588 			change |= remove_options(share, optlist, protocol,
2589 							&ret);
2590 			/* if a share optionset is empty, remove it */
2591 			optionset = sa_get_optionset((sa_share_t)share,
2592 							protocol);
2593 			if (optionset != NULL) {
2594 			    prop = sa_get_property(optionset, NULL);
2595 			    if (prop == NULL)
2596 				(void) sa_destroy_optionset(optionset);
2597 			}
2598 		    } else {
2599 			change |= remove_options(group, optlist, protocol,
2600 							&ret);
2601 		    }
2602 		    if (ret == SA_OK && change)
2603 			worklist = add_list(worklist, group, share);
2604 		    if (ret != SA_OK)
2605 			(void) printf(gettext("Could not remove properties:"
2606 						"%s\n"),
2607 				sa_errorstr(ret));
2608 		}
2609 	    } else {
2610 		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
2611 		ret = SA_NO_SUCH_GROUP;
2612 	    }
2613 	    free_opt(optlist);
2614 	}
2615 
2616 	/*
2617 	 * we have a group and potentially legal additions
2618 	 */
2619 	/* commit to configuration if not a dryrun */
2620 	if (!dryrun && ret == SA_OK) {
2621 	    if (change && worklist != NULL) {
2622 		/* properties changed, so update all shares */
2623 		(void) enable_all_groups(worklist, 0, 0, protocol);
2624 	    }
2625 	}
2626 	if (worklist != NULL)
2627 	    free_list(worklist);
2628 	return (ret);
2629 }
2630 
2631 /*
2632  * space_unset(groupname, optlist, protocol, sharepath, dryrun)
2633  *
2634  * unset named optionset properties.
2635  */
2636 static int
2637 space_unset(char *groupname, struct options *optlist, char *protocol,
2638 		char *sharepath, int dryrun, char *sectype)
2639 {
2640 	sa_group_t group;
2641 	int ret = SA_OK;
2642 	int change = 0;
2643 	struct list *worklist = NULL;
2644 
2645 	group = sa_get_group(groupname);
2646 	if (group != NULL) {
2647 	    sa_share_t share = NULL;
2648 	    if (sharepath != NULL) {
2649 		share = sa_get_share(group, sharepath);
2650 		if (share == NULL) {
2651 		    (void) printf(gettext("Share does not exist in group %s\n"),
2652 				groupname, sharepath);
2653 		    ret = SA_NO_SUCH_PATH;
2654 		}
2655 	    }
2656 	    if (ret == SA_OK) {
2657 		ret = valid_unset_security(share != NULL ? share : group,
2658 						optlist, protocol, sectype);
2659 		if (ret == SA_OK && !dryrun) {
2660 		    if (optlist != NULL) {
2661 			if (share != NULL) {
2662 			    sa_security_t optionset;
2663 			    sa_property_t prop;
2664 			    change = remove_security(share, sectype,
2665 							optlist, protocol,
2666 							&ret);
2667 			    /* if a share security is empty, remove it */
2668 			    optionset = sa_get_security((sa_group_t)share,
2669 							sectype,
2670 							protocol);
2671 			    if (optionset != NULL) {
2672 				prop = sa_get_property(optionset, NULL);
2673 				if (prop == NULL)
2674 				    ret = sa_destroy_security(optionset);
2675 			    }
2676 			} else {
2677 			    change = remove_security(group, sectype,
2678 							optlist, protocol,
2679 							&ret);
2680 			}
2681 		    } else {
2682 			sa_security_t security;
2683 			char *sec;
2684 			sec = sa_proto_space_alias(protocol, sectype);
2685 			security = sa_get_security(group, sec, protocol);
2686 			if (sec != NULL)
2687 			    sa_free_attr_string(sec);
2688 			if (security != NULL) {
2689 			    ret = sa_destroy_security(security);
2690 			    if (ret == SA_OK)
2691 				change = 1;
2692 			} else {
2693 			    ret = SA_NO_SUCH_PROP;
2694 			}
2695 		    }
2696 		    if (ret != SA_OK)
2697 			(void) printf(gettext("Could not unset property: %s\n"),
2698 				sa_errorstr(ret));
2699 		}
2700 
2701 		if (ret == SA_OK && change)
2702 		    worklist = add_list(worklist, group, 0);
2703 	    }
2704 	} else {
2705 	    (void) printf(gettext("Group \"%s\" not found\n"), groupname);
2706 	    ret = SA_NO_SUCH_GROUP;
2707 	}
2708 	free_opt(optlist);
2709 	/*
2710 	 * we have a group and potentially legal additions
2711 	 */
2712 
2713 	/* commit to configuration if not a dryrun */
2714 	if (!dryrun && ret == 0) {
2715 	    if (change && worklist != NULL) {
2716 		/* properties changed, so update all shares */
2717 		(void) enable_all_groups(worklist, 0, 0, protocol);
2718 	    }
2719 	    ret = sa_update_config();
2720 	}
2721 	if (worklist != NULL)
2722 	    free_list(worklist);
2723 	return (ret);
2724 }
2725 
2726 /*
2727  * sa_unset(flags, argc, argv)
2728  *
2729  * implements the unset subcommand. Parsing done here and then basic
2730  * or space versions of the real code are called.
2731  */
2732 
2733 int
2734 sa_unset(int flags, int argc, char *argv[])
2735 {
2736 	char *groupname;
2737 	int verbose = 0;
2738 	int dryrun = 0;
2739 	int c;
2740 	char *protocol = NULL;
2741 	int ret = SA_OK;
2742 	struct options *optlist = NULL;
2743 	char *sharepath = NULL;
2744 	char *optset = NULL;
2745 	int auth;
2746 
2747 	while ((c = getopt(argc, argv, "?hvnP:p:s:S:")) != EOF) {
2748 	    switch (c) {
2749 	    case 'v':
2750 		verbose++;
2751 		break;
2752 	    case 'n':
2753 		dryrun++;
2754 		break;
2755 	    case 'P':
2756 		protocol = optarg;
2757 		if (!sa_valid_protocol(protocol)) {
2758 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
2759 					protocol);
2760 		    return (SA_INVALID_PROTOCOL);
2761 		}
2762 		break;
2763 	    case 'p':
2764 		ret = add_opt(&optlist, optarg, 1);
2765 		switch (ret) {
2766 		case OPT_ADD_SYNTAX:
2767 		    (void) printf(gettext("Property syntax error for "
2768 						"property %s\n"),
2769 					optarg);
2770 		    return (SA_SYNTAX_ERR);
2771 		case OPT_ADD_PROPERTY:
2772 		    (void) printf(gettext("Properties need to be set"
2773 						" with set command: %s\n"),
2774 					optarg);
2775 		    return (SA_SYNTAX_ERR);
2776 		default:
2777 		    break;
2778 		}
2779 		break;
2780 	    case 's':
2781 		sharepath = optarg;
2782 		break;
2783 	    case 'S':
2784 		optset = optarg;
2785 		break;
2786 	    default:
2787 	    case 'h':
2788 	    case '?':
2789 		(void) printf(gettext("usage: %s\n"),
2790 				sa_get_usage(USAGE_UNSET));
2791 		return (SA_OK);
2792 	    }
2793 	}
2794 
2795 	if (optlist != NULL)
2796 	    ret = chk_opt(optlist, optset != NULL, protocol);
2797 
2798 	if (optind >= argc || (optlist == NULL && optset == NULL) ||
2799 	    protocol == NULL) {
2800 	    char *sep = "\t";
2801 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_UNSET));
2802 	    if (optind >= argc) {
2803 		(void) printf(gettext("%sgroup must be specified"), sep);
2804 		sep = ", ";
2805 	    }
2806 	    if (optlist == NULL) {
2807 		(void) printf(gettext("%sat least one property must be "
2808 					"specified"),
2809 			sep);
2810 		sep = ", ";
2811 	    }
2812 	    if (protocol == NULL) {
2813 		(void) printf(gettext("%sprotocol must be specified"), sep);
2814 		sep = ", ";
2815 	    }
2816 	    (void) printf("\n");
2817 	    ret = SA_SYNTAX_ERR;
2818 	} else {
2819 
2820 		/*
2821 		 * if a group already exists, we can only add a new
2822 		 * protocol to it and not create a new one or add the
2823 		 * same protocol again.
2824 		 */
2825 
2826 	    groupname = argv[optind];
2827 	    auth = check_authorizations(groupname, flags);
2828 	    if (optset == NULL)
2829 		ret = basic_unset(groupname, optlist, protocol,
2830 					sharepath, dryrun);
2831 	    else
2832 		ret = space_unset(groupname, optlist, protocol,
2833 					sharepath, dryrun, optset);
2834 
2835 	    if (dryrun && ret == SA_OK && !auth && verbose) {
2836 		(void) printf(gettext("Command would fail: %s\n"),
2837 			sa_errorstr(SA_NO_PERMISSION));
2838 	    }
2839 	}
2840 	return (ret);
2841 }
2842 
2843 /*
2844  * sa_enable_group(flags, argc, argv)
2845  *
2846  * Implements the enable subcommand
2847  */
2848 
2849 int
2850 sa_enable_group(int flags, int argc, char *argv[])
2851 {
2852 	int verbose = 0;
2853 	int dryrun = 0;
2854 	int all = 0;
2855 	int c;
2856 	int ret = SA_OK;
2857 	char *protocol = NULL;
2858 	char *state;
2859 	struct list *worklist = NULL;
2860 	int auth = 1;
2861 
2862 	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
2863 	    switch (c) {
2864 	    case 'a':
2865 		all = 1;
2866 		break;
2867 	    case 'n':
2868 		dryrun++;
2869 		break;
2870 	    case 'P':
2871 		protocol = optarg;
2872 		if (!sa_valid_protocol(protocol)) {
2873 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
2874 				    protocol);
2875 		    return (SA_INVALID_PROTOCOL);
2876 		}
2877 		break;
2878 	    case 'v':
2879 		verbose++;
2880 		break;
2881 	    default:
2882 	    case 'h':
2883 	    case '?':
2884 		(void) printf(gettext("usage: %s\n"),
2885 				sa_get_usage(USAGE_ENABLE));
2886 		return (0);
2887 	    }
2888 	}
2889 
2890 	if (optind == argc && !all) {
2891 	    (void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_ENABLE));
2892 	    (void) printf(gettext("\tmust specify group\n"));
2893 	    ret = SA_NO_SUCH_PATH;
2894 	} else {
2895 	    sa_group_t group;
2896 	    if (!all) {
2897 		while (optind < argc) {
2898 		    group = sa_get_group(argv[optind]);
2899 		    if (group != NULL) {
2900 			auth &= check_authorizations(argv[optind], flags);
2901 			state = sa_get_group_attr(group, "state");
2902 			if (state != NULL &&
2903 			    strcmp(state, "enabled") == 0) {
2904 			    /* already enabled */
2905 			    if (verbose)
2906 				(void) printf(gettext("Group \"%s\" is already "
2907 						"enabled\n"),
2908 					argv[optind]);
2909 			    ret = SA_BUSY; /* already enabled */
2910 			} else {
2911 			    worklist = add_list(worklist, group, 0);
2912 			    if (verbose)
2913 				(void) printf(gettext("Enabling group "
2914 							"\"%s\"\n"),
2915 					argv[optind]);
2916 			}
2917 			if (state != NULL)
2918 			    sa_free_attr_string(state);
2919 		    } else {
2920 			ret = SA_NO_SUCH_GROUP;
2921 		    }
2922 		    optind++;
2923 		}
2924 	    } else {
2925 		for (group = sa_get_group(NULL); group != NULL;
2926 		    group = sa_get_next_group(group)) {
2927 		    worklist = add_list(worklist, group, 0);
2928 		}
2929 	    }
2930 	    if (!dryrun && ret == SA_OK) {
2931 		ret = enable_all_groups(worklist, 1, 0, NULL);
2932 	    }
2933 	    if (ret != SA_OK && ret != SA_BUSY)
2934 		(void) printf(gettext("Could not enable group: %s\n"),
2935 			sa_errorstr(ret));
2936 	    if (ret == SA_BUSY)
2937 		ret = SA_OK;
2938 	}
2939 	if (worklist != NULL)
2940 	    free_list(worklist);
2941 	if (dryrun && ret == SA_OK && !auth && verbose) {
2942 	    (void) printf(gettext("Command would fail: %s\n"),
2943 			sa_errorstr(SA_NO_PERMISSION));
2944 	}
2945 	return (ret);
2946 }
2947 
2948 /*
2949  * disable_group(group, setstate)
2950  *
2951  * disable all the shares in the specified group honoring the setstate
2952  * argument. This is a helper for disable_all_groups in order to
2953  * simplify regular and subgroup (zfs) disabling. Group has already
2954  * been checked for non-NULL.
2955  */
2956 
2957 static int
2958 disable_group(sa_group_t group)
2959 {
2960 	sa_share_t share;
2961 	int ret = SA_OK;
2962 
2963 	for (share = sa_get_share(group, NULL);
2964 	    share != NULL && ret == SA_OK;
2965 	    share = sa_get_next_share(share)) {
2966 	    ret = sa_disable_share(share, NULL);
2967 	    if (ret == SA_NO_SUCH_PATH) {
2968 		/*
2969 		 * this is OK since the path is gone. we can't
2970 		 * re-share it anyway so no error.
2971 		 */
2972 		ret = SA_OK;
2973 	    }
2974 	}
2975 	return (ret);
2976 }
2977 
2978 
2979 /*
2980  * disable_all_groups(work, setstate)
2981  *
2982  * helper function that disables the shares in the list of groups
2983  * provided. It optionally marks the group as disabled. Used by both
2984  * enable and start subcommands.
2985  */
2986 
2987 static int
2988 disable_all_groups(struct list *work, int setstate)
2989 {
2990 	int ret = SA_OK;
2991 	sa_group_t subgroup, group;
2992 
2993 	while (work != NULL && ret == SA_OK) {
2994 	    group = (sa_group_t)work->item;
2995 	    if (setstate)
2996 		ret = sa_set_group_attr(group, "state", "disabled");
2997 	    if (ret == SA_OK) {
2998 		char *name;
2999 		name = sa_get_group_attr(group, "name");
3000 		if (name != NULL && strcmp(name, "zfs") == 0) {
3001 		    /* need to get the sub-groups for stopping */
3002 		    for (subgroup = sa_get_sub_group(group); subgroup != NULL;
3003 			subgroup = sa_get_next_group(subgroup)) {
3004 			ret = disable_group(subgroup);
3005 		    }
3006 		} else {
3007 		    ret = disable_group(group);
3008 		}
3009 		/*
3010 		 * we don't want to "disable" since it won't come
3011 		 * up after a reboot.  The SMF framework should do
3012 		 * the right thing. On enable we do want to do
3013 		 * something.
3014 		 */
3015 	    }
3016 	    work = work->next;
3017 	}
3018 	if (ret == SA_OK)
3019 	    ret = sa_update_config();
3020 	return (ret);
3021 }
3022 
3023 /*
3024  * sa_disable_group(flags, argc, argv)
3025  *
3026  * Implements the disable subcommand
3027  */
3028 
3029 int
3030 sa_disable_group(int flags, int argc, char *argv[])
3031 {
3032 	int verbose = 0;
3033 	int dryrun = 0;
3034 	int all = 0;
3035 	int c;
3036 	int ret = SA_OK;
3037 	char *protocol;
3038 	char *state;
3039 	struct list *worklist = NULL;
3040 	int auth = 1;
3041 
3042 	while ((c = getopt(argc, argv, "?havn")) != EOF) {
3043 	    switch (c) {
3044 	    case 'a':
3045 		all = 1;
3046 		break;
3047 	    case 'n':
3048 		dryrun++;
3049 		break;
3050 	    case 'P':
3051 		protocol = optarg;
3052 		if (!sa_valid_protocol(protocol)) {
3053 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
3054 					protocol);
3055 		    return (SA_INVALID_PROTOCOL);
3056 		}
3057 		break;
3058 	    case 'v':
3059 		verbose++;
3060 		break;
3061 	    default:
3062 	    case 'h':
3063 	    case '?':
3064 		(void) printf(gettext("usage: %s\n"),
3065 				sa_get_usage(USAGE_DISABLE));
3066 		return (0);
3067 	    }
3068 	}
3069 
3070 	if (optind == argc && !all) {
3071 		(void) printf(gettext("usage: %s\n"),
3072 				sa_get_usage(USAGE_DISABLE));
3073 		(void) printf(gettext("\tmust specify group\n"));
3074 		ret = SA_NO_SUCH_PATH;
3075 	} else {
3076 		sa_group_t group;
3077 		if (!all) {
3078 		    while (optind < argc) {
3079 			group = sa_get_group(argv[optind]);
3080 			if (group != NULL) {
3081 			    auth &= check_authorizations(argv[optind], flags);
3082 			    state = sa_get_group_attr(group, "state");
3083 			    if (state == NULL ||
3084 				strcmp(state, "disabled") == 0) {
3085 				/* already disabled */
3086 				if (verbose)
3087 				    (void) printf(gettext("Group \"%s\" is "
3088 							"already disabled\n"),
3089 					    argv[optind]);
3090 				ret = SA_BUSY; /* already disable */
3091 			    } else {
3092 				worklist = add_list(worklist, group, 0);
3093 				if (verbose)
3094 				    (void) printf(gettext("Disabling group "
3095 							    "\"%s\"\n"),
3096 					    argv[optind]);
3097 			    }
3098 			    if (state != NULL)
3099 				sa_free_attr_string(state);
3100 			} else {
3101 			    ret = SA_NO_SUCH_GROUP;
3102 			}
3103 			optind++;
3104 		    }
3105 		} else {
3106 		    for (group = sa_get_group(NULL); group != NULL;
3107 			    group = sa_get_next_group(group)) {
3108 			worklist = add_list(worklist, group, 0);
3109 		    }
3110 		}
3111 		if (ret == SA_OK && !dryrun) {
3112 			ret = disable_all_groups(worklist, 1);
3113 		}
3114 		if (ret != SA_OK && ret != SA_BUSY)
3115 		    (void) printf(gettext("Could not disable group: %s\n"),
3116 				sa_errorstr(ret));
3117 		if (ret == SA_BUSY)
3118 		    ret = SA_OK;
3119 	}
3120 	if (worklist != NULL)
3121 	    free_list(worklist);
3122 	if (dryrun && ret == SA_OK && !auth && verbose) {
3123 	    (void) printf(gettext("Command would fail: %s\n"),
3124 			sa_errorstr(SA_NO_PERMISSION));
3125 	}
3126 	return (ret);
3127 }
3128 
3129 /*
3130  * check_sharetab()
3131  *
3132  * Checks to see if the /etc/dfs/sharetab file is stale (exists from
3133  * before the current boot). If it is, truncate it since nothing is
3134  * really shared.
3135  */
3136 
3137 static void
3138 check_sharetab()
3139 {
3140 	int fd;
3141 	struct utmpx *utmpxp;
3142 	struct stat st;
3143 
3144 	fd = open(SA_LEGACY_SHARETAB, O_RDWR);
3145 	if (fd >= 0) {
3146 		/*
3147 		 * Attempt to get a lock on the file. Whgen we get
3148 		 * one, then check to see if it is older than the boot
3149 		 * time. Truncate if older than boot.
3150 		 */
3151 	    (void) lockf(fd, F_LOCK, 0);
3152 	    if ((fstat(fd, &st) == 0) && /* does sharetab exist? */
3153 		(utmpxp = getutxent()) != NULL && /* does utmpx exist? */
3154 			(utmpxp->ut_xtime > st.st_mtime)) /* sharetab older? */
3155 		(void) ftruncate(fd, 0);
3156 
3157 	    (void) lockf(fd, F_ULOCK, 0);
3158 	    (void) close(fd);
3159 	    endutxent();
3160 	}
3161 }
3162 
3163 /*
3164  * sa_start_group(flags, argc, argv)
3165  *
3166  * Implements the start command.
3167  * This is similar to enable except it doesn't change the state
3168  * of the group(s) and only enables shares if the group is already
3169  * enabled.
3170  */
3171 
3172 int
3173 sa_start_group(int flags, int argc, char *argv[])
3174 {
3175 	int verbose = 0;
3176 	int all = 0;
3177 	int c;
3178 	int ret = SMF_EXIT_OK;
3179 	char *protocol = NULL;
3180 	char *state;
3181 	struct list *worklist = NULL;
3182 #ifdef lint
3183 	flags = flags;
3184 #endif
3185 
3186 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
3187 	    switch (c) {
3188 	    case 'a':
3189 		all = 1;
3190 		break;
3191 	    case 'P':
3192 		protocol = optarg;
3193 		if (!sa_valid_protocol(protocol)) {
3194 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
3195 				    protocol);
3196 		    return (SA_INVALID_PROTOCOL);
3197 		}
3198 		break;
3199 	    case 'v':
3200 		verbose++;
3201 		break;
3202 	    default:
3203 	    case 'h':
3204 	    case '?':
3205 		(void) printf(gettext("usage: %s\n"),
3206 				sa_get_usage(USAGE_START));
3207 		return (SA_OK);
3208 	    }
3209 	}
3210 
3211 	if (optind == argc && !all) {
3212 		(void) printf(gettext("usage: %s\n"),
3213 				sa_get_usage(USAGE_START));
3214 		ret = SMF_EXIT_ERR_FATAL;
3215 	} else {
3216 		sa_group_t group;
3217 
3218 		check_sharetab();
3219 
3220 		if (!all) {
3221 		    while (optind < argc) {
3222 			group = sa_get_group(argv[optind]);
3223 			if (group != NULL) {
3224 			    state = sa_get_group_attr(group, "state");
3225 			    if (state == NULL ||
3226 				strcmp(state, "enabled") == 0) {
3227 				worklist = add_list(worklist, group, 0);
3228 				if (verbose)
3229 				    (void) printf(gettext("Starting group "
3230 								"\"%s\"\n"),
3231 					    argv[optind]);
3232 			    } else {
3233 				/*
3234 				 * determine if there are any
3235 				 * protocols.  if there aren't any,
3236 				 * then there isn't anything to do in
3237 				 * any case so no error.
3238 				 */
3239 				if (sa_get_optionset(group, protocol) != NULL) {
3240 				    ret = SMF_EXIT_OK;
3241 				}
3242 			    }
3243 			    if (state != NULL)
3244 				sa_free_attr_string(state);
3245 			}
3246 			optind++;
3247 		    }
3248 		} else {
3249 		    for (group = sa_get_group(NULL); group != NULL;
3250 			    group = sa_get_next_group(group)) {
3251 			state = sa_get_group_attr(group, "state");
3252 			if (state == NULL || strcmp(state, "enabled") == 0)
3253 			    worklist = add_list(worklist, group, 0);
3254 			if (state != NULL)
3255 			    sa_free_attr_string(state);
3256 		    }
3257 		}
3258 		(void) enable_all_groups(worklist, 0, 1, NULL);
3259 	}
3260 	if (worklist != NULL)
3261 	    free_list(worklist);
3262 	return (ret);
3263 }
3264 
3265 /*
3266  * sa_stop_group(flags, argc, argv)
3267  *
3268  * Implements the stop command.
3269  * This is similar to disable except it doesn't change the state
3270  * of the group(s) and only disables shares if the group is already
3271  * enabled.
3272  */
3273 
3274 int
3275 sa_stop_group(int flags, int argc, char *argv[])
3276 {
3277 	int verbose = 0;
3278 	int all = 0;
3279 	int c;
3280 	int ret = SMF_EXIT_OK;
3281 	char *protocol = NULL;
3282 	char *state;
3283 	struct list *worklist = NULL;
3284 #ifdef lint
3285 	flags = flags;
3286 #endif
3287 
3288 	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
3289 	    switch (c) {
3290 	    case 'a':
3291 		all = 1;
3292 		break;
3293 	    case 'P':
3294 		protocol = optarg;
3295 		if (!sa_valid_protocol(protocol)) {
3296 		    (void) printf(gettext("Invalid protocol specified: %s\n"),
3297 					protocol);
3298 		    return (SA_INVALID_PROTOCOL);
3299 		}
3300 		break;
3301 	    case 'v':
3302 		verbose++;
3303 		break;
3304 	    default:
3305 	    case 'h':
3306 	    case '?':
3307 		(void) printf(gettext("usage: %s\n"),
3308 				sa_get_usage(USAGE_STOP));
3309 		return (0);
3310 	    }
3311 	}
3312 
3313 	if (optind == argc && !all) {
3314 		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_STOP));
3315 		ret = SMF_EXIT_ERR_FATAL;
3316 	} else {
3317 		sa_group_t group;
3318 		if (!all) {
3319 		    while (optind < argc) {
3320 			group = sa_get_group(argv[optind]);
3321 			if (group != NULL) {
3322 			    state = sa_get_group_attr(group, "state");
3323 			    if (state == NULL ||
3324 				strcmp(state, "enabled") == 0) {
3325 				worklist = add_list(worklist, group, 0);
3326 				if (verbose)
3327 				    (void) printf(gettext("Stopping group "
3328 								"\"%s\"\n"),
3329 					    argv[optind]);
3330 			    } else {
3331 				ret = SMF_EXIT_OK;
3332 			    }
3333 			    if (state != NULL)
3334 				sa_free_attr_string(state);
3335 			}
3336 			optind++;
3337 		    }
3338 		} else {
3339 		    for (group = sa_get_group(NULL); group != NULL;
3340 			    group = sa_get_next_group(group)) {
3341 			state = sa_get_group_attr(group, "state");
3342 			if (state == NULL || strcmp(state, "enabled") == 0)
3343 			    worklist = add_list(worklist, group, 0);
3344 			if (state != NULL)
3345 			    sa_free_attr_string(state);
3346 		    }
3347 		}
3348 		(void) disable_all_groups(worklist, 0);
3349 		ret = sa_update_config();
3350 	}
3351 	if (worklist != NULL)
3352 	    free_list(worklist);
3353 	return (ret);
3354 }
3355 
3356 /*
3357  * remove_all_options(share, proto)
3358  *
3359  * Removes all options on a share.
3360  */
3361 
3362 static void
3363 remove_all_options(sa_share_t share, char *proto)
3364 {
3365 	sa_optionset_t optionset;
3366 	sa_security_t security;
3367 	sa_security_t prevsec = NULL;
3368 
3369 	optionset = sa_get_optionset(share, proto);
3370 	if (optionset != NULL)
3371 	    (void) sa_destroy_optionset(optionset);
3372 	for (security = sa_get_security(share, NULL, NULL);
3373 	    security != NULL;
3374 	    security = sa_get_next_security(security)) {
3375 	    char *type;
3376 		/*
3377 		 * we walk through the list.  prevsec keeps the
3378 		 * previous security so we can delete it without
3379 		 * destroying the list.
3380 		 */
3381 	    if (prevsec != NULL) {
3382 		/* remove the previously seen security */
3383 		(void) sa_destroy_security(prevsec);
3384 		/* set to NULL so we don't try multiple times */
3385 		prevsec = NULL;
3386 	    }
3387 	    type = sa_get_security_attr(security, "type");
3388 	    if (type != NULL) {
3389 		/*
3390 		 * if the security matches the specified protocol, we
3391 		 * want to remove it. prevsec holds it until either
3392 		 * the next pass or we fall out of the loop.
3393 		 */
3394 		if (strcmp(type, proto) == 0)
3395 		    prevsec = security;
3396 		sa_free_attr_string(type);
3397 	    }
3398 	}
3399 	/* in case there is one left */
3400 	if (prevsec != NULL)
3401 	    (void) sa_destroy_security(prevsec);
3402 }
3403 
3404 
3405 /*
3406  * for legacy support, we need to handle the old syntax. This is what
3407  * we get if sharemgr is called with the name "share" rather than
3408  * sharemgr.
3409  */
3410 
3411 static int
3412 format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
3413 {
3414 	int err;
3415 
3416 	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
3417 	if (err > buffsize)
3418 	    return (-1);
3419 	return (0);
3420 }
3421 
3422 
3423 /*
3424  * check_legacy_cmd(proto, cmd)
3425  *
3426  * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
3427  * executable.
3428  */
3429 
3430 static int
3431 check_legacy_cmd(char *path)
3432 {
3433 	struct stat st;
3434 	int ret = 0;
3435 
3436 	if (stat(path, &st) == 0) {
3437 	    if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
3438 		ret = 1;
3439 	}
3440 	return (ret);
3441 }
3442 
3443 /*
3444  * run_legacy_command(proto, cmd, argv)
3445  *
3446  * we know the command exists, so attempt to execute it with all the
3447  * arguments. This implements full legacy share support for those
3448  * protocols that don't have plugin providers.
3449  */
3450 
3451 static int
3452 run_legacy_command(char *path, char *argv[])
3453 {
3454 	int ret;
3455 
3456 	ret = execv(path, argv);
3457 	if (ret < 0) {
3458 	    switch (errno) {
3459 	    case EACCES:
3460 		ret = SA_NO_PERMISSION;
3461 		break;
3462 	    default:
3463 		ret = SA_SYSTEM_ERR;
3464 		break;
3465 	    }
3466 	}
3467 	return (ret);
3468 }
3469 
3470 /*
3471  * out_share(out, group, proto, options)
3472  *
3473  * Display the share information in the format that the "share"
3474  * command has traditionally used.
3475  */
3476 
3477 static void
3478 out_share(FILE *out, sa_group_t group, char *proto, char *options)
3479 {
3480 	sa_share_t share;
3481 	char resfmt[128];
3482 
3483 	for (share = sa_get_share(group, NULL); share != NULL;
3484 		share = sa_get_next_share(share)) {
3485 	    char *path;
3486 	    char *type;
3487 	    char *resource;
3488 	    char *description;
3489 	    char *groupname;
3490 	    char *sharedstate;
3491 	    int shared = 1;
3492 	    char *soptions;
3493 
3494 	    sharedstate = sa_get_share_attr(share, "shared");
3495 	    path = sa_get_share_attr(share, "path");
3496 	    type = sa_get_share_attr(share, "type");
3497 	    resource = sa_get_share_attr(share, "resource");
3498 	    groupname = sa_get_group_attr(group, "name");
3499 
3500 	    if (groupname != NULL && strcmp(groupname, "default") == 0) {
3501 		sa_free_attr_string(groupname);
3502 		groupname = NULL;
3503 	    }
3504 	    description = sa_get_share_description(share);
3505 	    soptions = options;
3506 
3507 	    if (sharedstate == NULL)
3508 		shared = 0;
3509 
3510 	    soptions = sa_proto_legacy_format(proto, share, 1);
3511 
3512 	    if (shared) {
3513 		/* only persisting share go here */
3514 		(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
3515 			resource != NULL ? resource : "-",
3516 			groupname != NULL ? "@" : "",
3517 			groupname != NULL ? groupname : "");
3518 		(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
3519 			resfmt,
3520 			path,
3521 			(soptions != NULL && strlen(soptions) > 0) ?
3522 					soptions : "rw",
3523 			(description != NULL) ? description : "");
3524 	    }
3525 
3526 	    if (path != NULL)
3527 		sa_free_attr_string(path);
3528 	    if (type != NULL)
3529 		sa_free_attr_string(type);
3530 	    if (resource != NULL)
3531 		sa_free_attr_string(resource);
3532 	    if (groupname != NULL)
3533 		sa_free_attr_string(groupname);
3534 	    if (description != NULL)
3535 		sa_free_share_description(description);
3536 	    if (sharedstate != NULL)
3537 		sa_free_attr_string(sharedstate);
3538 	    if (soptions != NULL && soptions != options)
3539 		sa_format_free(soptions);
3540 	}
3541 }
3542 
3543 /*
3544  * output_legacy_file(out, proto)
3545  *
3546  * Walk all of the groups for the specified protocol and call
3547  * out_share() to format and write in the format displayed by the
3548  * "share" command with no arguments.
3549  */
3550 
3551 static void
3552 output_legacy_file(FILE *out, char *proto)
3553 {
3554 	sa_group_t group;
3555 
3556 	for (group = sa_get_group(NULL); group != NULL;
3557 		group = sa_get_next_group(group)) {
3558 	    char *options;
3559 	    char *zfs;
3560 
3561 		/*
3562 		 * get default options preformated, being careful to
3563 		 * handle legacy shares differently from new style
3564 		 * shares. Legacy share have options on the share.
3565 		 */
3566 
3567 	    zfs = sa_get_group_attr(group, "zfs");
3568 	    if (zfs != NULL) {
3569 		sa_group_t zgroup;
3570 		sa_free_attr_string(zfs);
3571 		options = sa_proto_legacy_format(proto, group, 1);
3572 		for (zgroup = sa_get_sub_group(group); zgroup != NULL;
3573 		    zgroup = sa_get_next_group(zgroup)) {
3574 
3575 		    /* got a group, so display it */
3576 		    out_share(out, zgroup, proto, options);
3577 		}
3578 	    } else {
3579 		options = sa_proto_legacy_format(proto, group, 1);
3580 		out_share(out, group, proto, options);
3581 	    }
3582 	    if (options != NULL)
3583 		free(options);
3584 	}
3585 }
3586 
3587 int
3588 sa_legacy_share(int flags, int argc, char *argv[])
3589 {
3590 	char *protocol = "nfs";
3591 	char *options = NULL;
3592 	char *description = NULL;
3593 	char *groupname = NULL;
3594 	char *sharepath = NULL;
3595 	char *resource = NULL;
3596 	char *groupstatus = NULL;
3597 	int persist = SA_SHARE_TRANSIENT;
3598 	int argsused = 0;
3599 	int c;
3600 	int ret = SA_OK;
3601 	int zfs = 0;
3602 	int true_legacy = 0;
3603 	int curtype = SA_SHARE_TRANSIENT;
3604 	char cmd[MAXPATHLEN];
3605 #ifdef lint
3606 	flags = flags;
3607 #endif
3608 
3609 	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
3610 	    switch (c) {
3611 	    case 'd':
3612 		description = optarg;
3613 		argsused++;
3614 		break;
3615 	    case 'F':
3616 		protocol = optarg;
3617 		if (!sa_valid_protocol(protocol)) {
3618 		    if (format_legacy_path(cmd, MAXPATHLEN,
3619 			    protocol, "share") == 0 && check_legacy_cmd(cmd)) {
3620 			true_legacy++;
3621 		    } else {
3622 			(void) fprintf(stderr,
3623 					gettext("Invalid protocol specified:"
3624 						"%s\n"),
3625 				protocol);
3626 			return (SA_INVALID_PROTOCOL);
3627 		    }
3628 		}
3629 		break;
3630 	    case 'o':
3631 		options = optarg;
3632 		argsused++;
3633 		break;
3634 	    case 'p':
3635 		persist = SA_SHARE_PERMANENT;
3636 		argsused++;
3637 		break;
3638 	    case 'h':
3639 	    case '?':
3640 	    default:
3641 		(void) fprintf(stderr, gettext("usage: %s\n"),
3642 						sa_get_usage(USAGE_SHARE));
3643 		return (SA_OK);
3644 	    }
3645 	}
3646 
3647 	/* have the info so construct what is needed */
3648 	if (!argsused && optind == argc) {
3649 	    /* display current info in share format */
3650 	    (void) output_legacy_file(stdout, "nfs");
3651 	} else {
3652 	    sa_group_t group = NULL;
3653 	    sa_share_t share;
3654 	    char dir[MAXPATHLEN];
3655 
3656 	    /* we are modifying the configuration */
3657 	    if (optind == argc) {
3658 		(void) fprintf(stderr, gettext("usage: %s\n"),
3659 				sa_get_usage(USAGE_SHARE));
3660 		return (SA_LEGACY_ERR);
3661 	    }
3662 
3663 	    if (true_legacy) {
3664 		/* if still using legacy share/unshare, exec it */
3665 		ret = run_legacy_command(cmd, argv);
3666 		return (ret);
3667 	    }
3668 
3669 	    sharepath = argv[optind++];
3670 	    if (optind < argc) {
3671 		resource = argv[optind];
3672 		groupname = strchr(resource, '@');
3673 		if (groupname != NULL)
3674 		    *groupname++ = '\0';
3675 	    }
3676 	    if (realpath(sharepath, dir) == NULL)
3677 		ret = SA_BAD_PATH;
3678 	    else
3679 		sharepath = dir;
3680 	    if (ret == SA_OK) {
3681 		share = sa_find_share(sharepath);
3682 	    } else {
3683 		share = NULL;
3684 	    }
3685 	    if (groupname != NULL) {
3686 		    ret = SA_NOT_ALLOWED;
3687 	    } else if (ret == SA_OK) {
3688 		char *legacygroup = "default";
3689 		/*
3690 		 * the legacy group is always present and zfs groups
3691 		 * come and go.  zfs shares may be in sub-groups and
3692 		 * the zfs share will already be in that group so it
3693 		 * isn't an error.
3694 		 */
3695 		if (share != NULL) {
3696 		/*
3697 		 * if the share exists, then make sure it is one we
3698 		 * want to handle.
3699 		 */
3700 		    group = sa_get_parent_group(share);
3701 		} else {
3702 		    group = sa_get_group(legacygroup);
3703 		}
3704 		if (group != NULL) {
3705 		    groupstatus = group_status(group);
3706 		    if (share == NULL) {
3707 			share = sa_add_share(group, sharepath, persist, &ret);
3708 			if (share == NULL && ret == SA_DUPLICATE_NAME) {
3709 			    /* could be a ZFS path being started */
3710 			    if (sa_zfs_is_shared(sharepath)) {
3711 				ret = SA_OK;
3712 				group = sa_get_group("zfs");
3713 				if (group == NULL) {
3714 				    /* this shouldn't happen */
3715 				    ret = SA_CONFIG_ERR;
3716 				}
3717 				if (group != NULL) {
3718 				    share = sa_add_share(group, sharepath,
3719 							    persist, &ret);
3720 				}
3721 			    }
3722 			}
3723 		    } else {
3724 			char *type;
3725 			/*
3726 			 * may want to change persist state, but the
3727 			 * important thing is to change options. We
3728 			 * need to change them regardless of the
3729 			 * source.
3730 			 */
3731 			if (sa_zfs_is_shared(sharepath)) {
3732 			    zfs = 1;
3733 			}
3734 			remove_all_options(share, protocol);
3735 			type = sa_get_share_attr(share, "type");
3736 			if (type != NULL &&
3737 			    strcmp(type, "transient") != 0) {
3738 			    curtype = SA_SHARE_PERMANENT;
3739 			}
3740 			if (type != NULL)
3741 			    sa_free_attr_string(type);
3742 			if (curtype != persist) {
3743 			    (void) sa_set_share_attr(share, "type",
3744 					persist == SA_SHARE_PERMANENT ?
3745 						"persist" : "transient");
3746 			}
3747 		    }
3748 		    /* have a group to hold this share path */
3749 		    if (ret == SA_OK && options != NULL &&
3750 			strlen(options) > 0) {
3751 			ret = sa_parse_legacy_options(share,
3752 							options,
3753 							protocol);
3754 		    }
3755 		    if (!zfs) {
3756 			/*
3757 			 * zfs shares never have resource or
3758 			 * description and we can't store the values
3759 			 * so don't try.
3760 			 */
3761 			if (ret == SA_OK && description != NULL)
3762 			    ret = sa_set_share_description(share, description);
3763 			if (ret == SA_OK && resource != NULL)
3764 			    ret = sa_set_share_attr(share, "resource",
3765 						    resource);
3766 		    }
3767 		    if (ret == SA_OK) {
3768 			if (strcmp(groupstatus, "enabled") == 0)
3769 			    ret = sa_enable_share(share, protocol);
3770 			if (ret == SA_OK && persist == SA_SHARE_PERMANENT) {
3771 			    (void) sa_update_legacy(share, protocol);
3772 			}
3773 			if (ret == SA_OK)
3774 			    ret = sa_update_config();
3775 		    }
3776 		} else {
3777 		    ret = SA_SYSTEM_ERR;
3778 		}
3779 	    }
3780 	}
3781 	if (ret != SA_OK) {
3782 	    (void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
3783 				sharepath, sa_errorstr(ret));
3784 	    ret = SA_LEGACY_ERR;
3785 
3786 	}
3787 	return (ret);
3788 }
3789 
3790 /*
3791  * sa_legacy_unshare(flags, argc, argv)
3792  *
3793  * Implements the original unshare command.
3794  */
3795 
3796 int
3797 sa_legacy_unshare(int flags, int argc, char *argv[])
3798 {
3799 	char *protocol = "nfs"; /* for now */
3800 	char *options = NULL;
3801 	char *sharepath = NULL;
3802 	int persist = SA_SHARE_TRANSIENT;
3803 	int argsused = 0;
3804 	int c;
3805 	int ret = SA_OK;
3806 	int true_legacy = 0;
3807 	char cmd[MAXPATHLEN];
3808 #ifdef lint
3809 	flags = flags;
3810 	options = options;
3811 #endif
3812 
3813 	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
3814 	    switch (c) {
3815 	    case 'h':
3816 	    case '?':
3817 		break;
3818 	    case 'F':
3819 		protocol = optarg;
3820 		if (!sa_valid_protocol(protocol)) {
3821 		    if (format_legacy_path(cmd, MAXPATHLEN,
3822 						protocol, "unshare") == 0 &&
3823 			check_legacy_cmd(cmd)) {
3824 			true_legacy++;
3825 		    } else {
3826 			(void) printf(gettext("Invalid file system name\n"));
3827 			return (SA_INVALID_PROTOCOL);
3828 		    }
3829 		}
3830 		break;
3831 	    case 'o':
3832 		options = optarg;
3833 		argsused++;
3834 		break;
3835 	    case 'p':
3836 		persist = SA_SHARE_PERMANENT;
3837 		argsused++;
3838 		break;
3839 	    default:
3840 		(void) printf(gettext("usage: %s\n"),
3841 				sa_get_usage(USAGE_UNSHARE));
3842 		return (SA_OK);
3843 	    }
3844 	}
3845 
3846 	/* have the info so construct what is needed */
3847 	if (optind == argc || (optind + 1) < argc) {
3848 	    ret = SA_SYNTAX_ERR;
3849 	} else {
3850 	    sa_share_t share;
3851 	    char dir[MAXPATHLEN];
3852 	    if (true_legacy) {
3853 		/* if still using legacy share/unshare, exec it */
3854 		ret = run_legacy_command(cmd, argv);
3855 		return (ret);
3856 	    }
3857 	    sharepath = argv[optind++];
3858 	    if (realpath(sharepath, dir) == NULL) {
3859 		ret = SA_NO_SUCH_PATH;
3860 	    } else {
3861 		sharepath = dir;
3862 		share = sa_find_share(sharepath);
3863 		if (share != NULL) {
3864 		    ret = sa_disable_share(share, protocol);
3865 		    if (ret == SA_OK) {
3866 			if (persist == SA_SHARE_PERMANENT)
3867 			    ret = sa_remove_share(share);
3868 			ret = sa_update_config();
3869 		    }
3870 		} else {
3871 		    ret = SA_NOT_SHARED;
3872 		}
3873 	    }
3874 	}
3875 	switch (ret) {
3876 	default:
3877 	    (void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
3878 	    ret = SA_LEGACY_ERR;
3879 	    break;
3880 	case SA_SYNTAX_ERR:
3881 	    (void) printf(gettext("usage: %s\n"),
3882 				sa_get_usage(USAGE_UNSHARE));
3883 	    break;
3884 	case SA_OK:
3885 	    break;
3886 	}
3887 	return (ret);
3888 }
3889 
3890 /*
3891  * common commands that implement the sub-commands used by all
3892  * protcols. The entries are found via the lookup command
3893  */
3894 
3895 static sa_command_t commands[] = {
3896 	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
3897 	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
3898 	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
3899 	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
3900 	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
3901 	{"list", 0, sa_list, USAGE_LIST},
3902 	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
3903 	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
3904 	{"set", 0, sa_set, USAGE_SET, SVC_SET},
3905 	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
3906 	{"show", 0, sa_show, USAGE_SHOW},
3907 	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
3908 	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
3909 		SVC_SET|SVC_ACTION},
3910 	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
3911 	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
3912 	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
3913 	{NULL, 0, NULL, NULL}
3914 };
3915 
3916 static char *
3917 sa_get_usage(sa_usage_t index)
3918 {
3919 	char *ret = NULL;
3920 	switch (index) {
3921 	case USAGE_ADD_SHARE:
3922 	    ret = gettext("add-share [-nth] [-r resource-name] "
3923 			    "[-d \"description text\"] -s sharepath group");
3924 	    break;
3925 	case USAGE_CREATE:
3926 	    ret = gettext("create [-nvh] [-P proto [-p property=value]] group");
3927 	    break;
3928 	case USAGE_DELETE:
3929 	    ret = gettext("delete [-nvh] [-P proto] [-f] group");
3930 	    break;
3931 	case USAGE_DISABLE:
3932 	    ret = gettext("disable [-nvh] {-a | group ...}");
3933 	    break;
3934 	case USAGE_ENABLE:
3935 	    ret = gettext("enable [-nvh] {-a | group ...}");
3936 	    break;
3937 	case USAGE_LIST:
3938 	    ret = gettext("list [-vh] [-P proto]");
3939 	    break;
3940 	case USAGE_MOVE_SHARE:
3941 	    ret = gettext("move-share [-nvh] -s sharepath destination-group");
3942 	    break;
3943 	case USAGE_REMOVE_SHARE:
3944 	    ret = gettext("remove-share [-fnvh] -s sharepath group");
3945 	    break;
3946 	case USAGE_SET:
3947 	    ret = gettext("set [-nvh] -P proto [-S optspace] "
3948 				"[-p property=value]* [-s sharepath] group");
3949 	    break;
3950 	case USAGE_SET_SECURITY:
3951 	    ret = gettext("set-security [-nvh] -P proto -S security-type "
3952 			    "[-p property=value]* group");
3953 	    break;
3954 	case USAGE_SET_SHARE:
3955 	    ret = gettext("set-share [-nh] [-r resource] "
3956 			    "[-d \"description text\"] -s sharepath group");
3957 	    break;
3958 	case USAGE_SHOW:
3959 	    ret = gettext("show [-pvxh] [-P proto] [group ...]");
3960 	    break;
3961 	case USAGE_SHARE:
3962 	    ret = gettext("share [-F fstype] [-p] [-o optionlist]"
3963 			    "[-d description] [pathname [resourcename]]");
3964 	    break;
3965 	case USAGE_START:
3966 	    ret = gettext("start [-vh] [-P proto] {-a | group ...}");
3967 	    break;
3968 	case USAGE_STOP:
3969 	    ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
3970 	    break;
3971 	case USAGE_UNSET:
3972 	    ret = gettext("unset [-nvh] -P proto [-S optspace] "
3973 			    "[-p property]* group");
3974 	    break;
3975 	case USAGE_UNSET_SECURITY:
3976 	    ret = gettext("unset-security [-nvh] -P proto -S security-type "
3977 				"[-p property]* group");
3978 	    break;
3979 	case USAGE_UNSHARE:
3980 	    ret = gettext("unshare [-F fstype] [-p] [-o optionlist] sharepath");
3981 	    break;
3982 	}
3983 	return (ret);
3984 }
3985 
3986 /*
3987  * sa_lookup(cmd, proto)
3988  *
3989  * Lookup the sub-command. proto isn't currently used, but it may
3990  * eventually provide a way to provide protocol specific sub-commands.
3991  */
3992 
3993 sa_command_t *
3994 sa_lookup(char *cmd, char *proto)
3995 {
3996 	int i;
3997 	size_t len;
3998 #ifdef lint
3999 	proto = proto;
4000 #endif
4001 
4002 	len = strlen(cmd);
4003 	for (i = 0; commands[i].cmdname != NULL; i++) {
4004 	    if (strncmp(cmd, commands[i].cmdname, len) == 0)
4005 		return (&commands[i]);
4006 	}
4007 	return (NULL);
4008 }
4009 
4010 void
4011 sub_command_help(char *proto)
4012 {
4013 	int i;
4014 #ifdef lint
4015 	proto = proto;
4016 #endif
4017 
4018 	(void) printf(gettext("\tsub-commands:\n"));
4019 	for (i = 0; commands[i].cmdname != NULL; i++) {
4020 	    if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
4021 		(void) printf("\t%s\n",
4022 				sa_get_usage((sa_usage_t)commands[i].cmdidx));
4023 	}
4024 }
4025