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 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Copyright 2012 Milan Jurik. All rights reserved.
29 * Copyright 2018 Nexenta Systems, Inc.
30 * Copyright 2019, Joyent, Inc.
31 */
32
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <fcntl.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <string.h>
39#include <ctype.h>
40#include <unistd.h>
41#include <getopt.h>
42#include <utmpx.h>
43#include <pwd.h>
44#include <auth_attr.h>
45#include <secdb.h>
46#include <sys/param.h>
47#include <sys/stat.h>
48#include <errno.h>
49
50#include <libshare.h>
51#include "sharemgr.h"
52#include <libscf.h>
53#include <libxml/tree.h>
54#include <libintl.h>
55#include <assert.h>
56#include <iconv.h>
57#include <langinfo.h>
58#include <dirent.h>
59
60static char *sa_get_usage(sa_usage_t);
61
62/*
63 * Implementation of the common sub-commands supported by sharemgr.
64 * A number of helper functions are also included.
65 */
66
67/*
68 * has_protocol(group, proto)
69 *	If the group has an optionset with the specified protocol,
70 *	return true (1) otherwise false (0).
71 */
72static int
73has_protocol(sa_group_t group, char *protocol)
74{
75	sa_optionset_t optionset;
76	int result = 0;
77
78	optionset = sa_get_optionset(group, protocol);
79	if (optionset != NULL) {
80		result++;
81	}
82	return (result);
83}
84
85/*
86 * validresource(name)
87 *
88 * Check that name only has valid characters in it. The current valid
89 * set are the printable characters but not including:
90 *	" / \ [ ] : | < > + ; , ? * = \t
91 * Note that space is included and there is a maximum length.
92 */
93static int
94validresource(const char *name)
95{
96	const char *cp;
97	size_t len;
98
99	if (name == NULL)
100		return (B_FALSE);
101
102	len = strlen(name);
103	if (len == 0 || len > SA_MAX_RESOURCE_NAME)
104		return (B_FALSE);
105
106	if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) {
107		return (B_FALSE);
108	}
109
110	for (cp = name; *cp != '\0'; cp++)
111		if (iscntrl(*cp))
112			return (B_FALSE);
113
114	return (B_TRUE);
115}
116
117/*
118 * conv_to_utf8(input)
119 *
120 * Convert the input string to utf8 from the current locale.  If the
121 * conversion fails, use the current locale, it is likely close
122 * enough. For example, the "C" locale is a subset of utf-8. The
123 * return value may be a new string or the original input string.
124 */
125
126static char *
127conv_to_utf8(char *input)
128{
129	iconv_t cd;
130	char *inval = input;
131	char *output = input;
132	char *outleft;
133	char *curlocale;
134	size_t bytesleft;
135	size_t size;
136	size_t osize;
137	static int warned = 0;
138
139	curlocale = nl_langinfo(CODESET);
140	if (curlocale == NULL)
141		curlocale = "C";
142	cd = iconv_open("UTF-8", curlocale);
143	if (cd != NULL && cd != (iconv_t)-1) {
144		size = strlen(input);
145		/* Assume worst case of characters expanding to 4 bytes. */
146		bytesleft = size * 4;
147		output = calloc(bytesleft, 1);
148		if (output != NULL) {
149			outleft = output;
150			/* inval can be modified on return */
151			osize = iconv(cd, (const char **)&inval, &size,
152			    &outleft, &bytesleft);
153			if (osize == (size_t)-1 || size != 0) {
154				free(output);
155				output = input;
156			}
157		} else {
158			/* Need to return something. */
159			output = input;
160		}
161		(void) iconv_close(cd);
162	} else {
163		if (!warned)
164			(void) fprintf(stderr,
165			    gettext("Cannot convert to UTF-8 from %s\n"),
166			    curlocale ? curlocale : gettext("unknown"));
167		warned = 1;
168	}
169	return (output);
170}
171
172/*
173 * conv_from(input)
174 *
175 * Convert the input string from utf8 to current locale.  If the
176 * conversion isn't supported, just use as is. The return value may be
177 * a new string or the original input string.
178 */
179
180static char *
181conv_from_utf8(char *input)
182{
183	iconv_t cd;
184	char *output = input;
185	char *inval = input;
186	char *outleft;
187	char *curlocale;
188	size_t bytesleft;
189	size_t size;
190	size_t osize;
191	static int warned = 0;
192
193	curlocale = nl_langinfo(CODESET);
194	if (curlocale == NULL)
195		curlocale = "C";
196	cd = iconv_open(curlocale, "UTF-8");
197	if (cd != NULL && cd != (iconv_t)-1) {
198		size = strlen(input);
199		/* Assume worst case of characters expanding to 4 bytes. */
200		bytesleft = size * 4;
201		output = calloc(bytesleft, 1);
202		if (output != NULL) {
203			outleft = output;
204			osize = iconv(cd, (const char **)&inval, &size,
205			    &outleft, &bytesleft);
206			if (osize == (size_t)-1 || size != 0)
207				output = input;
208		} else {
209			/* Need to return something. */
210			output = input;
211		}
212		(void) iconv_close(cd);
213	} else {
214		if (!warned)
215			(void) fprintf(stderr,
216			    gettext("Cannot convert to %s from UTF-8\n"),
217			    curlocale ? curlocale : gettext("unknown"));
218		warned = 1;
219	}
220	return (output);
221}
222
223/*
224 * print_rsrc_desc(resource, sharedesc)
225 *
226 * Print the resource description string after converting from UTF8 to
227 * the current locale. If sharedesc is not NULL and there is no
228 * description on the resource, use sharedesc. sharedesc will already
229 * be converted to UTF8.
230 */
231
232static void
233print_rsrc_desc(sa_resource_t resource, char *sharedesc)
234{
235	char *description;
236	char *desc;
237
238	if (resource == NULL)
239		return;
240
241	description = sa_get_resource_description(resource);
242	if (description != NULL) {
243		desc = conv_from_utf8(description);
244		if (desc != description) {
245			sa_free_share_description(description);
246			description = desc;
247		}
248	} else if (sharedesc != NULL) {
249		description = strdup(sharedesc);
250	}
251	if (description != NULL) {
252		(void) printf("\t\"%s\"", description);
253		sa_free_share_description(description);
254	}
255}
256
257/*
258 * set_resource_desc(share, description)
259 *
260 * Set the share description value after converting the description
261 * string to UTF8 from the current locale.
262 */
263
264static int
265set_resource_desc(sa_share_t share, char *description)
266{
267	char *desc;
268	int ret;
269
270	desc = conv_to_utf8(description);
271	ret = sa_set_resource_description(share, desc);
272	if (description != desc)
273		sa_free_share_description(desc);
274	return (ret);
275}
276
277/*
278 * set_share_desc(share, description)
279 *
280 * Set the resource description value after converting the description
281 * string to UTF8 from the current locale.
282 */
283
284static int
285set_share_desc(sa_share_t share, char *description)
286{
287	char *desc;
288	int ret;
289
290	desc = conv_to_utf8(description);
291	ret = sa_set_share_description(share, desc);
292	if (description != desc)
293		sa_free_share_description(desc);
294	return (ret);
295}
296
297/*
298 * add_list(list, item, data, proto)
299 *	Adds a new list member that points holds item in the list.
300 *	If list is NULL, it starts a new list.  The function returns
301 *	the first member of the list.
302 */
303struct list *
304add_list(struct list *listp, void *item, void *data, char *proto)
305{
306	struct list *new, *tmp;
307
308	new = malloc(sizeof (struct list));
309	if (new != NULL) {
310		new->next = NULL;
311		new->item = item;
312		new->itemdata = data;
313		new->proto = proto;
314	} else {
315		return (listp);
316	}
317
318	if (listp == NULL)
319		return (new);
320
321	for (tmp = listp; tmp->next != NULL; tmp = tmp->next) {
322		/* get to end of list */
323	}
324	tmp->next = new;
325	return (listp);
326}
327
328/*
329 * free_list(list)
330 *	Given a list, free all the members of the list;
331 */
332static void
333free_list(struct list *listp)
334{
335	struct list *tmp;
336	while (listp != NULL) {
337		tmp = listp;
338		listp = listp->next;
339		free(tmp);
340	}
341}
342
343/*
344 * check_authorization(instname, which)
345 *
346 * Checks to see if the specific type of authorization in which is
347 * enabled for the user in this SMF service instance.
348 */
349
350static int
351check_authorization(char *instname, int which)
352{
353	scf_handle_t *handle = NULL;
354	scf_simple_prop_t *prop = NULL;
355	char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
356	char *authstr = NULL;
357	ssize_t numauths;
358	int ret = B_TRUE;
359	uid_t uid;
360	struct passwd *pw = NULL;
361
362	uid = getuid();
363	pw = getpwuid(uid);
364	if (pw == NULL) {
365		ret = B_FALSE;
366	} else {
367		/*
368		 * Since names are restricted to SA_MAX_NAME_LEN won't
369		 * overflow.
370		 */
371		(void) snprintf(svcstring, sizeof (svcstring), "%s:%s",
372		    SA_SVC_FMRI_BASE, instname);
373		handle = scf_handle_create(SCF_VERSION);
374		if (handle != NULL) {
375			if (scf_handle_bind(handle) == 0) {
376				switch (which) {
377				case SVC_SET:
378					prop = scf_simple_prop_get(handle,
379					    svcstring, "general",
380					    SVC_AUTH_VALUE);
381					break;
382				case SVC_ACTION:
383					prop = scf_simple_prop_get(handle,
384					    svcstring, "general",
385					    SVC_AUTH_ACTION);
386					break;
387				}
388			}
389		}
390	}
391	/* make sure we have an authorization string property */
392	if (prop != NULL) {
393		int i;
394		numauths = scf_simple_prop_numvalues(prop);
395		for (ret = 0, i = 0; i < numauths; i++) {
396			authstr = scf_simple_prop_next_astring(prop);
397			if (authstr != NULL) {
398				/* check if this user has one of the strings */
399				if (chkauthattr(authstr, pw->pw_name)) {
400					ret = 1;
401					break;
402				}
403			}
404		}
405		endauthattr();
406		scf_simple_prop_free(prop);
407	} else {
408		/* no authorization string defined */
409		ret = 0;
410	}
411	if (handle != NULL)
412		scf_handle_destroy(handle);
413	return (ret);
414}
415
416/*
417 * check_authorizations(instname, flags)
418 *
419 * check all the needed authorizations for the user in this service
420 * instance. Return value of 1(true) or 0(false) indicates whether
421 * there are authorizations for the user or not.
422 */
423
424static int
425check_authorizations(char *instname, int flags)
426{
427	int ret1 = 0;
428	int ret2 = 0;
429	int ret;
430
431	if (flags & SVC_SET)
432		ret1 = check_authorization(instname, SVC_SET);
433	if (flags & SVC_ACTION)
434		ret2 = check_authorization(instname, SVC_ACTION);
435	switch (flags) {
436	case SVC_ACTION:
437		ret = ret2;
438		break;
439	case SVC_SET:
440		ret = ret1;
441		break;
442	case SVC_ACTION|SVC_SET:
443		ret = ret1 & ret2;
444		break;
445	default:
446		/* if not flags set, we assume we don't need authorizations */
447		ret = 1;
448	}
449	return (ret);
450}
451
452/*
453 * notify_or_enable_share(share, protocol)
454 *
455 * Since some protocols don't want an "enable" when properties change,
456 * this function will use the protocol specific notify function
457 * first. If that fails, it will then attempt to use the
458 * sa_enable_share().  "protocol" is the protocol that was specified
459 * on the command line.
460 */
461static void
462notify_or_enable_share(sa_share_t share, char *protocol)
463{
464	sa_group_t group;
465	sa_optionset_t opt;
466	int ret = SA_OK;
467	char *path;
468	char *groupproto;
469	sa_share_t parent = share;
470
471	/* If really a resource, get parent share */
472	if (!sa_is_share(share)) {
473		parent = sa_get_resource_parent((sa_resource_t)share);
474	}
475
476	/*
477	 * Now that we've got a share in "parent", make sure it has a path.
478	 */
479	path = sa_get_share_attr(parent, "path");
480	if (path == NULL)
481		return;
482
483	group = sa_get_parent_group(parent);
484
485	if (group == NULL) {
486		sa_free_attr_string(path);
487		return;
488	}
489	for (opt = sa_get_optionset(group, NULL);
490	    opt != NULL;
491	    opt = sa_get_next_optionset(opt)) {
492		groupproto = sa_get_optionset_attr(opt, "type");
493		if (groupproto == NULL ||
494		    (protocol != NULL && strcmp(groupproto, protocol) != 0)) {
495			if (groupproto != NULL)
496				sa_free_attr_string(groupproto);
497			continue;
498		}
499		if (sa_is_share(share)) {
500			if ((ret = sa_proto_change_notify(share,
501			    groupproto)) != SA_OK) {
502				ret = sa_enable_share(share, groupproto);
503				if (ret != SA_OK) {
504					(void) printf(
505					    gettext("Could not reenable"
506					    " share %s: %s\n"),
507					    path, sa_errorstr(ret));
508				}
509			}
510		} else {
511			/* Must be a resource */
512			if ((ret = sa_proto_notify_resource(share,
513			    groupproto)) != SA_OK) {
514				ret = sa_enable_resource(share, groupproto);
515				if (ret != SA_OK) {
516					(void) printf(
517					    gettext("Could not "
518					    "reenable resource %s: "
519					    "%s\n"), path,
520					    sa_errorstr(ret));
521				}
522			}
523		}
524		sa_free_attr_string(groupproto);
525	}
526	sa_free_attr_string(path);
527}
528
529/*
530 * enable_group(group, updateproto, notify, proto)
531 *
532 * enable all the shares in the specified group. This is a helper for
533 * enable_all_groups in order to simplify regular and subgroup (zfs)
534 * enabling. Group has already been checked for non-NULL. If notify
535 * is non-zero, attempt to use the notify interface rather than
536 * enable.
537 */
538static void
539enable_group(sa_group_t group, char *updateproto, int notify, char *proto)
540{
541	sa_share_t share;
542
543	/* If the protocol isn't enabled for this group skip it */
544	if (!has_protocol(group, proto))
545		return;
546
547	for (share = sa_get_share(group, NULL);
548	    share != NULL;
549	    share = sa_get_next_share(share)) {
550		if (updateproto != NULL)
551			(void) sa_update_legacy(share, updateproto);
552		if (notify)
553			notify_or_enable_share(share, proto);
554		else
555			(void) sa_enable_share(share, proto);
556	}
557}
558
559/*
560 * isenabled(group)
561 *
562 * Returns B_TRUE if the group is enabled or B_FALSE if it isn't.
563 * Moved to separate function to reduce clutter in the code.
564 */
565
566static int
567isenabled(sa_group_t group)
568{
569	char *state;
570	int ret = B_FALSE;
571
572	if (group != NULL) {
573		state = sa_get_group_attr(group, "state");
574		if (state != NULL) {
575
576			if (strcmp(state, "enabled") == 0)
577				ret = B_TRUE;
578			sa_free_attr_string(state);
579		}
580	}
581	return (ret);
582}
583
584/*
585 * enable_all_groups(list, setstate, online, updateproto)
586 *
587 * Given a list of groups, enable each one found.  If updateproto is
588 * not NULL, then update all the shares for the protocol that was
589 * passed in. If enable is non-zero, tell enable_group to try the
590 * notify interface since this is a property change.
591 */
592static int
593enable_all_groups(sa_handle_t handle, struct list *work, int setstate,
594    int online, char *updateproto, int enable)
595{
596	int ret;
597	char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1];
598	char *state;
599	char *name;
600	char *zfs = NULL;
601	sa_group_t group;
602	sa_group_t subgroup;
603
604	for (ret = SA_OK; work != NULL;	work = work->next) {
605		group = (sa_group_t)work->item;
606
607		/*
608		 * If setstate == TRUE, then make sure to set
609		 * enabled. This needs to be done here in order for
610		 * the isenabled check to succeed on a newly enabled
611		 * group.
612		 */
613		if (setstate == B_TRUE) {
614			ret = sa_set_group_attr(group, "state",	"enabled");
615			if (ret != SA_OK)
616				break;
617		}
618
619		/*
620		 * Check to see if group is enabled. If it isn't, skip
621		 * the rest.  We don't want shares starting if the
622		 * group is disabled. The properties may have been
623		 * updated, but there won't be a change until the
624		 * group is enabled.
625		 */
626		if (!isenabled(group))
627			continue;
628
629		/* if itemdata != NULL then a single share */
630		if (work->itemdata != NULL) {
631			if (enable) {
632				if (work->itemdata != NULL)
633					notify_or_enable_share(work->itemdata,
634					    updateproto);
635				else
636					ret = SA_CONFIG_ERR;
637			} else {
638				if (sa_is_share(work->itemdata)) {
639					ret = sa_enable_share(
640					    (sa_share_t)work->itemdata,
641					    updateproto);
642				} else {
643					ret = sa_enable_resource(
644					    (sa_resource_t)work->itemdata,
645					    updateproto);
646				}
647			}
648		}
649		if (ret != SA_OK)
650			break;
651
652		/* if itemdata == NULL then the whole group */
653		if (work->itemdata == NULL) {
654			zfs = sa_get_group_attr(group, "zfs");
655			/*
656			 * If the share is managed by ZFS, don't
657			 * update any of the protocols since ZFS is
658			 * handling this.  Updateproto will contain
659			 * the name of the protocol that we want to
660			 * update legacy files for.
661			 */
662			enable_group(group, zfs == NULL ? updateproto : NULL,
663			    enable, work->proto);
664			if (zfs != NULL)
665				sa_free_attr_string(zfs);
666
667			for (subgroup = sa_get_sub_group(group);
668			    subgroup != NULL;
669			    subgroup = sa_get_next_group(subgroup)) {
670				/* never update legacy for ZFS subgroups */
671				enable_group(subgroup, NULL, enable,
672				    work->proto);
673			}
674		}
675		if (online) {
676			zfs = sa_get_group_attr(group, "zfs");
677			name = sa_get_group_attr(group, "name");
678			if (name != NULL) {
679				if (zfs == NULL) {
680					(void) snprintf(instance,
681					    sizeof (instance), "%s:%s",
682					    SA_SVC_FMRI_BASE, name);
683					state = smf_get_state(instance);
684					if (state == NULL ||
685					    strcmp(state, "online") != 0) {
686						(void) smf_enable_instance(
687						    instance, 0);
688						free(state);
689					}
690				} else {
691					sa_free_attr_string(zfs);
692					zfs = NULL;
693				}
694				if (name != NULL)
695					sa_free_attr_string(name);
696			}
697		}
698	}
699	if (ret == SA_OK) {
700		ret = sa_update_config(handle);
701	}
702	return (ret);
703}
704
705/*
706 * chk_opt(optlistp, security, proto)
707 *
708 * Do a sanity check on the optlist provided for the protocol.  This
709 * is a syntax check and verification that the property is either a
710 * general or specific to a names optionset.
711 */
712
713static int
714chk_opt(struct options *optlistp, int security, char *proto)
715{
716	struct options *optlist;
717	char *sep = "";
718	int notfirst = 0;
719	int ret;
720
721	for (optlist = optlistp; optlist != NULL; optlist = optlist->next) {
722		char *optname;
723
724		optname = optlist->optname;
725		ret = OPT_ADD_OK;
726		/* extract property/value pair */
727		if (sa_is_security(optname, proto)) {
728			if (!security)
729				ret = OPT_ADD_SECURITY;
730		} else {
731			if (security)
732				ret = OPT_ADD_PROPERTY;
733		}
734		if (ret != OPT_ADD_OK) {
735			if (notfirst == 0)
736				(void) printf(
737				    gettext("Property syntax error: "));
738			switch (ret) {
739			case OPT_ADD_SYNTAX:
740				(void) printf(gettext("%ssyntax error: %s"),
741				    sep, optname);
742				sep = ", ";
743				break;
744			case OPT_ADD_SECURITY:
745				(void) printf(gettext("%s%s requires -S"),
746				    optname, sep);
747				sep = ", ";
748				break;
749			case OPT_ADD_PROPERTY:
750				(void) printf(
751				    gettext("%s%s not supported with -S"),
752				    optname, sep);
753				sep = ", ";
754				break;
755			}
756			notfirst++;
757		}
758	}
759	if (notfirst) {
760		(void) printf("\n");
761		ret = SA_SYNTAX_ERR;
762	}
763	return (ret);
764}
765
766/*
767 * free_opt(optlist)
768 *	Free the specified option list.
769 */
770static void
771free_opt(struct options *optlist)
772{
773	struct options *nextopt;
774	while (optlist != NULL) {
775		nextopt = optlist->next;
776		free(optlist);
777		optlist = nextopt;
778	}
779}
780
781/*
782 * check property list for valid properties
783 * A null value is a remove which is always valid.
784 */
785static int
786valid_options(sa_handle_t handle, struct options *optlist, char *proto,
787    void *object, char *sec)
788{
789	int ret = SA_OK;
790	struct options *cur;
791	sa_property_t prop;
792	sa_optionset_t parent = NULL;
793
794	if (object != NULL) {
795		if (sec == NULL)
796			parent = sa_get_optionset(object, proto);
797		else
798			parent = sa_get_security(object, sec, proto);
799	}
800
801	for (cur = optlist; cur != NULL; cur = cur->next) {
802		if (cur->optvalue == NULL)
803			continue;
804		prop = sa_create_property(cur->optname, cur->optvalue);
805		if (prop == NULL)
806			ret = SA_NO_MEMORY;
807		if (ret != SA_OK ||
808		    (ret = sa_valid_property(handle, parent, proto, prop)) !=
809		    SA_OK) {
810			(void) printf(
811			    gettext("Could not add property %s: %s\n"),
812			    cur->optname, sa_errorstr(ret));
813		}
814		(void) sa_remove_property(prop);
815	}
816	return (ret);
817}
818
819/*
820 * add_optionset(group, optlist, protocol, *err)
821 *	Add the options in optlist to an optionset and then add the optionset
822 *	to the group.
823 *
824 *	The return value indicates if there was a "change" while errors are
825 *	returned via the *err parameters.
826 */
827static int
828add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err)
829{
830	sa_optionset_t optionset;
831	int ret = SA_OK;
832	int result = B_FALSE;
833	sa_handle_t handle;
834
835	optionset = sa_get_optionset(group, proto);
836	if (optionset == NULL) {
837		optionset = sa_create_optionset(group, proto);
838		if (optionset == NULL)
839			ret = SA_NO_MEMORY;
840		result = B_TRUE; /* adding a protocol is a change */
841	}
842	if (optionset == NULL) {
843		ret = SA_NO_MEMORY;
844		goto out;
845	}
846	handle = sa_find_group_handle(group);
847	if (handle == NULL) {
848		ret = SA_CONFIG_ERR;
849		goto out;
850	}
851	while (optlist != NULL) {
852		sa_property_t prop;
853		prop = sa_get_property(optionset, optlist->optname);
854		if (prop == NULL) {
855			/*
856			 * add the property, but only if it is
857			 * a non-NULL or non-zero length value
858			 */
859			if (optlist->optvalue != NULL) {
860				prop = sa_create_property(optlist->optname,
861				    optlist->optvalue);
862				if (prop != NULL) {
863					ret = sa_valid_property(handle,
864					    optionset, proto, prop);
865					if (ret != SA_OK) {
866						(void) sa_remove_property(prop);
867						(void) printf(gettext("Could "
868						    "not add property "
869						    "%s: %s\n"),
870						    optlist->optname,
871						    sa_errorstr(ret));
872					}
873				}
874				if (ret == SA_OK) {
875					ret = sa_add_property(optionset, prop);
876					if (ret != SA_OK) {
877						(void) printf(gettext(
878						    "Could not add property "
879						    "%s: %s\n"),
880						    optlist->optname,
881						    sa_errorstr(ret));
882					} else {
883						/* there was a change */
884						result = B_TRUE;
885					}
886				}
887			}
888		} else {
889			ret = sa_update_property(prop, optlist->optvalue);
890			/* should check to see if value changed */
891			if (ret != SA_OK) {
892				(void) printf(gettext("Could not update "
893				    "property %s: %s\n"), optlist->optname,
894				    sa_errorstr(ret));
895			} else {
896				result = B_TRUE;
897			}
898		}
899		optlist = optlist->next;
900	}
901	ret = sa_commit_properties(optionset, 0);
902
903out:
904	if (err != NULL)
905		*err = ret;
906	return (result);
907}
908
909/*
910 * resource_compliant(group)
911 *
912 * Go through all the shares in the group. Assume compliant, but if
913 * any share doesn't have at least one resource name, it isn't
914 * compliant.
915 */
916static int
917resource_compliant(sa_group_t group)
918{
919	sa_share_t share;
920
921	for (share = sa_get_share(group, NULL); share != NULL;
922	    share = sa_get_next_share(share)) {
923		if (sa_get_share_resource(share, NULL) == NULL) {
924			return (B_FALSE);
925		}
926	}
927	return (B_TRUE);
928}
929
930/*
931 * fix_path(path)
932 *
933 * change all illegal characters to something else.  For now, all get
934 * converted to '_' and the leading '/' is stripped off. This is used
935 * to construct an resource name (SMB share name) that is valid.
936 * Caller must pass a valid path.
937 */
938static void
939fix_path(char *path)
940{
941	char *cp;
942	size_t len;
943
944	assert(path != NULL);
945
946	/* make sure we are appropriate length */
947	cp = path + 1; /* skip leading slash */
948	while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) {
949		cp = strchr(cp, '/');
950		if (cp != NULL)
951			cp++;
952	}
953	/* two cases - cp == NULL and cp is substring of path */
954	if (cp == NULL) {
955		/* just take last SA_MAX_RESOURCE_NAME chars */
956		len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME;
957		(void) memmove(path, path + len, SA_MAX_RESOURCE_NAME);
958		path[SA_MAX_RESOURCE_NAME] = '\0';
959	} else {
960		len = strlen(cp) + 1;
961		(void) memmove(path, cp, len);
962	}
963
964	/*
965	 * Don't want any of the characters that are not allowed
966	 * in and SMB share name. Replace them with '_'.
967	 */
968	while (*path) {
969		switch (*path) {
970		case '/':
971		case '"':
972		case '\\':
973		case '[':
974		case ']':
975		case ':':
976		case '|':
977		case '<':
978		case '>':
979		case '+':
980		case ';':
981		case ',':
982		case '?':
983		case '*':
984		case '=':
985		case '\t':
986			*path = '_';
987			break;
988		}
989		path++;
990	}
991}
992
993/*
994 * name_adjust(path, count)
995 *
996 * Add a ~<count> in place of last few characters. The total number of
997 * characters is dependent on count.
998 */
999#define	MAX_MANGLE_NUMBER	10000
1000
1001static int
1002name_adjust(char *path, int count)
1003{
1004	size_t len;
1005
1006	len = strlen(path) - 2;
1007	if (count > 10)
1008		len--;
1009	if (count > 100)
1010		len--;
1011	if (count > 1000)
1012		len--;
1013	if (len > 0)
1014		(void) sprintf(path + len, "~%d", count);
1015	else
1016		return (SA_BAD_VALUE);
1017
1018	return (SA_OK);
1019}
1020
1021/*
1022 * make_resources(group)
1023 *
1024 * Go through all the shares in the group and make them have resource
1025 * names.
1026 */
1027static void
1028make_resources(sa_group_t group)
1029{
1030	sa_share_t share;
1031	int count;
1032	int err = SA_OK;
1033
1034	for (share = sa_get_share(group, NULL); share != NULL;
1035	    share = sa_get_next_share(share)) {
1036		/* Skip those with resources */
1037		if (sa_get_share_resource(share, NULL) == NULL) {
1038			char *path;
1039			path = sa_get_share_attr(share, "path");
1040			if (path == NULL)
1041				continue;
1042			fix_path(path);
1043			count = 0;	/* reset for next resource */
1044			while (sa_add_resource(share, path,
1045			    SA_SHARE_PERMANENT, &err) == NULL &&
1046			    err == SA_DUPLICATE_NAME) {
1047				int ret;
1048				ret = name_adjust(path, count);
1049				count++;
1050				if (ret != SA_OK ||
1051				    count >= MAX_MANGLE_NUMBER) {
1052					(void) printf(gettext(
1053					    "Cannot create resource name for"
1054					    " path: %s\n"), path);
1055					break;
1056				}
1057			}
1058			sa_free_attr_string(path);
1059		}
1060	}
1061}
1062
1063/*
1064 * check_valid_group(group, protocol)
1065 *
1066 * Check to see that the group should have the protocol added (if
1067 * there is one specified).
1068 */
1069
1070static int
1071check_valid_group(sa_group_t group, char *groupname, char *protocol)
1072{
1073
1074	if (protocol != NULL) {
1075		if (has_protocol(group, protocol)) {
1076			(void) printf(gettext(
1077			    "Group \"%s\" already exists"
1078			    " with protocol %s\n"), groupname,
1079			    protocol);
1080			return (SA_DUPLICATE_NAME);
1081		} else if (strcmp(groupname, "default") == 0 &&
1082		    strcmp(protocol, "nfs") != 0) {
1083			(void) printf(gettext(
1084			    "Group \"%s\" only allows protocol "
1085			    "\"%s\"\n"), groupname, "nfs");
1086			return (SA_INVALID_PROTOCOL);
1087		}
1088	} else {
1089		/* must add new protocol */
1090		(void) printf(gettext(
1091		    "Group already exists and no protocol "
1092		    "specified.\n"));
1093		return (SA_DUPLICATE_NAME);
1094	}
1095	return (SA_OK);
1096}
1097
1098/*
1099 * enforce_featureset(group, protocol, dryrun, force)
1100 *
1101 * Check the protocol featureset against the group and enforce any
1102 * rules that might be imposed.
1103 */
1104
1105static int
1106enforce_featureset(sa_group_t group, char *protocol, boolean_t dryrun,
1107    boolean_t force)
1108{
1109	uint64_t features;
1110
1111	if (protocol == NULL)
1112		return (SA_OK);
1113
1114	/*
1115	 * First check to see if specified protocol is one we want to
1116	 * allow on a group. Only server protocols are allowed here.
1117	 */
1118	features = sa_proto_get_featureset(protocol);
1119	if (!(features & SA_FEATURE_SERVER)) {
1120		(void) printf(
1121		    gettext("Protocol \"%s\" not supported.\n"), protocol);
1122		return (SA_INVALID_PROTOCOL);
1123	}
1124
1125	/*
1126	 * Check to see if the new protocol is one that requires
1127	 * resource names and make sure we are compliant before
1128	 * proceeding.
1129	 */
1130	if ((features & SA_FEATURE_RESOURCE) &&
1131	    !resource_compliant(group)) {
1132		if (force && !dryrun) {
1133			make_resources(group);
1134		} else {
1135			(void) printf(
1136			    gettext("Protocol requires resource names to be "
1137			    "set: %s\n"), protocol);
1138			return (SA_RESOURCE_REQUIRED);
1139		}
1140	}
1141	return (SA_OK);
1142}
1143
1144/*
1145 * set_all_protocols(group)
1146 *
1147 * Get the list of all protocols and add all server protocols to the
1148 * group.
1149 */
1150
1151static int
1152set_all_protocols(sa_group_t group)
1153{
1154	char **protolist;
1155	int numprotos, i;
1156	uint64_t features;
1157	sa_optionset_t optionset;
1158	int ret = SA_OK;
1159
1160	/*
1161	 * Now make sure we really want to put this protocol on a
1162	 * group. Only server protocols can go here.
1163	 */
1164	numprotos = sa_get_protocols(&protolist);
1165	for (i = 0; i < numprotos; i++) {
1166		features = sa_proto_get_featureset(protolist[i]);
1167		if (features & SA_FEATURE_SERVER) {
1168			optionset = sa_create_optionset(group, protolist[i]);
1169			if (optionset == NULL) {
1170				ret = SA_NO_MEMORY;
1171				break;
1172			}
1173		}
1174	}
1175
1176	if (protolist != NULL)
1177		free(protolist);
1178
1179	return (ret);
1180}
1181
1182/*
1183 * sa_create(flags, argc, argv)
1184 *	create a new group
1185 *	this may or may not have a protocol associated with it.
1186 *	No protocol means "all" protocols in this case.
1187 */
1188static int
1189sa_create(sa_handle_t handle, int flags, int argc, char *argv[])
1190{
1191	char *groupname;
1192
1193	sa_group_t group;
1194	boolean_t force = B_FALSE;
1195	boolean_t verbose = B_FALSE;
1196	boolean_t dryrun = B_FALSE;
1197	int c;
1198	char *protocol = NULL;
1199	int ret = SA_OK;
1200	struct options *optlist = NULL;
1201	int err = SA_OK;
1202	int auth;
1203	boolean_t created = B_FALSE;
1204
1205	while ((c = getopt(argc, argv, "?fhvnP:p:")) != EOF) {
1206		switch (c) {
1207		case 'f':
1208			force = B_TRUE;
1209			break;
1210		case 'v':
1211			verbose = B_TRUE;
1212			break;
1213		case 'n':
1214			dryrun = B_TRUE;
1215			break;
1216		case 'P':
1217			if (protocol != NULL) {
1218				(void) printf(gettext("Specifying "
1219				    "multiple protocols "
1220				    "not supported: %s\n"), protocol);
1221				return (SA_SYNTAX_ERR);
1222			}
1223			protocol = optarg;
1224			if (sa_valid_protocol(protocol))
1225				break;
1226			(void) printf(gettext(
1227			    "Invalid protocol specified: %s\n"), protocol);
1228			return (SA_INVALID_PROTOCOL);
1229		case 'p':
1230			ret = add_opt(&optlist, optarg, 0);
1231			switch (ret) {
1232			case OPT_ADD_SYNTAX:
1233				(void) printf(gettext(
1234				    "Property syntax error for property: %s\n"),
1235				    optarg);
1236				return (SA_SYNTAX_ERR);
1237			case OPT_ADD_SECURITY:
1238				(void) printf(gettext(
1239				    "Security properties need "
1240				    "to be set with set-security: %s\n"),
1241				    optarg);
1242				return (SA_SYNTAX_ERR);
1243			default:
1244				break;
1245			}
1246			break;
1247		case 'h':
1248			/* optopt on valid arg isn't defined */
1249			optopt = c;
1250			/*FALLTHROUGH*/
1251		case '?':
1252		default:
1253			/*
1254			 * Since a bad option gets to here, sort it
1255			 * out and return a syntax error return value
1256			 * if necessary.
1257			 */
1258			switch (optopt) {
1259			default:
1260				err = SA_SYNTAX_ERR;
1261				break;
1262			case 'h':
1263			case '?':
1264				break;
1265			}
1266			(void) printf(gettext("usage: %s\n"),
1267			    sa_get_usage(USAGE_CREATE));
1268			return (err);
1269		}
1270	}
1271
1272	if (optind >= argc) {
1273		(void) printf(gettext("usage: %s\n"),
1274		    sa_get_usage(USAGE_CREATE));
1275		(void) printf(gettext("\tgroup must be specified.\n"));
1276		return (SA_BAD_PATH);
1277	}
1278
1279	if ((optind + 1) < argc) {
1280		(void) printf(gettext("usage: %s\n"),
1281		    sa_get_usage(USAGE_CREATE));
1282		(void) printf(gettext("\textraneous group(s) at end\n"));
1283		return (SA_SYNTAX_ERR);
1284	}
1285
1286	if (protocol == NULL && optlist != NULL) {
1287		/* lookup default protocol */
1288		(void) printf(gettext("usage: %s\n"),
1289		    sa_get_usage(USAGE_CREATE));
1290		(void) printf(gettext("\tprotocol must be specified "
1291		    "with properties\n"));
1292		return (SA_INVALID_PROTOCOL);
1293	}
1294
1295	if (optlist != NULL)
1296		ret = chk_opt(optlist, 0, protocol);
1297	if (ret == OPT_ADD_SECURITY) {
1298		(void) printf(gettext("Security properties not "
1299		    "supported with create\n"));
1300		return (SA_SYNTAX_ERR);
1301	}
1302
1303	/*
1304	 * If a group already exists, we can only add a new protocol
1305	 * to it and not create a new one or add the same protocol
1306	 * again.
1307	 */
1308
1309	groupname = argv[optind];
1310
1311	auth = check_authorizations(groupname, flags);
1312
1313	group = sa_get_group(handle, groupname);
1314	if (group != NULL) {
1315		/* group exists so must be a protocol add */
1316		ret = check_valid_group(group, groupname, protocol);
1317	} else {
1318		/*
1319		 * is it a valid name? Must comply with SMF instance
1320		 * name restrictions.
1321		 */
1322		if (!sa_valid_group_name(groupname)) {
1323			ret = SA_INVALID_NAME;
1324			(void) printf(gettext("Invalid group name: %s\n"),
1325			    groupname);
1326		}
1327	}
1328	if (ret == SA_OK) {
1329		/* check protocol vs optlist */
1330		if (optlist != NULL) {
1331			/* check options, if any, for validity */
1332			ret = valid_options(handle, optlist, protocol,
1333			    group, NULL);
1334		}
1335	}
1336	if (ret == SA_OK && !dryrun) {
1337		if (group == NULL) {
1338			group = sa_create_group(handle, (char *)groupname,
1339			    &err);
1340			created = B_TRUE;
1341		}
1342		if (group != NULL) {
1343			sa_optionset_t optionset;
1344
1345			/*
1346			 * Check group and protocol against featureset
1347			 * requirements.
1348			 */
1349			ret = enforce_featureset(group, protocol,
1350			    dryrun, force);
1351			if (ret != SA_OK)
1352				goto err;
1353
1354			/*
1355			 * So far so good. Now add the required
1356			 * optionset(s) to the group.
1357			 */
1358			if (optlist != NULL) {
1359				(void) add_optionset(group, optlist, protocol,
1360				    &ret);
1361			} else if (protocol != NULL) {
1362				optionset = sa_create_optionset(group,
1363				    protocol);
1364				if (optionset == NULL)
1365					ret = SA_NO_MEMORY;
1366			} else if (protocol == NULL) {
1367				/* default group create so add all protocols */
1368				ret = set_all_protocols(group);
1369			}
1370			/*
1371			 * We have a group and legal additions
1372			 */
1373			if (ret == SA_OK) {
1374				/*
1375				 * Commit to configuration for protocols that
1376				 * need to do block updates. For NFS, this
1377				 * doesn't do anything but it will be run for
1378				 * all protocols that implement the
1379				 * appropriate plugin.
1380				 */
1381				ret = sa_update_config(handle);
1382			} else {
1383				if (group != NULL)
1384					(void) sa_remove_group(group);
1385			}
1386		} else {
1387			ret = err;
1388			(void) printf(gettext("Could not create group: %s\n"),
1389			    sa_errorstr(ret));
1390		}
1391	}
1392	if (dryrun && ret == SA_OK && !auth && verbose) {
1393		(void) printf(gettext("Command would fail: %s\n"),
1394		    sa_errorstr(SA_NO_PERMISSION));
1395		ret = SA_NO_PERMISSION;
1396	}
1397err:
1398	if (ret != SA_OK && created)
1399		ret = sa_remove_group(group);
1400
1401	free_opt(optlist);
1402	return (ret);
1403}
1404
1405/*
1406 * group_status(group)
1407 *
1408 * return the current status (enabled/disabled) of the group.
1409 */
1410
1411static char *
1412group_status(sa_group_t group)
1413{
1414	char *state;
1415	int enabled = 0;
1416
1417	state = sa_get_group_attr(group, "state");
1418	if (state != NULL) {
1419		if (strcmp(state, "enabled") == 0) {
1420			enabled = 1;
1421		}
1422		sa_free_attr_string(state);
1423	}
1424	return (enabled ? "enabled" : "disabled");
1425}
1426
1427/*
1428 * sa_delete(flags, argc, argv)
1429 *
1430 *	Delete a group.
1431 */
1432
1433static int
1434sa_delete(sa_handle_t handle, int flags, int argc, char *argv[])
1435{
1436	char *groupname;
1437	sa_group_t group;
1438	sa_share_t share;
1439	int verbose = 0;
1440	int dryrun = 0;
1441	int force = 0;
1442	int c;
1443	char *protocol = NULL;
1444	char *sectype = NULL;
1445	int ret = SA_OK;
1446	int auth;
1447
1448	while ((c = getopt(argc, argv, "?hvnP:fS:")) != EOF) {
1449		switch (c) {
1450		case 'v':
1451			verbose++;
1452			break;
1453		case 'n':
1454			dryrun++;
1455			break;
1456		case 'P':
1457			if (protocol != NULL) {
1458				(void) printf(gettext("Specifying "
1459				    "multiple protocols "
1460				    "not supported: %s\n"), protocol);
1461				return (SA_SYNTAX_ERR);
1462			}
1463			protocol = optarg;
1464			if (!sa_valid_protocol(protocol)) {
1465				(void) printf(gettext("Invalid protocol "
1466				    "specified: %s\n"), protocol);
1467				return (SA_INVALID_PROTOCOL);
1468			}
1469			break;
1470		case 'S':
1471			if (sectype != NULL) {
1472				(void) printf(gettext("Specifying "
1473				    "multiple property "
1474				    "spaces not supported: %s\n"), sectype);
1475				return (SA_SYNTAX_ERR);
1476			}
1477			sectype = optarg;
1478			break;
1479		case 'f':
1480			force++;
1481			break;
1482		case 'h':
1483			/* optopt on valid arg isn't defined */
1484			optopt = c;
1485			/*FALLTHROUGH*/
1486		case '?':
1487		default:
1488			/*
1489			 * Since a bad option gets to here, sort it
1490			 * out and return a syntax error return value
1491			 * if necessary.
1492			 */
1493			switch (optopt) {
1494			default:
1495				ret = SA_SYNTAX_ERR;
1496				break;
1497			case 'h':
1498			case '?':
1499				break;
1500			}
1501			(void) printf(gettext("usage: %s\n"),
1502			    sa_get_usage(USAGE_DELETE));
1503			return (ret);
1504		}
1505	}
1506
1507	if (optind >= argc) {
1508		(void) printf(gettext("usage: %s\n"),
1509		    sa_get_usage(USAGE_DELETE));
1510		(void) printf(gettext("\tgroup must be specified.\n"));
1511		return (SA_SYNTAX_ERR);
1512	}
1513
1514	if ((optind + 1) < argc) {
1515		(void) printf(gettext("usage: %s\n"),
1516		    sa_get_usage(USAGE_DELETE));
1517		(void) printf(gettext("\textraneous group(s) at end\n"));
1518		return (SA_SYNTAX_ERR);
1519	}
1520
1521	if (sectype != NULL && protocol == NULL) {
1522		(void) printf(gettext("usage: %s\n"),
1523		    sa_get_usage(USAGE_DELETE));
1524		(void) printf(gettext("\tsecurity requires protocol to be "
1525		    "specified.\n"));
1526		return (SA_SYNTAX_ERR);
1527	}
1528
1529	/*
1530	 * Determine if the group already exists since it must in
1531	 * order to be removed.
1532	 *
1533	 * We can delete when:
1534	 *
1535	 *	- group is empty
1536	 *	- force flag is set
1537	 *	- if protocol specified, only delete the protocol
1538	 */
1539
1540	groupname = argv[optind];
1541	group = sa_get_group(handle, groupname);
1542	if (group == NULL) {
1543		ret = SA_NO_SUCH_GROUP;
1544		goto done;
1545	}
1546	auth = check_authorizations(groupname, flags);
1547	if (protocol == NULL) {
1548		share = sa_get_share(group, NULL);
1549		if (share != NULL)
1550			ret = SA_BUSY;
1551		if (share == NULL || (share != NULL && force == 1)) {
1552			ret = SA_OK;
1553			if (!dryrun) {
1554				while (share != NULL) {
1555					sa_share_t next_share;
1556					next_share = sa_get_next_share(share);
1557					/*
1558					 * need to do the disable of
1559					 * each share, but don't
1560					 * actually do anything on a
1561					 * dryrun.
1562					 */
1563					ret = sa_disable_share(share, NULL);
1564					ret = sa_remove_share(share);
1565					share = next_share;
1566				}
1567				ret = sa_remove_group(group);
1568			}
1569		}
1570		/* Commit to configuration if not a dryrun */
1571		if (!dryrun && ret == SA_OK) {
1572			ret = sa_update_config(handle);
1573		}
1574	} else {
1575		/* a protocol delete */
1576		sa_optionset_t optionset;
1577		sa_security_t security;
1578		if (sectype != NULL) {
1579			/* only delete specified security */
1580			security = sa_get_security(group, sectype, protocol);
1581			if (security != NULL && !dryrun)
1582				ret = sa_destroy_security(security);
1583			else
1584				ret = SA_INVALID_PROTOCOL;
1585		} else {
1586			optionset = sa_get_optionset(group, protocol);
1587			if (optionset != NULL && !dryrun) {
1588				/*
1589				 * have an optionset with
1590				 * protocol to delete
1591				 */
1592				ret = sa_destroy_optionset(optionset);
1593				/*
1594				 * Now find all security sets
1595				 * for the protocol and remove
1596				 * them. Don't remove other
1597				 * protocols.
1598				 */
1599				for (security =
1600				    sa_get_security(group, NULL, NULL);
1601				    ret == SA_OK && security != NULL;
1602				    security = sa_get_next_security(security)) {
1603					char *secprot;
1604					secprot = sa_get_security_attr(security,
1605					    "type");
1606					if (secprot != NULL &&
1607					    strcmp(secprot, protocol) == 0)
1608						ret = sa_destroy_security(
1609						    security);
1610					if (secprot != NULL)
1611						sa_free_attr_string(secprot);
1612				}
1613			} else {
1614				if (!dryrun)
1615					ret = SA_INVALID_PROTOCOL;
1616			}
1617		}
1618		/*
1619		 * With the protocol items removed, make sure that all
1620		 * the shares are updated in the legacy files, if
1621		 * necessary.
1622		 */
1623		for (share = sa_get_share(group, NULL);
1624		    share != NULL;
1625		    share = sa_get_next_share(share)) {
1626			(void) sa_delete_legacy(share, protocol);
1627		}
1628	}
1629
1630done:
1631	if (ret != SA_OK) {
1632		(void) printf(gettext("Could not delete group: %s\n"),
1633		    sa_errorstr(ret));
1634	} else if (dryrun && !auth && verbose) {
1635		(void) printf(gettext("Command would fail: %s\n"),
1636		    sa_errorstr(SA_NO_PERMISSION));
1637	}
1638	return (ret);
1639}
1640
1641/*
1642 * strndupr(*buff, str, buffsize)
1643 *
1644 * used with small strings to duplicate and possibly increase the
1645 * buffer size of a string.
1646 */
1647static char *
1648strndupr(char *buff, char *str, int *buffsize)
1649{
1650	int limit;
1651	char *orig_buff = buff;
1652
1653	if (buff == NULL) {
1654		buff = (char *)malloc(64);
1655		if (buff == NULL)
1656			return (NULL);
1657		*buffsize = 64;
1658		buff[0] = '\0';
1659	}
1660	limit = strlen(buff) + strlen(str) + 1;
1661	if (limit > *buffsize) {
1662		limit = *buffsize = *buffsize + ((limit / 64) + 64);
1663		buff = realloc(buff, limit);
1664	}
1665	if (buff != NULL) {
1666		(void) strcat(buff, str);
1667	} else {
1668		/* if it fails, fail it hard */
1669		if (orig_buff != NULL)
1670			free(orig_buff);
1671	}
1672	return (buff);
1673}
1674
1675/*
1676 * group_proto(group)
1677 *
1678 * return a string of all the protocols (space separated) associated
1679 * with this group.
1680 */
1681
1682static char *
1683group_proto(sa_group_t group)
1684{
1685	sa_optionset_t optionset;
1686	char *proto;
1687	char *buff = NULL;
1688	int buffsize = 0;
1689	int addspace = 0;
1690	/*
1691	 * get the protocol list by finding the optionsets on this
1692	 * group and extracting the type value. The initial call to
1693	 * strndupr() initailizes buff.
1694	 */
1695	buff = strndupr(buff, "", &buffsize);
1696	if (buff != NULL) {
1697		for (optionset = sa_get_optionset(group, NULL);
1698		    optionset != NULL && buff != NULL;
1699		    optionset = sa_get_next_optionset(optionset)) {
1700			/*
1701			 * extract out the protocol type from this optionset
1702			 * and append it to the buffer "buff". strndupr() will
1703			 * reallocate space as necessay.
1704			 */
1705			proto = sa_get_optionset_attr(optionset, "type");
1706			if (proto != NULL) {
1707				if (addspace++)
1708					buff = strndupr(buff, " ", &buffsize);
1709				buff = strndupr(buff, proto, &buffsize);
1710				sa_free_attr_string(proto);
1711			}
1712		}
1713	}
1714	return (buff);
1715}
1716
1717/*
1718 * sa_list(flags, argc, argv)
1719 *
1720 * implements the "list" subcommand to list groups and optionally
1721 * their state and protocols.
1722 */
1723
1724static int
1725sa_list(sa_handle_t handle, int flags, int argc, char *argv[])
1726{
1727	sa_group_t group;
1728	int verbose = 0;
1729	int c;
1730	char *protocol = NULL;
1731	int ret = SA_OK;
1732#ifdef lint
1733	flags = flags;
1734#endif
1735
1736	while ((c = getopt(argc, argv, "?hvP:")) != EOF) {
1737		switch (c) {
1738		case 'v':
1739			verbose++;
1740			break;
1741		case 'P':
1742			if (protocol != NULL) {
1743				(void) printf(gettext(
1744				    "Specifying multiple protocols "
1745				    "not supported: %s\n"),
1746				    protocol);
1747				return (SA_SYNTAX_ERR);
1748			}
1749			protocol = optarg;
1750			if (!sa_valid_protocol(protocol)) {
1751				(void) printf(gettext(
1752				    "Invalid protocol specified: %s\n"),
1753				    protocol);
1754				return (SA_INVALID_PROTOCOL);
1755			}
1756			break;
1757		case 'h':
1758			/* optopt on valid arg isn't defined */
1759			optopt = c;
1760			/*FALLTHROUGH*/
1761		case '?':
1762		default:
1763			/*
1764			 * Since a bad option gets to here, sort it
1765			 * out and return a syntax error return value
1766			 * if necessary.
1767			 */
1768			switch (optopt) {
1769			default:
1770				ret = SA_SYNTAX_ERR;
1771				break;
1772			case 'h':
1773			case '?':
1774				break;
1775			}
1776			(void) printf(gettext("usage: %s\n"),
1777			    sa_get_usage(USAGE_LIST));
1778			return (ret);
1779		}
1780	}
1781
1782	if (optind != argc) {
1783		(void) printf(gettext("usage: %s\n"),
1784		    sa_get_usage(USAGE_LIST));
1785		return (SA_SYNTAX_ERR);
1786	}
1787
1788	for (group = sa_get_group(handle, NULL);
1789	    group != NULL;
1790	    group = sa_get_next_group(group)) {
1791		char *name;
1792		char *proto;
1793		if (protocol == NULL || has_protocol(group, protocol)) {
1794			name = sa_get_group_attr(group, "name");
1795			if (name != NULL && (verbose > 1 || name[0] != '#')) {
1796				(void) printf("%s", (char *)name);
1797				if (verbose) {
1798					/*
1799					 * Need the list of protocols
1800					 * and current status once
1801					 * available. We do want to
1802					 * translate the
1803					 * enabled/disabled text here.
1804					 */
1805					(void) printf("\t%s", isenabled(group) ?
1806					    gettext("enabled") :
1807					    gettext("disabled"));
1808					proto = group_proto(group);
1809					if (proto != NULL) {
1810						(void) printf("\t%s",
1811						    (char *)proto);
1812						free(proto);
1813					}
1814				}
1815				(void) printf("\n");
1816			}
1817			if (name != NULL)
1818				sa_free_attr_string(name);
1819		}
1820	}
1821	return (0);
1822}
1823
1824/*
1825 * out_properties(optionset, proto, sec)
1826 *
1827 * Format the properties and encode the protocol and optional named
1828 * optionset into the string.
1829 *
1830 * format is protocol[:name]=(property-list)
1831 */
1832
1833static void
1834out_properties(sa_optionset_t optionset, char *proto, char *sec)
1835{
1836	char *type;
1837	char *value;
1838	int spacer;
1839	sa_property_t prop;
1840
1841	if (sec == NULL)
1842		(void) printf(" %s=(", proto ? proto : gettext("all"));
1843	else
1844		(void) printf(" %s:%s=(", proto ? proto : gettext("all"), sec);
1845
1846	for (spacer = 0, prop = sa_get_property(optionset, NULL);
1847	    prop != NULL;
1848	    prop = sa_get_next_property(prop)) {
1849
1850		/*
1851		 * extract the property name/value and output with
1852		 * appropriate spacing. I.e. no prefixed space the
1853		 * first time through but a space on subsequent
1854		 * properties.
1855		 */
1856		type = sa_get_property_attr(prop, "type");
1857		value = sa_get_property_attr(prop, "value");
1858		if (type != NULL) {
1859			(void) printf("%s%s=", spacer ? " " : "",	type);
1860			spacer = 1;
1861			if (value != NULL)
1862				(void) printf("\"%s\"", value);
1863			else
1864				(void) printf("\"\"");
1865		}
1866		if (type != NULL)
1867			sa_free_attr_string(type);
1868		if (value != NULL)
1869			sa_free_attr_string(value);
1870	}
1871	(void) printf(")");
1872}
1873
1874/*
1875 * show_properties(group, protocol, prefix)
1876 *
1877 * print the properties for a group. If protocol is NULL, do all
1878 * protocols otherwise only the specified protocol. All security
1879 * (named groups specific to the protocol) are included.
1880 *
1881 * The "prefix" is always applied. The caller knows whether it wants
1882 * some type of prefix string (white space) or not.  Once the prefix
1883 * has been output, it is reduced to the zero length string for the
1884 * remainder of the property output.
1885 */
1886
1887static void
1888show_properties(sa_group_t group, char *protocol, char *prefix)
1889{
1890	sa_optionset_t optionset;
1891	sa_security_t security;
1892	char *value;
1893	char *secvalue;
1894
1895	if (protocol != NULL) {
1896		optionset = sa_get_optionset(group, protocol);
1897		if (optionset != NULL) {
1898			(void) printf("%s", prefix);
1899			prefix = "";
1900			out_properties(optionset, protocol, NULL);
1901		}
1902		security = sa_get_security(group, protocol, NULL);
1903		if (security != NULL) {
1904			(void) printf("%s", prefix);
1905			prefix = "";
1906			out_properties(security, protocol, NULL);
1907		}
1908	} else {
1909		for (optionset = sa_get_optionset(group, protocol);
1910		    optionset != NULL;
1911		    optionset = sa_get_next_optionset(optionset)) {
1912
1913			value = sa_get_optionset_attr(optionset, "type");
1914			(void) printf("%s", prefix);
1915			prefix = "";
1916			out_properties(optionset, value, 0);
1917			if (value != NULL)
1918				sa_free_attr_string(value);
1919		}
1920		for (security = sa_get_security(group, NULL, protocol);
1921		    security != NULL;
1922		    security = sa_get_next_security(security)) {
1923
1924			value = sa_get_security_attr(security, "type");
1925			secvalue = sa_get_security_attr(security, "sectype");
1926			(void) printf("%s", prefix);
1927			prefix = "";
1928			out_properties(security, value, secvalue);
1929			if (value != NULL)
1930				sa_free_attr_string(value);
1931			if (secvalue != NULL)
1932				sa_free_attr_string(secvalue);
1933		}
1934	}
1935}
1936
1937/*
1938 * get_resource(share)
1939 *
1940 * Get the first resource name, if any, and fix string to be in
1941 * current locale and have quotes if it has embedded spaces.  Return
1942 * an attr string that must be freed.
1943 */
1944
1945static char *
1946get_resource(sa_share_t share)
1947{
1948	sa_resource_t resource;
1949	char *resstring = NULL;
1950	char *retstring;
1951
1952	if ((resource = sa_get_share_resource(share, NULL)) != NULL) {
1953		resstring = sa_get_resource_attr(resource, "name");
1954		if (resstring != NULL) {
1955			char *cp;
1956			int len;
1957
1958			retstring = conv_from_utf8(resstring);
1959			if (retstring != resstring) {
1960				sa_free_attr_string(resstring);
1961				resstring = retstring;
1962			}
1963			if (strpbrk(resstring, " ") != NULL) {
1964				/* account for quotes */
1965				len = strlen(resstring) + 3;
1966				cp = calloc(len, sizeof (char));
1967				if (cp != NULL) {
1968					(void) snprintf(cp, len,
1969					    "\"%s\"", resstring);
1970					sa_free_attr_string(resstring);
1971					resstring = cp;
1972				} else {
1973					sa_free_attr_string(resstring);
1974					resstring = NULL;
1975				}
1976			}
1977		}
1978	}
1979	return (resstring);
1980}
1981
1982/*
1983 * has_resource_with_opt(share)
1984 *
1985 * Check to see if the share has any resource names with optionsets
1986 * set. Also indicate if multiple resource names since the syntax
1987 * would be about the same.
1988 */
1989static int
1990has_resource_with_opt(sa_share_t share)
1991{
1992	sa_resource_t resource;
1993	int ret = B_FALSE;
1994
1995	for (resource = sa_get_share_resource(share, NULL);
1996	    resource != NULL;
1997	    resource = sa_get_next_resource(resource)) {
1998
1999		if (sa_get_optionset(resource, NULL) != NULL) {
2000			ret = B_TRUE;
2001			break;
2002		}
2003	}
2004	return (ret);
2005}
2006
2007/*
2008 * has_multiple_resource(share)
2009 *
2010 * Check to see if the share has multiple resource names since
2011 * the syntax would be about the same.
2012 */
2013static boolean_t
2014has_multiple_resource(sa_share_t share)
2015{
2016	sa_resource_t resource;
2017	int num;
2018
2019	for (num = 0, resource = sa_get_share_resource(share, NULL);
2020	    resource != NULL;
2021	    resource = sa_get_next_resource(resource)) {
2022		num++;
2023		if (num > 1)
2024			return (B_TRUE);
2025	}
2026	return (B_FALSE);
2027}
2028
2029/*
2030 * show_share(share, verbose, properties, proto, iszfs, sharepath)
2031 *
2032 * print out the share information. With the addition of resource as a
2033 * full object that can have multiple instances below the share, we
2034 * need to display that as well.
2035 */
2036
2037static void
2038show_share(sa_share_t share, int verbose, int properties, char *proto,
2039    int iszfs, char *sharepath)
2040{
2041	char *drive;
2042	char *exclude;
2043	sa_resource_t resource = NULL;
2044	char *description;
2045	char *rsrcname;
2046	int rsrcwithopt;
2047	boolean_t multiple;
2048	char *type;
2049
2050	rsrcwithopt = has_resource_with_opt(share);
2051
2052	if (verbose || (properties && rsrcwithopt)) {
2053		/* First, indicate if transient */
2054		type = sa_get_share_attr(share, "type");
2055		if (type != NULL && !iszfs && verbose &&
2056		    strcmp(type, "transient") == 0)
2057			(void) printf("\t* ");
2058		else
2059			(void) printf("\t  ");
2060
2061		if (type != NULL)
2062			sa_free_attr_string(type);
2063
2064		/*
2065		 * If we came in with verbose, we want to handle the case of
2066		 * multiple resources as though they had properties set.
2067		 */
2068		multiple = has_multiple_resource(share);
2069
2070		/*
2071		 * if there is a description on the share and there
2072		 * are resources, treat as multiple resources in order
2073		 * to get all descriptions displayed.
2074		 */
2075		description = sa_get_share_description(share);
2076		resource = sa_get_share_resource(share, NULL);
2077
2078		if (description != NULL && resource != NULL)
2079			multiple = B_TRUE;
2080
2081		/* Next, if not multiple follow old model */
2082		if (!multiple && !rsrcwithopt) {
2083			rsrcname = get_resource(share);
2084			if (rsrcname != NULL && strlen(rsrcname) > 0) {
2085				(void) printf("%s=%s", rsrcname, sharepath);
2086			} else {
2087				(void) printf("%s", sharepath);
2088			}
2089			if (rsrcname != NULL)
2090				sa_free_attr_string(rsrcname);
2091			/* Print the description string if there is one. */
2092			print_rsrc_desc(resource, description);
2093		} else {
2094			/* Treat as simple and then resources come later */
2095			(void) printf("%s", sharepath);
2096		}
2097		drive = sa_get_share_attr(share, "drive-letter");
2098		if (drive != NULL) {
2099			if (strlen(drive) > 0)
2100				(void) printf(gettext("\tdrive-letter=\"%s:\""),
2101				    drive);
2102			sa_free_attr_string(drive);
2103		}
2104		if (properties)
2105			show_properties(share, proto, "\t");
2106		exclude = sa_get_share_attr(share, "exclude");
2107		if (exclude != NULL) {
2108			(void) printf(gettext("\tnot-shared-with=[%s]"),
2109			    exclude);
2110			sa_free_attr_string(exclude);
2111		}
2112
2113		if (description != NULL) {
2114			print_rsrc_desc((sa_resource_t)share, description);
2115		}
2116		/*
2117		 * If there are resource names with options, show them
2118		 * here, with one line per resource. Resource specific
2119		 * options are at the end of the line followed by
2120		 * description, if any.
2121		 */
2122		if (rsrcwithopt || multiple) {
2123			for (resource = sa_get_share_resource(share, NULL);
2124			    resource != NULL;
2125			    resource = sa_get_next_resource(resource)) {
2126				int has_space;
2127				char *rsrc;
2128
2129				(void) printf("\n\t\t  ");
2130				rsrcname = sa_get_resource_attr(resource,
2131				    "name");
2132				if (rsrcname == NULL)
2133					continue;
2134
2135				rsrc = conv_from_utf8(rsrcname);
2136				has_space = strpbrk(rsrc, " ") != NULL;
2137
2138				if (has_space)
2139					(void) printf("\"%s\"=%s", rsrc,
2140					    sharepath);
2141				else
2142					(void) printf("%s=%s", rsrc,
2143					    sharepath);
2144				if (rsrc != rsrcname)
2145					sa_free_attr_string(rsrc);
2146				sa_free_attr_string(rsrcname);
2147				if (properties || rsrcwithopt)
2148					show_properties(resource, proto, "\t");
2149
2150				/* Get description string if any */
2151				print_rsrc_desc(resource, description);
2152			}
2153		}
2154		if (description != NULL)
2155			sa_free_share_description(description);
2156	} else {
2157		(void) printf("\t  %s", sharepath);
2158		if (properties)
2159			show_properties(share, proto, "\t");
2160	}
2161	(void) printf("\n");
2162}
2163
2164/*
2165 * show_group(group, verbose, properties, proto, subgroup)
2166 *
2167 * helper function to show the contents of a group.
2168 */
2169
2170static void
2171show_group(sa_group_t group, int verbose, int properties, char *proto,
2172    char *subgroup)
2173{
2174	char *groupname;
2175	char *zfs = NULL;
2176	int iszfs = 0;
2177	char *sharepath;
2178
2179	groupname = sa_get_group_attr(group, "name");
2180	if (groupname != NULL) {
2181		sa_share_t share;
2182
2183		if (proto != NULL && !has_protocol(group, proto)) {
2184			sa_free_attr_string(groupname);
2185			return;
2186		}
2187		/*
2188		 * check to see if the group is managed by ZFS. If
2189		 * there is an attribute, then it is. A non-NULL zfs
2190		 * variable will trigger the different way to display
2191		 * and will remove the transient property indicator
2192		 * from the output.
2193		 */
2194		zfs = sa_get_group_attr(group, "zfs");
2195		if (zfs != NULL) {
2196			iszfs = 1;
2197			sa_free_attr_string(zfs);
2198		}
2199
2200		if (subgroup == NULL)
2201			(void) printf("%s", groupname);
2202		else
2203			(void) printf("    %s/%s", subgroup, groupname);
2204		if (properties)
2205			show_properties(group, proto, "");
2206		(void) printf("\n");
2207		if (strcmp(groupname, "zfs") == 0) {
2208			sa_group_t zgroup;
2209
2210			for (zgroup = sa_get_sub_group(group);
2211			    zgroup != NULL;
2212			    zgroup = sa_get_next_group(zgroup)) {
2213				show_group(zgroup, verbose, properties, proto,
2214				    "zfs");
2215			}
2216			sa_free_attr_string(groupname);
2217			return;
2218		}
2219		/*
2220		 * Have a group, so list the contents. Resource and
2221		 * description are only listed if verbose is set.
2222		 */
2223		for (share = sa_get_share(group, NULL);
2224		    share != NULL;
2225		    share = sa_get_next_share(share)) {
2226			sharepath = sa_get_share_attr(share, "path");
2227			if (sharepath != NULL) {
2228				show_share(share, verbose, properties, proto,
2229				    iszfs, sharepath);
2230				sa_free_attr_string(sharepath);
2231			}
2232		}
2233	}
2234	if (groupname != NULL) {
2235		sa_free_attr_string(groupname);
2236	}
2237}
2238
2239/*
2240 * show_group_xml_init()
2241 *
2242 * Create an XML document that will be used to display config info via
2243 * XML format.
2244 */
2245
2246xmlDocPtr
2247show_group_xml_init()
2248{
2249	xmlDocPtr doc;
2250	xmlNodePtr root;
2251
2252	doc = xmlNewDoc((xmlChar *)"1.0");
2253	if (doc != NULL) {
2254		root = xmlNewNode(NULL, (xmlChar *)"sharecfg");
2255		if (root != NULL)
2256			(void) xmlDocSetRootElement(doc, root);
2257	}
2258	return (doc);
2259}
2260
2261/*
2262 * show_group_xml(doc, group)
2263 *
2264 * Copy the group info into the XML doc.
2265 */
2266
2267static void
2268show_group_xml(xmlDocPtr doc, sa_group_t group)
2269{
2270	xmlNodePtr node;
2271	xmlNodePtr root;
2272
2273	root = xmlDocGetRootElement(doc);
2274	node = xmlCopyNode((xmlNodePtr)group, 1);
2275	if (node != NULL && root != NULL) {
2276		(void) xmlAddChild(root, node);
2277		/*
2278		 * In the future, we may have interally used tags that
2279		 * should not appear in the XML output. Remove
2280		 * anything we don't want to show here.
2281		 */
2282	}
2283}
2284
2285/*
2286 * sa_show(flags, argc, argv)
2287 *
2288 * Implements the show subcommand.
2289 */
2290
2291int
2292sa_show(sa_handle_t handle, int flags, int argc, char *argv[])
2293{
2294	sa_group_t group;
2295	int verbose = 0;
2296	int properties = 0;
2297	int c;
2298	int ret = SA_OK;
2299	char *protocol = NULL;
2300	int xml = 0;
2301	xmlDocPtr doc;
2302#ifdef lint
2303	flags = flags;
2304#endif
2305
2306	while ((c = getopt(argc, argv, "?hvP:px")) !=	EOF) {
2307		switch (c) {
2308		case 'v':
2309			verbose++;
2310			break;
2311		case 'p':
2312			properties++;
2313			break;
2314		case 'P':
2315			if (protocol != NULL) {
2316				(void) printf(gettext(
2317				    "Specifying multiple protocols "
2318				    "not supported: %s\n"),
2319				    protocol);
2320				return (SA_SYNTAX_ERR);
2321			}
2322			protocol = optarg;
2323			if (!sa_valid_protocol(protocol)) {
2324				(void) printf(gettext(
2325				    "Invalid protocol specified: %s\n"),
2326				    protocol);
2327				return (SA_INVALID_PROTOCOL);
2328			}
2329			break;
2330		case 'x':
2331			xml++;
2332			break;
2333		case 'h':
2334			/* optopt on valid arg isn't defined */
2335			optopt = c;
2336			/*FALLTHROUGH*/
2337		case '?':
2338		default:
2339			/*
2340			 * Since a bad option gets to here, sort it
2341			 * out and return a syntax error return value
2342			 * if necessary.
2343			 */
2344			switch (optopt) {
2345			default:
2346				ret = SA_SYNTAX_ERR;
2347				break;
2348			case 'h':
2349			case '?':
2350				break;
2351			}
2352			(void) printf(gettext("usage: %s\n"),
2353			    sa_get_usage(USAGE_SHOW));
2354			return (ret);
2355		}
2356	}
2357
2358	if (xml) {
2359		doc = show_group_xml_init();
2360		if (doc == NULL)
2361			ret = SA_NO_MEMORY;
2362	}
2363
2364	if (optind == argc) {
2365		/* No group specified so go through them all */
2366		for (group = sa_get_group(handle, NULL);
2367		    group != NULL;
2368		    group = sa_get_next_group(group)) {
2369			/*
2370			 * Have a group so check if one we want and then list
2371			 * contents with appropriate options.
2372			 */
2373			if (xml)
2374				show_group_xml(doc, group);
2375			else
2376				show_group(group, verbose, properties, protocol,
2377				    NULL);
2378		}
2379	} else {
2380		/* Have a specified list of groups */
2381		for (; optind < argc; optind++) {
2382			group = sa_get_group(handle, argv[optind]);
2383			if (group != NULL) {
2384				if (xml)
2385					show_group_xml(doc, group);
2386				else
2387					show_group(group, verbose, properties,
2388					    protocol, NULL);
2389			} else {
2390				(void) printf(gettext("%s: not found\n"),
2391				    argv[optind]);
2392				ret = SA_NO_SUCH_GROUP;
2393			}
2394		}
2395	}
2396	if (xml && ret == SA_OK) {
2397		(void) xmlDocFormatDump(stdout, doc, 1);
2398		xmlFreeDoc(doc);
2399	}
2400	return (ret);
2401
2402}
2403
2404/*
2405 * enable_share(group, share, update_legacy)
2406 *
2407 * helper function to enable a share if the group is enabled.
2408 */
2409
2410static int
2411enable_share(sa_handle_t handle, sa_group_t group, sa_share_t share,
2412    int update_legacy)
2413{
2414	char *value;
2415	int enabled;
2416	sa_optionset_t optionset;
2417	int err;
2418	int ret = SA_OK;
2419	char *zfs = NULL;
2420	int iszfs = 0;
2421	int isshare;
2422
2423	/*
2424	 * need to enable this share if the group is enabled but not
2425	 * otherwise. The enable is also done on each protocol
2426	 * represented in the group.
2427	 */
2428	value = sa_get_group_attr(group, "state");
2429	enabled = value != NULL && strcmp(value, "enabled") == 0;
2430	if (value != NULL)
2431		sa_free_attr_string(value);
2432	/* remove legacy config if necessary */
2433	if (update_legacy)
2434		ret = sa_delete_legacy(share, NULL);
2435	zfs = sa_get_group_attr(group, "zfs");
2436	if (zfs != NULL) {
2437		iszfs++;
2438		sa_free_attr_string(zfs);
2439	}
2440
2441	/*
2442	 * Step through each optionset at the group level and
2443	 * enable the share based on the protocol type. This
2444	 * works because protocols must be set on the group
2445	 * for the protocol to be enabled.
2446	 */
2447	isshare = sa_is_share(share);
2448	for (optionset = sa_get_optionset(group, NULL);
2449	    optionset != NULL && ret == SA_OK;
2450	    optionset = sa_get_next_optionset(optionset)) {
2451		value = sa_get_optionset_attr(optionset, "type");
2452		if (value != NULL) {
2453			if (enabled) {
2454				if (isshare) {
2455					err = sa_enable_share(share, value);
2456				} else {
2457					err = sa_enable_resource(share, value);
2458					if (err == SA_NOT_SUPPORTED) {
2459						sa_share_t parent;
2460						parent = sa_get_resource_parent(
2461						    share);
2462						if (parent != NULL)
2463							err = sa_enable_share(
2464							    parent, value);
2465					}
2466				}
2467				if (err != SA_OK) {
2468					ret = err;
2469					(void) printf(gettext(
2470					    "Failed to enable share for "
2471					    "\"%s\": %s\n"),
2472					    value, sa_errorstr(ret));
2473				}
2474			}
2475			/*
2476			 * If we want to update the legacy, use a copy of
2477			 * share so we can avoid breaking the loop we are in
2478			 * since we might also need to go up the tree to the
2479			 * parent.
2480			 */
2481			if (update_legacy && !iszfs) {
2482				sa_share_t update = share;
2483				if (!sa_is_share(share)) {
2484					update = sa_get_resource_parent(share);
2485				}
2486				(void) sa_update_legacy(update, value);
2487			}
2488			sa_free_attr_string(value);
2489		}
2490	}
2491	if (ret == SA_OK)
2492		(void) sa_update_config(handle);
2493	return (ret);
2494}
2495
2496/*
2497 * sa_require_resource(group)
2498 *
2499 * if any of the defined protocols on the group require resource
2500 * names, then all shares must have them.
2501 */
2502
2503static int
2504sa_require_resource(sa_group_t group)
2505{
2506	sa_optionset_t optionset;
2507
2508	for (optionset = sa_get_optionset(group, NULL);
2509	    optionset != NULL;
2510	    optionset = sa_get_next_optionset(optionset)) {
2511		char *proto;
2512
2513		proto = sa_get_optionset_attr(optionset, "type");
2514		if (proto != NULL) {
2515			uint64_t features;
2516
2517			features = sa_proto_get_featureset(proto);
2518			if (features & SA_FEATURE_RESOURCE) {
2519				sa_free_attr_string(proto);
2520				return (B_TRUE);
2521			}
2522			sa_free_attr_string(proto);
2523		}
2524	}
2525	return (B_FALSE);
2526}
2527
2528/*
2529 * sa_addshare(flags, argc, argv)
2530 *
2531 * implements add-share subcommand.
2532 */
2533
2534static int
2535sa_addshare(sa_handle_t handle, int flags, int argc, char *argv[])
2536{
2537	int verbose = 0;
2538	int dryrun = 0;
2539	int c;
2540	int ret = SA_OK;
2541	sa_group_t group;
2542	sa_share_t share;
2543	sa_resource_t resource = NULL;
2544	char *sharepath = NULL;
2545	char *description = NULL;
2546	char *rsrcname = NULL;
2547	char *rsrc = NULL;
2548	int persist = SA_SHARE_PERMANENT; /* default to persist */
2549	int auth;
2550	char dir[MAXPATHLEN];
2551
2552	while ((c = getopt(argc, argv, "?hvns:d:r:t")) != EOF) {
2553		switch (c) {
2554		case 'n':
2555			dryrun++;
2556			break;
2557		case 'v':
2558			verbose++;
2559			break;
2560		case 'd':
2561			description = optarg;
2562			break;
2563		case 'r':
2564			if (rsrcname != NULL) {
2565				(void) printf(gettext("Adding multiple "
2566				    "resource names not"
2567				    " supported\n"));
2568				return (SA_SYNTAX_ERR);
2569			}
2570			rsrcname = optarg;
2571			break;
2572		case 's':
2573			/*
2574			 * Save share path into group. Currently limit
2575			 * to one share per command.
2576			 */
2577			if (sharepath != NULL) {
2578				(void) printf(gettext(
2579				    "Adding multiple shares not supported\n"));
2580				return (SA_SYNTAX_ERR);
2581			}
2582			sharepath = optarg;
2583			break;
2584		case 't':
2585			persist = SA_SHARE_TRANSIENT;
2586			break;
2587		case 'h':
2588			/* optopt on valid arg isn't defined */
2589			optopt = c;
2590			/*FALLTHROUGH*/
2591		case '?':
2592		default:
2593			/*
2594			 * Since a bad option gets to here, sort it
2595			 * out and return a syntax error return value
2596			 * if necessary.
2597			 */
2598			switch (optopt) {
2599			default:
2600				ret = SA_SYNTAX_ERR;
2601				break;
2602			case 'h':
2603			case '?':
2604				break;
2605			}
2606			(void) printf(gettext("usage: %s\n"),
2607			    sa_get_usage(USAGE_ADD_SHARE));
2608			return (ret);
2609		}
2610	}
2611
2612	if (optind >= argc) {
2613		(void) printf(gettext("usage: %s\n"),
2614		    sa_get_usage(USAGE_ADD_SHARE));
2615		if (dryrun || sharepath != NULL || description != NULL ||
2616		    rsrcname != NULL || verbose || persist) {
2617			(void) printf(gettext("\tgroup must be specified\n"));
2618			ret = SA_NO_SUCH_GROUP;
2619		} else {
2620			ret = SA_OK;
2621		}
2622	} else {
2623		if (sharepath == NULL) {
2624			(void) printf(gettext("usage: %s\n"),
2625			    sa_get_usage(USAGE_ADD_SHARE));
2626			(void) printf(gettext(
2627			    "\t-s sharepath must be specified\n"));
2628			ret = SA_BAD_PATH;
2629		}
2630		if (ret == SA_OK) {
2631			if (realpath(sharepath, dir) == NULL) {
2632				ret = SA_BAD_PATH;
2633				(void) printf(gettext("Path "
2634				    "is not valid: %s\n"),
2635				    sharepath);
2636			} else {
2637				sharepath = dir;
2638			}
2639		}
2640		if (ret == SA_OK && rsrcname != NULL) {
2641			/* check for valid syntax */
2642			if (validresource(rsrcname)) {
2643				rsrc = conv_to_utf8(rsrcname);
2644				resource = sa_find_resource(handle, rsrc);
2645				if (resource != NULL) {
2646					/*
2647					 * Resource names must be
2648					 * unique in the system
2649					 */
2650					ret = SA_DUPLICATE_NAME;
2651					(void) printf(gettext("usage: %s\n"),
2652					    sa_get_usage(USAGE_ADD_SHARE));
2653					(void) printf(gettext(
2654					    "\tresource names must be unique "
2655					    "in the system\n"));
2656				}
2657			} else {
2658				(void) printf(gettext("usage: %s\n"),
2659				    sa_get_usage(USAGE_ADD_SHARE));
2660				(void) printf(gettext(
2661				    "\tresource names use restricted "
2662				    "character set\n"));
2663				ret = SA_INVALID_NAME;
2664			}
2665		}
2666
2667		if (ret != SA_OK) {
2668			if (rsrc != NULL && rsrcname != rsrc)
2669				sa_free_attr_string(rsrc);
2670			return (ret);
2671		}
2672
2673		share = sa_find_share(handle, sharepath);
2674		if (share != NULL) {
2675			if (rsrcname == NULL) {
2676				/*
2677				 * Can only have a duplicate share if a new
2678				 * resource name is being added.
2679				 */
2680				ret = SA_DUPLICATE_NAME;
2681				(void) printf(gettext("Share path already "
2682				    "shared: %s\n"), sharepath);
2683			}
2684		}
2685		if (ret != SA_OK)
2686			return (ret);
2687
2688		group = sa_get_group(handle, argv[optind]);
2689		if (group != NULL) {
2690			if (sa_require_resource(group) == B_TRUE &&
2691			    rsrcname == NULL) {
2692				(void) printf(gettext(
2693				    "Resource name is required "
2694				    "by at least one enabled protocol "
2695				    "in group\n"));
2696				return (SA_RESOURCE_REQUIRED);
2697			}
2698			if (share == NULL && ret == SA_OK) {
2699				if (dryrun)
2700					ret = sa_check_path(group, sharepath,
2701					    SA_CHECK_NORMAL);
2702				else
2703					share = sa_add_share(group, sharepath,
2704					    persist, &ret);
2705			}
2706			/*
2707			 * Make sure this isn't an attempt to put a resourced
2708			 * share into a different group than it already is in.
2709			 */
2710			if (share != NULL) {
2711				sa_group_t parent;
2712				parent = sa_get_parent_group(share);
2713				if (parent != group) {
2714					ret = SA_DUPLICATE_NAME;
2715					(void) printf(gettext(
2716					    "Share path already "
2717					    "shared: %s\n"), sharepath);
2718				}
2719			}
2720			if (!dryrun && share == NULL) {
2721				(void) printf(gettext(
2722				    "Could not add share: %s\n"),
2723				    sa_errorstr(ret));
2724			} else {
2725				auth = check_authorizations(argv[optind],
2726				    flags);
2727				if (!dryrun && ret == SA_OK) {
2728					if (rsrcname != NULL) {
2729						resource = sa_add_resource(
2730						    share,
2731						    rsrc,
2732						    SA_SHARE_PERMANENT,
2733						    &ret);
2734					}
2735					if (ret == SA_OK &&
2736					    description != NULL) {
2737						if (resource != NULL)
2738							ret =
2739							    set_resource_desc(
2740							    resource,
2741							    description);
2742						else
2743							ret =
2744							    set_share_desc(
2745							    share,
2746							    description);
2747					}
2748					if (ret == SA_OK) {
2749						/* now enable the share(s) */
2750						if (resource != NULL) {
2751							ret = enable_share(
2752							    handle,
2753							    group,
2754							    resource,
2755							    1);
2756						} else {
2757							ret = enable_share(
2758							    handle,
2759							    group,
2760							    share,
2761							    1);
2762						}
2763						ret = sa_update_config(handle);
2764					}
2765					switch (ret) {
2766					case SA_DUPLICATE_NAME:
2767						(void) printf(gettext(
2768						    "Resource name in"
2769						    "use: %s\n"),
2770						    rsrcname);
2771						break;
2772					default:
2773						(void) printf(gettext(
2774						    "Could not set "
2775						    "attribute: %s\n"),
2776						    sa_errorstr(ret));
2777						break;
2778					case SA_OK:
2779						break;
2780					}
2781				} else if (dryrun && ret == SA_OK &&
2782				    !auth && verbose) {
2783					(void) printf(gettext(
2784					    "Command would fail: %s\n"),
2785					    sa_errorstr(SA_NO_PERMISSION));
2786					ret = SA_NO_PERMISSION;
2787				}
2788			}
2789		} else {
2790			switch (ret) {
2791			default:
2792				(void) printf(gettext(
2793				    "Group \"%s\" not found\n"), argv[optind]);
2794				ret = SA_NO_SUCH_GROUP;
2795				break;
2796			case SA_BAD_PATH:
2797			case SA_DUPLICATE_NAME:
2798				break;
2799			}
2800		}
2801	}
2802	return (ret);
2803}
2804
2805/*
2806 * sa_moveshare(flags, argc, argv)
2807 *
2808 * implements move-share subcommand.
2809 */
2810
2811int
2812sa_moveshare(sa_handle_t handle, int flags, int argc, char *argv[])
2813{
2814	int verbose = 0;
2815	int dryrun = 0;
2816	int c;
2817	int ret = SA_OK;
2818	sa_group_t group;
2819	sa_share_t share;
2820	char *rsrcname = NULL;
2821	char *sharepath = NULL;
2822	int authsrc = 0, authdst = 0;
2823	char dir[MAXPATHLEN];
2824
2825	while ((c = getopt(argc, argv, "?hvnr:s:")) != EOF) {
2826		switch (c) {
2827		case 'n':
2828			dryrun++;
2829			break;
2830		case 'v':
2831			verbose++;
2832			break;
2833		case 'r':
2834			if (rsrcname != NULL) {
2835				(void) printf(gettext(
2836				    "Moving multiple resource names not"
2837				    " supported\n"));
2838				return (SA_SYNTAX_ERR);
2839			}
2840			rsrcname = optarg;
2841			break;
2842		case 's':
2843			/*
2844			 * Remove share path from group. Currently limit
2845			 * to one share per command.
2846			 */
2847			if (sharepath != NULL) {
2848				(void) printf(gettext("Moving multiple shares"
2849				    " not supported\n"));
2850				return (SA_SYNTAX_ERR);
2851			}
2852			sharepath = optarg;
2853			break;
2854		case 'h':
2855			/* optopt on valid arg isn't defined */
2856			optopt = c;
2857			/*FALLTHROUGH*/
2858		case '?':
2859		default:
2860			/*
2861			 * Since a bad option gets to here, sort it
2862			 * out and return a syntax error return value
2863			 * if necessary.
2864			 */
2865			switch (optopt) {
2866			default:
2867				ret = SA_SYNTAX_ERR;
2868				break;
2869			case 'h':
2870			case '?':
2871				break;
2872			}
2873			(void) printf(gettext("usage: %s\n"),
2874			    sa_get_usage(USAGE_MOVE_SHARE));
2875			return (ret);
2876		}
2877	}
2878
2879	if (optind >= argc || sharepath == NULL) {
2880		(void) printf(gettext("usage: %s\n"),
2881		    sa_get_usage(USAGE_MOVE_SHARE));
2882		if (dryrun || verbose || sharepath != NULL) {
2883			(void) printf(gettext("\tgroup must be specified\n"));
2884			ret = SA_NO_SUCH_GROUP;
2885		} else {
2886			if (sharepath == NULL) {
2887				ret = SA_SYNTAX_ERR;
2888				(void) printf(gettext(
2889				    "\tsharepath must be specified\n"));
2890			} else {
2891				ret = SA_OK;
2892			}
2893		}
2894	} else {
2895		sa_group_t parent;
2896		char *zfsold;
2897		char *zfsnew;
2898
2899		if (sharepath == NULL) {
2900			(void) printf(gettext(
2901			    "sharepath must be specified with the -s "
2902			    "option\n"));
2903			return (SA_BAD_PATH);
2904		}
2905		group = sa_get_group(handle, argv[optind]);
2906		if (group == NULL) {
2907			(void) printf(gettext("Group \"%s\" not found\n"),
2908			    argv[optind]);
2909			return (SA_NO_SUCH_GROUP);
2910		}
2911		share = sa_find_share(handle, sharepath);
2912		/*
2913		 * If a share wasn't found, it may have been a symlink
2914		 * or has a trailing '/'. Try again after resolving
2915		 * with realpath().
2916		 */
2917		if (share == NULL) {
2918			if (realpath(sharepath, dir) == NULL) {
2919				(void) printf(gettext("Path "
2920				    "is not valid: %s\n"),
2921				    sharepath);
2922				return (SA_BAD_PATH);
2923			}
2924			sharepath = dir;
2925			share = sa_find_share(handle, sharepath);
2926		}
2927		if (share == NULL) {
2928			(void) printf(gettext("Share not found: %s\n"),
2929			    sharepath);
2930			return (SA_NO_SUCH_PATH);
2931		}
2932		authdst = check_authorizations(argv[optind], flags);
2933
2934		parent = sa_get_parent_group(share);
2935		if (parent != NULL) {
2936			char *pname;
2937			pname = sa_get_group_attr(parent, "name");
2938			if (pname != NULL) {
2939				authsrc = check_authorizations(pname, flags);
2940				sa_free_attr_string(pname);
2941			}
2942			zfsold = sa_get_group_attr(parent, "zfs");
2943			zfsnew = sa_get_group_attr(group, "zfs");
2944			if ((zfsold != NULL && zfsnew == NULL) ||
2945			    (zfsold == NULL && zfsnew != NULL)) {
2946				ret = SA_NOT_ALLOWED;
2947			}
2948			if (zfsold != NULL)
2949				sa_free_attr_string(zfsold);
2950			if (zfsnew != NULL)
2951				sa_free_attr_string(zfsnew);
2952		}
2953
2954		if (ret == SA_OK && parent != group && !dryrun) {
2955			char *oldstate;
2956			/*
2957			 * Note that the share may need to be
2958			 * "unshared" if the new group is disabled and
2959			 * the old was enabled or it may need to be
2960			 * share to update if the new group is
2961			 * enabled. We disable before the move and
2962			 * will have to enable after the move in order
2963			 * to cleanup entries for protocols that
2964			 * aren't in the new group.
2965			 */
2966			oldstate = sa_get_group_attr(parent, "state");
2967			if (oldstate != NULL) {
2968				/* enable_share determines what to do */
2969				if (strcmp(oldstate, "enabled") == 0)
2970					(void) sa_disable_share(share, NULL);
2971				sa_free_attr_string(oldstate);
2972			}
2973		}
2974
2975		if (!dryrun && ret == SA_OK)
2976			ret = sa_move_share(group, share);
2977
2978		/*
2979		 * Reenable and update any config information.
2980		 */
2981		if (ret == SA_OK && parent != group && !dryrun) {
2982			ret = sa_update_config(handle);
2983
2984			(void) enable_share(handle, group, share, 1);
2985		}
2986
2987		if (ret != SA_OK)
2988			(void) printf(gettext("Could not move share: %s\n"),
2989			    sa_errorstr(ret));
2990
2991		if (dryrun && ret == SA_OK && !(authsrc & authdst) &&
2992		    verbose) {
2993			(void) printf(gettext("Command would fail: %s\n"),
2994			    sa_errorstr(SA_NO_PERMISSION));
2995		}
2996	}
2997	return (ret);
2998}
2999
3000/*
3001 * sa_removeshare(flags, argc, argv)
3002 *
3003 * implements remove-share subcommand.
3004 */
3005
3006int
3007sa_removeshare(sa_handle_t handle, int flags, int argc, char *argv[])
3008{
3009	int verbose = 0;
3010	int dryrun = 0;
3011	int force = 0;
3012	int c;
3013	int ret = SA_OK;
3014	sa_group_t group;
3015	sa_resource_t resource = NULL;
3016	sa_share_t share = NULL;
3017	char *rsrcname = NULL;
3018	char *sharepath = NULL;
3019	char dir[MAXPATHLEN];
3020	int auth;
3021
3022	while ((c = getopt(argc, argv, "?hfnr:s:v")) != EOF) {
3023		switch (c) {
3024		case 'n':
3025			dryrun++;
3026			break;
3027		case 'v':
3028			verbose++;
3029			break;
3030		case 'f':
3031			force++;
3032			break;
3033		case 's':
3034			/*
3035			 * Remove share path from group. Currently limit
3036			 * to one share per command.
3037			 */
3038			if (sharepath != NULL) {
3039				(void) printf(gettext(
3040				    "Removing multiple shares not "
3041				    "supported\n"));
3042				return (SA_SYNTAX_ERR);
3043			}
3044			sharepath = optarg;
3045			break;
3046		case 'r':
3047			/*
3048			 * Remove share from group if last resource or remove
3049			 * resource from share if multiple resources.
3050			 */
3051			if (rsrcname != NULL) {
3052				(void) printf(gettext(
3053				    "Removing multiple resource names not "
3054				    "supported\n"));
3055				return (SA_SYNTAX_ERR);
3056			}
3057			rsrcname = optarg;
3058			break;
3059		case 'h':
3060			/* optopt on valid arg isn't defined */
3061			optopt = c;
3062			/*FALLTHROUGH*/
3063		case '?':
3064		default:
3065			/*
3066			 * Since a bad option gets to here, sort it
3067			 * out and return a syntax error return value
3068			 * if necessary.
3069			 */
3070			switch (optopt) {
3071			default:
3072				ret = SA_SYNTAX_ERR;
3073				break;
3074			case 'h':
3075			case '?':
3076				break;
3077			}
3078			(void) printf(gettext("usage: %s\n"),
3079			    sa_get_usage(USAGE_REMOVE_SHARE));
3080			return (ret);
3081		}
3082	}
3083
3084	if (optind >= argc || (rsrcname == NULL && sharepath == NULL)) {
3085		if (sharepath == NULL && rsrcname == NULL) {
3086			(void) printf(gettext("usage: %s\n"),
3087			    sa_get_usage(USAGE_REMOVE_SHARE));
3088			(void) printf(gettext("\t-s sharepath or -r resource"
3089			    " must be specified\n"));
3090			ret = SA_BAD_PATH;
3091		} else {
3092			ret = SA_OK;
3093		}
3094	}
3095	if (ret != SA_OK) {
3096		return (ret);
3097	}
3098
3099	if (optind < argc) {
3100		if ((optind + 1) < argc) {
3101			(void) printf(gettext("Extraneous group(s) at end of "
3102			    "command\n"));
3103			ret = SA_SYNTAX_ERR;
3104		} else {
3105			group = sa_get_group(handle, argv[optind]);
3106			if (group == NULL) {
3107				(void) printf(gettext(
3108				    "Group \"%s\" not found\n"), argv[optind]);
3109				ret = SA_NO_SUCH_GROUP;
3110			}
3111		}
3112	} else {
3113		group = NULL;
3114	}
3115
3116	if (rsrcname != NULL) {
3117		resource = sa_find_resource(handle, rsrcname);
3118		if (resource == NULL) {
3119			ret = SA_NO_SUCH_RESOURCE;
3120			(void) printf(gettext(
3121			    "Resource name not found for share: %s\n"),
3122			    rsrcname);
3123		}
3124	}
3125
3126	/*
3127	 * Lookup the path in the internal configuration. Care
3128	 * must be taken to handle the case where the
3129	 * underlying path has been removed since we need to
3130	 * be able to deal with that as well.
3131	 */
3132	if (ret == SA_OK) {
3133		if (sharepath != NULL) {
3134			if (group != NULL)
3135				share = sa_get_share(group, sharepath);
3136			else
3137				share = sa_find_share(handle, sharepath);
3138		}
3139
3140		if (resource != NULL) {
3141			sa_share_t rsrcshare;
3142			rsrcshare = sa_get_resource_parent(resource);
3143			if (share == NULL)
3144				share = rsrcshare;
3145			else if (share != rsrcshare) {
3146				ret = SA_NO_SUCH_RESOURCE;
3147				(void) printf(gettext(
3148				    "Bad resource name for share: %s\n"),
3149				    rsrcname);
3150				share = NULL;
3151			}
3152		}
3153
3154		/*
3155		 * If we didn't find the share with the provided path,
3156		 * it may be a symlink so attempt to resolve it using
3157		 * realpath and try again. Realpath will resolve any
3158		 * symlinks and place them in "dir". Note that
3159		 * sharepath is only used for the lookup the first
3160		 * time and later for error messages. dir will be used
3161		 * on the second attempt. Once a share is found, all
3162		 * operations are based off of the share variable.
3163		 */
3164		if (share == NULL) {
3165			if (realpath(sharepath, dir) == NULL) {
3166				ret = SA_BAD_PATH;
3167				(void) printf(gettext(
3168				    "Path is not valid: %s\n"), sharepath);
3169			} else {
3170				if (group != NULL)
3171					share = sa_get_share(group, dir);
3172				else
3173					share = sa_find_share(handle, dir);
3174			}
3175		}
3176	}
3177
3178	/*
3179	 * If there hasn't been an error, there was likely a
3180	 * path found. If not, give the appropriate error
3181	 * message and set the return error. If it was found,
3182	 * then disable the share and then remove it from the
3183	 * configuration.
3184	 */
3185	if (ret != SA_OK) {
3186		return (ret);
3187	}
3188	if (share == NULL) {
3189		if (group != NULL)
3190			(void) printf(gettext("Share not found in group %s:"
3191			    " %s\n"), argv[optind], sharepath);
3192		else
3193			(void) printf(gettext("Share not found: %s\n"),
3194			    sharepath);
3195		ret = SA_NO_SUCH_PATH;
3196	} else {
3197		if (group == NULL)
3198			group = sa_get_parent_group(share);
3199		if (!dryrun) {
3200			if (ret == SA_OK) {
3201				if (resource != NULL)
3202					ret = sa_disable_resource(resource,
3203					    NULL);
3204				else
3205					ret = sa_disable_share(share, NULL);
3206				/*
3207				 * We don't care if it fails since it
3208				 * could be disabled already. Some
3209				 * unexpected errors could occur that
3210				 * prevent removal, so also check for
3211				 * force being set.
3212				 */
3213				if ((ret == SA_OK || ret == SA_NO_SUCH_PATH ||
3214				    ret == SA_NOT_SUPPORTED ||
3215				    ret == SA_SYSTEM_ERR || force) &&
3216				    resource == NULL)
3217					ret = sa_remove_share(share);
3218
3219				if ((ret == SA_OK || ret == SA_NO_SUCH_PATH ||
3220				    ret == SA_NOT_SUPPORTED ||
3221				    ret == SA_SYSTEM_ERR || force) &&
3222				    resource != NULL) {
3223					ret = sa_remove_resource(resource);
3224					if (ret == SA_OK) {
3225						/*
3226						 * If this was the
3227						 * last one, remove
3228						 * the share as well.
3229						 */
3230						resource =
3231						    sa_get_share_resource(
3232						    share, NULL);
3233						if (resource == NULL)
3234							ret = sa_remove_share(
3235							    share);
3236					}
3237				}
3238				if (ret == SA_OK)
3239					ret = sa_update_config(handle);
3240			}
3241			if (ret != SA_OK)
3242				(void) printf(gettext("Could not remove share:"
3243				    " %s\n"), sa_errorstr(ret));
3244		} else if (ret == SA_OK) {
3245			char *pname;
3246			pname = sa_get_group_attr(group, "name");
3247			if (pname != NULL) {
3248				auth = check_authorizations(pname, flags);
3249				sa_free_attr_string(pname);
3250			}
3251			if (!auth && verbose) {
3252				(void) printf(gettext(
3253				    "Command would fail: %s\n"),
3254				    sa_errorstr(SA_NO_PERMISSION));
3255			}
3256		}
3257	}
3258	return (ret);
3259}
3260
3261/*
3262 * sa_set_share(flags, argc, argv)
3263 *
3264 * implements set-share subcommand.
3265 */
3266
3267int
3268sa_set_share(sa_handle_t handle, int flags, int argc, char *argv[])
3269{
3270	int dryrun = 0;
3271	int c;
3272	int ret = SA_OK;
3273	sa_group_t group, sharegroup;
3274	sa_share_t share = NULL;
3275	sa_resource_t resource = NULL;
3276	char *sharepath = NULL;
3277	char *description = NULL;
3278	char *rsrcname = NULL;
3279	char *rsrc = NULL;
3280	char *newname = NULL;
3281	char *newrsrc;
3282	char *groupname = NULL;
3283	int auth;
3284	int verbose = 0;
3285
3286	while ((c = getopt(argc, argv, "?hnd:r:s:")) != EOF) {
3287		switch (c) {
3288		case 'n':
3289			dryrun++;
3290			break;
3291		case 'd':
3292			description = optarg;
3293			break;
3294		case 'v':
3295			verbose++;
3296			break;
3297		case 'r':
3298			/*
3299			 * Update share by resource name
3300			 */
3301			if (rsrcname != NULL) {
3302				(void) printf(gettext(
3303				    "Updating multiple resource names not "
3304				    "supported\n"));
3305				return (SA_SYNTAX_ERR);
3306			}
3307			rsrcname = optarg;
3308			break;
3309		case 's':
3310			/*
3311			 * Save share path into group. Currently limit
3312			 * to one share per command.
3313			 */
3314			if (sharepath != NULL) {
3315				(void) printf(gettext(
3316				    "Updating multiple shares not "
3317				    "supported\n"));
3318				return (SA_SYNTAX_ERR);
3319			}
3320			sharepath = optarg;
3321			break;
3322		case 'h':
3323			/* optopt on valid arg isn't defined */
3324			optopt = c;
3325			/*FALLTHROUGH*/
3326		case '?':
3327		default:
3328			/*
3329			 * Since a bad option gets to here, sort it
3330			 * out and return a syntax error return value
3331			 * if necessary.
3332			 */
3333			switch (optopt) {
3334			default:
3335				ret = SA_SYNTAX_ERR;
3336				break;
3337			case 'h':
3338			case '?':
3339				break;
3340			}
3341			(void) printf(gettext("usage: %s\n"),
3342			    sa_get_usage(USAGE_SET_SHARE));
3343			return (ret);
3344		}
3345	}
3346
3347	if (optind >= argc && sharepath == NULL && rsrcname == NULL) {
3348		if (sharepath == NULL) {
3349			(void) printf(gettext("usage: %s\n"),
3350			    sa_get_usage(USAGE_SET_SHARE));
3351			(void) printf(gettext("\tgroup must be specified\n"));
3352			ret = SA_BAD_PATH;
3353		} else {
3354			ret = SA_OK;
3355		}
3356	}
3357	if ((optind + 1) < argc) {
3358		(void) printf(gettext("usage: %s\n"),
3359		    sa_get_usage(USAGE_SET_SHARE));
3360		(void) printf(gettext("\tExtraneous group(s) at end\n"));
3361		ret = SA_SYNTAX_ERR;
3362	}
3363
3364	/*
3365	 * Must have at least one of sharepath and rsrcrname.
3366	 * It is a syntax error to be missing both.
3367	 */
3368	if (sharepath == NULL && rsrcname == NULL) {
3369		(void) printf(gettext("usage: %s\n"),
3370		    sa_get_usage(USAGE_SET_SHARE));
3371		ret = SA_SYNTAX_ERR;
3372	}
3373
3374	if (ret != SA_OK)
3375		return (ret);
3376
3377	if (optind < argc) {
3378		groupname = argv[optind];
3379		group = sa_get_group(handle, groupname);
3380	} else {
3381		group = NULL;
3382		groupname = NULL;
3383	}
3384	if (rsrcname != NULL) {
3385		/*
3386		 * If rsrcname exists, split rename syntax and then
3387		 * convert to utf 8 if no errors.
3388		 */
3389		newname = strchr(rsrcname, '=');
3390		if (newname != NULL) {
3391			*newname++ = '\0';
3392		}
3393		if (!validresource(rsrcname)) {
3394			ret = SA_INVALID_NAME;
3395			(void) printf(gettext("Invalid resource name: "
3396			    "\"%s\"\n"), rsrcname);
3397		} else {
3398			rsrc = conv_to_utf8(rsrcname);
3399		}
3400		if (newname != NULL) {
3401			if (!validresource(newname)) {
3402				ret = SA_INVALID_NAME;
3403				(void) printf(gettext("Invalid resource name: "
3404				    "%s\n"), newname);
3405				newname = NULL;
3406			} else {
3407				newrsrc = conv_to_utf8(newname);
3408			}
3409		}
3410	}
3411
3412	if (ret != SA_OK) {
3413		if (rsrcname != NULL && rsrcname != rsrc)
3414			sa_free_attr_string(rsrc);
3415		if (newname != NULL && newname != newrsrc)
3416			sa_free_attr_string(newrsrc);
3417		return (ret);
3418	}
3419
3420	if (sharepath != NULL) {
3421		share = sa_find_share(handle, sharepath);
3422	} else if (rsrcname != NULL) {
3423		resource = sa_find_resource(handle, rsrc);
3424		if (resource != NULL)
3425			share = sa_get_resource_parent(resource);
3426		else
3427			ret = SA_NO_SUCH_RESOURCE;
3428	}
3429	if (share != NULL) {
3430		sharegroup = sa_get_parent_group(share);
3431		if (group != NULL && group != sharegroup) {
3432			(void) printf(gettext("Group \"%s\" does not contain "
3433			    "share %s\n"),
3434			    argv[optind], sharepath);
3435			ret = SA_BAD_PATH;
3436		} else {
3437			int delgroupname = 0;
3438			if (groupname == NULL) {
3439				groupname = sa_get_group_attr(sharegroup,
3440				    "name");
3441				delgroupname = 1;
3442			}
3443			if (groupname != NULL) {
3444				auth = check_authorizations(groupname, flags);
3445				if (delgroupname) {
3446					sa_free_attr_string(groupname);
3447					groupname = NULL;
3448				}
3449			} else {
3450				ret = SA_NO_MEMORY;
3451			}
3452			if (rsrcname != NULL) {
3453				resource = sa_find_resource(handle, rsrc);
3454				if (!dryrun) {
3455					if (newname != NULL &&
3456					    resource != NULL)
3457						ret = sa_rename_resource(
3458						    resource, newrsrc);
3459					else if (newname != NULL)
3460						ret = SA_NO_SUCH_RESOURCE;
3461					if (newname != NULL &&
3462					    newname != newrsrc)
3463						sa_free_attr_string(newrsrc);
3464				}
3465				if (rsrc != rsrcname)
3466					sa_free_attr_string(rsrc);
3467			}
3468
3469			/*
3470			 * If the user has set a description, it will be
3471			 * on the resource if -r was used otherwise it
3472			 * must be on the share.
3473			 */
3474			if (!dryrun && ret == SA_OK && description != NULL) {
3475				char *desc;
3476				desc = conv_to_utf8(description);
3477				if (resource != NULL)
3478					ret = sa_set_resource_description(
3479					    resource, desc);
3480				else
3481					ret = sa_set_share_description(share,
3482					    desc);
3483				if (desc != description)
3484					sa_free_share_description(desc);
3485			}
3486		}
3487		if (!dryrun && ret == SA_OK) {
3488			if (resource != NULL)
3489				(void) sa_enable_resource(resource, NULL);
3490			ret = sa_update_config(handle);
3491		}
3492		switch (ret) {
3493		case SA_DUPLICATE_NAME:
3494			(void) printf(gettext("Resource name in use: %s\n"),
3495			    rsrcname);
3496			break;
3497		default:
3498			(void) printf(gettext("Could not set: %s\n"),
3499			    sa_errorstr(ret));
3500			break;
3501		case SA_OK:
3502			if (dryrun && !auth && verbose) {
3503				(void) printf(gettext(
3504				    "Command would fail: %s\n"),
3505				    sa_errorstr(SA_NO_PERMISSION));
3506			}
3507			break;
3508		}
3509	} else {
3510		switch (ret) {
3511		case SA_NO_SUCH_RESOURCE:
3512			(void) printf(gettext("Resource \"%s\" not found\n"),
3513			    rsrcname);
3514			break;
3515		default:
3516			if (sharepath != NULL) {
3517				(void) printf(
3518				    gettext("Share path \"%s\" not found\n"),
3519				    sharepath);
3520				ret = SA_NO_SUCH_PATH;
3521			} else {
3522				(void) printf(gettext("Set failed: %s\n"),
3523				    sa_errorstr(ret));
3524			}
3525		}
3526	}
3527
3528	return (ret);
3529}
3530
3531/*
3532 * add_security(group, sectype, optlist, proto, *err)
3533 *
3534 * Helper function to add a security option (named optionset) to the
3535 * group.
3536 */
3537
3538static int
3539add_security(sa_group_t group, char *sectype,
3540    struct options *optlist, char *proto, int *err)
3541{
3542	sa_security_t security;
3543	int ret = SA_OK;
3544	int result = 0;
3545	sa_handle_t handle;
3546
3547	sectype = sa_proto_space_alias(proto, sectype);
3548	security = sa_get_security(group, sectype, proto);
3549	if (security == NULL)
3550		security = sa_create_security(group, sectype, proto);
3551
3552	if (sectype != NULL)
3553		sa_free_attr_string(sectype);
3554
3555	if (security == NULL)
3556		goto done;
3557
3558	handle = sa_find_group_handle(group);
3559	if (handle == NULL) {
3560		ret = SA_CONFIG_ERR;
3561		goto done;
3562	}
3563	while (optlist != NULL) {
3564		sa_property_t prop;
3565		prop = sa_get_property(security, optlist->optname);
3566		if (prop == NULL) {
3567			/*
3568			 * Add the property, but only if it is
3569			 * a non-NULL or non-zero length value
3570			 */
3571			if (optlist->optvalue != NULL) {
3572				prop = sa_create_property(optlist->optname,
3573				    optlist->optvalue);
3574				if (prop != NULL) {
3575					ret = sa_valid_property(handle,
3576					    security, proto, prop);
3577					if (ret != SA_OK) {
3578						(void) sa_remove_property(prop);
3579						(void) printf(gettext(
3580						    "Could not add "
3581						    "property %s: %s\n"),
3582						    optlist->optname,
3583						    sa_errorstr(ret));
3584					}
3585					if (ret == SA_OK) {
3586						ret = sa_add_property(security,
3587						    prop);
3588						if (ret != SA_OK) {
3589							(void) printf(gettext(
3590							    "Could not add "
3591							    "property (%s=%s):"
3592							    " %s\n"),
3593							    optlist->optname,
3594							    optlist->optvalue,
3595							    sa_errorstr(ret));
3596						} else {
3597							result = 1;
3598						}
3599					}
3600				}
3601			}
3602		} else {
3603			ret = sa_update_property(prop, optlist->optvalue);
3604			result = 1; /* should check if really changed */
3605		}
3606		optlist = optlist->next;
3607	}
3608	/*
3609	 * When done, properties may have all been removed but
3610	 * we need to keep the security type itself until
3611	 * explicitly removed.
3612	 */
3613	if (result)
3614		ret = sa_commit_properties(security, 0);
3615done:
3616	*err = ret;
3617	return (result);
3618}
3619
3620/*
3621 * zfscheck(group, share)
3622 *
3623 * For the special case where a share was provided, make sure it is a
3624 * compatible path for a ZFS property change.  The only path
3625 * acceptable is the path that defines the zfs sub-group (dataset with
3626 * the sharenfs property set) and not one of the paths that inherited
3627 * the NFS properties. Returns SA_OK if it is usable and
3628 * SA_NOT_ALLOWED if it isn't.
3629 *
3630 * If group is not a ZFS group/subgroup, we assume OK since the check
3631 * on return will catch errors for those cases.  What we are looking
3632 * for here is that the group is ZFS and the share is not the defining
3633 * share.  All else is SA_OK.
3634 */
3635
3636static int
3637zfscheck(sa_group_t group, sa_share_t share)
3638{
3639	int ret = SA_OK;
3640	char *attr;
3641
3642	if (sa_group_is_zfs(group)) {
3643		/*
3644		 * The group is a ZFS group.  Does the share represent
3645		 * the dataset that defined the group? It is only OK
3646		 * if the attribute "subgroup" exists on the share and
3647		 * has a value of "true".
3648		 */
3649
3650		ret = SA_NOT_ALLOWED;
3651		attr = sa_get_share_attr(share, "subgroup");
3652		if (attr != NULL) {
3653			if (strcmp(attr, "true") == 0)
3654				ret = SA_OK;
3655			sa_free_attr_string(attr);
3656		}
3657	}
3658	return (ret);
3659}
3660
3661/*
3662 * basic_set(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
3663 *
3664 * This function implements "set" when a name space (-S) is not
3665 * specified. It is a basic set. Options and other CLI parsing has
3666 * already been done.
3667 *
3668 * "rsrcname" is a "resource name". If it is non-NULL, it must match
3669 * the sharepath if present or group if present, otherwise it is used
3670 * to set options.
3671 *
3672 * Resource names may take options if the protocol supports it. If the
3673 * protocol doesn't support resource level options, rsrcname is just
3674 * an alias for the share.
3675 */
3676
3677static int
3678basic_set(sa_handle_t handle, char *groupname, struct options *optlist,
3679    char *protocol, char *sharepath, char *rsrcname, int dryrun)
3680{
3681	sa_group_t group;
3682	int ret = SA_OK;
3683	int change = 0;
3684	struct list *worklist = NULL;
3685
3686	group = sa_get_group(handle, groupname);
3687	if (group != NULL) {
3688		sa_share_t share = NULL;
3689		sa_resource_t resource = NULL;
3690
3691		/*
3692		 * If there is a sharepath, make sure it belongs to
3693		 * the group.
3694		 */
3695		if (sharepath != NULL) {
3696			share = sa_get_share(group, sharepath);
3697			if (share == NULL) {
3698				(void) printf(gettext(
3699				    "Share does not exist in group %s\n"),
3700				    groupname, sharepath);
3701				ret = SA_NO_SUCH_PATH;
3702			} else {
3703				/* if ZFS and OK, then only group */
3704				ret = zfscheck(group, share);
3705				if (ret == SA_OK &&
3706				    sa_group_is_zfs(group))
3707					share = NULL;
3708				if (ret == SA_NOT_ALLOWED)
3709					(void) printf(gettext(
3710					    "Properties on ZFS group shares "
3711					    "not supported: %s\n"), sharepath);
3712			}
3713		}
3714
3715		/*
3716		 * If a resource name exists, make sure it belongs to
3717		 * the share if present else it belongs to the
3718		 * group. Also check the protocol to see if it
3719		 * supports resource level properties or not. If not,
3720		 * use share only.
3721		 */
3722		if (rsrcname != NULL) {
3723			if (share != NULL) {
3724				resource = sa_get_share_resource(share,
3725				    rsrcname);
3726				if (resource == NULL)
3727					ret = SA_NO_SUCH_RESOURCE;
3728			} else {
3729				resource = sa_get_resource(group, rsrcname);
3730				if (resource != NULL)
3731					share = sa_get_resource_parent(
3732					    resource);
3733				else
3734					ret = SA_NO_SUCH_RESOURCE;
3735			}
3736			if (ret == SA_OK && resource != NULL) {
3737				uint64_t features;
3738				/*
3739				 * Check to see if the resource can take
3740				 * properties. If so, stick the resource into
3741				 * "share" so it will all just work.
3742				 */
3743				features = sa_proto_get_featureset(protocol);
3744				if (features & SA_FEATURE_RESOURCE)
3745					share = (sa_share_t)resource;
3746			}
3747		}
3748
3749		if (ret == SA_OK) {
3750			/* group must exist */
3751			ret = valid_options(handle, optlist, protocol,
3752			    share == NULL ? group : share, NULL);
3753			if (ret == SA_OK && !dryrun) {
3754				if (share != NULL)
3755					change |= add_optionset(share, optlist,
3756					    protocol, &ret);
3757				else
3758					change |= add_optionset(group, optlist,
3759					    protocol, &ret);
3760				if (ret == SA_OK && change)
3761					worklist = add_list(worklist, group,
3762					    share, protocol);
3763			}
3764		}
3765		free_opt(optlist);
3766	} else {
3767		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
3768		ret = SA_NO_SUCH_GROUP;
3769	}
3770	/*
3771	 * we have a group and potentially legal additions
3772	 */
3773
3774	/*
3775	 * Commit to configuration if not a dryrunp and properties
3776	 * have changed.
3777	 */
3778	if (!dryrun && ret == SA_OK && change && worklist != NULL)
3779		/* properties changed, so update all shares */
3780		(void) enable_all_groups(handle, worklist, 0, 0, protocol,
3781		    B_TRUE);
3782
3783	if (worklist != NULL)
3784		free_list(worklist);
3785	return (ret);
3786}
3787
3788/*
3789 * space_set(groupname, optlist, protocol, sharepath, dryrun)
3790 *
3791 * This function implements "set" when a name space (-S) is
3792 * specified. It is a namespace set. Options and other CLI parsing has
3793 * already been done.
3794 */
3795
3796static int
3797space_set(sa_handle_t handle, char *groupname, struct options *optlist,
3798    char *protocol, char *sharepath, int dryrun, char *sectype)
3799{
3800	sa_group_t group;
3801	int ret = SA_OK;
3802	int change = 0;
3803	struct list *worklist = NULL;
3804
3805	/*
3806	 * make sure protcol and sectype are valid
3807	 */
3808
3809	if (sa_proto_valid_space(protocol, sectype) == 0) {
3810		(void) printf(gettext("Option space \"%s\" not valid "
3811		    "for protocol.\n"), sectype);
3812		return (SA_INVALID_SECURITY);
3813	}
3814
3815	group = sa_get_group(handle, groupname);
3816	if (group != NULL) {
3817		sa_share_t share = NULL;
3818		if (sharepath != NULL) {
3819			share = sa_get_share(group, sharepath);
3820			if (share == NULL) {
3821				(void) printf(gettext(
3822				    "Share does not exist in group %s\n"),
3823				    groupname, sharepath);
3824				ret = SA_NO_SUCH_PATH;
3825			} else {
3826				/* if ZFS and OK, then only group */
3827				ret = zfscheck(group, share);
3828				if (ret == SA_OK &&
3829				    sa_group_is_zfs(group))
3830					share = NULL;
3831				if (ret == SA_NOT_ALLOWED)
3832					(void) printf(gettext(
3833					    "Properties on ZFS group shares "
3834					    "not supported: %s\n"), sharepath);
3835			}
3836		}
3837		if (ret == SA_OK) {
3838			/* group must exist */
3839			ret = valid_options(handle, optlist, protocol,
3840			    share == NULL ? group : share, sectype);
3841			if (ret == SA_OK && !dryrun) {
3842				if (share != NULL)
3843					change = add_security(share, sectype,
3844					    optlist, protocol, &ret);
3845				else
3846					change = add_security(group, sectype,
3847					    optlist, protocol, &ret);
3848				if (ret != SA_OK)
3849					(void) printf(gettext(
3850					    "Could not set property: %s\n"),
3851					    sa_errorstr(ret));
3852			}
3853			if (ret == SA_OK && change)
3854				worklist = add_list(worklist, group, share,
3855				    protocol);
3856		}
3857		free_opt(optlist);
3858	} else {
3859		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
3860		ret = SA_NO_SUCH_GROUP;
3861	}
3862
3863	/*
3864	 * We have a group and potentially legal additions.
3865	 */
3866
3867	/* Commit to configuration if not a dryrun */
3868	if (!dryrun && ret == 0) {
3869		if (change && worklist != NULL) {
3870			/* properties changed, so update all shares */
3871			(void) enable_all_groups(handle, worklist, 0, 0,
3872			    protocol, B_TRUE);
3873		}
3874		ret = sa_update_config(handle);
3875	}
3876	if (worklist != NULL)
3877		free_list(worklist);
3878	return (ret);
3879}
3880
3881/*
3882 * sa_set(flags, argc, argv)
3883 *
3884 * Implements the set subcommand. It keys off of -S to determine which
3885 * set of operations to actually do.
3886 */
3887
3888int
3889sa_set(sa_handle_t handle, int flags, int argc, char *argv[])
3890{
3891	char *groupname;
3892	int verbose = 0;
3893	int dryrun = 0;
3894	int c;
3895	char *protocol = NULL;
3896	int ret = SA_OK;
3897	struct options *optlist = NULL;
3898	char *rsrcname = NULL;
3899	char *sharepath = NULL;
3900	char *optset = NULL;
3901	int auth;
3902
3903	while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
3904		switch (c) {
3905		case 'v':
3906			verbose++;
3907			break;
3908		case 'n':
3909			dryrun++;
3910			break;
3911		case 'P':
3912			if (protocol != NULL) {
3913				(void) printf(gettext(
3914				    "Specifying multiple protocols "
3915				    "not supported: %s\n"), protocol);
3916				return (SA_SYNTAX_ERR);
3917			}
3918			protocol = optarg;
3919			if (!sa_valid_protocol(protocol)) {
3920				(void) printf(gettext(
3921				    "Invalid protocol specified: %s\n"),
3922				    protocol);
3923				return (SA_INVALID_PROTOCOL);
3924			}
3925			break;
3926		case 'p':
3927			ret = add_opt(&optlist, optarg, 0);
3928			switch (ret) {
3929			case OPT_ADD_SYNTAX:
3930				(void) printf(gettext("Property syntax error:"
3931				    " %s\n"), optarg);
3932				return (SA_SYNTAX_ERR);
3933			case OPT_ADD_MEMORY:
3934				(void) printf(gettext("No memory to set "
3935				    "property: %s\n"), optarg);
3936				return (SA_NO_MEMORY);
3937			default:
3938				break;
3939			}
3940			break;
3941		case 'r':
3942			if (rsrcname != NULL) {
3943				(void) printf(gettext(
3944				    "Setting multiple resource names not"
3945				    " supported\n"));
3946				return (SA_SYNTAX_ERR);
3947			}
3948			rsrcname = optarg;
3949			break;
3950		case 's':
3951			if (sharepath != NULL) {
3952				(void) printf(gettext(
3953				    "Setting multiple shares not supported\n"));
3954				return (SA_SYNTAX_ERR);
3955			}
3956			sharepath = optarg;
3957			break;
3958		case 'S':
3959			if (optset != NULL) {
3960				(void) printf(gettext(
3961				    "Specifying multiple property "
3962				    "spaces not supported: %s\n"), optset);
3963				return (SA_SYNTAX_ERR);
3964			}
3965			optset = optarg;
3966			break;
3967		case 'h':
3968			/* optopt on valid arg isn't defined */
3969			optopt = c;
3970			/*FALLTHROUGH*/
3971		case '?':
3972		default:
3973			/*
3974			 * Since a bad option gets to here, sort it
3975			 * out and return a syntax error return value
3976			 * if necessary.
3977			 */
3978			switch (optopt) {
3979			default:
3980				ret = SA_SYNTAX_ERR;
3981				break;
3982			case 'h':
3983			case '?':
3984				break;
3985			}
3986			(void) printf(gettext("usage: %s\n"),
3987			    sa_get_usage(USAGE_SET));
3988			return (ret);
3989		}
3990	}
3991
3992	if (optlist != NULL)
3993		ret = chk_opt(optlist, optset != NULL, protocol);
3994
3995	if (optind >= argc || (optlist == NULL && optset == NULL) ||
3996	    protocol == NULL || ret != OPT_ADD_OK) {
3997		char *sep = "\t";
3998
3999		(void) printf(gettext("usage: %s\n"), sa_get_usage(USAGE_SET));
4000		if (optind >= argc) {
4001			(void) printf(gettext("%sgroup must be specified"),
4002			    sep);
4003			sep = ", ";
4004		}
4005		if (optlist == NULL) {
4006			(void) printf(gettext("%sat least one property must be"
4007			    " specified"), sep);
4008			sep = ", ";
4009		}
4010		if (protocol == NULL) {
4011			(void) printf(gettext("%sprotocol must be specified"),
4012			    sep);
4013			sep = ", ";
4014		}
4015		(void) printf("\n");
4016		ret = SA_SYNTAX_ERR;
4017	} else {
4018		/*
4019		 * Group already exists so we can proceed after a few
4020		 * additional checks related to ZFS handling.
4021		 */
4022
4023		groupname = argv[optind];
4024		if (strcmp(groupname, "zfs") == 0) {
4025			(void) printf(gettext("Changing properties for group "
4026			    "\"zfs\" not allowed\n"));
4027			return (SA_NOT_ALLOWED);
4028		}
4029
4030		auth = check_authorizations(groupname, flags);
4031		if (optset == NULL)
4032			ret = basic_set(handle, groupname, optlist, protocol,
4033			    sharepath, rsrcname, dryrun);
4034		else
4035			ret = space_set(handle, groupname, optlist, protocol,
4036			    sharepath, dryrun, optset);
4037		if (dryrun && ret == SA_OK && !auth && verbose) {
4038			(void) printf(gettext("Command would fail: %s\n"),
4039			    sa_errorstr(SA_NO_PERMISSION));
4040		}
4041	}
4042	return (ret);
4043}
4044
4045/*
4046 * remove_options(group, optlist, proto, *err)
4047 *
4048 * Helper function to actually remove options from a group after all
4049 * preprocessing is done.
4050 */
4051
4052static int
4053remove_options(sa_group_t group, struct options *optlist,
4054    char *proto, int *err)
4055{
4056	struct options *cur;
4057	sa_optionset_t optionset;
4058	sa_property_t prop;
4059	int change = 0;
4060	int ret = SA_OK;
4061
4062	optionset = sa_get_optionset(group, proto);
4063	if (optionset != NULL) {
4064		for (cur = optlist; cur != NULL; cur = cur->next) {
4065			prop = sa_get_property(optionset, cur->optname);
4066			if (prop != NULL) {
4067				ret = sa_remove_property(prop);
4068				if (ret != SA_OK)
4069					break;
4070				change = 1;
4071			}
4072		}
4073	}
4074	if (ret == SA_OK && change)
4075		ret = sa_commit_properties(optionset, 0);
4076
4077	if (err != NULL)
4078		*err = ret;
4079	return (change);
4080}
4081
4082/*
4083 * valid_unset(group, optlist, proto)
4084 *
4085 * Sanity check the optlist to make sure they can be removed. Issue an
4086 * error if a property doesn't exist.
4087 */
4088
4089static int
4090valid_unset(sa_group_t group, struct options *optlist, char *proto)
4091{
4092	struct options *cur;
4093	sa_optionset_t optionset;
4094	sa_property_t prop;
4095	int ret = SA_OK;
4096
4097	optionset = sa_get_optionset(group, proto);
4098	if (optionset != NULL) {
4099		for (cur = optlist; cur != NULL; cur = cur->next) {
4100			prop = sa_get_property(optionset, cur->optname);
4101			if (prop == NULL) {
4102				(void) printf(gettext(
4103				    "Could not unset property %s: not set\n"),
4104				    cur->optname);
4105				ret = SA_NO_SUCH_PROP;
4106			}
4107		}
4108	}
4109	return (ret);
4110}
4111
4112/*
4113 * valid_unset_security(group, optlist, proto)
4114 *
4115 * Sanity check the optlist to make sure they can be removed. Issue an
4116 * error if a property doesn't exist.
4117 */
4118
4119static int
4120valid_unset_security(sa_group_t group, struct options *optlist, char *proto,
4121    char *sectype)
4122{
4123	struct options *cur;
4124	sa_security_t security;
4125	sa_property_t prop;
4126	int ret = SA_OK;
4127	char *sec;
4128
4129	sec = sa_proto_space_alias(proto, sectype);
4130	security = sa_get_security(group, sec, proto);
4131	if (security != NULL) {
4132		for (cur = optlist; cur != NULL; cur = cur->next) {
4133			prop = sa_get_property(security, cur->optname);
4134			if (prop == NULL) {
4135				(void) printf(gettext(
4136				    "Could not unset property %s: not set\n"),
4137				    cur->optname);
4138				ret = SA_NO_SUCH_PROP;
4139			}
4140		}
4141	} else {
4142		(void) printf(gettext(
4143		    "Could not unset %s: space not defined\n"), sectype);
4144		ret = SA_NO_SUCH_SECURITY;
4145	}
4146	if (sec != NULL)
4147		sa_free_attr_string(sec);
4148	return (ret);
4149}
4150
4151/*
4152 * remove_security(group, optlist, proto)
4153 *
4154 * Remove the properties since they were checked as valid.
4155 */
4156
4157static int
4158remove_security(sa_group_t group, char *sectype,
4159    struct options *optlist, char *proto, int *err)
4160{
4161	sa_security_t security;
4162	int ret = SA_OK;
4163	int change = 0;
4164
4165	sectype = sa_proto_space_alias(proto, sectype);
4166	security = sa_get_security(group, sectype, proto);
4167	if (sectype != NULL)
4168		sa_free_attr_string(sectype);
4169
4170	if (security != NULL) {
4171		while (optlist != NULL) {
4172			sa_property_t prop;
4173			prop = sa_get_property(security, optlist->optname);
4174			if (prop != NULL) {
4175				ret = sa_remove_property(prop);
4176				if (ret != SA_OK)
4177					break;
4178				change = 1;
4179			}
4180			optlist = optlist->next;
4181		}
4182		/*
4183		 * when done, properties may have all been removed but
4184		 * we need to keep the security type itself until
4185		 * explicitly removed.
4186		 */
4187		if (ret == SA_OK && change)
4188			ret = sa_commit_properties(security, 0);
4189	} else {
4190		ret = SA_NO_SUCH_PROP;
4191	}
4192	if (err != NULL)
4193		*err = ret;
4194	return (change);
4195}
4196
4197/*
4198 * basic_unset(groupname, optlist, protocol, sharepath, rsrcname, dryrun)
4199 *
4200 * Unset non-named optionset properties.
4201 */
4202
4203static int
4204basic_unset(sa_handle_t handle, char *groupname, struct options *optlist,
4205    char *protocol, char *sharepath, char *rsrcname, int dryrun)
4206{
4207	sa_group_t group;
4208	int ret = SA_OK;
4209	int change = 0;
4210	struct list *worklist = NULL;
4211	sa_share_t share = NULL;
4212	sa_resource_t resource = NULL;
4213
4214	group = sa_get_group(handle, groupname);
4215	if (group == NULL)
4216		return (ret);
4217
4218	/*
4219	 * If there is a sharepath, make sure it belongs to
4220	 * the group.
4221	 */
4222	if (sharepath != NULL) {
4223		share = sa_get_share(group, sharepath);
4224		if (share == NULL) {
4225			(void) printf(gettext(
4226			    "Share does not exist in group %s\n"),
4227			    groupname, sharepath);
4228			ret = SA_NO_SUCH_PATH;
4229		}
4230	}
4231	/*
4232	 * If a resource name exists, make sure it belongs to
4233	 * the share if present else it belongs to the
4234	 * group. Also check the protocol to see if it
4235	 * supports resource level properties or not. If not,
4236	 * use share only.
4237	 */
4238	if (rsrcname != NULL) {
4239		if (share != NULL) {
4240			resource = sa_get_share_resource(share, rsrcname);
4241			if (resource == NULL)
4242				ret = SA_NO_SUCH_RESOURCE;
4243		} else {
4244			resource = sa_get_resource(group, rsrcname);
4245			if (resource != NULL) {
4246				share = sa_get_resource_parent(resource);
4247			} else {
4248				ret = SA_NO_SUCH_RESOURCE;
4249			}
4250		}
4251		if (ret == SA_OK && resource != NULL) {
4252			uint64_t features;
4253			/*
4254			 * Check to see if the resource can take
4255			 * properties. If so, stick the resource into
4256			 * "share" so it will all just work.
4257			 */
4258			features = sa_proto_get_featureset(protocol);
4259			if (features & SA_FEATURE_RESOURCE)
4260				share = (sa_share_t)resource;
4261		}
4262	}
4263
4264	if (ret == SA_OK) {
4265		/* group must exist */
4266		ret = valid_unset(share != NULL ? share : group,
4267		    optlist, protocol);
4268		if (ret == SA_OK && !dryrun) {
4269			if (share != NULL) {
4270				sa_optionset_t optionset;
4271				sa_property_t prop;
4272				change |= remove_options(share, optlist,
4273				    protocol, &ret);
4274				/*
4275				 * If a share optionset is
4276				 * empty, remove it.
4277				 */
4278				optionset = sa_get_optionset((sa_share_t)share,
4279				    protocol);
4280				if (optionset != NULL) {
4281					prop = sa_get_property(optionset, NULL);
4282					if (prop == NULL)
4283						(void) sa_destroy_optionset(
4284						    optionset);
4285				}
4286			} else {
4287				change |= remove_options(group,
4288				    optlist, protocol, &ret);
4289			}
4290			if (ret == SA_OK && change)
4291				worklist = add_list(worklist, group, share,
4292				    protocol);
4293			if (ret != SA_OK)
4294				(void) printf(gettext(
4295				    "Could not remove properties: "
4296				    "%s\n"), sa_errorstr(ret));
4297		}
4298	} else {
4299		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
4300		ret = SA_NO_SUCH_GROUP;
4301	}
4302	free_opt(optlist);
4303
4304	/*
4305	 * We have a group and potentially legal additions
4306	 *
4307	 * Commit to configuration if not a dryrun
4308	 */
4309	if (!dryrun && ret == SA_OK) {
4310		if (change && worklist != NULL) {
4311			/* properties changed, so update all shares */
4312			(void) enable_all_groups(handle, worklist, 0, 0,
4313			    protocol, B_TRUE);
4314		}
4315	}
4316	if (worklist != NULL)
4317		free_list(worklist);
4318	return (ret);
4319}
4320
4321/*
4322 * space_unset(groupname, optlist, protocol, sharepath, dryrun)
4323 *
4324 * Unset named optionset properties.
4325 */
4326static int
4327space_unset(sa_handle_t handle, char *groupname, struct options *optlist,
4328    char *protocol, char *sharepath, int dryrun, char *sectype)
4329{
4330	sa_group_t group;
4331	int ret = SA_OK;
4332	int change = 0;
4333	struct list *worklist = NULL;
4334	sa_share_t share = NULL;
4335
4336	group = sa_get_group(handle, groupname);
4337	if (group == NULL) {
4338		(void) printf(gettext("Group \"%s\" not found\n"), groupname);
4339		return (SA_NO_SUCH_GROUP);
4340	}
4341	if (sharepath != NULL) {
4342		share = sa_get_share(group, sharepath);
4343		if (share == NULL) {
4344			(void) printf(gettext(
4345			    "Share does not exist in group %s\n"),
4346			    groupname, sharepath);
4347			return (SA_NO_SUCH_PATH);
4348		}
4349	}
4350	ret = valid_unset_security(share != NULL ? share : group,
4351	    optlist, protocol, sectype);
4352
4353	if (ret == SA_OK && !dryrun) {
4354		if (optlist != NULL) {
4355			if (share != NULL) {
4356				sa_security_t optionset;
4357				sa_property_t prop;
4358				change = remove_security(share,
4359				    sectype, optlist, protocol, &ret);
4360
4361				/* If a share security is empty, remove it */
4362				optionset = sa_get_security((sa_group_t)share,
4363				    sectype, protocol);
4364				if (optionset != NULL) {
4365					prop = sa_get_property(optionset,
4366					    NULL);
4367					if (prop == NULL)
4368						ret = sa_destroy_security(
4369						    optionset);
4370				}
4371			} else {
4372				change = remove_security(group, sectype,
4373				    optlist, protocol, &ret);
4374			}
4375		} else {
4376			sa_security_t security;
4377			char *sec;
4378			sec = sa_proto_space_alias(protocol, sectype);
4379			security = sa_get_security(group, sec, protocol);
4380			if (sec != NULL)
4381				sa_free_attr_string(sec);
4382			if (security != NULL) {
4383				ret = sa_destroy_security(security);
4384				if (ret == SA_OK)
4385					change = 1;
4386			} else {
4387				ret = SA_NO_SUCH_PROP;
4388			}
4389		}
4390		if (ret != SA_OK)
4391			(void) printf(gettext("Could not unset property: %s\n"),
4392			    sa_errorstr(ret));
4393	}
4394
4395	if (ret == SA_OK && change)
4396		worklist = add_list(worklist, group, 0, protocol);
4397
4398	free_opt(optlist);
4399	/*
4400	 * We have a group and potentially legal additions
4401	 */
4402
4403	/* Commit to configuration if not a dryrun */
4404	if (!dryrun && ret == 0) {
4405		/* properties changed, so update all shares */
4406		if (change && worklist != NULL)
4407			(void) enable_all_groups(handle, worklist, 0, 0,
4408			    protocol, B_TRUE);
4409		ret = sa_update_config(handle);
4410	}
4411	if (worklist != NULL)
4412		free_list(worklist);
4413	return (ret);
4414}
4415
4416/*
4417 * sa_unset(flags, argc, argv)
4418 *
4419 * Implements the unset subcommand. Parsing done here and then basic
4420 * or space versions of the real code are called.
4421 */
4422
4423int
4424sa_unset(sa_handle_t handle, int flags, int argc, char *argv[])
4425{
4426	char *groupname;
4427	int verbose = 0;
4428	int dryrun = 0;
4429	int c;
4430	char *protocol = NULL;
4431	int ret = SA_OK;
4432	struct options *optlist = NULL;
4433	char *rsrcname = NULL;
4434	char *sharepath = NULL;
4435	char *optset = NULL;
4436	int auth;
4437
4438	while ((c = getopt(argc, argv, "?hvnP:p:r:s:S:")) != EOF) {
4439		switch (c) {
4440		case 'v':
4441			verbose++;
4442			break;
4443		case 'n':
4444			dryrun++;
4445			break;
4446		case 'P':
4447			if (protocol != NULL) {
4448				(void) printf(gettext(
4449				    "Specifying multiple protocols "
4450				    "not supported: %s\n"), protocol);
4451				return (SA_SYNTAX_ERR);
4452			}
4453			protocol = optarg;
4454			if (!sa_valid_protocol(protocol)) {
4455				(void) printf(gettext(
4456				    "Invalid protocol specified: %s\n"),
4457				    protocol);
4458				return (SA_INVALID_PROTOCOL);
4459			}
4460			break;
4461		case 'p':
4462			ret = add_opt(&optlist, optarg, 1);
4463			switch (ret) {
4464			case OPT_ADD_SYNTAX:
4465				(void) printf(gettext("Property syntax error "
4466				    "for property %s\n"), optarg);
4467				return (SA_SYNTAX_ERR);
4468
4469			case OPT_ADD_PROPERTY:
4470				(void) printf(gettext("Properties need to be "
4471				    "set with set command: %s\n"), optarg);
4472				return (SA_SYNTAX_ERR);
4473
4474			default:
4475				break;
4476			}
4477			break;
4478		case 'r':
4479			/*
4480			 * Unset properties on resource if applicable or on
4481			 * share if resource for this protocol doesn't use
4482			 * resources.
4483			 */
4484			if (rsrcname != NULL) {
4485				(void) printf(gettext(
4486				    "Unsetting multiple resource "
4487				    "names not supported\n"));
4488				return (SA_SYNTAX_ERR);
4489			}
4490			rsrcname = optarg;
4491			break;
4492		case 's':
4493			if (sharepath != NULL) {
4494				(void) printf(gettext(
4495				    "Adding multiple shares not supported\n"));
4496				return (SA_SYNTAX_ERR);
4497			}
4498			sharepath = optarg;
4499			break;
4500		case 'S':
4501			if (optset != NULL) {
4502				(void) printf(gettext(
4503				    "Specifying multiple property "
4504				    "spaces not supported: %s\n"), optset);
4505				return (SA_SYNTAX_ERR);
4506			}
4507			optset = optarg;
4508			break;
4509		case 'h':
4510			/* optopt on valid arg isn't defined */
4511			optopt = c;
4512			/*FALLTHROUGH*/
4513		case '?':
4514		default:
4515			/*
4516			 * Since a bad option gets to here, sort it
4517			 * out and return a syntax error return value
4518			 * if necessary.
4519			 */
4520			switch (optopt) {
4521			default:
4522				ret = SA_SYNTAX_ERR;
4523				break;
4524			case 'h':
4525			case '?':
4526				break;
4527			}
4528			(void) printf(gettext("usage: %s\n"),
4529			    sa_get_usage(USAGE_UNSET));
4530			return (ret);
4531		}
4532	}
4533
4534	if (optlist != NULL)
4535		ret = chk_opt(optlist, optset != NULL, protocol);
4536
4537	if (optind >= argc || (optlist == NULL && optset == NULL) ||
4538	    protocol == NULL) {
4539		char *sep = "\t";
4540		(void) printf(gettext("usage: %s\n"),
4541		    sa_get_usage(USAGE_UNSET));
4542		if (optind >= argc) {
4543			(void) printf(gettext("%sgroup must be specified"),
4544			    sep);
4545			sep = ", ";
4546		}
4547		if (optlist == NULL) {
4548			(void) printf(gettext("%sat least one property must "
4549			    "be specified"), sep);
4550			sep = ", ";
4551		}
4552		if (protocol == NULL) {
4553			(void) printf(gettext("%sprotocol must be specified"),
4554			    sep);
4555			sep = ", ";
4556		}
4557		(void) printf("\n");
4558		ret = SA_SYNTAX_ERR;
4559	} else {
4560
4561		/*
4562		 * If a group already exists, we can only add a new
4563		 * protocol to it and not create a new one or add the
4564		 * same protocol again.
4565		 */
4566
4567		groupname = argv[optind];
4568		auth = check_authorizations(groupname, flags);
4569		if (optset == NULL)
4570			ret = basic_unset(handle, groupname, optlist, protocol,
4571			    sharepath, rsrcname, dryrun);
4572		else
4573			ret = space_unset(handle, groupname, optlist, protocol,
4574			    sharepath, dryrun, optset);
4575
4576		if (dryrun && ret == SA_OK && !auth && verbose)
4577			(void) printf(gettext("Command would fail: %s\n"),
4578			    sa_errorstr(SA_NO_PERMISSION));
4579	}
4580	return (ret);
4581}
4582
4583/*
4584 * sa_enable_group(flags, argc, argv)
4585 *
4586 * Implements the enable subcommand
4587 */
4588
4589int
4590sa_enable_group(sa_handle_t handle, int flags, int argc, char *argv[])
4591{
4592	int verbose = 0;
4593	int dryrun = 0;
4594	int all = 0;
4595	int c;
4596	int ret = SA_OK;
4597	char *protocol = NULL;
4598	char *state;
4599	struct list *worklist = NULL;
4600	int auth = 1;
4601	sa_group_t group;
4602
4603	while ((c = getopt(argc, argv, "?havnP:")) != EOF) {
4604		switch (c) {
4605		case 'a':
4606			all = 1;
4607			break;
4608		case 'n':
4609			dryrun++;
4610			break;
4611		case 'P':
4612			if (protocol != NULL) {
4613				(void) printf(gettext(
4614				    "Specifying multiple protocols "
4615				    "not supported: %s\n"), protocol);
4616				return (SA_SYNTAX_ERR);
4617			}
4618			protocol = optarg;
4619			if (!sa_valid_protocol(protocol)) {
4620				(void) printf(gettext(
4621				    "Invalid protocol specified: %s\n"),
4622				    protocol);
4623				return (SA_INVALID_PROTOCOL);
4624			}
4625			break;
4626		case 'v':
4627			verbose++;
4628			break;
4629		case 'h':
4630			/* optopt on valid arg isn't defined */
4631			optopt = c;
4632			/*FALLTHROUGH*/
4633		case '?':
4634		default:
4635			/*
4636			 * Since a bad option gets to here, sort it
4637			 * out and return a syntax error return value
4638			 * if necessary.
4639			 */
4640			switch (optopt) {
4641			default:
4642				ret = SA_SYNTAX_ERR;
4643				break;
4644			case 'h':
4645			case '?':
4646				(void) printf(gettext("usage: %s\n"),
4647				    sa_get_usage(USAGE_ENABLE));
4648				return (ret);
4649			}
4650		}
4651	}
4652
4653	if (optind == argc && !all) {
4654		(void) printf(gettext("usage: %s\n"),
4655		    sa_get_usage(USAGE_ENABLE));
4656		(void) printf(gettext("\tmust specify group\n"));
4657		return (SA_NO_SUCH_PATH);
4658	}
4659	if (!all) {
4660		while (optind < argc) {
4661			group = sa_get_group(handle, argv[optind]);
4662			if (group != NULL) {
4663				auth &= check_authorizations(argv[optind],
4664				    flags);
4665				state = sa_get_group_attr(group, "state");
4666				if (state != NULL &&
4667				    strcmp(state, "enabled") == 0) {
4668					/* already enabled */
4669					if (verbose)
4670						(void) printf(gettext(
4671						    "Group \"%s\" is already "
4672						    "enabled\n"),
4673						    argv[optind]);
4674					ret = SA_BUSY; /* already enabled */
4675				} else {
4676					worklist = add_list(worklist, group,
4677					    0, protocol);
4678					if (verbose)
4679						(void) printf(gettext(
4680						    "Enabling group \"%s\"\n"),
4681						    argv[optind]);
4682				}
4683				if (state != NULL)
4684					sa_free_attr_string(state);
4685			} else {
4686				ret = SA_NO_SUCH_GROUP;
4687			}
4688			optind++;
4689		}
4690	} else {
4691		for (group = sa_get_group(handle, NULL);
4692		    group != NULL;
4693		    group = sa_get_next_group(group)) {
4694			worklist = add_list(worklist, group, 0, protocol);
4695		}
4696	}
4697	if (!dryrun && ret == SA_OK)
4698		ret = enable_all_groups(handle, worklist, 1, 0, NULL, B_FALSE);
4699
4700	if (ret != SA_OK && ret != SA_BUSY)
4701		(void) printf(gettext("Could not enable group: %s\n"),
4702		    sa_errorstr(ret));
4703	if (ret == SA_BUSY)
4704		ret = SA_OK;
4705
4706	if (worklist != NULL)
4707		free_list(worklist);
4708	if (dryrun && ret == SA_OK && !auth && verbose) {
4709		(void) printf(gettext("Command would fail: %s\n"),
4710		    sa_errorstr(SA_NO_PERMISSION));
4711	}
4712	return (ret);
4713}
4714
4715/*
4716 * disable_group(group, proto)
4717 *
4718 * Disable all the shares in the specified group.. This is a helper
4719 * for disable_all_groups in order to simplify regular and subgroup
4720 * (zfs) disabling. Group has already been checked for non-NULL.
4721 */
4722
4723static int
4724disable_group(sa_group_t group, char *proto)
4725{
4726	sa_share_t share;
4727	int ret = SA_OK;
4728
4729	/*
4730	 * If the protocol isn't enabled, skip it and treat as
4731	 * successful.
4732	 */
4733	if (!has_protocol(group, proto))
4734		return (ret);
4735
4736	for (share = sa_get_share(group, NULL);
4737	    share != NULL && ret == SA_OK;
4738	    share = sa_get_next_share(share)) {
4739		ret = sa_disable_share(share, proto);
4740		if (ret == SA_NO_SUCH_PATH) {
4741			/*
4742			 * this is OK since the path is gone. we can't
4743			 * re-share it anyway so no error.
4744			 */
4745			ret = SA_OK;
4746		}
4747	}
4748	return (ret);
4749}
4750
4751/*
4752 * disable_all_groups(work, setstate)
4753 *
4754 * helper function that disables the shares in the list of groups
4755 * provided. It optionally marks the group as disabled. Used by both
4756 * enable and start subcommands.
4757 */
4758
4759static int
4760disable_all_groups(sa_handle_t handle, struct list *work, int setstate)
4761{
4762	int ret = SA_OK;
4763	sa_group_t subgroup, group;
4764
4765	while (work != NULL && ret == SA_OK) {
4766		group = (sa_group_t)work->item;
4767		if (setstate)
4768			ret = sa_set_group_attr(group, "state", "disabled");
4769		if (ret == SA_OK) {
4770			char *name;
4771			name = sa_get_group_attr(group, "name");
4772			if (name != NULL && strcmp(name, "zfs") == 0) {
4773				/* need to get the sub-groups for stopping */
4774				for (subgroup = sa_get_sub_group(group);
4775				    subgroup != NULL;
4776				    subgroup = sa_get_next_group(subgroup)) {
4777					ret = disable_group(subgroup,
4778					    work->proto);
4779				}
4780			} else {
4781				ret = disable_group(group, work->proto);
4782			}
4783			if (name != NULL)
4784				sa_free_attr_string(name);
4785			/*
4786			 * We don't want to "disable" since it won't come
4787			 * up after a reboot.  The SMF framework should do
4788			 * the right thing. On enable we do want to do
4789			 * something.
4790			 */
4791		}
4792		work = work->next;
4793	}
4794	if (ret == SA_OK)
4795		ret = sa_update_config(handle);
4796	return (ret);
4797}
4798
4799/*
4800 * sa_disable_group(flags, argc, argv)
4801 *
4802 * Implements the disable subcommand
4803 */
4804
4805int
4806sa_disable_group(sa_handle_t handle, int flags, int argc, char *argv[])
4807{
4808	int verbose = 0;
4809	int dryrun = 0;
4810	int all = 0;
4811	int c;
4812	int ret = SA_OK;
4813	char *protocol = NULL;
4814	char *state;
4815	struct list *worklist = NULL;
4816	sa_group_t group;
4817	int auth = 1;
4818
4819	while ((c = getopt(argc, argv, "?havn")) != EOF) {
4820		switch (c) {
4821		case 'a':
4822			all = 1;
4823			break;
4824		case 'n':
4825			dryrun++;
4826			break;
4827		case 'P':
4828			if (protocol != NULL) {
4829				(void) printf(gettext(
4830				    "Specifying multiple protocols "
4831				    "not supported: %s\n"), protocol);
4832				return (SA_SYNTAX_ERR);
4833			}
4834			protocol = optarg;
4835			if (!sa_valid_protocol(protocol)) {
4836				(void) printf(gettext(
4837				    "Invalid protocol specified: %s\n"),
4838				    protocol);
4839				return (SA_INVALID_PROTOCOL);
4840			}
4841			break;
4842		case 'v':
4843			verbose++;
4844			break;
4845		case 'h':
4846			/* optopt on valid arg isn't defined */
4847			optopt = c;
4848			/*FALLTHROUGH*/
4849		case '?':
4850		default:
4851			/*
4852			 * Since a bad option gets to here, sort it
4853			 * out and return a syntax error return value
4854			 * if necessary.
4855			 */
4856			switch (optopt) {
4857			default:
4858				ret = SA_SYNTAX_ERR;
4859				break;
4860			case 'h':
4861			case '?':
4862				break;
4863			}
4864			(void) printf(gettext("usage: %s\n"),
4865			    sa_get_usage(USAGE_DISABLE));
4866			return (ret);
4867		}
4868	}
4869
4870	if (optind == argc && !all) {
4871		(void) printf(gettext("usage: %s\n"),
4872		    sa_get_usage(USAGE_DISABLE));
4873		(void) printf(gettext("\tmust specify group\n"));
4874		return (SA_NO_SUCH_PATH);
4875	}
4876	if (!all) {
4877		while (optind < argc) {
4878			group = sa_get_group(handle, argv[optind]);
4879			if (group != NULL) {
4880				auth &= check_authorizations(argv[optind],
4881				    flags);
4882				state = sa_get_group_attr(group, "state");
4883				if (state == NULL ||
4884				    strcmp(state, "disabled") == 0) {
4885					/* already disabled */
4886					if (verbose)
4887						(void) printf(gettext(
4888						    "Group \"%s\" is "
4889						    "already disabled\n"),
4890						    argv[optind]);
4891					ret = SA_BUSY; /* already disabled */
4892				} else {
4893					worklist = add_list(worklist, group, 0,
4894					    protocol);
4895					if (verbose)
4896						(void) printf(gettext(
4897						    "Disabling group "
4898						    "\"%s\"\n"), argv[optind]);
4899				}
4900				if (state != NULL)
4901					sa_free_attr_string(state);
4902			} else {
4903				ret = SA_NO_SUCH_GROUP;
4904			}
4905			optind++;
4906		}
4907	} else {
4908		for (group = sa_get_group(handle, NULL);
4909		    group != NULL;
4910		    group = sa_get_next_group(group))
4911			worklist = add_list(worklist, group, 0, protocol);
4912	}
4913
4914	if (ret == SA_OK && !dryrun)
4915		ret = disable_all_groups(handle, worklist, 1);
4916	if (ret != SA_OK && ret != SA_BUSY)
4917		(void) printf(gettext("Could not disable group: %s\n"),
4918		    sa_errorstr(ret));
4919	if (ret == SA_BUSY)
4920		ret = SA_OK;
4921	if (worklist != NULL)
4922		free_list(worklist);
4923	if (dryrun && ret == SA_OK && !auth && verbose)
4924		(void) printf(gettext("Command would fail: %s\n"),
4925		    sa_errorstr(SA_NO_PERMISSION));
4926	return (ret);
4927}
4928
4929/*
4930 * sa_start_group(flags, argc, argv)
4931 *
4932 * Implements the start command.
4933 * This is similar to enable except it doesn't change the state
4934 * of the group(s) and only enables shares if the group is already
4935 * enabled.
4936 */
4937
4938int
4939sa_start_group(sa_handle_t handle, int flags, int argc, char *argv[])
4940{
4941	int verbose = 0;
4942	int all = 0;
4943	int c;
4944	int ret = SMF_EXIT_OK;
4945	char *protocol = NULL;
4946	char *state;
4947	struct list *worklist = NULL;
4948	sa_group_t group;
4949#ifdef lint
4950	flags = flags;
4951#endif
4952
4953	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
4954		switch (c) {
4955		case 'a':
4956			all = 1;
4957			break;
4958		case 'P':
4959			if (protocol != NULL) {
4960				(void) printf(gettext(
4961				    "Specifying multiple protocols "
4962				    "not supported: %s\n"), protocol);
4963				return (SA_SYNTAX_ERR);
4964			}
4965			protocol = optarg;
4966			if (!sa_valid_protocol(protocol)) {
4967				(void) printf(gettext(
4968				    "Invalid protocol specified: %s\n"),
4969				    protocol);
4970				return (SA_INVALID_PROTOCOL);
4971			}
4972			break;
4973		case 'v':
4974			verbose++;
4975			break;
4976		case 'h':
4977			/* optopt on valid arg isn't defined */
4978			optopt = c;
4979			/*FALLTHROUGH*/
4980		case '?':
4981		default:
4982			/*
4983			 * Since a bad option gets to here, sort it
4984			 * out and return a syntax error return value
4985			 * if necessary.
4986			 */
4987			ret = SA_OK;
4988			switch (optopt) {
4989			default:
4990				ret = SA_SYNTAX_ERR;
4991				break;
4992			case 'h':
4993			case '?':
4994				break;
4995			}
4996			(void) printf(gettext("usage: %s\n"),
4997			    sa_get_usage(USAGE_START));
4998			return (ret);
4999		}
5000	}
5001
5002	if (optind == argc && !all) {
5003		(void) printf(gettext("usage: %s\n"),
5004		    sa_get_usage(USAGE_START));
5005		return (SMF_EXIT_ERR_FATAL);
5006	}
5007
5008	if (!all) {
5009		while (optind < argc) {
5010			group = sa_get_group(handle, argv[optind]);
5011			if (group != NULL) {
5012				state = sa_get_group_attr(group, "state");
5013				if (state == NULL ||
5014				    strcmp(state, "enabled") == 0) {
5015					worklist = add_list(worklist, group, 0,
5016					    protocol);
5017					if (verbose)
5018						(void) printf(gettext(
5019						    "Starting group \"%s\"\n"),
5020						    argv[optind]);
5021				} else {
5022					/*
5023					 * Determine if there are any
5024					 * protocols.  If there aren't any,
5025					 * then there isn't anything to do in
5026					 * any case so no error.
5027					 */
5028					if (sa_get_optionset(group,
5029					    protocol) != NULL) {
5030						ret = SMF_EXIT_OK;
5031					}
5032				}
5033				if (state != NULL)
5034					sa_free_attr_string(state);
5035			}
5036			optind++;
5037		}
5038	} else {
5039		for (group = sa_get_group(handle, NULL);
5040		    group != NULL;
5041		    group = sa_get_next_group(group)) {
5042			state = sa_get_group_attr(group, "state");
5043			if (state == NULL || strcmp(state, "enabled") == 0)
5044				worklist = add_list(worklist, group, 0,
5045				    protocol);
5046			if (state != NULL)
5047				sa_free_attr_string(state);
5048		}
5049	}
5050
5051	(void) enable_all_groups(handle, worklist, 0, 1, protocol, B_FALSE);
5052
5053	if (worklist != NULL)
5054		free_list(worklist);
5055	return (ret);
5056}
5057
5058/*
5059 * sa_stop_group(flags, argc, argv)
5060 *
5061 * Implements the stop command.
5062 * This is similar to disable except it doesn't change the state
5063 * of the group(s) and only disables shares if the group is already
5064 * enabled.
5065 */
5066int
5067sa_stop_group(sa_handle_t handle, int flags, int argc, char *argv[])
5068{
5069	int verbose = 0;
5070	int all = 0;
5071	int c;
5072	int ret = SMF_EXIT_OK;
5073	char *protocol = NULL;
5074	char *state;
5075	struct list *worklist = NULL;
5076	sa_group_t group;
5077#ifdef lint
5078	flags = flags;
5079#endif
5080
5081	while ((c = getopt(argc, argv, "?havP:")) != EOF) {
5082		switch (c) {
5083		case 'a':
5084			all = 1;
5085			break;
5086		case 'P':
5087			if (protocol != NULL) {
5088				(void) printf(gettext(
5089				    "Specifying multiple protocols "
5090				    "not supported: %s\n"), protocol);
5091				return (SA_SYNTAX_ERR);
5092			}
5093			protocol = optarg;
5094			if (!sa_valid_protocol(protocol)) {
5095				(void) printf(gettext(
5096				    "Invalid protocol specified: %s\n"),
5097				    protocol);
5098				return (SA_INVALID_PROTOCOL);
5099			}
5100			break;
5101		case 'v':
5102			verbose++;
5103			break;
5104		case 'h':
5105			/* optopt on valid arg isn't defined */
5106			optopt = c;
5107			/*FALLTHROUGH*/
5108		case '?':
5109		default:
5110			/*
5111			 * Since a bad option gets to here, sort it
5112			 * out and return a syntax error return value
5113			 * if necessary.
5114			 */
5115			ret = SA_OK;
5116			switch (optopt) {
5117			default:
5118				ret = SA_SYNTAX_ERR;
5119				break;
5120			case 'h':
5121			case '?':
5122				break;
5123			}
5124			(void) printf(gettext("usage: %s\n"),
5125			    sa_get_usage(USAGE_STOP));
5126			return (ret);
5127		}
5128	}
5129
5130	if (optind == argc && !all) {
5131		(void) printf(gettext("usage: %s\n"),
5132		    sa_get_usage(USAGE_STOP));
5133		return (SMF_EXIT_ERR_FATAL);
5134	} else if (!all) {
5135		while (optind < argc) {
5136			group = sa_get_group(handle, argv[optind]);
5137			if (group != NULL) {
5138				state = sa_get_group_attr(group, "state");
5139				if (state == NULL ||
5140				    strcmp(state, "enabled") == 0) {
5141					worklist = add_list(worklist, group, 0,
5142					    protocol);
5143					if (verbose)
5144						(void) printf(gettext(
5145						    "Stopping group \"%s\"\n"),
5146						    argv[optind]);
5147				} else {
5148					ret = SMF_EXIT_OK;
5149				}
5150				if (state != NULL)
5151					sa_free_attr_string(state);
5152			}
5153			optind++;
5154		}
5155	} else {
5156		for (group = sa_get_group(handle, NULL);
5157		    group != NULL;
5158		    group = sa_get_next_group(group)) {
5159			state = sa_get_group_attr(group, "state");
5160			if (state == NULL || strcmp(state, "enabled") == 0)
5161				worklist = add_list(worklist, group, 0,
5162				    protocol);
5163			if (state != NULL)
5164				sa_free_attr_string(state);
5165		}
5166	}
5167	(void) disable_all_groups(handle, worklist, 0);
5168	ret = sa_update_config(handle);
5169
5170	if (worklist != NULL)
5171		free_list(worklist);
5172	return (ret);
5173}
5174
5175/*
5176 * remove_all_options(share, proto)
5177 *
5178 * Removes all options on a share.
5179 */
5180
5181static void
5182remove_all_options(sa_share_t share, char *proto)
5183{
5184	sa_optionset_t optionset;
5185	sa_security_t security;
5186	sa_security_t prevsec = NULL;
5187
5188	optionset = sa_get_optionset(share, proto);
5189	if (optionset != NULL)
5190		(void) sa_destroy_optionset(optionset);
5191	for (security = sa_get_security(share, NULL, NULL);
5192	    security != NULL;
5193	    security = sa_get_next_security(security)) {
5194		char *type;
5195		/*
5196		 * We walk through the list.  prevsec keeps the
5197		 * previous security so we can delete it without
5198		 * destroying the list.
5199		 */
5200		if (prevsec != NULL) {
5201			/* remove the previously seen security */
5202			(void) sa_destroy_security(prevsec);
5203			/* set to NULL so we don't try multiple times */
5204			prevsec = NULL;
5205		}
5206		type = sa_get_security_attr(security, "type");
5207		if (type != NULL) {
5208			/*
5209			 * if the security matches the specified protocol, we
5210			 * want to remove it. prevsec holds it until either
5211			 * the next pass or we fall out of the loop.
5212			 */
5213			if (strcmp(type, proto) == 0)
5214				prevsec = security;
5215			sa_free_attr_string(type);
5216		}
5217	}
5218	/* in case there is one left */
5219	if (prevsec != NULL)
5220		(void) sa_destroy_security(prevsec);
5221}
5222
5223
5224/*
5225 * for legacy support, we need to handle the old syntax. This is what
5226 * we get if sharemgr is called with the name "share" rather than
5227 * sharemgr.
5228 */
5229
5230static int
5231format_legacy_path(char *buff, int buffsize, char *proto, char *cmd)
5232{
5233	int err;
5234
5235	err = snprintf(buff, buffsize, "/usr/lib/fs/%s/%s", proto, cmd);
5236	if (err > buffsize)
5237		return (-1);
5238	return (0);
5239}
5240
5241
5242/*
5243 * check_legacy_cmd(proto, cmd)
5244 *
5245 * Check to see if the cmd exists in /usr/lib/fs/<proto>/<cmd> and is
5246 * executable.
5247 */
5248
5249static int
5250check_legacy_cmd(char *path)
5251{
5252	struct stat st;
5253	int ret = 0;
5254
5255	if (stat(path, &st) == 0) {
5256		if (S_ISREG(st.st_mode) &&
5257		    st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
5258			ret = 1;
5259	}
5260	return (ret);
5261}
5262
5263/*
5264 * run_legacy_command(proto, cmd, argv)
5265 *
5266 * We know the command exists, so attempt to execute it with all the
5267 * arguments. This implements full legacy share support for those
5268 * protocols that don't have plugin providers.
5269 */
5270
5271static int
5272run_legacy_command(char *path, char *argv[])
5273{
5274	int ret;
5275
5276	ret = execv(path, argv);
5277	if (ret < 0) {
5278		switch (errno) {
5279		case EACCES:
5280			ret = SA_NO_PERMISSION;
5281			break;
5282		default:
5283			ret = SA_SYSTEM_ERR;
5284			break;
5285		}
5286	}
5287	return (ret);
5288}
5289
5290/*
5291 * out_share(out, group, proto)
5292 *
5293 * Display the share information in the format that the "share"
5294 * command has traditionally used.
5295 */
5296
5297static void
5298out_share(FILE *out, sa_group_t group, char *proto)
5299{
5300	sa_share_t share;
5301	char resfmt[128];
5302	char *defprop;
5303
5304	/*
5305	 * The original share command defaulted to displaying NFS
5306	 * shares or allowed a protocol to be specified. We want to
5307	 * skip those shares that are not the specified protocol.
5308	 */
5309	if (proto != NULL && sa_get_optionset(group, proto) == NULL)
5310		return;
5311
5312	if (proto == NULL)
5313		proto = "nfs";
5314
5315	/*
5316	 * get the default property string.  NFS uses "rw" but
5317	 * everything else will use "".
5318	 */
5319	if (proto != NULL && strcmp(proto, "nfs") != 0)
5320		defprop = "\"\"";
5321	else
5322		defprop = "rw";
5323
5324	for (share = sa_get_share(group, NULL);
5325	    share != NULL;
5326	    share = sa_get_next_share(share)) {
5327		char *path;
5328		char *type;
5329		char *resource;
5330		char *description;
5331		char *groupname;
5332		char *sharedstate;
5333		int shared = 1;
5334		char *soptions;
5335		char shareopts[MAXNAMLEN];
5336
5337		sharedstate = sa_get_share_attr(share, "shared");
5338		path = sa_get_share_attr(share, "path");
5339		type = sa_get_share_attr(share, "type");
5340		resource = get_resource(share);
5341		groupname = sa_get_group_attr(group, "name");
5342
5343		if (groupname != NULL && strcmp(groupname, "default") == 0) {
5344			sa_free_attr_string(groupname);
5345			groupname = NULL;
5346		}
5347		description = sa_get_share_description(share);
5348
5349		/*
5350		 * Want the sharetab version if it exists, defaulting
5351		 * to NFS if no protocol specified.
5352		 */
5353		(void) snprintf(shareopts, MAXNAMLEN, "shareopts-%s", proto);
5354		soptions = sa_get_share_attr(share, shareopts);
5355
5356		if (sharedstate == NULL)
5357			shared = 0;
5358
5359		if (soptions == NULL)
5360			soptions = sa_proto_legacy_format(proto, share, 1);
5361
5362		if (shared) {
5363			/* only active shares go here */
5364			(void) snprintf(resfmt, sizeof (resfmt), "%s%s%s",
5365			    resource != NULL ? resource : "-",
5366			    groupname != NULL ? "@" : "",
5367			    groupname != NULL ? groupname : "");
5368			(void) fprintf(out, "%-14.14s  %s   %s   \"%s\"  \n",
5369			    resfmt, (path != NULL) ? path : "",
5370			    (soptions != NULL && strlen(soptions) > 0) ?
5371			    soptions : defprop,
5372			    (description != NULL) ? description : "");
5373		}
5374
5375		if (path != NULL)
5376			sa_free_attr_string(path);
5377		if (type != NULL)
5378			sa_free_attr_string(type);
5379		if (resource != NULL)
5380			sa_free_attr_string(resource);
5381		if (groupname != NULL)
5382			sa_free_attr_string(groupname);
5383		if (description != NULL)
5384			sa_free_share_description(description);
5385		if (sharedstate != NULL)
5386			sa_free_attr_string(sharedstate);
5387		if (soptions != NULL)
5388			sa_format_free(soptions);
5389	}
5390}
5391
5392/*
5393 * output_legacy_file(out, proto)
5394 *
5395 * Walk all of the groups for the specified protocol and call
5396 * out_share() to format and write in the format displayed by the
5397 * "share" command with no arguments.
5398 */
5399
5400static void
5401output_legacy_file(FILE *out, char *proto, sa_handle_t handle)
5402{
5403	sa_group_t group;
5404
5405	for (group = sa_get_group(handle, NULL);
5406	    group != NULL;
5407	    group = sa_get_next_group(group)) {
5408		char *zfs;
5409
5410		/*
5411		 * Go through all the groups and ZFS
5412		 * sub-groups. out_share() will format the shares in
5413		 * the group appropriately.
5414		 */
5415
5416		zfs = sa_get_group_attr(group, "zfs");
5417		if (zfs != NULL) {
5418			sa_group_t zgroup;
5419			sa_free_attr_string(zfs);
5420			for (zgroup = sa_get_sub_group(group);
5421			    zgroup != NULL;
5422			    zgroup = sa_get_next_group(zgroup)) {
5423
5424				/* got a group, so display it */
5425				out_share(out, zgroup, proto);
5426			}
5427		} else {
5428			out_share(out, group, proto);
5429		}
5430	}
5431}
5432
5433int
5434sa_legacy_share(sa_handle_t handle, int flags, int argc, char *argv[])
5435{
5436	char *protocol = "nfs";
5437	char *options = NULL;
5438	char *description = NULL;
5439	char *groupname = NULL;
5440	char *sharepath = NULL;
5441	char *resource = NULL;
5442	char *groupstatus = NULL;
5443	int persist = SA_SHARE_TRANSIENT;
5444	int argsused = 0;
5445	int c;
5446	int ret = SA_OK;
5447	int zfs = 0;
5448	int true_legacy = 0;
5449	int curtype = SA_SHARE_TRANSIENT;
5450	char cmd[MAXPATHLEN];
5451	sa_group_t group = NULL;
5452	sa_resource_t rsrc = NULL;
5453	sa_share_t share;
5454	char dir[MAXPATHLEN];
5455	uint64_t features;
5456#ifdef lint
5457	flags = flags;
5458#endif
5459
5460	while ((c = getopt(argc, argv, "?hF:d:o:p")) != EOF) {
5461		switch (c) {
5462		case 'd':
5463			description = optarg;
5464			argsused++;
5465			break;
5466		case 'F':
5467			protocol = optarg;
5468			if (!sa_valid_protocol(protocol)) {
5469				if (format_legacy_path(cmd, MAXPATHLEN,
5470				    protocol, "share") == 0 &&
5471				    check_legacy_cmd(cmd)) {
5472					true_legacy++;
5473				} else {
5474					(void) fprintf(stderr, gettext(
5475					    "Invalid protocol specified: "
5476					    "%s\n"), protocol);
5477					return (SA_INVALID_PROTOCOL);
5478				}
5479			}
5480			break;
5481		case 'o':
5482			options = optarg;
5483			argsused++;
5484			break;
5485		case 'p':
5486			persist = SA_SHARE_PERMANENT;
5487			argsused++;
5488			break;
5489		case 'h':
5490			/* optopt on valid arg isn't defined */
5491			optopt = c;
5492			/*FALLTHROUGH*/
5493		case '?':
5494		default:
5495			/*
5496			 * Since a bad option gets to here, sort it
5497			 * out and return a syntax error return value
5498			 * if necessary.
5499			 */
5500			switch (optopt) {
5501			default:
5502				ret = SA_LEGACY_ERR;
5503				break;
5504			case 'h':
5505			case '?':
5506				break;
5507			}
5508			(void) fprintf(stderr, gettext("usage: %s\n"),
5509			    sa_get_usage(USAGE_SHARE));
5510			return (ret);
5511		}
5512	}
5513
5514	/* Have the info so construct what is needed */
5515	if (!argsused && optind == argc) {
5516		/* display current info in share format */
5517		(void) output_legacy_file(stdout, protocol, handle);
5518		return (ret);
5519	}
5520
5521	/* We are modifying the configuration */
5522	if (optind == argc) {
5523		(void) fprintf(stderr, gettext("usage: %s\n"),
5524		    sa_get_usage(USAGE_SHARE));
5525		return (SA_LEGACY_ERR);
5526	}
5527	if (true_legacy) {
5528		/* If still using legacy share/unshare, exec it */
5529		ret = run_legacy_command(cmd, argv);
5530		return (ret);
5531	}
5532
5533	sharepath = argv[optind++];
5534	if (optind < argc) {
5535		resource = argv[optind];
5536		groupname = strchr(resource, '@');
5537		if (groupname != NULL)
5538			*groupname++ = '\0';
5539	}
5540	if (realpath(sharepath, dir) == NULL)
5541		ret = SA_BAD_PATH;
5542	else
5543		sharepath = dir;
5544	if (ret == SA_OK)
5545		share = sa_find_share(handle, sharepath);
5546	else
5547		share = NULL;
5548
5549	features = sa_proto_get_featureset(protocol);
5550
5551	if (groupname != NULL) {
5552		ret = SA_NOT_ALLOWED;
5553	} else if (ret == SA_OK) {
5554		char *legacygroup;
5555		/*
5556		 * The legacy group is always present and zfs groups
5557		 * come and go.  zfs shares may be in sub-groups and
5558		 * the zfs share will already be in that group so it
5559		 * isn't an error. If the protocol is "smb", the group
5560		 * "smb" is used when "default" would otherwise be
5561		 * used.  "default" is NFS only and "smb" is SMB only.
5562		 */
5563		if (strcmp(protocol, "smb") == 0)
5564			legacygroup = "smb";
5565		else
5566			legacygroup = "default";
5567
5568		/*
5569		 * If the share exists (not NULL), then make sure it
5570		 * is one we want to handle by getting the parent
5571		 * group.
5572		 */
5573		if (share != NULL) {
5574			group = sa_get_parent_group(share);
5575		} else {
5576			group = sa_get_group(handle, legacygroup);
5577			if (group == NULL && strcmp(legacygroup, "smb") == 0) {
5578				/*
5579				 * This group may not exist, so create
5580				 * as necessary. It only contains the
5581				 * "smb" protocol.
5582				 */
5583				group = sa_create_group(handle, legacygroup,
5584				    &ret);
5585				if (group != NULL)
5586					(void) sa_create_optionset(group,
5587					    protocol);
5588			}
5589		}
5590
5591		if (group == NULL) {
5592			ret = SA_SYSTEM_ERR;
5593			goto err;
5594		}
5595
5596		groupstatus = group_status(group);
5597		if (share == NULL) {
5598			share = sa_add_share(group, sharepath,
5599			    persist, &ret);
5600			if (share == NULL &&
5601			    ret == SA_DUPLICATE_NAME) {
5602				/*
5603				 * Could be a ZFS path being started
5604				 */
5605				if (sa_zfs_is_shared(handle,
5606				    sharepath)) {
5607					ret = SA_OK;
5608					group = sa_get_group(handle,
5609					    "zfs");
5610					if (group == NULL) {
5611						/*
5612						 * This shouldn't
5613						 * happen.
5614						 */
5615						ret = SA_CONFIG_ERR;
5616					} else {
5617						share = sa_add_share(
5618						    group, sharepath,
5619						    persist, &ret);
5620					}
5621				}
5622			}
5623		} else {
5624			char *type;
5625			/*
5626			 * May want to change persist state, but the
5627			 * important thing is to change options. We
5628			 * need to change them regardless of the
5629			 * source.
5630			 */
5631
5632			if (sa_zfs_is_shared(handle, sharepath)) {
5633				zfs = 1;
5634			}
5635			remove_all_options(share, protocol);
5636			type = sa_get_share_attr(share, "type");
5637			if (type != NULL &&
5638			    strcmp(type, "transient") != 0) {
5639				curtype = SA_SHARE_PERMANENT;
5640			}
5641			if (type != NULL)
5642				sa_free_attr_string(type);
5643			if (curtype != persist) {
5644				(void) sa_set_share_attr(share, "type",
5645				    persist == SA_SHARE_PERMANENT ?
5646				    "persist" : "transient");
5647			}
5648		}
5649
5650		/*
5651		 * If there is a resource name, we may
5652		 * actually care about it if this is share for
5653		 * a protocol that uses resource level sharing
5654		 * (SMB). We need to find the resource and, if
5655		 * it exists, make sure it belongs to the
5656		 * current share. If it doesn't exist, attempt
5657		 * to create it.
5658		 */
5659
5660		if (ret == SA_OK && resource != NULL) {
5661			rsrc = sa_find_resource(handle, resource);
5662			if (rsrc != NULL) {
5663				if (share != sa_get_resource_parent(rsrc))
5664					ret = SA_DUPLICATE_NAME;
5665			} else {
5666				rsrc = sa_add_resource(share, resource,
5667				    persist, &ret);
5668			}
5669			if (features & SA_FEATURE_RESOURCE)
5670				share = rsrc;
5671		}
5672
5673		/* Have a group to hold this share path */
5674		if (ret == SA_OK && options != NULL &&
5675		    strlen(options) > 0) {
5676			ret = sa_parse_legacy_options(share,
5677			    options,
5678			    protocol);
5679		}
5680		if (!zfs) {
5681			/*
5682			 * ZFS shares never have a description
5683			 * and we can't store the values so
5684			 * don't try.
5685			 */
5686			if (ret == SA_OK && description != NULL)
5687				ret = sa_set_share_description(share,
5688				    description);
5689		}
5690		if (ret == SA_OK &&
5691		    strcmp(groupstatus, "enabled") == 0) {
5692			if (rsrc != share)
5693				ret = sa_enable_share(share, protocol);
5694			else
5695				ret = sa_enable_resource(rsrc,
5696				    protocol);
5697			if (ret == SA_OK &&
5698			    persist == SA_SHARE_PERMANENT) {
5699				(void) sa_update_legacy(share,
5700				    protocol);
5701			}
5702			if (ret == SA_OK)
5703				ret = sa_update_config(handle);
5704		}
5705	}
5706err:
5707	if (ret != SA_OK) {
5708		(void) fprintf(stderr, gettext("Could not share: %s: %s\n"),
5709		    sharepath, sa_errorstr(ret));
5710		ret = SA_LEGACY_ERR;
5711	}
5712	return (ret);
5713}
5714
5715/*
5716 * sa_legacy_unshare(flags, argc, argv)
5717 *
5718 * Implements the original unshare command.
5719 */
5720int
5721sa_legacy_unshare(sa_handle_t handle, int flags, int argc, char *argv[])
5722{
5723	char *protocol = "nfs"; /* for now */
5724	char *options = NULL;
5725	char *sharepath = NULL;
5726	int persist = SA_SHARE_TRANSIENT;
5727	int argsused = 0;
5728	int c;
5729	int ret = SA_OK;
5730	int true_legacy = 0;
5731	uint64_t features = 0;
5732	sa_resource_t resource = NULL;
5733	char cmd[MAXPATHLEN];
5734#ifdef lint
5735	flags = flags;
5736	options = options;
5737#endif
5738
5739	while ((c = getopt(argc, argv, "?hF:o:p")) != EOF) {
5740		switch (c) {
5741		case 'F':
5742			protocol = optarg;
5743			if (!sa_valid_protocol(protocol)) {
5744				if (format_legacy_path(cmd, MAXPATHLEN,
5745				    protocol, "unshare") == 0 &&
5746				    check_legacy_cmd(cmd)) {
5747					true_legacy++;
5748				} else {
5749					(void) printf(gettext(
5750					    "Invalid file system name\n"));
5751					return (SA_INVALID_PROTOCOL);
5752				}
5753			}
5754			break;
5755		case 'o':
5756			options = optarg;
5757			argsused++;
5758			break;
5759		case 'p':
5760			persist = SA_SHARE_PERMANENT;
5761			argsused++;
5762			break;
5763		case 'h':
5764			/* optopt on valid arg isn't defined */
5765			optopt = c;
5766			/*FALLTHROUGH*/
5767		case '?':
5768		default:
5769			/*
5770			 * Since a bad option gets to here, sort it
5771			 * out and return a syntax error return value
5772			 * if necessary.
5773			 */
5774			switch (optopt) {
5775			default:
5776				ret = SA_LEGACY_ERR;
5777				break;
5778			case 'h':
5779			case '?':
5780				break;
5781			}
5782			(void) printf(gettext("usage: %s\n"),
5783			    sa_get_usage(USAGE_UNSHARE));
5784			return (ret);
5785		}
5786	}
5787
5788	/* Have the info so construct what is needed */
5789	if (optind == argc || (optind + 1) < argc || options != NULL) {
5790		ret = SA_SYNTAX_ERR;
5791	} else {
5792		sa_share_t share;
5793		char dir[MAXPATHLEN];
5794		if (true_legacy) {
5795			/* if still using legacy share/unshare, exec it */
5796			ret = run_legacy_command(cmd, argv);
5797			return (ret);
5798		}
5799		/*
5800		 * Find the path in the internal configuration. If it
5801		 * isn't found, attempt to resolve the path via
5802		 * realpath() and try again.
5803		 */
5804		sharepath = argv[optind++];
5805		share = sa_find_share(handle, sharepath);
5806		if (share == NULL) {
5807			if (realpath(sharepath, dir) == NULL) {
5808				ret = SA_NO_SUCH_PATH;
5809			} else {
5810				share = sa_find_share(handle, dir);
5811			}
5812		}
5813		if (share == NULL) {
5814			/* Could be a resource name so check that next */
5815			features = sa_proto_get_featureset(protocol);
5816			resource = sa_find_resource(handle, sharepath);
5817			if (resource != NULL) {
5818				share = sa_get_resource_parent(resource);
5819				if (features & SA_FEATURE_RESOURCE)
5820					(void) sa_disable_resource(resource,
5821					    protocol);
5822				if (persist == SA_SHARE_PERMANENT) {
5823					ret = sa_remove_resource(resource);
5824					if (ret == SA_OK)
5825						ret = sa_update_config(handle);
5826				}
5827				/*
5828				 * If we still have a resource on the
5829				 * share, we don't disable the share
5830				 * itself. IF there aren't anymore, we
5831				 * need to remove the share. The
5832				 * removal will be done in the next
5833				 * section if appropriate.
5834				 */
5835				resource = sa_get_share_resource(share, NULL);
5836				if (resource != NULL)
5837					share = NULL;
5838			} else if (ret == SA_OK) {
5839				/* Didn't find path and no  resource */
5840				ret = SA_BAD_PATH;
5841			}
5842		}
5843		if (share != NULL && resource == NULL) {
5844			ret = sa_disable_share(share, protocol);
5845			/*
5846			 * Errors are ok and removal should still occur. The
5847			 * legacy unshare is more forgiving of errors than the
5848			 * remove-share subcommand which may need the force
5849			 * flag set for some error conditions. That is, the
5850			 * "unshare" command will always unshare if it can
5851			 * while "remove-share" might require the force option.
5852			 */
5853			if (persist == SA_SHARE_PERMANENT) {
5854				ret = sa_remove_share(share);
5855				if (ret == SA_OK)
5856					ret = sa_update_config(handle);
5857			}
5858		} else if (ret == SA_OK && share == NULL && resource == NULL) {
5859			/*
5860			 * If both share and resource are NULL, then
5861			 * share not found. If one or the other was
5862			 * found or there was an earlier error, we
5863			 * assume it was handled earlier.
5864			 */
5865			ret = SA_NOT_SHARED;
5866		}
5867	}
5868	switch (ret) {
5869	default:
5870		(void) printf("%s: %s\n", sharepath, sa_errorstr(ret));
5871		ret = SA_LEGACY_ERR;
5872		break;
5873	case SA_SYNTAX_ERR:
5874		(void) printf(gettext("usage: %s\n"),
5875		    sa_get_usage(USAGE_UNSHARE));
5876		break;
5877	case SA_OK:
5878		break;
5879	}
5880	return (ret);
5881}
5882
5883/*
5884 * Common commands that implement the sub-commands used by all
5885 * protocols. The entries are found via the lookup command
5886 */
5887
5888static sa_command_t commands[] = {
5889	{"add-share", 0, sa_addshare, USAGE_ADD_SHARE, SVC_SET},
5890	{"create", 0, sa_create, USAGE_CREATE, SVC_SET|SVC_ACTION},
5891	{"delete", 0, sa_delete, USAGE_DELETE, SVC_SET|SVC_ACTION},
5892	{"disable", 0, sa_disable_group, USAGE_DISABLE, SVC_SET|SVC_ACTION},
5893	{"enable", 0, sa_enable_group, USAGE_ENABLE, SVC_SET|SVC_ACTION},
5894	{"list", 0, sa_list, USAGE_LIST},
5895	{"move-share", 0, sa_moveshare, USAGE_MOVE_SHARE, SVC_SET},
5896	{"remove-share", 0, sa_removeshare, USAGE_REMOVE_SHARE, SVC_SET},
5897	{"set", 0, sa_set, USAGE_SET, SVC_SET},
5898	{"set-share", 0, sa_set_share, USAGE_SET_SHARE, SVC_SET},
5899	{"show", 0, sa_show, USAGE_SHOW},
5900	{"share", 0, sa_legacy_share, USAGE_SHARE, SVC_SET|SVC_ACTION},
5901	{"start", CMD_NODISPLAY, sa_start_group, USAGE_START,
5902	    SVC_SET|SVC_ACTION},
5903	{"stop", CMD_NODISPLAY, sa_stop_group, USAGE_STOP, SVC_SET|SVC_ACTION},
5904	{"unset", 0, sa_unset, USAGE_UNSET, SVC_SET},
5905	{"unshare", 0, sa_legacy_unshare, USAGE_UNSHARE, SVC_SET|SVC_ACTION},
5906	{NULL, 0, NULL, 0}
5907};
5908
5909static char *
5910sa_get_usage(sa_usage_t index)
5911{
5912	char *ret = NULL;
5913	switch (index) {
5914	case USAGE_ADD_SHARE:
5915		ret = gettext("add-share [-nth] [-r resource-name] "
5916		    "[-d \"description text\"] -s sharepath group");
5917		break;
5918	case USAGE_CREATE:
5919		ret = gettext(
5920		    "create [-nvh] [-P proto [-p property=value]] group");
5921		break;
5922	case USAGE_DELETE:
5923		ret = gettext("delete [-nvh] [-P proto] [-f] group");
5924		break;
5925	case USAGE_DISABLE:
5926		ret = gettext("disable [-nvh] {-a | group ...}");
5927		break;
5928	case USAGE_ENABLE:
5929		ret = gettext("enable [-nvh] {-a | group ...}");
5930		break;
5931	case USAGE_LIST:
5932		ret = gettext("list [-vh] [-P proto]");
5933		break;
5934	case USAGE_MOVE_SHARE:
5935		ret = gettext(
5936		    "move-share [-nvh] -s sharepath destination-group");
5937		break;
5938	case USAGE_REMOVE_SHARE:
5939		ret = gettext(
5940		    "remove-share [-fnvh] {-s sharepath | -r resource} "
5941		    "group");
5942		break;
5943	case USAGE_SET:
5944		ret = gettext("set [-nvh] -P proto [-S optspace] "
5945		    "[-p property=value]* [-s sharepath] [-r resource]] "
5946		    "group");
5947		break;
5948	case USAGE_SET_SECURITY:
5949		ret = gettext("set-security [-nvh] -P proto -S security-type "
5950		    "[-p property=value]* group");
5951		break;
5952	case USAGE_SET_SHARE:
5953		ret = gettext("set-share [-nh] [-r resource] "
5954		    "[-d \"description text\"] -s sharepath group");
5955		break;
5956	case USAGE_SHOW:
5957		ret = gettext("show [-pvxh] [-P proto] [group ...]");
5958		break;
5959	case USAGE_SHARE:
5960		ret = gettext("share [-F fstype] [-p] [-o optionlist]"
5961		    "[-d description] [pathname [resourcename]]");
5962		break;
5963	case USAGE_START:
5964		ret = gettext("start [-vh] [-P proto] {-a | group ...}");
5965		break;
5966	case USAGE_STOP:
5967		ret = gettext("stop [-vh] [-P proto] {-a | group ...}");
5968		break;
5969	case USAGE_UNSET:
5970		ret = gettext("unset [-nvh] -P proto [-S optspace] "
5971		    "[-p property]* group");
5972		break;
5973	case USAGE_UNSET_SECURITY:
5974		ret = gettext("unset-security [-nvh] -P proto "
5975		    "-S security-type [-p property]* group");
5976		break;
5977	case USAGE_UNSHARE:
5978		ret = gettext(
5979		    "unshare [-F fstype] [-p] [-o optionlist] sharepath");
5980		break;
5981	}
5982	return (ret);
5983}
5984
5985/*
5986 * sa_lookup(cmd, proto)
5987 *
5988 * Lookup the sub-command. proto isn't currently used, but it may
5989 * eventually provide a way to provide protocol specific sub-commands.
5990 */
5991sa_command_t *
5992sa_lookup(char *cmd, char *proto)
5993{
5994	int i;
5995	size_t len;
5996#ifdef lint
5997	proto = proto;
5998#endif
5999
6000	len = strlen(cmd);
6001	for (i = 0; commands[i].cmdname != NULL; i++) {
6002		if (strncmp(cmd, commands[i].cmdname, len) == 0)
6003			return (&commands[i]);
6004	}
6005	return (NULL);
6006}
6007
6008void
6009sub_command_help(char *proto)
6010{
6011	int i;
6012#ifdef lint
6013	proto = proto;
6014#endif
6015
6016	(void) printf(gettext("\tsub-commands:\n"));
6017	for (i = 0; commands[i].cmdname != NULL; i++) {
6018		if (!(commands[i].flags & (CMD_ALIAS|CMD_NODISPLAY)))
6019			(void) printf("\t%s\n",
6020			    sa_get_usage((sa_usage_t)commands[i].cmdidx));
6021	}
6022}
6023