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