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