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