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