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