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