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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <fcntl.h> 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <string.h> 33 #include <ctype.h> 34 #include <unistd.h> 35 #include <getopt.h> 36 #include <utmpx.h> 37 #include <pwd.h> 38 #include <auth_attr.h> 39 #include <secdb.h> 40 #include <sys/param.h> 41 #include <sys/stat.h> 42 #include <errno.h> 43 44 #include <libshare.h> 45 #include "sharemgr.h" 46 #include <libscf.h> 47 #include <libxml/tree.h> 48 #include <libintl.h> 49 #include <assert.h> 50 #include <iconv.h> 51 #include <langinfo.h> 52 #include <dirent.h> 53 54 static char *sa_get_usage(sa_usage_t); 55 56 /* 57 * Implementation of the common sub-commands supported by sharemgr. 58 * A number of helper functions are also included. 59 */ 60 61 /* 62 * has_protocol(group, proto) 63 * If the group has an optionset with the specified protocol, 64 * return true (1) otherwise false (0). 65 */ 66 static int 67 has_protocol(sa_group_t group, char *protocol) 68 { 69 sa_optionset_t optionset; 70 int result = 0; 71 72 optionset = sa_get_optionset(group, protocol); 73 if (optionset != NULL) { 74 result++; 75 } 76 return (result); 77 } 78 79 /* 80 * validresource(name) 81 * 82 * Check that name only has valid characters in it. The current valid 83 * set are the printable characters but not including: 84 * " / \ [ ] : | < > + ; , ? * = \t 85 * Note that space is included and there is a maximum length. 86 */ 87 static int 88 validresource(const char *name) 89 { 90 const char *cp; 91 size_t len; 92 93 if (name == NULL) 94 return (B_FALSE); 95 96 len = strlen(name); 97 if (len == 0 || len > SA_MAX_RESOURCE_NAME) 98 return (B_FALSE); 99 100 if (strpbrk(name, "\"/\\[]:|<>+;,?*=\t") != NULL) { 101 return (B_FALSE); 102 } 103 104 for (cp = name; *cp != '\0'; cp++) 105 if (iscntrl(*cp)) 106 return (B_FALSE); 107 108 return (B_TRUE); 109 } 110 111 /* 112 * conv_to_utf8(input) 113 * 114 * Convert the input string to utf8 from the current locale. If the 115 * conversion fails, use the current locale, it is likely close 116 * enough. For example, the "C" locale is a subset of utf-8. The 117 * return value may be a new string or the original input string. 118 */ 119 120 static char * 121 conv_to_utf8(char *input) 122 { 123 iconv_t cd; 124 char *inval = input; 125 char *output = input; 126 char *outleft; 127 char *curlocale; 128 size_t bytesleft; 129 size_t size; 130 size_t osize; 131 static int warned = 0; 132 133 curlocale = nl_langinfo(CODESET); 134 if (curlocale == NULL) 135 curlocale = "C"; 136 cd = iconv_open("UTF-8", curlocale); 137 if (cd != NULL && cd != (iconv_t)-1) { 138 size = strlen(input); 139 /* Assume worst case of characters expanding to 4 bytes. */ 140 bytesleft = size * 4; 141 output = calloc(bytesleft, 1); 142 if (output != NULL) { 143 outleft = output; 144 /* inval can be modified on return */ 145 osize = iconv(cd, (const char **)&inval, &size, 146 &outleft, &bytesleft); 147 if (osize == (size_t)-1 || size != 0) { 148 free(output); 149 output = input; 150 } 151 } else { 152 /* Need to return something. */ 153 output = input; 154 } 155 (void) iconv_close(cd); 156 } else { 157 if (!warned) 158 (void) fprintf(stderr, 159 gettext("Cannot convert to UTF-8 from %s\n"), 160 curlocale ? curlocale : gettext("unknown")); 161 warned = 1; 162 } 163 return (output); 164 } 165 166 /* 167 * conv_from(input) 168 * 169 * Convert the input string from utf8 to current locale. If the 170 * conversion isn't supported, just use as is. The return value may be 171 * a new string or the original input string. 172 */ 173 174 static char * 175 conv_from_utf8(char *input) 176 { 177 iconv_t cd; 178 char *output = input; 179 char *inval = input; 180 char *outleft; 181 char *curlocale; 182 size_t bytesleft; 183 size_t size; 184 size_t osize; 185 static int warned = 0; 186 187 curlocale = nl_langinfo(CODESET); 188 if (curlocale == NULL) 189 curlocale = "C"; 190 cd = iconv_open(curlocale, "UTF-8"); 191 if (cd != NULL && cd != (iconv_t)-1) { 192 size = strlen(input); 193 /* Assume worst case of characters expanding to 4 bytes. */ 194 bytesleft = size * 4; 195 output = calloc(bytesleft, 1); 196 if (output != NULL) { 197 outleft = output; 198 osize = iconv(cd, (const char **)&inval, &size, 199 &outleft, &bytesleft); 200 if (osize == (size_t)-1 || size != 0) 201 output = input; 202 } else { 203 /* Need to return something. */ 204 output = input; 205 } 206 (void) iconv_close(cd); 207 } else { 208 if (!warned) 209 (void) fprintf(stderr, 210 gettext("Cannot convert to %s from UTF-8\n"), 211 curlocale ? curlocale : gettext("unknown")); 212 warned = 1; 213 } 214 return (output); 215 } 216 217 /* 218 * print_rsrc_desc(resource, sharedesc) 219 * 220 * Print the resource description string after converting from UTF8 to 221 * the current locale. If sharedesc is not NULL and there is no 222 * description on the resource, use sharedesc. sharedesc will already 223 * be converted to UTF8. 224 */ 225 226 static void 227 print_rsrc_desc(sa_resource_t resource, char *sharedesc) 228 { 229 char *description; 230 char *desc; 231 232 if (resource == NULL) 233 return; 234 235 description = sa_get_resource_description(resource); 236 if (description != NULL) { 237 desc = conv_from_utf8(description); 238 if (desc != description) { 239 sa_free_share_description(description); 240 description = desc; 241 } 242 } else if (sharedesc != NULL) { 243 description = strdup(sharedesc); 244 } 245 if (description != NULL) { 246 (void) printf("\t\"%s\"", description); 247 sa_free_share_description(description); 248 } 249 } 250 251 /* 252 * set_resource_desc(share, description) 253 * 254 * Set the share description value after converting the description 255 * string to UTF8 from the current locale. 256 */ 257 258 static int 259 set_resource_desc(sa_share_t share, char *description) 260 { 261 char *desc; 262 int ret; 263 264 desc = conv_to_utf8(description); 265 ret = sa_set_resource_description(share, desc); 266 if (description != desc) 267 sa_free_share_description(desc); 268 return (ret); 269 } 270 271 /* 272 * set_share_desc(share, description) 273 * 274 * Set the resource description value after converting the description 275 * string to UTF8 from the current locale. 276 */ 277 278 static int 279 set_share_desc(sa_share_t share, char *description) 280 { 281 char *desc; 282 int ret; 283 284 desc = conv_to_utf8(description); 285 ret = sa_set_share_description(share, desc); 286 if (description != desc) 287 sa_free_share_description(desc); 288 return (ret); 289 } 290 291 /* 292 * add_list(list, item, data, proto) 293 * Adds a new list member that points holds item in the list. 294 * If list is NULL, it starts a new list. The function returns 295 * the first member of the list. 296 */ 297 struct list * 298 add_list(struct list *listp, void *item, void *data, char *proto) 299 { 300 struct list *new, *tmp; 301 302 new = malloc(sizeof (struct list)); 303 if (new != NULL) { 304 new->next = NULL; 305 new->item = item; 306 new->itemdata = data; 307 new->proto = proto; 308 } else { 309 return (listp); 310 } 311 312 if (listp == NULL) 313 return (new); 314 315 for (tmp = listp; tmp->next != NULL; tmp = tmp->next) { 316 /* get to end of list */ 317 } 318 tmp->next = new; 319 return (listp); 320 } 321 322 /* 323 * free_list(list) 324 * Given a list, free all the members of the list; 325 */ 326 static void 327 free_list(struct list *listp) 328 { 329 struct list *tmp; 330 while (listp != NULL) { 331 tmp = listp; 332 listp = listp->next; 333 free(tmp); 334 } 335 } 336 337 /* 338 * check_authorization(instname, which) 339 * 340 * Checks to see if the specific type of authorization in which is 341 * enabled for the user in this SMF service instance. 342 */ 343 344 static int 345 check_authorization(char *instname, int which) 346 { 347 scf_handle_t *handle = NULL; 348 scf_simple_prop_t *prop = NULL; 349 char svcstring[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1]; 350 char *authstr = NULL; 351 ssize_t numauths; 352 int ret = B_TRUE; 353 uid_t uid; 354 struct passwd *pw = NULL; 355 356 uid = getuid(); 357 pw = getpwuid(uid); 358 if (pw == NULL) { 359 ret = B_FALSE; 360 } else { 361 /* 362 * Since names are restricted to SA_MAX_NAME_LEN won't 363 * overflow. 364 */ 365 (void) snprintf(svcstring, sizeof (svcstring), "%s:%s", 366 SA_SVC_FMRI_BASE, instname); 367 handle = scf_handle_create(SCF_VERSION); 368 if (handle != NULL) { 369 if (scf_handle_bind(handle) == 0) { 370 switch (which) { 371 case SVC_SET: 372 prop = scf_simple_prop_get(handle, 373 svcstring, "general", 374 SVC_AUTH_VALUE); 375 break; 376 case SVC_ACTION: 377 prop = scf_simple_prop_get(handle, 378 svcstring, "general", 379 SVC_AUTH_ACTION); 380 break; 381 } 382 } 383 } 384 } 385 /* make sure we have an authorization string property */ 386 if (prop != NULL) { 387 int i; 388 numauths = scf_simple_prop_numvalues(prop); 389 for (ret = 0, i = 0; i < numauths; i++) { 390 authstr = scf_simple_prop_next_astring(prop); 391 if (authstr != NULL) { 392 /* check if this user has one of the strings */ 393 if (chkauthattr(authstr, pw->pw_name)) { 394 ret = 1; 395 break; 396 } 397 } 398 } 399 endauthattr(); 400 scf_simple_prop_free(prop); 401 } else { 402 /* no authorization string defined */ 403 ret = 0; 404 } 405 if (handle != NULL) 406 scf_handle_destroy(handle); 407 return (ret); 408 } 409 410 /* 411 * check_authorizations(instname, flags) 412 * 413 * check all the needed authorizations for the user in this service 414 * instance. Return value of 1(true) or 0(false) indicates whether 415 * there are authorizations for the user or not. 416 */ 417 418 static int 419 check_authorizations(char *instname, int flags) 420 { 421 int ret1 = 0; 422 int ret2 = 0; 423 int ret; 424 425 if (flags & SVC_SET) 426 ret1 = check_authorization(instname, SVC_SET); 427 if (flags & SVC_ACTION) 428 ret2 = check_authorization(instname, SVC_ACTION); 429 switch (flags) { 430 case SVC_ACTION: 431 ret = ret2; 432 break; 433 case SVC_SET: 434 ret = ret1; 435 break; 436 case SVC_ACTION|SVC_SET: 437 ret = ret1 & ret2; 438 break; 439 default: 440 /* if not flags set, we assume we don't need authorizations */ 441 ret = 1; 442 } 443 return (ret); 444 } 445 446 /* 447 * notify_or_enable_share(share, protocol) 448 * 449 * Since some protocols don't want an "enable" when properties change, 450 * this function will use the protocol specific notify function 451 * first. If that fails, it will then attempt to use the 452 * sa_enable_share(). "protocol" is the protocol that was specified 453 * on the command line. 454 */ 455 static void 456 notify_or_enable_share(sa_share_t share, char *protocol) 457 { 458 sa_group_t group; 459 sa_optionset_t opt; 460 int ret = SA_OK; 461 char *path; 462 char *groupproto; 463 sa_share_t parent = share; 464 465 /* If really a resource, get parent share */ 466 if (!sa_is_share(share)) { 467 parent = sa_get_resource_parent((sa_resource_t)share); 468 } 469 470 /* 471 * Now that we've got a share in "parent", make sure it has a path. 472 */ 473 path = sa_get_share_attr(parent, "path"); 474 if (path == NULL) 475 return; 476 477 group = sa_get_parent_group(parent); 478 479 if (group == NULL) { 480 sa_free_attr_string(path); 481 return; 482 } 483 for (opt = sa_get_optionset(group, NULL); 484 opt != NULL; 485 opt = sa_get_next_optionset(opt)) { 486 groupproto = sa_get_optionset_attr(opt, "type"); 487 if (groupproto == NULL || 488 (protocol != NULL && strcmp(groupproto, protocol) != 0)) { 489 sa_free_attr_string(groupproto); 490 continue; 491 } 492 if (sa_is_share(share)) { 493 if ((ret = sa_proto_change_notify(share, 494 groupproto)) != SA_OK) { 495 ret = sa_enable_share(share, groupproto); 496 if (ret != SA_OK) { 497 (void) printf( 498 gettext("Could not reenable" 499 " share %s: %s\n"), 500 path, sa_errorstr(ret)); 501 } 502 } 503 } else { 504 /* Must be a resource */ 505 if ((ret = sa_proto_notify_resource(share, 506 groupproto)) != SA_OK) { 507 ret = sa_enable_resource(share, groupproto); 508 if (ret != SA_OK) { 509 (void) printf( 510 gettext("Could not " 511 "reenable resource %s: " 512 "%s\n"), path, 513 sa_errorstr(ret)); 514 } 515 } 516 } 517 sa_free_attr_string(groupproto); 518 } 519 sa_free_attr_string(path); 520 } 521 522 /* 523 * enable_group(group, updateproto, notify, proto) 524 * 525 * enable all the shares in the specified group. This is a helper for 526 * enable_all_groups in order to simplify regular and subgroup (zfs) 527 * enabling. Group has already been checked for non-NULL. If notify 528 * is non-zero, attempt to use the notify interface rather than 529 * enable. 530 */ 531 static void 532 enable_group(sa_group_t group, char *updateproto, int notify, char *proto) 533 { 534 sa_share_t share; 535 536 for (share = sa_get_share(group, NULL); 537 share != NULL; 538 share = sa_get_next_share(share)) { 539 if (updateproto != NULL) 540 (void) sa_update_legacy(share, updateproto); 541 if (notify) 542 notify_or_enable_share(share, proto); 543 else 544 (void) sa_enable_share(share, proto); 545 } 546 } 547 548 /* 549 * isenabled(group) 550 * 551 * Returns B_TRUE if the group is enabled or B_FALSE if it isn't. 552 * Moved to separate function to reduce clutter in the code. 553 */ 554 555 static int 556 isenabled(sa_group_t group) 557 { 558 char *state; 559 int ret = B_FALSE; 560 561 if (group != NULL) { 562 state = sa_get_group_attr(group, "state"); 563 if (state != NULL) { 564 565 if (strcmp(state, "enabled") == 0) 566 ret = B_TRUE; 567 sa_free_attr_string(state); 568 } 569 } 570 return (ret); 571 } 572 573 /* 574 * enable_all_groups(list, setstate, online, updateproto) 575 * 576 * Given a list of groups, enable each one found. If updateproto is 577 * not NULL, then update all the shares for the protocol that was 578 * passed in. If enable is non-zero, tell enable_group to try the 579 * notify interface since this is a property change. 580 */ 581 static int 582 enable_all_groups(sa_handle_t handle, struct list *work, int setstate, 583 int online, char *updateproto, int enable) 584 { 585 int ret; 586 char instance[SA_MAX_NAME_LEN + sizeof (SA_SVC_FMRI_BASE) + 1]; 587 char *state; 588 char *name; 589 char *zfs = NULL; 590 sa_group_t group; 591 sa_group_t subgroup; 592 593 for (ret = SA_OK; work != NULL; work = work->next) { 594 group = (sa_group_t)work->item; 595 596 /* 597 * If setstate == TRUE, then make sure to set 598 * enabled. This needs to be done here in order for 599 * the isenabled check to succeed on a newly enabled 600 * group. 601 */ 602 if (setstate == B_TRUE) { 603 ret = sa_set_group_attr(group, "state", "enabled"); 604 if (ret != SA_OK) 605 break; 606 } 607 608 /* 609 * Check to see if group is enabled. If it isn't, skip 610 * the rest. We don't want shares starting if the 611 * group is disabled. The properties may have been 612 * updated, but there won't be a change until the 613 * group is enabled. 614 */ 615 if (!isenabled(group)) 616 continue; 617 618 /* if itemdata != NULL then a single share */ 619 if (work->itemdata != NULL) { 620 if (enable) { 621 if (work->itemdata != NULL) 622 notify_or_enable_share(work->itemdata, 623 updateproto); 624 else 625 ret = SA_CONFIG_ERR; 626 } else { 627 if (sa_is_share(work->itemdata)) { 628 ret = sa_enable_share( 629 (sa_share_t)work->itemdata, 630 updateproto); 631 } else { 632 ret = sa_enable_resource( 633 (sa_resource_t)work->itemdata, 634 updateproto); 635 } 636 } 637 } 638 if (ret != SA_OK) 639 break; 640 641 /* if itemdata == NULL then the whole group */ 642 if (work->itemdata == NULL) { 643 zfs = sa_get_group_attr(group, "zfs"); 644 /* 645 * If the share is managed by ZFS, don't 646 * update any of the protocols since ZFS is 647 * handling this. Updateproto will contain 648 * the name of the protocol that we want to 649 * update legacy files for. 650 */ 651 enable_group(group, zfs == NULL ? updateproto : NULL, 652 enable, work->proto); 653 for (subgroup = sa_get_sub_group(group); 654 subgroup != NULL; 655 subgroup = sa_get_next_group(subgroup)) { 656 /* never update legacy for ZFS subgroups */ 657 enable_group(subgroup, NULL, enable, 658 work->proto); 659 } 660 } 661 if (online) { 662 zfs = sa_get_group_attr(group, "zfs"); 663 name = sa_get_group_attr(group, "name"); 664 if (name != NULL) { 665 if (zfs == NULL) { 666 (void) snprintf(instance, 667 sizeof (instance), "%s:%s", 668 SA_SVC_FMRI_BASE, name); 669 state = smf_get_state(instance); 670 if (state == NULL || 671 strcmp(state, "online") != 0) { 672 (void) smf_enable_instance( 673 instance, 0); 674 free(state); 675 } 676 } else { 677 sa_free_attr_string(zfs); 678 zfs = NULL; 679 } 680 if (name != NULL) 681 sa_free_attr_string(name); 682 } 683 } 684 } 685 if (ret == SA_OK) { 686 ret = sa_update_config(handle); 687 } 688 return (ret); 689 } 690 691 /* 692 * chk_opt(optlistp, security, proto) 693 * 694 * Do a sanity check on the optlist provided for the protocol. This 695 * is a syntax check and verification that the property is either a 696 * general or specific to a names optionset. 697 */ 698 699 static int 700 chk_opt(struct options *optlistp, int security, char *proto) 701 { 702 struct options *optlist; 703 char *sep = ""; 704 int notfirst = 0; 705 int ret; 706 707 for (optlist = optlistp; optlist != NULL; optlist = optlist->next) { 708 char *optname; 709 710 optname = optlist->optname; 711 ret = OPT_ADD_OK; 712 /* extract property/value pair */ 713 if (sa_is_security(optname, proto)) { 714 if (!security) 715 ret = OPT_ADD_SECURITY; 716 } else { 717 if (security) 718 ret = OPT_ADD_PROPERTY; 719 } 720 if (ret != OPT_ADD_OK) { 721 if (notfirst == 0) 722 (void) printf( 723 gettext("Property syntax error: ")); 724 switch (ret) { 725 case OPT_ADD_SYNTAX: 726 (void) printf(gettext("%ssyntax error: %s"), 727 sep, optname); 728 sep = ", "; 729 break; 730 case OPT_ADD_SECURITY: 731 (void) printf(gettext("%s%s requires -S"), 732 optname, sep); 733 sep = ", "; 734 break; 735 case OPT_ADD_PROPERTY: 736 (void) printf( 737 gettext("%s%s not supported with -S"), 738 optname, sep); 739 sep = ", "; 740 break; 741 } 742 notfirst++; 743 } 744 } 745 if (notfirst) { 746 (void) printf("\n"); 747 ret = SA_SYNTAX_ERR; 748 } 749 return (ret); 750 } 751 752 /* 753 * free_opt(optlist) 754 * Free the specified option list. 755 */ 756 static void 757 free_opt(struct options *optlist) 758 { 759 struct options *nextopt; 760 while (optlist != NULL) { 761 nextopt = optlist->next; 762 free(optlist); 763 optlist = nextopt; 764 } 765 } 766 767 /* 768 * check property list for valid properties 769 * A null value is a remove which is always valid. 770 */ 771 static int 772 valid_options(sa_handle_t handle, struct options *optlist, char *proto, 773 void *object, char *sec) 774 { 775 int ret = SA_OK; 776 struct options *cur; 777 sa_property_t prop; 778 sa_optionset_t parent = NULL; 779 780 if (object != NULL) { 781 if (sec == NULL) 782 parent = sa_get_optionset(object, proto); 783 else 784 parent = sa_get_security(object, sec, proto); 785 } 786 787 for (cur = optlist; cur != NULL; cur = cur->next) { 788 if (cur->optvalue == NULL) 789 continue; 790 prop = sa_create_property(cur->optname, cur->optvalue); 791 if (prop == NULL) 792 ret = SA_NO_MEMORY; 793 if (ret != SA_OK || 794 (ret = sa_valid_property(handle, parent, proto, prop)) != 795 SA_OK) { 796 (void) printf( 797 gettext("Could not add property %s: %s\n"), 798 cur->optname, sa_errorstr(ret)); 799 } 800 (void) sa_remove_property(prop); 801 } 802 return (ret); 803 } 804 805 /* 806 * add_optionset(group, optlist, protocol, *err) 807 * Add the options in optlist to an optionset and then add the optionset 808 * to the group. 809 * 810 * The return value indicates if there was a "change" while errors are 811 * returned via the *err parameters. 812 */ 813 static int 814 add_optionset(sa_group_t group, struct options *optlist, char *proto, int *err) 815 { 816 sa_optionset_t optionset; 817 int ret = SA_OK; 818 int result = B_FALSE; 819 sa_handle_t handle; 820 821 optionset = sa_get_optionset(group, proto); 822 if (optionset == NULL) { 823 optionset = sa_create_optionset(group, proto); 824 if (optionset == NULL) 825 ret = SA_NO_MEMORY; 826 result = B_TRUE; /* adding a protocol is a change */ 827 } 828 if (optionset == NULL) { 829 ret = SA_NO_MEMORY; 830 goto out; 831 } 832 handle = sa_find_group_handle(group); 833 if (handle == NULL) { 834 ret = SA_CONFIG_ERR; 835 goto out; 836 } 837 while (optlist != NULL) { 838 sa_property_t prop; 839 prop = sa_get_property(optionset, optlist->optname); 840 if (prop == NULL) { 841 /* 842 * add the property, but only if it is 843 * a non-NULL or non-zero length value 844 */ 845 if (optlist->optvalue != NULL) { 846 prop = sa_create_property(optlist->optname, 847 optlist->optvalue); 848 if (prop != NULL) { 849 ret = sa_valid_property(handle, 850 optionset, proto, prop); 851 if (ret != SA_OK) { 852 (void) sa_remove_property(prop); 853 (void) printf(gettext("Could " 854 "not add property " 855 "%s: %s\n"), 856 optlist->optname, 857 sa_errorstr(ret)); 858 } 859 } 860 if (ret == SA_OK) { 861 ret = sa_add_property(optionset, prop); 862 if (ret != SA_OK) { 863 (void) printf(gettext( 864 "Could not add property " 865 "%s: %s\n"), 866 optlist->optname, 867 sa_errorstr(ret)); 868 } else { 869 /* there was a change */ 870 result = B_TRUE; 871 } 872 } 873 } 874 } else { 875 ret = sa_update_property(prop, optlist->optvalue); 876 /* should check to see if value changed */ 877 if (ret != SA_OK) { 878 (void) printf(gettext("Could not update " 879 "property %s: %s\n"), optlist->optname, 880 sa_errorstr(ret)); 881 } else { 882 result = B_TRUE; 883 } 884 } 885 optlist = optlist->next; 886 } 887 ret = sa_commit_properties(optionset, 0); 888 889 out: 890 if (err != NULL) 891 *err = ret; 892 return (result); 893 } 894 895 /* 896 * resource_compliant(group) 897 * 898 * Go through all the shares in the group. Assume compliant, but if 899 * any share doesn't have at least one resource name, it isn't 900 * compliant. 901 */ 902 static int 903 resource_compliant(sa_group_t group) 904 { 905 sa_share_t share; 906 907 for (share = sa_get_share(group, NULL); share != NULL; 908 share = sa_get_next_share(share)) { 909 if (sa_get_share_resource(share, NULL) == NULL) { 910 return (B_FALSE); 911 } 912 } 913 return (B_TRUE); 914 } 915 916 /* 917 * fix_path(path) 918 * 919 * change all illegal characters to something else. For now, all get 920 * converted to '_' and the leading '/' is stripped off. This is used 921 * to construct an resource name (SMB share name) that is valid. 922 * Caller must pass a valid path. 923 */ 924 static void 925 fix_path(char *path) 926 { 927 char *cp; 928 size_t len; 929 930 assert(path != NULL); 931 932 /* make sure we are appropriate length */ 933 cp = path + 1; /* skip leading slash */ 934 while (cp != NULL && strlen(cp) > SA_MAX_RESOURCE_NAME) { 935 cp = strchr(cp, '/'); 936 if (cp != NULL) 937 cp++; 938 } 939 /* two cases - cp == NULL and cp is substring of path */ 940 if (cp == NULL) { 941 /* just take last SA_MAX_RESOURCE_NAME chars */ 942 len = 1 + strlen(path) - SA_MAX_RESOURCE_NAME; 943 (void) memmove(path, path + len, SA_MAX_RESOURCE_NAME); 944 path[SA_MAX_RESOURCE_NAME] = '\0'; 945 } else { 946 len = strlen(cp) + 1; 947 (void) memmove(path, cp, len); 948 } 949 950 /* 951 * Don't want any of the characters that are not allowed 952 * in and SMB share name. Replace them with '_'. 953 */ 954 while (*path) { 955 switch (*path) { 956 case '/': 957 case '"': 958 case '\\': 959 case '[': 960 case ']': 961 case ':': 962 case '|': 963 case '<': 964 case '>': 965 case '+': 966 case ';': 967 case ',': 968 case '?': 969 case '*': 970 case '=': 971 case '\t': 972 *path = '_'; 973 break; 974 } 975 path++; 976 } 977 } 978 979 /* 980 * name_adjust(path, count) 981 * 982 * Add a ~<count> in place of last few characters. The total number of 983 * characters is dependent on count. 984 */ 985 #define MAX_MANGLE_NUMBER 10000 986 987 static int 988 name_adjust(char *path, int count) 989 { 990 size_t len; 991 992 len = strlen(path) - 2; 993 if (count > 10) 994 len--; 995 if (count > 100) 996 len--; 997 if (count > 1000) 998 len--; 999 if (len > 0) 1000 (void) sprintf(path + len, "~%d", count); 1001 else 1002 return (SA_BAD_VALUE); 1003 1004 return (SA_OK); 1005 } 1006 1007 /* 1008 * make_resources(group) 1009 * 1010 * Go through all the shares in the group and make them have resource 1011 * names. 1012 */ 1013 static void 1014 make_resources(sa_group_t group) 1015 { 1016 sa_share_t share; 1017 int count; 1018 int err = SA_OK; 1019 1020 for (share = sa_get_share(group, NULL); share != NULL; 1021 share = sa_get_next_share(share)) { 1022 /* Skip those with resources */ 1023 if (sa_get_share_resource(share, NULL) == NULL) { 1024 char *path; 1025 path = sa_get_share_attr(share, "path"); 1026 if (path == NULL) 1027 continue; 1028 fix_path(path); 1029 count = 0; /* reset for next resource */ 1030 while (sa_add_resource(share, path, 1031 SA_SHARE_PERMANENT, &err) == NULL && 1032 err == SA_DUPLICATE_NAME) { 1033 int ret; 1034 ret = name_adjust(path, count); 1035 count++; 1036 if (ret != SA_OK || 1037 count >= MAX_MANGLE_NUMBER) { 1038 (void) printf(gettext( 1039 "Cannot create resource name for" 1040 " path: %s\n"), path); 1041 break; 1042 } 1043 } 1044 sa_free_attr_string(path); 1045 } 1046 } 1047 } 1048 1049 /* 1050 * check_valid_group(group, protocol) 1051 * 1052 * Check to see that the group should have the protocol added (if 1053 * there is one specified). 1054 */ 1055 1056 static int 1057 check_valid_group(sa_group_t group, char *groupname, char *protocol) 1058 { 1059 1060 if (protocol != NULL) { 1061 if (has_protocol(group, protocol)) { 1062 (void) printf(gettext( 1063 "Group \"%s\" already exists" 1064 " with protocol %s\n"), groupname, 1065 protocol); 1066 return (SA_DUPLICATE_NAME); 1067 } else if (strcmp(groupname, "default") == 0 && 1068 strcmp(protocol, "nfs") != 0) { 1069 (void) printf(gettext( 1070 "Group \"%s\" only allows protocol " 1071 "\"%s\"\n"), groupname, "nfs"); 1072 return (SA_INVALID_PROTOCOL); 1073 } 1074 } else { 1075 /* must add new protocol */ 1076 (void) printf(gettext( 1077 "Group already exists and no protocol " 1078 "specified.\n")); 1079 return (SA_DUPLICATE_NAME); 1080 } 1081 return (SA_OK); 1082 } 1083 1084 /* 1085 * enforce_featureset(group, protocol, dryrun, force) 1086 * 1087 * Check the protocol featureset against the group and enforce any 1088 * rules that might be imposed. 1089 */ 1090 1091 static int 1092 enforce_featureset(sa_group_t group, char *protocol, boolean_t dryrun, 1093 boolean_t force) 1094 { 1095 uint64_t features; 1096 1097 if (protocol == NULL) 1098 return (SA_OK); 1099 1100 /* 1101 * First check to see if specified protocol is one we want to 1102 * allow on a group. Only server protocols are allowed here. 1103 */ 1104 features = sa_proto_get_featureset(protocol); 1105 if (!(features & SA_FEATURE_SERVER)) { 1106 (void) printf( 1107 gettext("Protocol \"%s\" not supported.\n"), protocol); 1108 return (SA_INVALID_PROTOCOL); 1109 } 1110 1111 /* 1112 * Check to see if the new protocol is one that requires 1113 * resource names and make sure we are compliant before 1114 * proceeding. 1115 */ 1116 if ((features & SA_FEATURE_RESOURCE) && 1117 !resource_compliant(group)) { 1118 if (force && !dryrun) { 1119 make_resources(group); 1120 } else { 1121 (void) printf( 1122 gettext("Protocol requires resource names to be " 1123 "set: %s\n"), protocol); 1124 return (SA_RESOURCE_REQUIRED); 1125 } 1126 } 1127 return (SA_OK); 1128 } 1129 1130 /* 1131 * set_all_protocols(group) 1132 * 1133 * Get the list of all protocols and add all server protocols to the 1134 * group. 1135 */ 1136 1137 static int 1138 set_all_protocols(sa_group_t group) 1139 { 1140 char **protolist; 1141 int numprotos, i; 1142 uint64_t features; 1143 sa_optionset_t optionset; 1144 int ret = SA_OK; 1145 1146 /* 1147 * Now make sure we really want to put this protocol on a 1148 * group. Only server protocols can go here. 1149 */ 1150 numprotos = sa_get_protocols(&protolist); 1151 for (i = 0; i < numprotos; i++) { 1152 features = sa_proto_get_featureset(protolist[i]); 1153 if (features & SA_FEATURE_SERVER) { 1154 optionset = sa_create_optionset(group, protolist[i]); 1155 if (optionset == NULL) { 1156 ret = SA_NO_MEMORY; 1157 break; 1158 } 1159 } 1160 } 1161 1162 if (protolist != NULL) 1163 free(protolist); 1164 1165 return (ret); 1166 } 1167 1168 /* 1169 * sa_create(flags, argc, argv) 1170 * create a new group 1171 * this may or may not have a protocol associated with it. 1172 * No protocol means "all" protocols in this case. 1173 */ 1174 static int 1175 sa_create(sa_handle_t handle, int flags, int argc, char *argv[]) 1176 { 1177 char *groupname; 1178 1179 sa_group_t group; 1180 boolean_t force = B_FALSE; 1181 boolean_t verbose = B_FALSE; 1182 boolean_t dryrun = B_FALSE; 1183 int c; 1184 char *protocol = NULL; 1185 int ret = SA_OK; 1186 struct options *optlist = NULL; 1187 int err = SA_OK; 1188 int auth; 1189 boolean_t created = B_FALSE; 1190 1191 while ((c = getopt(argc, argv, "?fhvnP:p:")) != EOF) { 1192 switch (c) { 1193 case 'f': 1194 force = B_TRUE; 1195 break; 1196 case 'v': 1197 verbose = B_TRUE; 1198 break; 1199 case 'n': 1200 dryrun = B_TRUE; 1201 break; 1202 case 'P': 1203 if (protocol != NULL) { 1204 (void) printf(gettext("Specifying " 1205 "multiple protocols " 1206 "not supported: %s\n"), protocol); 1207 return (SA_SYNTAX_ERR); 1208 } 1209 protocol = optarg; 1210 if (sa_valid_protocol(protocol)) 1211 break; 1212 (void) printf(