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 /* 30 * Share control API 31 */ 32 #include <stdio.h> 33 #include <string.h> 34 #include <ctype.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <unistd.h> 39 #include <libxml/parser.h> 40 #include <libxml/tree.h> 41 #include "libshare.h" 42 #include "libshare_impl.h" 43 #include <libscf.h> 44 #include "scfutil.h" 45 #include <ctype.h> 46 #include <libintl.h> 47 #include <thread.h> 48 #include <synch.h> 49 50 #if _NOT_SMF 51 #define CONFIG_FILE "/var/tmp/share.cfg" 52 #define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp" 53 #endif 54 #define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \ 55 (tm.tv_nsec & 0xffffffff)) 56 57 #define DFS_LOCK_FILE "/etc/dfs/fstypes" 58 #define SA_STRSIZE 256 /* max string size for names */ 59 60 /* 61 * internal object type values returned by sa_get_object_type() 62 */ 63 #define SA_TYPE_UNKNOWN 0 64 #define SA_TYPE_GROUP 1 65 #define SA_TYPE_SHARE 2 66 #define SA_TYPE_RESOURCE 3 67 #define SA_TYPE_OPTIONSET 4 68 #define SA_TYPE_ALTSPACE 5 69 70 /* 71 * internal data structures 72 */ 73 74 extern struct sa_proto_plugin *sap_proto_list; 75 76 /* current SMF/SVC repository handle */ 77 extern void getlegacyconfig(sa_handle_t, char *, xmlNodePtr *); 78 extern int gettransients(sa_handle_impl_t, xmlNodePtr *); 79 extern int sa_valid_property(void *, char *, sa_property_t); 80 extern char *sa_fstype(char *); 81 extern int sa_is_share(void *); 82 extern int sa_is_resource(void *); 83 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ 84 extern int sa_group_is_zfs(sa_group_t); 85 extern int sa_path_is_zfs(char *); 86 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); 87 extern int sa_zfs_set_sharesmb(sa_group_t, char *, int); 88 extern void update_legacy_config(sa_handle_t); 89 extern int issubdir(char *, char *); 90 extern int sa_zfs_init(sa_handle_impl_t); 91 extern void sa_zfs_fini(sa_handle_impl_t); 92 extern void sablocksigs(sigset_t *); 93 extern void saunblocksigs(sigset_t *); 94 static sa_group_t sa_get_optionset_parent(sa_optionset_t); 95 static char *get_node_attr(void *, char *); 96 97 /* 98 * Data structures for finding/managing the document root to access 99 * handle mapping. The list isn't expected to grow very large so a 100 * simple list is acceptable. The purpose is to provide a way to start 101 * with a group or share and find the library handle needed for 102 * various operations. 103 */ 104 mutex_t sa_global_lock; 105 struct doc2handle { 106 struct doc2handle *next; 107 xmlNodePtr root; 108 sa_handle_impl_t handle; 109 }; 110 111 /* definitions used in a couple of property functions */ 112 #define SA_PROP_OP_REMOVE 1 113 #define SA_PROP_OP_ADD 2 114 #define SA_PROP_OP_UPDATE 3 115 116 static struct doc2handle *sa_global_handles = NULL; 117 118 /* helper functions */ 119 120 /* 121 * sa_errorstr(err) 122 * 123 * convert an error value to an error string 124 */ 125 126 char * 127 sa_errorstr(int err) 128 { 129 static char errstr[32]; 130 char *ret = NULL; 131 132 switch (err) { 133 case SA_OK: 134 ret = dgettext(TEXT_DOMAIN, "ok"); 135 break; 136 case SA_NO_SUCH_PATH: 137 ret = dgettext(TEXT_DOMAIN, "path doesn't exist"); 138 break; 139 case SA_NO_MEMORY: 140 ret = dgettext(TEXT_DOMAIN, "no memory"); 141 break; 142 case SA_DUPLICATE_NAME: 143 ret = dgettext(TEXT_DOMAIN, "name in use"); 144 break; 145 case SA_BAD_PATH: 146 ret = dgettext(TEXT_DOMAIN, "bad path"); 147 break; 148 case SA_NO_SUCH_GROUP: 149 ret = dgettext(TEXT_DOMAIN, "no such group"); 150 break; 151 case SA_CONFIG_ERR: 152 ret = dgettext(TEXT_DOMAIN, "configuration error"); 153 break; 154 case SA_SYSTEM_ERR: 155 ret = dgettext(TEXT_DOMAIN, "system error"); 156 break; 157 case SA_SYNTAX_ERR: 158 ret = dgettext(TEXT_DOMAIN, "syntax error"); 159 break; 160 case SA_NO_PERMISSION: 161 ret = dgettext(TEXT_DOMAIN, "no permission"); 162 break; 163 case SA_BUSY: 164 ret = dgettext(TEXT_DOMAIN, "busy"); 165 break; 166 case SA_NO_SUCH_PROP: 167 ret = dgettext(TEXT_DOMAIN, "no such property"); 168 break; 169 case SA_INVALID_NAME: 170 ret = dgettext(TEXT_DOMAIN, "invalid name"); 171 break; 172 case SA_INVALID_PROTOCOL: 173 ret = dgettext(TEXT_DOMAIN, "invalid protocol"); 174 break; 175 case SA_NOT_ALLOWED: 176 ret = dgettext(TEXT_DOMAIN, "operation not allowed"); 177 break; 178 case SA_BAD_VALUE: 179 ret = dgettext(TEXT_DOMAIN, "bad property value"); 180 break; 181 case SA_INVALID_SECURITY: 182 ret = dgettext(TEXT_DOMAIN, "invalid security type"); 183 break; 184 case SA_NO_SUCH_SECURITY: 185 ret = dgettext(TEXT_DOMAIN, "security type not found"); 186 break; 187 case SA_VALUE_CONFLICT: 188 ret = dgettext(TEXT_DOMAIN, "property value conflict"); 189 break; 190 case SA_NOT_IMPLEMENTED: 191 ret = dgettext(TEXT_DOMAIN, "not implemented"); 192 break; 193 case SA_INVALID_PATH: 194 ret = dgettext(TEXT_DOMAIN, "invalid path"); 195 break; 196 case SA_NOT_SUPPORTED: 197 ret = dgettext(TEXT_DOMAIN, "operation not supported"); 198 break; 199 case SA_PROP_SHARE_ONLY: 200 ret = dgettext(TEXT_DOMAIN, "property not valid for group"); 201 break; 202 case SA_NOT_SHARED: 203 ret = dgettext(TEXT_DOMAIN, "not shared"); 204 break; 205 case SA_NO_SUCH_RESOURCE: 206 ret = dgettext(TEXT_DOMAIN, "no such resource"); 207 break; 208 case SA_RESOURCE_REQUIRED: 209 ret = dgettext(TEXT_DOMAIN, "resource name required"); 210 break; 211 case SA_MULTIPLE_ERROR: 212 ret = dgettext(TEXT_DOMAIN, "errors from multiple protocols"); 213 break; 214 case SA_PATH_IS_SUBDIR: 215 ret = dgettext(TEXT_DOMAIN, "path is a subpath of share"); 216 break; 217 case SA_PATH_IS_PARENTDIR: 218 ret = dgettext(TEXT_DOMAIN, "path is parent of a share"); 219 break; 220 case SA_KRB_KEYTAB_ERR: 221 ret = dgettext(TEXT_DOMAIN, "unable to remove the old keys" 222 " from the Kerberos keytab. Please manually remove" 223 " the old keys for your host principal prior to setting" 224 " the ads_domain property"); 225 break; 226 case SA_NO_SERVICE: 227 ret = dgettext(TEXT_DOMAIN, "service is not running"); 228 break; 229 default: 230 (void) snprintf(errstr, sizeof (errstr), 231 dgettext(TEXT_DOMAIN, "unknown %d"), err); 232 ret = errstr; 233 } 234 return (ret); 235 } 236 237 /* 238 * Document root to active handle mapping functions. These are only 239 * used internally. A mutex is used to prevent access while the list 240 * is changing. In general, the list will be relatively short - one 241 * item per thread that has called sa_init(). 242 */ 243 244 sa_handle_impl_t 245 get_handle_for_root(xmlNodePtr root) 246 { 247 struct doc2handle *item; 248 249 (void) mutex_lock(&sa_global_lock); 250 for (item = sa_global_handles; item != NULL; item = item->next) { 251 if (item->root == root) 252 break; 253 } 254 (void) mutex_unlock(&sa_global_lock); 255 if (item != NULL) 256 return (item->handle); 257 return (NULL); 258 } 259 260 static int 261 add_handle_for_root(xmlNodePtr root, sa_handle_impl_t handle) 262 { 263 struct doc2handle *item; 264 int ret = SA_NO_MEMORY; 265 266 item = (struct doc2handle *)calloc(sizeof (struct doc2handle), 1); 267 if (item != NULL) { 268 item->root = root; 269 item->handle = handle; 270 (void) mutex_lock(&sa_global_lock); 271 item->next = sa_global_handles; 272 sa_global_handles = item; 273 (void) mutex_unlock(&sa_global_lock); 274 ret = SA_OK; 275 } 276 return (ret); 277 } 278 279 /* 280 * remove_handle_for_root(root) 281 * 282 * Walks the list of handles and removes the one for this "root" from 283 * the list. It is up to the caller to free the data. 284 */ 285 286 static void 287 remove_handle_for_root(xmlNodePtr root) 288 { 289 struct doc2handle *item, *prev; 290 291 (void) mutex_lock(&sa_global_lock); 292 for (prev = NULL, item = sa_global_handles; item != NULL; 293 item = item->next) { 294 if (item->root == root) { 295 /* first in the list */ 296 if (prev == NULL) 297 sa_global_handles = sa_global_handles->next; 298 else 299 prev->next = item->next; 300 /* Item is out of the list so free the list structure */ 301 free(item); 302 break; 303 } 304 prev = item; 305 } 306 (void) mutex_unlock(&sa_global_lock); 307 } 308 309 /* 310 * sa_find_group_handle(sa_group_t group) 311 * 312 * Find the sa_handle_t for the configuration associated with this 313 * group. 314 */ 315 sa_handle_t 316 sa_find_group_handle(sa_group_t group) 317 { 318 xmlNodePtr node = (xmlNodePtr)group; 319 sa_handle_t handle; 320 321 while (node != NULL) { 322 if (strcmp((char *)(node->name), "sharecfg") == 0) { 323 /* have the root so get the handle */ 324 handle = (sa_handle_t)get_handle_for_root(node); 325 return (handle); 326 } 327 node = node->parent; 328 } 329 return (NULL); 330 } 331 332 /* 333 * set_legacy_timestamp(root, path, timevalue) 334 * 335 * add the current timestamp value to the configuration for use in 336 * determining when to update the legacy files. For SMF, this 337 * property is kept in default/operation/legacy_timestamp 338 */ 339 340 static void 341 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval) 342 { 343 xmlNodePtr node; 344 xmlChar *lpath = NULL; 345 sa_handle_impl_t handle; 346 347 /* Have to have a handle or else we weren't initialized. */ 348 handle = get_handle_for_root(root); 349 if (handle == NULL) 350 return; 351 352 for (node = root->xmlChildrenNode; node != NULL; 353 node = node->next) { 354 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 355 /* a possible legacy node for this path */ 356 lpath = xmlGetProp(node, (xmlChar *)"path"); 357 if (lpath != NULL && 358 xmlStrcmp(lpath, (xmlChar *)path) == 0) { 359 xmlFree(lpath); 360 break; 361 } 362 if (lpath != NULL) 363 xmlFree(lpath); 364 } 365 } 366 if (node == NULL) { 367 /* need to create the first legacy timestamp node */ 368 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL); 369 } 370 if (node != NULL) { 371 char tstring[32]; 372 int ret; 373 374 (void) snprintf(tstring, sizeof (tstring), "%lld", tval); 375 xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring); 376 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path); 377 /* now commit to SMF */ 378 ret = sa_get_instance(handle->scfhandle, "default"); 379 if (ret == SA_OK) { 380 ret = sa_start_transaction(handle->scfhandle, 381 "operation"); 382 if (ret == SA_OK) { 383 ret = sa_set_property(handle->scfhandle, 384 "legacy-timestamp", tstring); 385 if (ret == SA_OK) { 386 (void) sa_end_transaction( 387 handle->scfhandle); 388 } else { 389 sa_abort_transaction(handle->scfhandle); 390 } 391 } 392 } 393 } 394 } 395 396 /* 397 * is_shared(share) 398 * 399 * determine if the specified share is currently shared or not. 400 */ 401 static int 402 is_shared(sa_share_t share) 403 { 404 char *shared; 405 int result = 0; /* assume not */ 406 407 shared = sa_get_share_attr(share, "shared"); 408 if (shared != NULL) { 409 if (strcmp(shared, "true") == 0) 410 result = 1; 411 sa_free_attr_string(shared); 412 } 413 return (result); 414 } 415 416 /* 417 * excluded_protocol(share, proto) 418 * 419 * Returns B_TRUE if the specified protocol appears in the "exclude" 420 * property. This is used to prevent sharing special case shares 421 * (e.g. subdirs when SMB wants a subdir and NFS doesn't. B_FALSE is 422 * returned if the protocol isn't in the list. 423 */ 424 static boolean_t 425 excluded_protocol(sa_share_t share, char *proto) 426 { 427 char *protolist; 428 char *str; 429 char *token; 430 431 protolist = sa_get_share_attr(share, "exclude"); 432 if (protolist != NULL) { 433 str = protolist; 434 while ((token = strtok(str, ",")) != NULL) { 435 if (strcmp(token, proto) == 0) { 436 sa_free_attr_string(protolist); 437 return (B_TRUE); 438 } 439 str = NULL; 440 } 441 sa_free_attr_string(protolist); 442 } 443 return (B_FALSE); 444 } 445 446 /* 447 * checksubdirgroup(group, newpath, strictness) 448 * 449 * check all the specified newpath against all the paths in the 450 * group. This is a helper function for checksubdir to make it easier 451 * to also check ZFS subgroups. 452 * The strictness values mean: 453 * SA_CHECK_NORMAL == only check newpath against shares that are active 454 * SA_CHECK_STRICT == check newpath against both active shares and those 455 * stored in the repository 456 */ 457 static int 458 checksubdirgroup(sa_group_t group, char *newpath, int strictness) 459 { 460 sa_share_t share; 461 char *path; 462 int issub = SA_OK; 463 int subdir; 464 int parent; 465 466 if (newpath == NULL) 467 return (SA_INVALID_PATH); 468 469 for (share = sa_get_share(group, NULL); share != NULL; 470 share = sa_get_next_share(share)) { 471 /* 472 * The original behavior of share never checked 473 * against the permanent configuration 474 * (/etc/dfs/dfstab). PIT has a number of cases where 475 * it depends on this older behavior even though it 476 * could be considered incorrect. We may tighten this 477 * up in the future. 478 */ 479 if (strictness == SA_CHECK_NORMAL && !is_shared(share)) 480 continue; 481 482 path = sa_get_share_attr(share, "path"); 483 /* 484 * If path is NULL, then a share is in the process of 485 * construction or someone has modified the property 486 * group inappropriately. It should be 487 * ignored. issubdir() comes from the original share 488 * implementation and does the difficult part of 489 * checking subdirectories. 490 */ 491 if (path == NULL) 492 continue; 493 494 if (strcmp(path, newpath) == 0) { 495 issub = SA_INVALID_PATH; 496 } else { 497 subdir = issubdir(newpath, path); 498 parent = issubdir(path, newpath); 499 if (subdir || parent) { 500 sa_free_attr_string(path); 501 path = NULL; 502 return (subdir ? 503 SA_PATH_IS_SUBDIR : SA_PATH_IS_PARENTDIR); 504 } 505 } 506 sa_free_attr_string(path); 507 path = NULL; 508 } 509 return (issub); 510 } 511 512 /* 513 * checksubdir(newpath, strictness) 514 * 515 * checksubdir determines if the specified path (newpath) is a 516 * subdirectory of another share. It calls checksubdirgroup() to do 517 * the complicated work. The strictness parameter determines how 518 * strict a check to make against the path. The strictness values 519 * mean: SA_CHECK_NORMAL == only check newpath against shares that are 520 * active SA_CHECK_STRICT == check newpath against both active shares 521 * and those * stored in the repository 522 */ 523 static int 524 checksubdir(sa_handle_t handle, char *newpath, int strictness) 525 { 526 sa_group_t group; 527 int issub = SA_OK; 528 char *path = NULL; 529 530 for (group = sa_get_group(handle, NULL); 531 group != NULL && issub == SA_OK; 532 group = sa_get_next_group(group)) { 533 if (sa_group_is_zfs(group)) { 534 sa_group_t subgroup; 535 for (subgroup = sa_get_sub_group(group); 536 subgroup != NULL && issub == SA_OK; 537 subgroup = sa_get_next_group(subgroup)) 538 issub = checksubdirgroup(subgroup, newpath, 539 strictness); 540 } else { 541 issub = checksubdirgroup(group, newpath, strictness); 542 } 543 } 544 if (path != NULL) 545 sa_free_attr_string(path); 546 return (issub); 547 } 548 549 /* 550 * validpath(path, strictness) 551 * determine if the provided path is valid for a share. It shouldn't 552 * be a sub-dir of an already shared path or the parent directory of a 553 * share path. 554 */ 555 static int 556 validpath(sa_handle_t handle, char *path, int strictness) 557 { 558 int error = SA_OK; 559 struct stat st; 560 sa_share_t share; 561 char *fstype; 562 563 if (*path != '/') 564 return (SA_BAD_PATH); 565 566 if (stat(path, &st) < 0) { 567 error = SA_NO_SUCH_PATH; 568 } else { 569 share = sa_find_share(handle, path); 570 if (share != NULL) 571 error = SA_DUPLICATE_NAME; 572 573 if (error == SA_OK) { 574 /* 575 * check for special case with file system 576 * that might have restrictions. For now, ZFS 577 * is the only case since it has its own idea 578 * of how to configure shares. We do this 579 * before subdir checking since things like 580 * ZFS will do that for us. This should also 581 * be done via plugin interface. 582 */ 583 fstype = sa_fstype(path); 584 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 585 if (sa_zfs_is_shared(handle, path)) 586 error = SA_INVALID_NAME; 587 } 588 if (fstype != NULL) 589 sa_free_fstype(fstype); 590 } 591 if (error == SA_OK) 592 error = checksubdir(handle, path, strictness); 593 } 594 return (error); 595 } 596 597 /* 598 * check to see if group/share is persistent. 599 * 600 * "group" can be either an sa_group_t or an sa_share_t. (void *) 601 * works since both thse types are also void *. 602 */ 603 int 604 sa_is_persistent(void *group) 605 { 606 char *type; 607 int persist = 1; 608 609 type = sa_get_group_attr((sa_group_t)group, "type"); 610 if (type != NULL && strcmp(type, "transient") == 0) 611 persist = 0; 612 if (type != NULL) 613 sa_free_attr_string(type); 614 return (persist); 615 } 616 617 /* 618 * sa_valid_group_name(name) 619 * 620 * check that the "name" contains only valid characters and otherwise 621 * fits the required naming conventions. Valid names must start with 622 * an alphabetic and the remainder may consist of only alphanumeric 623 * plus the '-' and '_' characters. This name limitation comes from 624 * inherent limitations in SMF. 625 */ 626 627 int 628 sa_valid_group_name(char *name) 629 { 630 int ret = 1; 631 ssize_t len; 632 633 if (name != NULL && isalpha(*name)) { 634 char c; 635 len = strlen(name); 636 if (len < (scf_max_name_len - sizeof ("group:"))) { 637 for (c = *name++; c != '\0' && ret != 0; c = *name++) { 638 if (!isalnum(c) && c != '-' && c != '_') 639 ret = 0; 640 } 641 } else { 642 ret = 0; 643 } 644 } else { 645 ret = 0; 646 } 647 return (ret); 648 } 649 650 651 /* 652 * is_zfs_group(group) 653 * Determine if the specified group is a ZFS sharenfs group 654 */ 655 static int 656 is_zfs_group(sa_group_t group) 657 { 658 int ret = 0; 659 xmlNodePtr parent; 660 xmlChar *zfs; 661 662 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) 663 parent = (xmlNodePtr)sa_get_parent_group(group); 664 else 665 parent = (xmlNodePtr)group; 666 zfs = xmlGetProp(parent, (xmlChar *)"zfs"); 667 if (zfs != NULL) { 668 xmlFree(zfs); 669 ret = 1; 670 } 671 return (ret); 672 } 673 674 /* 675 * sa_get_object_type(object) 676 * 677 * This function returns a numeric value representing the object 678 * type. This allows using simpler checks when doing type specific 679 * operations. 680 */ 681 682 static int 683 sa_get_object_type(void *object) 684 { 685 xmlNodePtr node = (xmlNodePtr)object; 686 int type; 687 688 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) 689 type = SA_TYPE_GROUP; 690 else if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) 691 type = SA_TYPE_SHARE; 692 else if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) 693 type = SA_TYPE_RESOURCE; 694 else if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) 695 type = SA_TYPE_OPTIONSET; 696 else if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) 697 type = SA_TYPE_ALTSPACE; 698 else 699 assert(0); 700 return (type); 701 } 702 703 /* 704 * sa_optionset_name(optionset, oname, len, id) 705 * return the SMF name for the optionset. If id is not NULL, it 706 * will have the GUID value for a share and should be used 707 * instead of the keyword "optionset" which is used for 708 * groups. If the optionset doesn't have a protocol type 709 * associated with it, "default" is used. This shouldn't happen 710 * at this point but may be desirable in the future if there are 711 * protocol independent properties added. The name is returned in 712 * oname. 713 */ 714 715 static int 716 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) 717 { 718 char *proto; 719 void *parent; 720 int ptype; 721 722 if (id == NULL) 723 id = "optionset"; 724 725 parent = sa_get_optionset_parent(optionset); 726 if (parent != NULL) { 727 ptype = sa_get_object_type(parent); 728 proto = sa_get_optionset_attr(optionset, "type"); 729 if (ptype != SA_TYPE_RESOURCE) { 730 len = snprintf(oname, len, "%s_%s", id, 731 proto ? proto : "default"); 732 } else { 733 char *index; 734 index = get_node_attr((void *)parent, "id"); 735 if (index != NULL) 736 len = snprintf(oname, len, "%s_%s_%s", id, 737 proto ? proto : "default", index); 738 else 739 len = 0; 740 } 741 742 if (proto != NULL) 743 sa_free_attr_string(proto); 744 } else { 745 len = 0; 746 } 747 return (len); 748 } 749 750 /* 751 * sa_security_name(optionset, oname, len, id) 752 * 753 * return the SMF name for the security. If id is not NULL, it will 754 * have the GUID value for a share and should be used instead of the 755 * keyword "optionset" which is used for groups. If the optionset 756 * doesn't have a protocol type associated with it, "default" is 757 * used. This shouldn't happen at this point but may be desirable in 758 * the future if there are protocol independent properties added. The 759 * name is returned in oname. The security type is also encoded into 760 * the name. In the future, this wil *be handled a bit differently. 761 */ 762 763 static int 764 sa_security_name(sa_security_t security, char *oname, size_t len, char *id) 765 { 766 char *proto; 767 char *sectype; 768 769 if (id == NULL) 770 id = "optionset"; 771 772 proto = sa_get_security_attr(security, "type"); 773 sectype = sa_get_security_attr(security, "sectype"); 774 len = snprintf(oname, len, "%s_%s_%s", id, proto ? proto : "default", 775 sectype ? sectype : "default"); 776 if (proto != NULL) 777 sa_free_attr_string(proto); 778 if (sectype != NULL) 779 sa_free_attr_string(sectype); 780 return (len); 781 } 782 783 /* 784 * verifydefgroupopts(handle) 785 * 786 * Make sure a "default" group exists and has default protocols enabled. 787 */ 788 static void 789 verifydefgroupopts(sa_handle_t handle) 790 { 791 sa_group_t defgrp; 792 sa_optionset_t opt; 793 794 defgrp = sa_get_group(handle, "default"); 795 if (defgrp != NULL) { 796 opt = sa_get_optionset(defgrp, NULL); 797 /* 798 * NFS is the default for default group 799 */ 800 if (opt == NULL) 801 opt = sa_create_optionset(defgrp, "nfs"); 802 } 803 } 804 805 /* 806 * sa_init(init_service) 807 * Initialize the API 808 * find all the shared objects 809 * init the tables with all objects 810 * read in the current configuration 811 */ 812 813 #define GETPROP(prop) scf_simple_prop_next_astring(prop) 814 #define CHECKTSTAMP(st, tval) stat(SA_LEGACY_DFSTAB, &st) >= 0 && \ 815 tval != TSTAMP(st.st_ctim) 816 817 sa_handle_t 818 sa_init(int init_service) 819 { 820 struct stat st; 821 int legacy = 0; 822 uint64_t tval = 0; 823 int lockfd; 824 sigset_t old; 825 int updatelegacy = B_FALSE; 826 scf_simple_prop_t *prop; 827 sa_handle_impl_t handle; 828 int err; 829 830 handle = calloc(sizeof (struct sa_handle_impl), 1); 831 832 if (handle != NULL) { 833 /* get protocol specific structures */ 834 (void) proto_plugin_init(); 835 if (init_service & SA_INIT_SHARE_API) { 836 /* 837 * initialize access into libzfs. We use this 838 * when collecting info about ZFS datasets and 839 * shares. 840 */ 841 if (sa_zfs_init(handle) == B_FALSE) { 842 free(handle); 843 (void) proto_plugin_fini(); 844 return (NULL); 845 } 846 /* 847 * since we want to use SMF, initialize an svc handle 848 * and find out what is there. 849 */ 850 handle->scfhandle = sa_scf_init(handle); 851 if (handle->scfhandle != NULL) { 852 /* 853 * Need to lock the extraction of the 854 * configuration if the dfstab file has 855 * changed. Lock everything now and release if 856 * not needed. Use a file that isn't being 857 * manipulated by other parts of the system in 858 * order to not interfere with locking. Using 859 * dfstab doesn't work. 860 */ 861 sablocksigs(&old); 862 lockfd = open(DFS_LOCK_FILE, O_RDWR); 863 if (lockfd >= 0) { 864 extern int errno; 865 errno = 0; 866 (void) lockf(lockfd, F_LOCK, 0); 867 /* 868 * Check whether we are going to need 869 * to merge any dfstab changes. This 870 * is done by comparing the value of 871 * legacy-timestamp with the current 872 * st_ctim of the file. If they are 873 * different, an update is needed and 874 * the file must remain locked until 875 * the merge is done in order to 876 * prevent multiple startups from 877 * changing the SMF repository at the 878 * same time. The first to get the 879 * lock will make any changes before 880 * the others can read the repository. 881 */ 882 prop = scf_simple_prop_get 883 (handle->scfhandle->handle, 884 (const char *)SA_SVC_FMRI_BASE 885 ":default", "operation", 886 "legacy-timestamp"); 887 if (prop != NULL) { 888 char *i64; 889 i64 = GETPROP(prop); 890 if (i64 != NULL) 891 tval = strtoull(i64, 892 NULL, 0); 893 if (CHECKTSTAMP(st, tval)) 894 updatelegacy = B_TRUE; 895 scf_simple_prop_free(prop); 896 } else { 897 /* 898 * We haven't set the 899 * timestamp before so do it. 900 */ 901 updatelegacy = B_TRUE; 902 } 903 } 904 if (updatelegacy == B_FALSE) { 905 /* Don't need the lock anymore */ 906 (void) lockf(lockfd, F_ULOCK, 0); 907 (void) close(lockfd); 908 } 909 910 /* 911 * It is essential that the document tree and 912 * the internal list of roots to handles be 913 * setup before anything that might try to 914 * create a new object is called. The document 915 * tree is the combination of handle->doc and 916 * handle->tree. This allows searches, 917 * etc. when all you have is an object in the 918 * tree. 919 */ 920 handle->doc = xmlNewDoc((xmlChar *)"1.0"); 921 handle->tree = xmlNewNode(NULL, 922 (xmlChar *)"sharecfg"); 923 if (handle->doc != NULL && 924 handle->tree != NULL) { 925 xmlDocSetRootElement(handle->doc, 926 handle->tree); 927 err = add_handle_for_root(handle->tree, 928 handle); 929 if (err == SA_OK) 930 err = sa_get_config( 931 handle->scfhandle, 932 handle->tree, handle); 933 } else { 934 if (handle->doc != NULL) 935 xmlFreeDoc(handle->doc); 936 if (handle->tree != NULL) 937 xmlFreeNode(handle->tree); 938 err = SA_NO_MEMORY; 939 } 940 941 saunblocksigs(&old); 942 943 if (err != SA_OK) { 944 /* 945 * If we couldn't add the tree handle 946 * to the list, then things are going 947 * to fail badly. Might as well undo 948 * everything now and fail the 949 * sa_init(). 950 */ 951 sa_fini(handle); 952 return (NULL); 953 } 954 955 if (tval == 0) { 956 /* 957 * first time so make sure 958 * default is setup 959 */ 960 verifydefgroupopts(handle); 961 } 962 963 if (updatelegacy == B_TRUE) { 964 sablocksigs(&old); 965 getlegacyconfig((sa_handle_t)handle, 966 SA_LEGACY_DFSTAB, &handle->tree); 967 if (stat(SA_LEGACY_DFSTAB, &st) >= 0) 968 set_legacy_timestamp( 969 handle->tree, 970 SA_LEGACY_DFSTAB, 971 TSTAMP(st.st_ctim)); 972 saunblocksigs(&old); 973 /* 974 * Safe to unlock now to allow 975 * others to run 976 */ 977 (void) lockf(lockfd, F_ULOCK, 0); 978 (void) close(lockfd); 979 } 980 legacy |= sa_get_zfs_shares(handle, "zfs"); 981 legacy |= gettransients(handle, &handle->tree); 982 } 983 } 984 } 985 return ((sa_handle_t)handle); 986 } 987 988 /* 989 * sa_fini(handle) 990 * Uninitialize the API structures including the configuration 991 * data structures and ZFS related data. 992 */ 993 994 void 995 sa_fini(sa_handle_t handle) 996 { 997 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 998 999 if (impl_handle != NULL) { 1000 /* 1001 * Free the config trees and any other data structures 1002 * used in the handle. 1003 */ 1004 if (impl_handle->doc != NULL) 1005 xmlFreeDoc(impl_handle->doc); 1006 sa_scf_fini(impl_handle->scfhandle); 1007 sa_zfs_fini(impl_handle); 1008 1009 /* Remove and free the entry in the global list. */ 1010 remove_handle_for_root(impl_handle->tree); 1011 1012 /* Make sure we free the handle */ 1013 free(impl_handle); 1014 1015 /* 1016 * If this was the last handle to release, unload the 1017 * plugins that were loaded. 1018 */ 1019 if (sa_global_handles == NULL) 1020 (void) proto_plugin_fini(); 1021 1022 } 1023 } 1024 1025 /* 1026 * sa_get_protocols(char **protocol) 1027 * Get array of protocols that are supported 1028 * Returns pointer to an allocated and NULL terminated 1029 * array of strings. Caller must free. 1030 * This really should be determined dynamically. 1031 * If there aren't any defined, return -1. 1032 * Use free() to return memory. 1033 */ 1034 1035 int 1036 sa_get_protocols(char ***protocols) 1037 { 1038 int numproto = -1; 1039 1040 if (protocols != NULL) { 1041 struct sa_proto_plugin *plug; 1042 for (numproto = 0, plug = sap_proto_list; plug != NULL; 1043 plug = plug->plugin_next) { 1044 numproto++; 1045 } 1046 1047 *protocols = calloc(numproto + 1, sizeof (char *)); 1048 if (*protocols != NULL) { 1049 int ret = 0; 1050 for (plug = sap_proto_list; plug != NULL; 1051 plug = plug->plugin_next) { 1052 /* faking for now */ 1053 (*protocols)[ret++] = 1054 plug->plugin_ops->sa_protocol; 1055 } 1056 } else { 1057 numproto = -1; 1058 } 1059 } 1060 return (numproto); 1061 } 1062 1063 /* 1064 * find_group_by_name(node, group) 1065 * 1066 * search the XML document subtree specified by node to find the group 1067 * specified by group. Searching subtree allows subgroups to be 1068 * searched for. 1069 */ 1070 1071 static xmlNodePtr 1072 find_group_by_name(xmlNodePtr node, xmlChar *group) 1073 { 1074 xmlChar *name = NULL; 1075 1076 for (node = node->xmlChildrenNode; node != NULL; 1077 node = node->next) { 1078 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 1079 /* if no groupname, return the first found */ 1080 if (group == NULL) 1081 break; 1082 name = xmlGetProp(node, (xmlChar *)"name"); 1083 if (name != NULL && xmlStrcmp(name, group) == 0) 1084 break; 1085 if (name != NULL) { 1086 xmlFree(name); 1087 name = NULL; 1088 } 1089 } 1090 } 1091 if (name != NULL) 1092 xmlFree(name); 1093 return (node); 1094 } 1095 1096 /* 1097 * sa_get_group(groupname) 1098 * Return the "group" specified. If groupname is NULL, 1099 * return the first group of the list of groups. 1100 */ 1101 sa_group_t 1102 sa_get_group(sa_handle_t handle, char *groupname) 1103 { 1104 xmlNodePtr node = NULL; 1105 char *subgroup = NULL; 1106 char *group = NULL; 1107 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 1108 1109 if (impl_handle != NULL && impl_handle->tree != NULL) { 1110 if (groupname != NULL) { 1111 group = strdup(groupname); 1112 if (group != NULL) { 1113 subgroup = strchr(group, '/'); 1114 if (subgroup != NULL) 1115 *subgroup++ = '\0'; 1116 } 1117 } 1118 /* 1119 * We want to find the, possibly, named group. If 1120 * group is not NULL, then lookup the name. If it is 1121 * NULL, we only do the find if groupname is also 1122 * NULL. This allows lookup of the "first" group in 1123 * the internal list. 1124 */ 1125 if (group != NULL || groupname == NULL) 1126 node = find_group_by_name(impl_handle->tree, 1127 (xmlChar *)group); 1128 1129 /* if a subgroup, find it before returning */ 1130 if (subgroup != NULL && node != NULL) 1131 node = find_group_by_name(node, (xmlChar *)subgroup); 1132 } 1133 if (node != NULL && (char *)group != NULL) 1134 (void) sa_get_instance(impl_handle->scfhandle, (char *)group); 1135 if (group != NULL) 1136 free(group); 1137 return ((sa_group_t)(node)); 1138 } 1139 1140 /* 1141 * sa_get_next_group(group) 1142 * Return the "next" group after the specified group from 1143 * the internal group list. NULL if there are no more. 1144 */ 1145 sa_group_t 1146 sa_get_next_group(sa_group_t group) 1147 { 1148 xmlNodePtr ngroup = NULL; 1149 if (group != NULL) { 1150 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 1151 ngroup = ngroup->next) { 1152 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 1153 break; 1154 } 1155 } 1156 return ((sa_group_t)ngroup); 1157 } 1158 1159 /* 1160 * sa_get_share(group, sharepath) 1161 * Return the share object for the share specified. The share 1162 * must be in the specified group. Return NULL if not found. 1163 */ 1164 sa_share_t 1165 sa_get_share(sa_group_t group, char *sharepath) 1166 { 1167 xmlNodePtr node = NULL; 1168 xmlChar *path; 1169 1170 /* 1171 * For future scalability, this should end up building a cache 1172 * since it will get called regularly by the mountd and info 1173 * services. 1174 */ 1175 if (group != NULL) { 1176 for (node = ((xmlNodePtr)group)->children; node != NULL; 1177 node = node->next) { 1178 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1179 if (sharepath == NULL) { 1180 break; 1181 } else { 1182 /* is it the correct share? */ 1183 path = xmlGetProp(node, 1184 (xmlChar *)"path"); 1185 if (path != NULL && 1186 xmlStrcmp(path, 1187 (xmlChar *)sharepath) == 0) { 1188 xmlFree(path); 1189 break; 1190 } 1191 xmlFree(path); 1192 } 1193 } 1194 } 1195 } 1196 return ((sa_share_t)node); 1197 } 1198 1199 /* 1200 * sa_get_next_share(share) 1201 * Return the next share following the specified share 1202 * from the internal list of shares. Returns NULL if there 1203 * are no more shares. The list is relative to the same 1204 * group. 1205 */ 1206 sa_share_t 1207 sa_get_next_share(sa_share_t share) 1208 { 1209 xmlNodePtr node = NULL; 1210 1211 if (share != NULL) { 1212 for (node = ((xmlNodePtr)share)->next; node != NULL; 1213 node = node->next) { 1214 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 1215 break; 1216 } 1217 } 1218 } 1219 return ((sa_share_t)node); 1220 } 1221 1222 /* 1223 * _sa_get_child_node(node, type) 1224 * 1225 * find the child node of the specified node that has "type". This is 1226 * used to implement several internal functions. 1227 */ 1228 1229 static xmlNodePtr 1230 _sa_get_child_node(xmlNodePtr node, xmlChar *type) 1231 { 1232 xmlNodePtr child; 1233 for (child = node->xmlChildrenNode; child != NULL; 1234 child = child->next) 1235 if (xmlStrcmp(child->name, type) == 0) 1236 return (child); 1237 return ((xmlNodePtr)NULL); 1238 } 1239 1240 /* 1241 * find_share(group, path) 1242 * 1243 * Search all the shares in the specified group for one that has the 1244 * specified path. 1245 */ 1246 1247 static sa_share_t 1248 find_share(sa_group_t group, char *sharepath) 1249 { 1250 sa_share_t share; 1251 char *path; 1252 1253 for (share = sa_get_share(group, NULL); share != NULL; 1254 share = sa_get_next_share(share)) { 1255 path = sa_get_share_attr(share, "path"); 1256 if (path != NULL && strcmp(path, sharepath) == 0) { 1257 sa_free_attr_string(path); 1258 break; 1259 } 1260 if (path != NULL) 1261 sa_free_attr_string(path); 1262 } 1263 return (share); 1264 } 1265 1266 /* 1267 * sa_get_sub_group(group) 1268 * 1269 * Get the first sub-group of group. The sa_get_next_group() function 1270 * can be used to get the rest. This is currently only used for ZFS 1271 * sub-groups but could be used to implement a more general mechanism. 1272 */ 1273 1274 sa_group_t 1275 sa_get_sub_group(sa_group_t group) 1276 { 1277 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1278 (xmlChar *)"group")); 1279 } 1280 1281 /* 1282 * sa_find_share(sharepath) 1283 * Finds a share regardless of group. In the future, this 1284 * function should utilize a cache and hash table of some kind. 1285 * The current assumption is that a path will only be shared 1286 * once. In the future, this may change as implementation of 1287 * resource names comes into being. 1288 */ 1289 sa_share_t 1290 sa_find_share(sa_handle_t handle, char *sharepath) 1291 { 1292 sa_group_t group; 1293 sa_group_t zgroup; 1294 sa_share_t share = NULL; 1295 int done = 0; 1296 1297 for (group = sa_get_group(handle, NULL); group != NULL && !done; 1298 group = sa_get_next_group(group)) { 1299 if (is_zfs_group(group)) { 1300 for (zgroup = 1301 (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 1302 (xmlChar *)"group"); 1303 zgroup != NULL; 1304 zgroup = sa_get_next_group(zgroup)) { 1305 share = find_share(zgroup, sharepath); 1306 if (share != NULL) 1307 break; 1308 } 1309 } else { 1310 share = find_share(group, sharepath); 1311 } 1312 if (share != NULL) 1313 break; 1314 } 1315 return (share); 1316 } 1317 1318 /* 1319 * sa_check_path(group, path, strictness) 1320 * 1321 * Check that path is a valid path relative to the group. Currently, 1322 * we are ignoring the group and checking only the NFS rules. Later, 1323 * we may want to use the group to then check against the protocols 1324 * enabled on the group. The strictness values mean: 1325 * SA_CHECK_NORMAL == only check newpath against shares that are active 1326 * SA_CHECK_STRICT == check newpath against both active shares and those 1327 * stored in the repository 1328 */ 1329 1330 int 1331 sa_check_path(sa_group_t group, char *path, int strictness) 1332 { 1333 sa_handle_t handle; 1334 1335 handle = sa_find_group_handle(group); 1336 return (validpath(handle, path, strictness)); 1337 } 1338 1339 /* 1340 * mark_excluded_protos(group, share, flags) 1341 * 1342 * Walk through all the protocols enabled for the group and check to 1343 * see if the share has any of them should be in the exclude list 1344 * based on the featureset of the protocol. If there are any, add the 1345 * "exclude" property to the share. 1346 */ 1347 static void 1348 mark_excluded_protos(sa_group_t group, xmlNodePtr share, uint64_t flags) 1349 { 1350 sa_optionset_t optionset; 1351 char exclude_list[SA_STRSIZE]; 1352 char *sep = ""; 1353 1354 exclude_list[0] = '\0'; 1355 for (optionset = sa_get_optionset(group, NULL); 1356 optionset != NULL; 1357 optionset = sa_get_next_optionset(optionset)) { 1358 char *value; 1359 uint64_t features; 1360 value = sa_get_optionset_attr(optionset, "type"); 1361 if (value == NULL) 1362 continue; 1363 features = sa_proto_get_featureset(value); 1364 sa_free_attr_string(value); 1365 if (!(features & flags)) { 1366 (void) strlcat(exclude_list, sep, 1367 sizeof (exclude_list)); 1368 (void) strlcat(exclude_list, value, 1369 sizeof (exclude_list)); 1370 sep = ","; 1371 } 1372 } 1373 if (exclude_list[0] != '\0') 1374 xmlSetProp(share, (xmlChar *)"exclude", 1375 (xmlChar *)exclude_list); 1376 } 1377 1378 /* 1379 * get_all_features(group) 1380 * 1381 * Walk through all the protocols on the group and collect all 1382 * possible enabled features. This is the OR of all the featuresets. 1383 */ 1384 static uint64_t 1385 get_all_features(sa_group_t group) 1386 { 1387 sa_optionset_t optionset; 1388 uint64_t features = 0; 1389 1390 for (optionset = sa_get_optionset(group, NULL); 1391 optionset != NULL; 1392 optionset = sa_get_next_optionset(optionset)) { 1393 char *value; 1394 value = sa_get_optionset_attr(optionset, "type"); 1395 if (value == NULL) 1396 continue; 1397 features |= sa_proto_get_featureset(value); 1398 sa_free_attr_string(value); 1399 } 1400 return (features); 1401 } 1402 1403 1404 /* 1405 * _sa_add_share(group, sharepath, persist, *error, flags) 1406 * 1407 * Common code for all types of add_share. sa_add_share() is the 1408 * public API, we also need to be able to do this when parsing legacy 1409 * files and construction of the internal configuration while 1410 * extracting config info from SMF. "flags" indicates if some 1411 * protocols need relaxed rules while other don't. These values are 1412 * the featureset values defined in libshare.h. 1413 */ 1414 1415 sa_share_t 1416 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error, 1417 uint64_t flags) 1418 { 1419 xmlNodePtr node = NULL; 1420 int err; 1421 1422 err = SA_OK; /* assume success */ 1423 1424 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"share", NULL); 1425 if (node == NULL) { 1426 if (error != NULL) 1427 *error = SA_NO_MEMORY; 1428 return (node); 1429 } 1430 1431 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 1432 xmlSetProp(node, (xmlChar *)"type", 1433 persist ? (xmlChar *)"persist" : (xmlChar *)"transient"); 1434 if (flags != 0) 1435 mark_excluded_protos(group, node, flags); 1436 if (persist != SA_SHARE_TRANSIENT) { 1437 /* 1438 * persistent shares come in two flavors: SMF and 1439 * ZFS. Sort this one out based on target group and 1440 * path type. Both NFS and SMB are supported. First, 1441 * check to see if the protocol is enabled on the 1442 * subgroup and then setup the share appropriately. 1443 */ 1444 if (sa_group_is_zfs(group) && 1445 sa_path_is_zfs(sharepath)) { 1446 if (sa_get_optionset(group, "nfs") != NULL) 1447 err = sa_zfs_set_sharenfs(group, sharepath, 1); 1448 else if (sa_get_optionset(group, "smb") != NULL) 1449 err = sa_zfs_set_sharesmb(group, sharepath, 1); 1450 } else { 1451 sa_handle_impl_t impl_handle; 1452 impl_handle = 1453 (sa_handle_impl_t)sa_find_group_handle(group); 1454 if (impl_handle != NULL) { 1455 err = sa_commit_share(impl_handle->scfhandle, 1456 group, (sa_share_t)node); 1457 } else { 1458 err = SA_SYSTEM_ERR; 1459 } 1460 } 1461 } 1462 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) 1463 /* called by the dfstab parser so could be a show */ 1464 err = SA_OK; 1465 1466 if (err != SA_OK) { 1467 /* 1468 * we couldn't commit to the repository so undo 1469 * our internal state to reflect reality. 1470 */ 1471 xmlUnlinkNode(node); 1472 xmlFreeNode(node); 1473 node = NULL; 1474 } 1475 1476 if (error != NULL) 1477 *error = err; 1478 1479 return (node); 1480 } 1481 1482 /* 1483 * sa_add_share(group, sharepath, persist, *error) 1484 * 1485 * Add a new share object to the specified group. The share will 1486 * have the specified sharepath and will only be constructed if 1487 * it is a valid path to be shared. NULL is returned on error 1488 * and a detailed error value will be returned via the error 1489 * pointer. 1490 */ 1491 sa_share_t 1492 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 1493 { 1494 xmlNodePtr node = NULL; 1495 int strictness = SA_CHECK_NORMAL; 1496 sa_handle_t handle; 1497 uint64_t special = 0; 1498 uint64_t features; 1499 1500 /* 1501 * If the share is to be permanent, use strict checking so a 1502 * bad config doesn't get created. Transient shares only need 1503 * to check against the currently active 1504 * shares. SA_SHARE_PARSER is a modifier used internally to 1505 * indicate that we are being called by the dfstab parser and 1506 * that we need strict checking in all cases. Normally persist 1507 * is in integer value but SA_SHARE_PARSER may be or'd into 1508 * it as an override. 1509 */ 1510 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 1511 strictness = SA_CHECK_STRICT; 1512 1513 handle = sa_find_group_handle(group); 1514 1515 /* 1516 * need to determine if the share is valid. The rules are: 1517 * - The path must not already exist 1518 * - The path must not be a subdir or parent dir of an 1519 * existing path unless at least one protocol allows it. 1520 * The sub/parent check is done in sa_check_path(). 1521 */ 1522 1523 if (sa_find_share(handle, sharepath) == NULL) { 1524 *error = sa_check_path(group, sharepath, strictness); 1525 features = get_all_features(group); 1526 switch (*error) { 1527 case SA_PATH_IS_SUBDIR: 1528 if (features & SA_FEATURE_ALLOWSUBDIRS) 1529 special |= SA_FEATURE_ALLOWSUBDIRS; 1530 break; 1531 case SA_PATH_IS_PARENTDIR: 1532 if (features & SA_FEATURE_ALLOWPARDIRS) 1533 special |= SA_FEATURE_ALLOWPARDIRS; 1534 break; 1535 } 1536 if (*error == SA_OK || special != SA_FEATURE_NONE) 1537 node = _sa_add_share(group, sharepath, persist, 1538 error, special); 1539 } else { 1540 *error = SA_DUPLICATE_NAME; 1541 } 1542 1543 return ((sa_share_t)node); 1544 } 1545 1546 /* 1547 * sa_enable_share(share, protocol) 1548 * Enable the specified share to the specified protocol. 1549 * If protocol is NULL, then all protocols. 1550 */ 1551 int 1552 sa_enable_share(sa_share_t share, char *protocol) 1553 { 1554 char *sharepath; 1555 struct stat st; 1556 int err = SA_OK; 1557 int ret; 1558 1559 sharepath = sa_get_share_attr(share, "path"); 1560 if (sharepath == NULL) 1561 return (SA_NO_MEMORY); 1562 if (stat(sharepath, &st) < 0) { 1563 err = SA_NO_SUCH_PATH; 1564 } else { 1565 /* tell the server about the share */ 1566 if (protocol != NULL) { 1567 if (excluded_protocol(share, protocol)) 1568 goto done; 1569 1570 /* lookup protocol specific handler */ 1571 err = sa_proto_share(protocol, share); 1572 if (err == SA_OK) 1573 (void) sa_set_share_attr(share, 1574 "shared", "true"); 1575 } else { 1576 /* Tell all protocols about the share */ 1577 sa_group_t group; 1578 sa_optionset_t optionset; 1579 1580 group = sa_get_parent_group(share); 1581 1582 for (optionset = sa_get_optionset(group, NULL); 1583 optionset != NULL; 1584 optionset = sa_get_next_optionset(optionset)) { 1585 char *proto; 1586 proto = sa_get_optionset_attr(optionset, 1587 "type"); 1588 if (proto != NULL) { 1589 if (!excluded_protocol(share, proto)) { 1590 ret = sa_proto_share(proto, 1591 share); 1592 if (ret != SA_OK) 1593 err = ret; 1594 } 1595 sa_free_attr_string(proto); 1596 } 1597 } 1598 (void) sa_set_share_attr(share, "shared", "true"); 1599 } 1600 } 1601 done: 1602 if (sharepath != NULL) 1603 sa_free_attr_string(sharepath); 1604 return (err); 1605 } 1606 1607 /* 1608 * sa_disable_share(share, protocol) 1609 * Disable the specified share to the specified protocol. If 1610 * protocol is NULL, then all protocols that are enabled for the 1611 * share should be disabled. 1612 */ 1613 int 1614 sa_disable_share(sa_share_t share, char *protocol) 1615 { 1616 char *path; 1617 int err = SA_OK; 1618 int ret = SA_OK; 1619 1620 path = sa_get_share_attr(share, "path"); 1621 1622 if (protocol != NULL) { 1623 ret = sa_proto_unshare(share, protocol, path); 1624 } else { 1625 /* need to do all protocols */ 1626 sa_group_t group; 1627 sa_optionset_t optionset; 1628 1629 group = sa_get_parent_group(share); 1630 1631 /* Tell all protocols about the share */ 1632 for (optionset = sa_get_optionset(group, NULL); 1633 optionset != NULL; 1634 optionset = sa_get_next_optionset(optionset)) { 1635 char *proto; 1636 1637 proto = sa_get_optionset_attr(optionset, "type"); 1638 if (proto != NULL) { 1639 err = sa_proto_unshare(share, proto, path); 1640 if (err != SA_OK) 1641 ret = err; 1642 sa_free_attr_string(proto); 1643 } 1644 } 1645 } 1646 if (ret == SA_OK) 1647 (void) sa_set_share_attr(share, "shared", NULL); 1648 if (path != NULL) 1649 sa_free_attr_string(path); 1650 return (ret); 1651 } 1652 1653 /* 1654 * sa_remove_share(share) 1655 * 1656 * remove the specified share from its containing group. 1657 * Remove from the SMF or ZFS configuration space. 1658 */ 1659 1660 int 1661 sa_remove_share(sa_share_t share) 1662 { 1663 sa_group_t group; 1664 int ret = SA_OK; 1665 char *type; 1666 int transient = 0; 1667 char *groupname; 1668 char *zfs; 1669 1670 type = sa_get_share_attr(share, "type"); 1671 group = sa_get_parent_group(share); 1672 zfs = sa_get_group_attr(group, "zfs"); 1673 groupname = sa_get_group_attr(group, "name"); 1674 if (type != NULL && strcmp(type, "persist") != 0) 1675 transient = 1; 1676 if (type != NULL) 1677 sa_free_attr_string(type); 1678 1679 /* remove the node from its group then free the memory */ 1680 1681 /* 1682 * need to test if "busy" 1683 */ 1684 /* only do SMF action if permanent */ 1685 if (!transient || zfs != NULL) { 1686 /* remove from legacy dfstab as well as possible SMF */ 1687 ret = sa_delete_legacy(share, NULL); 1688 if (ret == SA_OK) { 1689 if (!sa_group_is_zfs(group)) { 1690 sa_handle_impl_t impl_handle; 1691 impl_handle = (sa_handle_impl_t) 1692 sa_find_group_handle(group); 1693 if (impl_handle != NULL) { 1694 ret = sa_delete_share( 1695 impl_handle->scfhandle, group, 1696 share); 1697 } else { 1698 ret = SA_SYSTEM_ERR; 1699 } 1700 } else { 1701 char *sharepath = sa_get_share_attr(share, 1702 "path"); 1703 if (sharepath != NULL) { 1704 ret = sa_zfs_set_sharenfs(group, 1705 sharepath, 0); 1706 sa_free_attr_string(sharepath); 1707 } 1708 } 1709 } 1710 } 1711 if (groupname != NULL) 1712 sa_free_attr_string(groupname); 1713 if (zfs != NULL) 1714 sa_free_attr_string(zfs); 1715 1716 xmlUnlinkNode((xmlNodePtr)share); 1717 xmlFreeNode((xmlNodePtr)share); 1718 return (ret); 1719 } 1720 1721 /* 1722 * sa_move_share(group, share) 1723 * 1724 * move the specified share to the specified group. Update SMF 1725 * appropriately. 1726 */ 1727 1728 int 1729 sa_move_share(sa_group_t group, sa_share_t share) 1730 { 1731 sa_group_t oldgroup; 1732 int ret = SA_OK; 1733 1734 /* remove the node from its group then free the memory */ 1735 1736 oldgroup = sa_get_parent_group(share); 1737 if (oldgroup != group) { 1738 sa_handle_impl_t impl_handle; 1739 xmlUnlinkNode((xmlNodePtr)share); 1740 /* 1741 * now that the share isn't in its old group, add to 1742 * the new one 1743 */ 1744 xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 1745 /* need to deal with SMF */ 1746 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1747 if (impl_handle != NULL) { 1748 /* 1749 * need to remove from old group first and then add to 1750 * new group. Ideally, we would do the other order but 1751 * need to avoid having the share in two groups at the 1752 * same time. 1753 */ 1754 ret = sa_delete_share(impl_handle->scfhandle, oldgroup, 1755 share); 1756 if (ret == SA_OK) 1757 ret = sa_commit_share(impl_handle->scfhandle, 1758 group, share); 1759 } else { 1760 ret = SA_SYSTEM_ERR; 1761 } 1762 } 1763 return (ret); 1764 } 1765 1766 /* 1767 * sa_get_parent_group(share) 1768 * 1769 * Return the containing group for the share. If a group was actually 1770 * passed in, we don't want a parent so return NULL. 1771 */ 1772 1773 sa_group_t 1774 sa_get_parent_group(sa_share_t share) 1775 { 1776 xmlNodePtr node = NULL; 1777 if (share != NULL) { 1778 node = ((xmlNodePtr)share)->parent; 1779 /* 1780 * make sure parent is a group and not sharecfg since 1781 * we may be cheating and passing in a group. 1782 * Eventually, groups of groups might come into being. 1783 */ 1784 if (node == NULL || 1785 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 1786 node = NULL; 1787 } 1788 return ((sa_group_t)node); 1789 } 1790 1791 /* 1792 * _sa_create_group(impl_handle, groupname) 1793 * 1794 * Create a group in the document. The caller will need to deal with 1795 * configuration store and activation. 1796 */ 1797 1798 sa_group_t 1799 _sa_create_group(sa_handle_impl_t impl_handle, char *groupname) 1800 { 1801 xmlNodePtr node = NULL; 1802 1803 if (sa_valid_group_name(groupname)) { 1804 node = xmlNewChild(impl_handle->tree, NULL, (xmlChar *)"group", 1805 NULL); 1806 if (node != NULL) { 1807 xmlSetProp(node, (xmlChar *)"name", 1808 (xmlChar *)groupname); 1809 xmlSetProp(node, (xmlChar *)"state", 1810 (xmlChar *)"enabled"); 1811 } 1812 } 1813 return ((sa_group_t)node); 1814 } 1815 1816 /* 1817 * _sa_create_zfs_group(group, groupname) 1818 * 1819 * Create a ZFS subgroup under the specified group. This may 1820 * eventually form the basis of general sub-groups, but is currently 1821 * restricted to ZFS. 1822 */ 1823 sa_group_t 1824 _sa_create_zfs_group(sa_group_t group, char *groupname) 1825 { 1826 xmlNodePtr node = NULL; 1827 1828 node = xmlNewChild((xmlNodePtr)group, NULL, (xmlChar *)"group", NULL); 1829 if (node != NULL) { 1830 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1831 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1832 } 1833 1834 return ((sa_group_t)node); 1835 } 1836 1837 /* 1838 * sa_create_group(groupname, *error) 1839 * 1840 * Create a new group with groupname. Need to validate that it is a 1841 * legal name for SMF and the construct the SMF service instance of 1842 * svc:/network/shares/group to implement the group. All necessary 1843 * operational properties must be added to the group at this point 1844 * (via the SMF transaction model). 1845 */ 1846 sa_group_t 1847 sa_create_group(sa_handle_t handle, char *groupname, int *error) 1848 { 1849 xmlNodePtr node = NULL; 1850 sa_group_t group; 1851 int ret; 1852 char rbacstr[SA_STRSIZE]; 1853 sa_handle_impl_t impl_handle = (sa_handle_impl_t)handle; 1854 1855 ret = SA_OK; 1856 1857 if (impl_handle == NULL || impl_handle->scfhandle == NULL) { 1858 ret = SA_SYSTEM_ERR; 1859 goto err; 1860 } 1861 1862 group = sa_get_group(handle, groupname); 1863 if (group != NULL) { 1864 ret = SA_DUPLICATE_NAME; 1865 } else { 1866 if (sa_valid_group_name(groupname)) { 1867 node = xmlNewChild(impl_handle->tree, NULL, 1868 (xmlChar *)"group", NULL); 1869 if (node != NULL) { 1870 xmlSetProp(node, (xmlChar *)"name", 1871 (xmlChar *)groupname); 1872 /* default to the group being enabled */ 1873 xmlSetProp(node, (xmlChar *)"state", 1874 (xmlChar *)"enabled"); 1875 ret = sa_create_instance(impl_handle->scfhandle, 1876 groupname); 1877 if (ret == SA_OK) { 1878 ret = sa_start_transaction( 1879 impl_handle->scfhandle, 1880 "operation"); 1881 } 1882 if (ret == SA_OK) { 1883 ret = sa_set_property( 1884 impl_handle->scfhandle, 1885 "state", "enabled"); 1886 if (ret == SA_OK) { 1887 ret = sa_end_transaction( 1888 impl_handle->scfhandle); 1889 } else { 1890 sa_abort_transaction( 1891 impl_handle->scfhandle); 1892 } 1893 } 1894 if (ret == SA_OK) { 1895 /* initialize the RBAC strings */ 1896 ret = sa_start_transaction( 1897 impl_handle->scfhandle, 1898 "general"); 1899 if (ret == SA_OK) { 1900 (void) snprintf(rbacstr, 1901 sizeof (rbacstr), "%s.%s", 1902 SA_RBAC_MANAGE, groupname); 1903 ret = sa_set_property( 1904 impl_handle->scfhandle, 1905 "action_authorization", 1906 rbacstr); 1907 } 1908 if (ret == SA_OK) { 1909 (void) snprintf(rbacstr, 1910 sizeof (rbacstr), "%s.%s", 1911 SA_RBAC_VALUE, groupname); 1912 ret = sa_set_property( 1913 impl_handle->scfhandle, 1914 "value_authorization", 1915 rbacstr); 1916 } 1917 if (ret == SA_OK) { 1918 ret = sa_end_transaction( 1919 impl_handle->scfhandle); 1920 } else { 1921 sa_abort_transaction( 1922 impl_handle->scfhandle); 1923 } 1924 } 1925 if (ret != SA_OK) { 1926 /* 1927 * Couldn't commit the group 1928 * so we need to undo 1929 * internally. 1930 */ 1931 xmlUnlinkNode(node); 1932 xmlFreeNode(node); 1933 node = NULL; 1934 } 1935 } else { 1936 ret = SA_NO_MEMORY; 1937 } 1938 } else { 1939 ret = SA_INVALID_NAME; 1940 } 1941 } 1942 err: 1943 if (error != NULL) 1944 *error = ret; 1945 return ((sa_group_t)node); 1946 } 1947 1948 /* 1949 * sa_remove_group(group) 1950 * 1951 * Remove the specified group. This deletes from the SMF repository. 1952 * All property groups and properties are removed. 1953 */ 1954 1955 int 1956 sa_remove_group(sa_group_t group) 1957 { 1958 char *name; 1959 int ret = SA_OK; 1960 sa_handle_impl_t impl_handle; 1961 1962 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 1963 if (impl_handle != NULL) { 1964 name = sa_get_group_attr(group, "name"); 1965 if (name != NULL) { 1966 ret = sa_delete_instance(impl_handle->scfhandle, name); 1967 sa_free_attr_string(name); 1968 } 1969 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 1970 xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 1971 } else { 1972 ret = SA_SYSTEM_ERR; 1973 } 1974 return (ret); 1975 } 1976 1977 /* 1978 * sa_update_config() 1979 * 1980 * Used to update legacy files that need to be updated in bulk 1981 * Currently, this is a placeholder and will go away in a future 1982 * release. 1983 */ 1984 1985 int 1986 sa_update_config(sa_handle_t handle) 1987 { 1988 /* 1989 * do legacy files first so we can tell when they change. 1990 * This will go away when we start updating individual records 1991 * rather than the whole file. 1992 */ 1993 update_legacy_config(handle); 1994 return (SA_OK); 1995 } 1996 1997 /* 1998 * get_node_attr(node, tag) 1999 * 2000 * Get the specified tag(attribute) if it exists on the node. This is 2001 * used internally by a number of attribute oriented functions. 2002 */ 2003 2004 static char * 2005 get_node_attr(void *nodehdl, char *tag) 2006 { 2007 xmlNodePtr node = (xmlNodePtr)nodehdl; 2008 xmlChar *name = NULL; 2009 2010 if (node != NULL) 2011 name = xmlGetProp(node, (xmlChar *)tag); 2012 return ((char *)name); 2013 } 2014 2015 /* 2016 * get_node_attr(node, tag) 2017 * 2018 * Set the specified tag(attribute) to the specified value This is 2019 * used internally by a number of attribute oriented functions. It 2020 * doesn't update the repository, only the internal document state. 2021 */ 2022 2023 void 2024 set_node_attr(void *nodehdl, char *tag, char *value) 2025 { 2026 xmlNodePtr node = (xmlNodePtr)nodehdl; 2027 if (node != NULL && tag != NULL) { 2028 if (value != NULL) 2029 xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 2030 else 2031 xmlUnsetProp(node, (xmlChar *)tag); 2032 } 2033 } 2034 2035 /* 2036 * sa_get_group_attr(group, tag) 2037 * 2038 * Get the specied attribute, if defined, for the group. 2039 */ 2040 2041 char * 2042 sa_get_group_attr(sa_group_t group, char *tag) 2043 { 2044 return (get_node_attr((void *)group, tag)); 2045 } 2046 2047 /* 2048 * sa_set_group_attr(group, tag, value) 2049 * 2050 * set the specified tag/attribute on the group using value as its 2051 * value. 2052 * 2053 * This will result in setting the property in the SMF repository as 2054 * well as in the internal document. 2055 */ 2056 2057 int 2058 sa_set_group_attr(sa_group_t group, char *tag, char *value) 2059 { 2060 int ret; 2061 char *groupname; 2062 sa_handle_impl_t impl_handle; 2063 2064 /* 2065 * ZFS group/subgroup doesn't need the handle so shortcut. 2066 */ 2067 if (sa_group_is_zfs(group)) { 2068 set_node_attr((void *)group, tag, value); 2069 return (SA_OK); 2070 } 2071 2072 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2073 if (impl_handle != NULL) { 2074 groupname = sa_get_group_attr(group, "name"); 2075 ret = sa_get_instance(impl_handle->scfhandle, groupname); 2076 if (ret == SA_OK) { 2077 set_node_attr((void *)group, tag, value); 2078 ret = sa_start_transaction(impl_handle->scfhandle, 2079 "operation"); 2080 if (ret == SA_OK) { 2081 ret = sa_set_property(impl_handle->scfhandle, 2082 tag, value); 2083 if (ret == SA_OK) 2084 (void) sa_end_transaction( 2085 impl_handle->scfhandle); 2086 else 2087 sa_abort_transaction( 2088 impl_handle->scfhandle); 2089 } 2090 } 2091 if (groupname != NULL) 2092 sa_free_attr_string(groupname); 2093 } else { 2094 ret = SA_SYSTEM_ERR; 2095 } 2096 return (ret); 2097 } 2098 2099 /* 2100 * sa_get_share_attr(share, tag) 2101 * 2102 * Return the value of the tag/attribute set on the specified 2103 * share. Returns NULL if the tag doesn't exist. 2104 */ 2105 2106 char * 2107 sa_get_share_attr(sa_share_t share, char *tag) 2108 { 2109 return (get_node_attr((void *)share, tag)); 2110 } 2111 2112 /* 2113 * _sa_set_share_description(share, description) 2114 * 2115 * Add a description tag with text contents to the specified share. A 2116 * separate XML tag is used rather than a property. This can also be 2117 * used with resources. 2118 */ 2119 2120 xmlNodePtr 2121 _sa_set_share_description(void *share, char *content) 2122 { 2123 xmlNodePtr node; 2124 node = xmlNewChild((xmlNodePtr)share, NULL, (xmlChar *)"description", 2125 NULL); 2126 xmlNodeSetContent(node, (xmlChar *)content); 2127 return (node); 2128 } 2129 2130 /* 2131 * sa_set_share_attr(share, tag, value) 2132 * 2133 * Set the share attribute specified by tag to the specified value. In 2134 * the case of "resource", enforce a no duplicates in a group rule. If 2135 * the share is not transient, commit the changes to the repository 2136 * else just update the share internally. 2137 */ 2138 2139 int 2140 sa_set_share_attr(sa_share_t share, char *tag, char *value) 2141 { 2142 sa_group_t group; 2143 sa_share_t resource; 2144 int ret = SA_OK; 2145 2146 group = sa_get_parent_group(share); 2147 2148 /* 2149 * There are some attributes that may have specific 2150 * restrictions on them. Initially, only "resource" has 2151 * special meaning that needs to be checked. Only one instance 2152 * of a resource name may exist within a group. 2153 */ 2154 2155 if (strcmp(tag, "resource") == 0) { 2156 resource = sa_get_resource(group, value); 2157 if (resource != share && resource != NULL) 2158 ret = SA_DUPLICATE_NAME; 2159 } 2160 if (ret == SA_OK) { 2161 set_node_attr((void *)share, tag, value); 2162 if (group != NULL) { 2163 char *type; 2164 /* we can probably optimize this some */ 2165 type = sa_get_share_attr(share, "type"); 2166 if (type == NULL || strcmp(type, "transient") != 0) { 2167 sa_handle_impl_t impl_handle; 2168 impl_handle = 2169 (sa_handle_impl_t)sa_find_group_handle( 2170 group); 2171 if (impl_handle != NULL) { 2172 ret = sa_commit_share( 2173 impl_handle->scfhandle, group, 2174 share); 2175 } else { 2176 ret = SA_SYSTEM_ERR; 2177 } 2178 } 2179 if (type != NULL) 2180 sa_free_attr_string(type); 2181 } 2182 } 2183 return (ret); 2184 } 2185 2186 /* 2187 * sa_get_property_attr(prop, tag) 2188 * 2189 * Get the value of the specified property attribute. Standard 2190 * attributes are "type" and "value". 2191 */ 2192 2193 char * 2194 sa_get_property_attr(sa_property_t prop, char *tag) 2195 { 2196 return (get_node_attr((void *)prop, tag)); 2197 } 2198 2199 /* 2200 * sa_get_optionset_attr(prop, tag) 2201 * 2202 * Get the value of the specified property attribute. Standard 2203 * attribute is "type". 2204 */ 2205 2206 char * 2207 sa_get_optionset_attr(sa_property_t optionset, char *tag) 2208 { 2209 return (get_node_attr((void *)optionset, tag)); 2210 2211 } 2212 2213 /* 2214 * sa_set_optionset_attr(optionset, tag, value) 2215 * 2216 * Set the specified attribute(tag) to the specified value on the 2217 * optionset. 2218 */ 2219 2220 void 2221 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 2222 { 2223 set_node_attr((void *)optionset, tag, value); 2224 } 2225 2226 /* 2227 * sa_free_attr_string(string) 2228 * 2229 * Free the string that was returned in one of the sa_get_*_attr() 2230 * functions. 2231 */ 2232 2233 void 2234 sa_free_attr_string(char *string) 2235 { 2236 xmlFree((xmlChar *)string); 2237 } 2238 2239 /* 2240 * sa_get_optionset(group, proto) 2241 * 2242 * Return the optionset, if it exists, that is associated with the 2243 * specified protocol. 2244 */ 2245 2246 sa_optionset_t 2247 sa_get_optionset(void *group, char *proto) 2248 { 2249 xmlNodePtr node; 2250 xmlChar *value = NULL; 2251 2252 for (node = ((xmlNodePtr)group)->children; node != NULL; 2253 node = node->next) { 2254 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2255 value = xmlGetProp(node, (xmlChar *)"type"); 2256 if (proto != NULL) { 2257 if (value != NULL && 2258 xmlStrcmp(value, (xmlChar *)proto) == 0) { 2259 break; 2260 } 2261 if (value != NULL) { 2262 xmlFree(value); 2263 value = NULL; 2264 } 2265 } else { 2266 break; 2267 } 2268 } 2269 } 2270 if (value != NULL) 2271 xmlFree(value); 2272 return ((sa_optionset_t)node); 2273 } 2274 2275 /* 2276 * sa_get_next_optionset(optionset) 2277 * 2278 * Return the next optionset in the group. NULL if this was the last. 2279 */ 2280 2281 sa_optionset_t 2282 sa_get_next_optionset(sa_optionset_t optionset) 2283 { 2284 xmlNodePtr node; 2285 2286 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 2287 node = node->next) { 2288 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 2289 break; 2290 } 2291 } 2292 return ((sa_optionset_t)node); 2293 } 2294 2295 /* 2296 * sa_get_security(group, sectype, proto) 2297 * 2298 * Return the security optionset. The internal name is a hold over 2299 * from the implementation and will be changed before the API is 2300 * finalized. This is really a named optionset that can be negotiated 2301 * as a group of properties (like NFS security options). 2302 */ 2303 2304 sa_security_t 2305 sa_get_security(sa_group_t group, char *sectype, char *proto) 2306 { 2307 xmlNodePtr node; 2308 xmlChar *value = NULL; 2309 2310 for (node = ((xmlNodePtr)group)->children; node != NULL; 2311 node = node->next) { 2312 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2313 if (proto != NULL) { 2314 value = xmlGetProp(node, (xmlChar *)"type"); 2315 if (value == NULL || 2316 (value != NULL && 2317 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 2318 /* it doesn't match so continue */ 2319 xmlFree(value); 2320 value = NULL; 2321 continue; 2322 } 2323 } 2324 if (value != NULL) { 2325 xmlFree(value); 2326 value = NULL; 2327 } 2328 /* potential match */ 2329 if (sectype != NULL) { 2330 value = xmlGetProp(node, (xmlChar *)"sectype"); 2331 if (value != NULL && 2332 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 2333 break; 2334 } 2335 } else { 2336 break; 2337 } 2338 } 2339 if (value != NULL) { 2340 xmlFree(value); 2341 value = NULL; 2342 } 2343 } 2344 if (value != NULL) 2345 xmlFree(value); 2346 return ((sa_security_t)node); 2347 } 2348 2349 /* 2350 * sa_get_next_security(security) 2351 * 2352 * Get the next security optionset if one exists. 2353 */ 2354 2355 sa_security_t 2356 sa_get_next_security(sa_security_t security) 2357 { 2358 xmlNodePtr node; 2359 2360 for (node = ((xmlNodePtr)security)->next; node != NULL; 2361 node = node->next) { 2362 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 2363 break; 2364 } 2365 } 2366 return ((sa_security_t)node); 2367 } 2368 2369 /* 2370 * sa_get_property(optionset, prop) 2371 * 2372 * Get the property object with the name specified in prop from the 2373 * optionset. 2374 */ 2375 2376 sa_property_t 2377 sa_get_property(sa_optionset_t optionset, char *prop) 2378 { 2379 xmlNodePtr node = (xmlNodePtr)optionset; 2380 xmlChar *value = NULL; 2381 2382 if (optionset == NULL) 2383 return (NULL); 2384 2385 for (node = node->children; node != NULL; 2386 node = node->next) { 2387 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2388 if (prop == NULL) 2389 break; 2390 value = xmlGetProp(node, (xmlChar *)"type"); 2391 if (value != NULL && 2392 xmlStrcmp(value, (xmlChar *)prop) == 0) { 2393 break; 2394 } 2395 if (value != NULL) { 2396 xmlFree(value); 2397 value = NULL; 2398 } 2399 } 2400 } 2401 if (value != NULL) 2402 xmlFree(value); 2403 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2404 /* 2405 * avoid a non option node -- it is possible to be a 2406 * text node 2407 */ 2408 node = NULL; 2409 } 2410 return ((sa_property_t)node); 2411 } 2412 2413 /* 2414 * sa_get_next_property(property) 2415 * 2416 * Get the next property following the specified property. NULL if 2417 * this was the last. 2418 */ 2419 2420 sa_property_t 2421 sa_get_next_property(sa_property_t property) 2422 { 2423 xmlNodePtr node; 2424 2425 for (node = ((xmlNodePtr)property)->next; node != NULL; 2426 node = node->next) { 2427 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2428 break; 2429 } 2430 } 2431 return ((sa_property_t)node); 2432 } 2433 2434 /* 2435 * sa_set_share_description(share, content) 2436 * 2437 * Set the description of share to content. 2438 */ 2439 2440 int 2441 sa_set_share_description(sa_share_t share, char *content) 2442 { 2443 xmlNodePtr node; 2444 sa_group_t group; 2445 int ret = SA_OK; 2446 2447 for (node = ((xmlNodePtr)share)->children; node != NULL; 2448 node = node->next) { 2449 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2450 break; 2451 } 2452 } 2453 /* no existing description but want to add */ 2454 if (node == NULL && content != NULL) { 2455 /* add a description */ 2456 node = _sa_set_share_description(share, content); 2457 } else if (node != NULL && content != NULL) { 2458 /* update a description */ 2459 xmlNodeSetContent(node, (xmlChar *)content); 2460 } else if (node != NULL && content == NULL) { 2461 /* remove an existing description */ 2462 xmlUnlinkNode(node); 2463 xmlFreeNode(node); 2464 } 2465 group = sa_get_parent_group(share); 2466 if (group != NULL && sa_is_persistent(share)) { 2467 sa_handle_impl_t impl_handle; 2468 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2469 if (impl_handle != NULL) { 2470 ret = sa_commit_share(impl_handle->scfhandle, group, 2471 share); 2472 } else { 2473 ret = SA_SYSTEM_ERR; 2474 } 2475 } 2476 return (ret); 2477 } 2478 2479 /* 2480 * fixproblemchars(string) 2481 * 2482 * don't want any newline or tab characters in the text since these 2483 * could break display of data and legacy file formats. 2484 */ 2485 static void 2486 fixproblemchars(char *str) 2487 { 2488 int c; 2489 for (c = *str; c != '\0'; c = *++str) { 2490 if (c == '\t' || c == '\n') 2491 *str = ' '; 2492 else if (c == '"') 2493 *str = '\''; 2494 } 2495 } 2496 2497 /* 2498 * sa_get_share_description(share) 2499 * 2500 * Return the description text for the specified share if it 2501 * exists. NULL if no description exists. 2502 */ 2503 2504 char * 2505 sa_get_share_description(sa_share_t share) 2506 { 2507 xmlChar *description = NULL; 2508 xmlNodePtr node; 2509 2510 for (node = ((xmlNodePtr)share)->children; node != NULL; 2511 node = node->next) { 2512 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 2513 break; 2514 } 2515 } 2516 if (node != NULL) { 2517 description = xmlNodeGetContent(node); 2518 fixproblemchars((char *)description); 2519 } 2520 return ((char *)description); 2521 } 2522 2523 /* 2524 * sa_free(share_description(description) 2525 * 2526 * Free the description string. 2527 */ 2528 2529 void 2530 sa_free_share_description(char *description) 2531 { 2532 xmlFree((xmlChar *)description); 2533 } 2534 2535 /* 2536 * sa_create_optionset(group, proto) 2537 * 2538 * Create an optionset for the specified protocol in the specied 2539 * group. This is manifested as a property group within SMF. 2540 */ 2541 2542 sa_optionset_t 2543 sa_create_optionset(sa_group_t group, char *proto) 2544 { 2545 sa_optionset_t optionset; 2546 sa_group_t parent = group; 2547 sa_share_t share = NULL; 2548 int err = SA_OK; 2549 char *id = NULL; 2550 2551 optionset = sa_get_optionset(group, proto); 2552 if (optionset != NULL) { 2553 /* can't have a duplicate protocol */ 2554 optionset = NULL; 2555 } else { 2556 /* 2557 * Account for resource names being slightly 2558 * different. 2559 */ 2560 if (sa_is_share(group)) { 2561 /* 2562 * Transient shares do not have an "id" so not an 2563 * error to not find one. 2564 */ 2565 id = sa_get_share_attr((sa_share_t)group, "id"); 2566 } else if (sa_is_resource(group)) { 2567 share = sa_get_resource_parent( 2568 (sa_resource_t)group); 2569 id = sa_get_resource_attr(share, "id"); 2570 2571 /* id can be NULL if the group is transient (ZFS) */ 2572 if (id == NULL && sa_is_persistent(group)) 2573 err = SA_NO_MEMORY; 2574 } 2575 if (err == SA_NO_MEMORY) { 2576 /* 2577 * Couldn't get the id for the share or 2578 * resource. While this could be a 2579 * configuration issue, it is most likely an 2580 * out of memory. In any case, fail the create. 2581 */ 2582 return (NULL); 2583 } 2584 2585 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 2586 NULL, (xmlChar *)"optionset", NULL); 2587 /* 2588 * only put to repository if on a group and we were 2589 * able to create an optionset. 2590 */ 2591 if (optionset != NULL) { 2592 char oname[SA_STRSIZE]; 2593 char *groupname; 2594 2595 /* 2596 * Need to get parent group in all cases, but also get 2597 * the share if this is a resource. 2598 */ 2599 if (sa_is_share(group)) { 2600 parent = sa_get_parent_group((sa_share_t)group); 2601 } else if (sa_is_resource(group)) { 2602 share = sa_get_resource_parent( 2603 (sa_resource_t)group); 2604 parent = sa_get_parent_group(share); 2605 } 2606 2607 sa_set_optionset_attr(optionset, "type", proto); 2608 2609 (void) sa_optionset_name(optionset, oname, 2610 sizeof (oname), id); 2611 groupname = sa_get_group_attr(parent, "name"); 2612 if (groupname != NULL && sa_is_persistent(group)) { 2613 sa_handle_impl_t impl_handle; 2614 impl_handle = 2615 (sa_handle_impl_t)sa_find_group_handle( 2616 group); 2617 assert(impl_handle != NULL); 2618 if (impl_handle != NULL) { 2619 (void) sa_get_instance( 2620 impl_handle->scfhandle, groupname); 2621 (void) sa_create_pgroup( 2622 impl_handle->scfhandle, oname); 2623 } 2624 } 2625 if (groupname != NULL) 2626 sa_free_attr_string(groupname); 2627 } 2628 } 2629 2630 if (id != NULL) 2631 sa_free_attr_string(id); 2632 return (optionset); 2633 } 2634 2635 /* 2636 * sa_get_property_parent(property) 2637 * 2638 * Given a property, return the object it is a property of. This will 2639 * be an optionset of some type. 2640 */ 2641 2642 static sa_optionset_t 2643 sa_get_property_parent(sa_property_t property) 2644 { 2645 xmlNodePtr node = NULL; 2646 2647 if (property != NULL) 2648 node = ((xmlNodePtr)property)->parent; 2649 return ((sa_optionset_t)node); 2650 } 2651 2652 /* 2653 * sa_get_optionset_parent(optionset) 2654 * 2655 * Return the parent of the specified optionset. This could be a group 2656 * or a share. 2657 */ 2658 2659 static sa_group_t 2660 sa_get_optionset_parent(sa_optionset_t optionset) 2661 { 2662 xmlNodePtr node = NULL; 2663 2664 if (optionset != NULL) 2665 node = ((xmlNodePtr)optionset)->parent; 2666 return ((sa_group_t)node); 2667 } 2668 2669 /* 2670 * zfs_needs_update(share) 2671 * 2672 * In order to avoid making multiple updates to a ZFS share when 2673 * setting properties, the share attribute "changed" will be set to 2674 * true when a property is added or modified. When done adding 2675 * properties, we can then detect that an update is needed. We then 2676 * clear the state here to detect additional changes. 2677 */ 2678 2679 static int 2680 zfs_needs_update(sa_share_t share) 2681 { 2682 char *attr; 2683 int result = 0; 2684 2685 attr = sa_get_share_attr(share, "changed"); 2686 if (attr != NULL) { 2687 sa_free_attr_string(attr); 2688 result = 1; 2689 } 2690 set_node_attr((void *)share, "changed", NULL); 2691 return (result); 2692 } 2693 2694 /* 2695 * zfs_set_update(share) 2696 * 2697 * Set the changed attribute of the share to true. 2698 */ 2699 2700 static void 2701 zfs_set_update(sa_share_t share) 2702 { 2703 set_node_attr((void *)share, "changed", "true"); 2704 } 2705 2706 /* 2707 * sa_commit_properties(optionset, clear) 2708 * 2709 * Check if SMF or ZFS config and either update or abort the pending 2710 * changes. 2711 */ 2712 2713 int 2714 sa_commit_properties(sa_optionset_t optionset, int clear) 2715 { 2716 sa_group_t group; 2717 sa_group_t parent; 2718 int zfs = 0; 2719 int needsupdate = 0; 2720 int ret = SA_OK; 2721 sa_handle_impl_t impl_handle; 2722 2723 group = sa_get_optionset_parent(optionset); 2724 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2725 /* only update ZFS if on a share */ 2726 parent = sa_get_parent_group(group); 2727 zfs++; 2728 if (parent != NULL && is_zfs_group(parent)) 2729 needsupdate = zfs_needs_update(group); 2730 else 2731 zfs = 0; 2732 } 2733 if (zfs) { 2734 if (!clear && needsupdate) 2735 ret = sa_zfs_update((sa_share_t)group); 2736 } else { 2737 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2738 if (impl_handle != NULL) { 2739 if (clear) { 2740 (void) sa_abort_transaction( 2741 impl_handle->scfhandle); 2742 } else { 2743 ret = sa_end_transaction( 2744 impl_handle->scfhandle); 2745 } 2746 } else { 2747 ret = SA_SYSTEM_ERR; 2748 } 2749 } 2750 return (ret); 2751 } 2752 2753 /* 2754 * sa_destroy_optionset(optionset) 2755 * 2756 * Remove the optionset from its group. Update the repository to 2757 * reflect this change. 2758 */ 2759 2760 int 2761 sa_destroy_optionset(sa_optionset_t optionset) 2762 { 2763 char name[SA_STRSIZE]; 2764 int len; 2765 int ret; 2766 char *id = NULL; 2767 sa_group_t group; 2768 int ispersist = 1; 2769 2770 /* now delete the prop group */ 2771 group = sa_get_optionset_parent(optionset); 2772 if (group != NULL) { 2773 if (sa_is_resource(group)) { 2774 sa_resource_t resource = group; 2775 sa_share_t share = sa_get_resource_parent(resource); 2776 group = sa_get_parent_group(share); 2777 id = sa_get_share_attr(share, "id"); 2778 } else if (sa_is_share(group)) { 2779 id = sa_get_share_attr((sa_share_t)group, "id"); 2780 } 2781 ispersist = sa_is_persistent(group); 2782 } 2783 if (ispersist) { 2784 sa_handle_impl_t impl_handle; 2785 len = sa_optionset_name(optionset, name, sizeof (name), id); 2786 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 2787 if (impl_handle != NULL) { 2788 if (len > 0) { 2789 ret = sa_delete_pgroup(impl_handle->scfhandle, 2790 name); 2791 } 2792 } else { 2793 ret = SA_SYSTEM_ERR; 2794 } 2795 } 2796 xmlUnlinkNode((xmlNodePtr)optionset); 2797 xmlFreeNode((xmlNodePtr)optionset); 2798 if (id != NULL) 2799 sa_free_attr_string(id); 2800 return (ret); 2801 } 2802 2803 /* private to the implementation */ 2804 int 2805 _sa_remove_optionset(sa_optionset_t optionset) 2806 { 2807 int ret = SA_OK; 2808 2809 xmlUnlinkNode((xmlNodePtr)optionset); 2810 xmlFreeNode((xmlNodePtr)optionset); 2811 return (ret); 2812 } 2813 2814 /* 2815 * sa_create_security(group, sectype, proto) 2816 * 2817 * Create a security optionset (one that has a type name and a 2818 * proto). Security is left over from a pure NFS implementation. The 2819 * naming will change in the future when the API is released. 2820 */ 2821 sa_security_t 2822 sa_create_security(sa_group_t group, char *sectype, char *proto) 2823 { 2824 sa_security_t security; 2825 char *id = NULL; 2826 sa_group_t parent; 2827 char *groupname = NULL; 2828 2829 if (group != NULL && sa_is_share(group)) { 2830 id = sa_get_share_attr((sa_share_t)group, "id"); 2831 parent = sa_get_parent_group(group); 2832 if (parent != NULL) 2833 groupname = sa_get_group_attr(parent, "name"); 2834 } else if (group != NULL) { 2835 groupname = sa_get_group_attr(group, "name"); 2836 } 2837 2838 security = sa_get_security(group, sectype, proto); 2839 if (security != NULL) { 2840 /* can't have a duplicate security option */ 2841 security = NULL; 2842 } else { 2843 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2844 NULL, (xmlChar *)"security", NULL); 2845 if (security != NULL) { 2846 char oname[SA_STRSIZE]; 2847 sa_set_security_attr(security, "type", proto); 2848 2849 sa_set_security_attr(security, "sectype", sectype); 2850 (void) sa_security_name(security, oname, 2851 sizeof (oname), id); 2852 if (groupname != NULL && sa_is_persistent(group)) { 2853 sa_handle_impl_t impl_handle; 2854 impl_handle = 2855 (sa_handle_impl_t)sa_find_group_handle( 2856 group); 2857 if (impl_handle != NULL) { 2858 (void) sa_get_instance( 2859 impl_handle->scfhandle, groupname); 2860 (void) sa_create_pgroup( 2861 impl_handle->scfhandle, oname); 2862 } 2863 } 2864 } 2865 } 2866 if (groupname != NULL) 2867 sa_free_attr_string(groupname); 2868 return (security); 2869 } 2870 2871 /* 2872 * sa_destroy_security(security) 2873 * 2874 * Remove the specified optionset from the document and the 2875 * configuration. 2876 */ 2877 2878 int 2879 sa_destroy_security(sa_security_t security) 2880 { 2881 char name[SA_STRSIZE]; 2882 int len; 2883 int ret = SA_OK; 2884 char *id = NULL; 2885 sa_group_t group; 2886 int iszfs = 0; 2887 int ispersist = 1; 2888 2889 group = sa_get_optionset_parent(security); 2890 2891 if (group != NULL) 2892 iszfs = sa_group_is_zfs(group); 2893 2894 if (group != NULL && !iszfs) { 2895 if (sa_is_share(group)) 2896 ispersist = sa_is_persistent(group); 2897 id = sa_get_share_attr((sa_share_t)group, "id"); 2898 } 2899 if (ispersist) { 2900 len = sa_security_name(security, name, sizeof (name), id); 2901 if (!iszfs && len > 0) { 2902 sa_handle_impl_t impl_handle; 2903 impl_handle = 2904 (sa_handle_impl_t)sa_find_group_handle(group); 2905 if (impl_handle != NULL) { 2906 ret = sa_delete_pgroup(impl_handle->scfhandle, 2907 name); 2908 } else { 2909 ret = SA_SYSTEM_ERR; 2910 } 2911 } 2912 } 2913 xmlUnlinkNode((xmlNodePtr)security); 2914 xmlFreeNode((xmlNodePtr)security); 2915 if (iszfs) 2916 ret = sa_zfs_update(group); 2917 if (id != NULL) 2918 sa_free_attr_string(id); 2919 return (ret); 2920 } 2921 2922 /* 2923 * sa_get_security_attr(optionset, tag) 2924 * 2925 * Return the specified attribute value from the optionset. 2926 */ 2927 2928 char * 2929 sa_get_security_attr(sa_property_t optionset, char *tag) 2930 { 2931 return (get_node_attr((void *)optionset, tag)); 2932 2933 } 2934 2935 /* 2936 * sa_set_security_attr(optionset, tag, value) 2937 * 2938 * Set the optioset attribute specied by tag to the specified value. 2939 */ 2940 2941 void 2942 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2943 { 2944 set_node_attr((void *)optionset, tag, value); 2945 } 2946 2947 /* 2948 * is_nodetype(node, type) 2949 * 2950 * Check to see if node is of the type specified. 2951 */ 2952 2953 static int 2954 is_nodetype(void *node, char *type) 2955 { 2956 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2957 } 2958 2959 /* 2960 * add_or_update() 2961 * 2962 * Add or update a property. Pulled out of sa_set_prop_by_prop for 2963 * readability. 2964 */ 2965 static int 2966 add_or_update(scfutilhandle_t *scf_handle, int type, scf_value_t *value, 2967 scf_transaction_entry_t *entry, char *name, char *valstr) 2968 { 2969 int ret = SA_SYSTEM_ERR; 2970 2971 if (value != NULL) { 2972 if (type == SA_PROP_OP_ADD) 2973 ret = scf_transaction_property_new(scf_handle->trans, 2974 entry, name, SCF_TYPE_ASTRING); 2975 else 2976 ret = scf_transaction_property_change(scf_handle->trans, 2977 entry, name, SCF_TYPE_ASTRING); 2978 if (ret == 0) { 2979 ret = scf_value_set_astring(value, valstr); 2980 if (ret == 0) 2981 ret = scf_entry_add_value(entry, value); 2982 if (ret == 0) 2983 return (ret); 2984 scf_value_destroy(value); 2985 } else { 2986 scf_entry_destroy(entry); 2987 } 2988 } 2989 return (SA_SYSTEM_ERR); 2990 } 2991 2992 /* 2993 * sa_set_prop_by_prop(optionset, group, prop, type) 2994 * 2995 * Add/remove/update the specified property prop into the optionset or 2996 * share. If a share, sort out which property group based on GUID. In 2997 * all cases, the appropriate transaction is set (or ZFS share is 2998 * marked as needing an update) 2999 */ 3000 3001 static int 3002 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 3003 sa_property_t prop, int type) 3004 { 3005 char *name; 3006 char *valstr; 3007 int ret = SA_OK; 3008 scf_transaction_entry_t *entry; 3009 scf_value_t *value; 3010 int opttype; /* 1 == optionset, 0 == security */ 3011 char *id = NULL; 3012 int iszfs = 0; 3013 sa_group_t parent = NULL; 3014 sa_share_t share = NULL; 3015 sa_handle_impl_t impl_handle; 3016 scfutilhandle_t *scf_handle; 3017 3018 if (!sa_is_persistent(group)) { 3019 /* 3020 * if the group/share is not persistent we don't need 3021 * to do anything here 3022 */ 3023 return (SA_OK); 3024 } 3025 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 3026 if (impl_handle == NULL || impl_handle->scfhandle == NULL) 3027 return (SA_SYSTEM_ERR); 3028 scf_handle = impl_handle->scfhandle; 3029 name = sa_get_property_attr(prop, "type"); 3030 valstr = sa_get_property_attr(prop, "value"); 3031 entry = scf_entry_create(scf_handle->handle); 3032 opttype = is_nodetype((void *)optionset, "optionset"); 3033 3034 /* 3035 * Check for share vs. resource since they need slightly 3036 * different treatment given the hierarchy. 3037 */ 3038 if (valstr != NULL && entry != NULL) { 3039 if (sa_is_share(group)) { 3040 parent = sa_get_parent_group(group); 3041 share = (sa_share_t)group; 3042 if (parent != NULL) 3043 iszfs = is_zfs_group(parent); 3044 } else if (sa_is_resource(group)) { 3045 share = sa_get_parent_group(group); 3046 if (share != NULL) 3047 parent = sa_get_parent_group(share); 3048 } else { 3049 iszfs = is_zfs_group(group); 3050 } 3051 if (!iszfs) { 3052 if (scf_handle->trans == NULL) { 3053 char oname[SA_STRSIZE]; 3054 char *groupname = NULL; 3055 if (share != NULL) { 3056 if (parent != NULL) 3057 groupname = 3058 sa_get_group_attr(parent, 3059 "name"); 3060 id = sa_get_share_attr( 3061 (sa_share_t)share, "id"); 3062 } else { 3063 groupname = sa_get_group_attr(group, 3064 "name"); 3065 } 3066 if (groupname != NULL) { 3067 ret = sa_get_instance(scf_handle, 3068 groupname); 3069 sa_free_attr_string(groupname); 3070 } 3071 if (opttype) 3072 (void) sa_optionset_name(optionset, 3073 oname, sizeof (oname), id); 3074 else 3075 (void) sa_security_name(optionset, 3076 oname, sizeof (oname), id); 3077 ret = sa_start_transaction(scf_handle, oname); 3078 } 3079 if (ret == SA_OK) { 3080 switch (type) { 3081 case SA_PROP_OP_REMOVE: 3082 ret = scf_transaction_property_delete( 3083 scf_handle->trans, entry, name); 3084 break; 3085 case SA_PROP_OP_ADD: 3086 case SA_PROP_OP_UPDATE: 3087 value = scf_value_create( 3088 scf_handle->handle); 3089 ret = add_or_update(scf_handle, type, 3090 value, entry, name, valstr); 3091 break; 3092 } 3093 } 3094 } else { 3095 /* 3096 * ZFS update. The calling function would have updated 3097 * the internal XML structure. Just need to flag it as 3098 * changed for ZFS. 3099 */ 3100 zfs_set_update((sa_share_t)group); 3101 } 3102 } 3103 3104 if (name != NULL) 3105 sa_free_attr_string(name); 3106 if (valstr != NULL) 3107 sa_free_attr_string(valstr); 3108 else if (entry != NULL) 3109 scf_entry_destroy(entry); 3110 3111 if (ret == -1) 3112 ret = SA_SYSTEM_ERR; 3113 3114 return (ret); 3115 } 3116 3117 /* 3118 * sa_create_property(name, value) 3119 * 3120 * Create a new property with the specified name and value. 3121 */ 3122 3123 sa_property_t 3124 sa_create_property(char *name, char *value) 3125 { 3126 xmlNodePtr node; 3127 3128 node = xmlNewNode(NULL, (xmlChar *)"option"); 3129 if (node != NULL) { 3130 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 3131 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 3132 } 3133 return ((sa_property_t)node); 3134 } 3135 3136 /* 3137 * sa_add_property(object, property) 3138 * 3139 * Add the specified property to the object. Issue the appropriate 3140 * transaction or mark a ZFS object as needing an update. 3141 */ 3142 3143 int 3144 sa_add_property(void *object, sa_property_t property) 3145 { 3146 int ret = SA_OK; 3147 sa_group_t parent; 3148 sa_group_t group; 3149 char *proto; 3150 3151 proto = sa_get_optionset_attr(object, "type"); 3152 if (property != NULL) { 3153 if ((ret = sa_valid_property(object, proto, property)) == 3154 SA_OK) { 3155 property = (sa_property_t)xmlAddChild( 3156 (xmlNodePtr)object, (xmlNodePtr)property); 3157 } else { 3158 if (proto != NULL) 3159 sa_free_attr_string(proto); 3160 return (ret); 3161 } 3162 } 3163 3164 if (proto != NULL) 3165 sa_free_attr_string(proto); 3166 3167 parent = sa_get_parent_group(object); 3168 if (!sa_is_persistent(parent)) 3169 return (ret); 3170 3171 if (sa_is_resource(parent)) { 3172 /* 3173 * Resources are children of share. Need to go up two 3174 * levels to find the group but the parent needs to be 3175 * the share at this point in order to get the "id". 3176 */ 3177 parent = sa_get_parent_group(parent); 3178 group = sa_get_parent_group(parent); 3179 } else if (sa_is_share(parent)) { 3180 group = sa_get_parent_group(parent); 3181 } else { 3182 group = parent; 3183 } 3184 3185 if (property == NULL) { 3186 ret = SA_NO_MEMORY; 3187 } else { 3188 char oname[SA_STRSIZE]; 3189 3190 if (!is_zfs_group(group)) { 3191 char *id = NULL; 3192 sa_handle_impl_t impl_handle; 3193 scfutilhandle_t *scf_handle; 3194 3195 impl_handle = (sa_handle_impl_t)sa_find_group_handle( 3196 group); 3197 if (impl_handle == NULL || 3198 impl_handle->scfhandle == NULL) 3199 ret = SA_SYSTEM_ERR; 3200 if (ret == SA_OK) { 3201 scf_handle = impl_handle->scfhandle; 3202 if (sa_is_share((sa_group_t)parent)) { 3203 id = sa_get_share_attr( 3204 (sa_share_t)parent, "id"); 3205 } 3206 if (scf_handle->trans == NULL) { 3207 if (is_nodetype(object, "optionset")) { 3208 (void) sa_optionset_name( 3209 (sa_optionset_t)object, 3210 oname, sizeof (oname), id); 3211 } else { 3212 (void) sa_security_name( 3213 (sa_optionset_t)object, 3214 oname, sizeof (oname), id); 3215 } 3216 ret = sa_start_transaction(scf_handle, 3217 oname); 3218 } 3219 if (ret == SA_OK) { 3220 char *name; 3221 char *value; 3222 name = sa_get_property_attr(property, 3223 "type"); 3224 value = sa_get_property_attr(property, 3225 "value"); 3226 if (name != NULL && value != NULL) { 3227 if (scf_handle->scf_state == 3228 SCH_STATE_INIT) { 3229 ret = sa_set_property( 3230 scf_handle, name, 3231 value); 3232 } 3233 } else { 3234 ret = SA_CONFIG_ERR; 3235 } 3236 if (name != NULL) 3237 sa_free_attr_string( 3238 name); 3239 if (value != NULL) 3240 sa_free_attr_string(value); 3241 } 3242 if (id != NULL) 3243 sa_free_attr_string(id); 3244 } 3245 } else { 3246 /* 3247 * ZFS is a special case. We do want 3248 * to allow editing property/security 3249 * lists since we can have a better 3250 * syntax and we also want to keep 3251 * things consistent when possible. 3252 * 3253 * Right now, we defer until the 3254 * sa_commit_properties so we can get 3255 * them all at once. We do need to 3256 * mark the share as "changed" 3257 */ 3258 zfs_set_update((sa_share_t)parent); 3259 } 3260 } 3261 return (ret); 3262 } 3263 3264 /* 3265 * sa_remove_property(property) 3266 * 3267 * Remove the specied property from its containing object. Update the 3268 * repository as appropriate. 3269 */ 3270 3271 int 3272 sa_remove_property(sa_property_t property) 3273 { 3274 int ret = SA_OK; 3275 3276 if (property != NULL) { 3277 sa_optionset_t optionset; 3278 sa_group_t group; 3279 optionset = sa_get_property_parent(property); 3280 if (optionset != NULL) { 3281 group = sa_get_optionset_parent(optionset); 3282 if (group != NULL) { 3283 ret = sa_set_prop_by_prop(optionset, group, 3284 property, SA_PROP_OP_REMOVE); 3285 } 3286 } 3287 xmlUnlinkNode((xmlNodePtr)property); 3288 xmlFreeNode((xmlNodePtr)property); 3289 } else { 3290 ret = SA_NO_SUCH_PROP; 3291 } 3292 return (ret); 3293 } 3294 3295 /* 3296 * sa_update_property(property, value) 3297 * 3298 * Update the specified property to the new value. If value is NULL, 3299 * we currently treat this as a remove. 3300 */ 3301 3302 int 3303 sa_update_property(sa_property_t property, char *value) 3304 { 3305 int ret = SA_OK; 3306 if (value == NULL) { 3307 return (sa_remove_property(property)); 3308 } else { 3309 sa_optionset_t optionset; 3310 sa_group_t group; 3311 set_node_attr((void *)property, "value", value); 3312 optionset = sa_get_property_parent(property); 3313 if (optionset != NULL) { 3314 group = sa_get_optionset_parent(optionset); 3315 if (group != NULL) { 3316 ret = sa_set_prop_by_prop(optionset, group, 3317 property, SA_PROP_OP_UPDATE); 3318 } 3319 } else { 3320 ret = SA_NO_SUCH_PROP; 3321 } 3322 } 3323 return (ret); 3324 } 3325 3326 /* 3327 * sa_get_protocol_property(propset, prop) 3328 * 3329 * Get the specified protocol specific property. These are global to 3330 * the protocol and not specific to a group or share. 3331 */ 3332 3333 sa_property_t 3334 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 3335 { 3336 xmlNodePtr node = (xmlNodePtr)propset; 3337 xmlChar *value = NULL; 3338 3339 for (node = node->children; node != NULL; 3340 node = node->next) { 3341 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3342 if (prop == NULL) 3343 break; 3344 value = xmlGetProp(node, (xmlChar *)"type"); 3345 if (value != NULL && 3346 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 3347 break; 3348 } 3349 if (value != NULL) { 3350 xmlFree(value); 3351 value = NULL; 3352 } 3353 } 3354 } 3355 if (value != NULL) 3356 xmlFree(value); 3357 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 3358 /* 3359 * avoid a non option node -- it is possible to be a 3360 * text node 3361 */ 3362 node = NULL; 3363 } 3364 return ((sa_property_t)node); 3365 } 3366 3367 /* 3368 * sa_get_next_protocol_property(prop) 3369 * 3370 * Get the next protocol specific property in the list. 3371 */ 3372 3373 sa_property_t 3374 sa_get_next_protocol_property(sa_property_t prop) 3375 { 3376 xmlNodePtr node; 3377 3378 for (node = ((xmlNodePtr)prop)->next; node != NULL; 3379 node = node->next) { 3380 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 3381 break; 3382 } 3383 } 3384 return ((sa_property_t)node); 3385 } 3386 3387 /* 3388 * sa_set_protocol_property(prop, value) 3389 * 3390 * Set the specified property to have the new value. The protocol 3391 * specific plugin will then be called to update the property. 3392 */ 3393 3394 int 3395 sa_set_protocol_property(sa_property_t prop, char *value) 3396 { 3397 sa_protocol_properties_t propset; 3398 char *proto; 3399 int ret = SA_INVALID_PROTOCOL; 3400 3401 propset = ((xmlNodePtr)prop)->parent; 3402 if (propset != NULL) { 3403 proto = sa_get_optionset_attr(propset, "type"); 3404 if (proto != NULL) { 3405 set_node_attr((xmlNodePtr)prop, "value", value); 3406 ret = sa_proto_set_property(proto, prop); 3407 sa_free_attr_string(proto); 3408 } 3409 } 3410 return (ret); 3411 } 3412 3413 /* 3414 * sa_add_protocol_property(propset, prop) 3415 * 3416 * Add a new property to the protocol specific property set. 3417 */ 3418 3419 int 3420 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 3421 { 3422 xmlNodePtr node; 3423 3424 /* should check for legitimacy */ 3425 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 3426 if (node != NULL) 3427 return (SA_OK); 3428 return (SA_NO_MEMORY); 3429 } 3430 3431 /* 3432 * sa_create_protocol_properties(proto) 3433 * 3434 * Create a protocol specific property set. 3435 */ 3436 3437 sa_protocol_properties_t 3438 sa_create_protocol_properties(char *proto) 3439 { 3440 xmlNodePtr node; 3441 3442 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 3443 if (node != NULL) 3444 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 3445 return (node); 3446 } 3447 3448 /* 3449 * sa_get_share_resource(share, resource) 3450 * 3451 * Get the named resource from the share, if it exists. If resource is 3452 * NULL, get the first resource. 3453 */ 3454 3455 sa_resource_t 3456 sa_get_share_resource(sa_share_t share, char *resource) 3457 { 3458 xmlNodePtr node = NULL; 3459 xmlChar *name; 3460 3461 if (share != NULL) { 3462 for (node = ((xmlNodePtr)share)->children; node != NULL; 3463 node = node->next) { 3464 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) { 3465 if (resource == NULL) { 3466 /* 3467 * We are looking for the first 3468 * resource node and not a names 3469 * resource. 3470 */ 3471 break; 3472 } else { 3473 /* is it the correct share? */ 3474 name = xmlGetProp(node, 3475 (xmlChar *)"name"); 3476 if (name != NULL && 3477 xmlStrcasecmp(name, 3478 (xmlChar *)resource) == 0) { 3479 xmlFree(name); 3480 break; 3481 } 3482 xmlFree(name); 3483 } 3484 } 3485 } 3486 } 3487 return ((sa_resource_t)node); 3488 } 3489 3490 /* 3491 * sa_get_next_resource(resource) 3492 * Return the next share following the specified share 3493 * from the internal list of shares. Returns NULL if there 3494 * are no more shares. The list is relative to the same 3495 * group. 3496 */ 3497 sa_share_t 3498 sa_get_next_resource(sa_resource_t resource) 3499 { 3500 xmlNodePtr node = NULL; 3501 3502 if (resource != NULL) { 3503 for (node = ((xmlNodePtr)resource)->next; node != NULL; 3504 node = node->next) { 3505 if (xmlStrcmp(node->name, (xmlChar *)"resource") == 0) 3506 break; 3507 } 3508 } 3509 return ((sa_share_t)node); 3510 } 3511 3512 /* 3513 * _sa_get_next_resource_index(share) 3514 * 3515 * get the next resource index number (one greater then current largest) 3516 */ 3517 3518 static int 3519 _sa_get_next_resource_index(sa_share_t share) 3520 { 3521 sa_resource_t resource; 3522 int index = 0; 3523 char *id; 3524 3525 for (resource = sa_get_share_resource(share, NULL); 3526 resource != NULL; 3527 resource = sa_get_next_resource(resource)) { 3528 id = get_node_attr((void *)resource, "id"); 3529 if (id != NULL) { 3530 int val; 3531 val = atoi(id); 3532 if (val > index) 3533 index = val; 3534 sa_free_attr_string(id); 3535 } 3536 } 3537 return (index + 1); 3538 } 3539 3540 3541 /* 3542 * sa_add_resource(share, resource, persist, &err) 3543 * 3544 * Adds a new resource name associated with share. The resource name 3545 * must be unique in the system and will be case insensitive (eventually). 3546 */ 3547 3548 sa_resource_t 3549 sa_add_resource(sa_share_t share, char *resource, int persist, int *error) 3550 { 3551 xmlNodePtr node; 3552 int err = SA_OK; 3553 sa_resource_t res; 3554 sa_group_t group; 3555 sa_handle_t handle; 3556 char istring[8]; /* just big enough for an integer value */ 3557 int index; 3558 3559 group = sa_get_parent_group(share); 3560 handle = sa_find_group_handle(group); 3561 res = sa_find_resource(handle, resource); 3562 if (res != NULL) { 3563 err = SA_DUPLICATE_NAME; 3564 res = NULL; 3565 } else { 3566 node = xmlNewChild((xmlNodePtr)share, NULL, 3567 (xmlChar *)"resource", NULL); 3568 if (node != NULL) { 3569 xmlSetProp(node, (xmlChar *)"name", 3570 (xmlChar *)resource); 3571 xmlSetProp(node, (xmlChar *)"type", persist ? 3572 (xmlChar *)"persist" : (xmlChar *)"transient"); 3573 if (persist != SA_SHARE_TRANSIENT) { 3574 index = _sa_get_next_resource_index(share); 3575 (void) snprintf(istring, sizeof (istring), "%d", 3576 index); 3577 xmlSetProp(node, (xmlChar *)"id", 3578 (xmlChar *)istring); 3579 if (!sa_group_is_zfs(group) && 3580 sa_is_persistent((sa_group_t)share)) { 3581 /* ZFS doesn't use resource names */ 3582 sa_handle_impl_t ihandle; 3583 ihandle = (sa_handle_impl_t) 3584 sa_find_group_handle( 3585 group); 3586 if (ihandle != NULL) 3587 err = sa_commit_share( 3588 ihandle->scfhandle, group, 3589 share); 3590 else 3591 err = SA_SYSTEM_ERR; 3592 } 3593 } 3594 } 3595 } 3596 if (error != NULL) 3597 *error = err; 3598 return ((sa_resource_t)node); 3599 } 3600 3601 /* 3602 * sa_remove_resource(resource) 3603 * 3604 * Remove the resource name from the share (and the system) 3605 */ 3606 3607 int 3608 sa_remove_resource(sa_resource_t resource) 3609 { 3610 sa_share_t share; 3611 sa_group_t group; 3612 char *type; 3613 int ret = SA_OK; 3614 int transient = 0; 3615 sa_optionset_t opt; 3616 3617 share = sa_get_resource_parent(resource); 3618 type = sa_get_share_attr(share, "type"); 3619 group = sa_get_parent_group(share); 3620 3621 3622 if (type != NULL) { 3623 if (strcmp(type, "persist") != 0) 3624 transient = 1; 3625 sa_free_attr_string(type); 3626 } 3627 3628 /* Disable the resource for all protocols. */ 3629 (void) sa_disable_resource(resource, NULL); 3630 3631 /* Remove any optionsets from the resource. */ 3632 for (opt = sa_get_optionset(resource, NULL); 3633 opt != NULL; 3634 opt = sa_get_next_optionset(opt)) 3635 (void) sa_destroy_optionset(opt); 3636 3637 /* Remove from the share */ 3638 xmlUnlinkNode((xmlNode *)resource); 3639 xmlFreeNode((xmlNode *)resource); 3640 3641 /* only do SMF action if permanent and not ZFS */ 3642 if (!transient && !sa_group_is_zfs(group)) { 3643 sa_handle_impl_t ihandle; 3644 ihandle = (sa_handle_impl_t)sa_find_group_handle(group); 3645 if (ihandle != NULL) 3646 ret = sa_commit_share(ihandle->scfhandle, group, share); 3647 else 3648 ret = SA_SYSTEM_ERR; 3649 } 3650 return (ret); 3651 } 3652 3653 /* 3654 * proto_resource_rename(handle, group, resource, newname) 3655 * 3656 * Helper function for sa_rename_resource that notifies the protocol 3657 * of a resource name change prior to a config repository update. 3658 */ 3659 static int 3660 proto_rename_resource(sa_handle_t handle, sa_group_t group, 3661 sa_resource_t resource, char *newname) 3662 { 3663 sa_optionset_t optionset; 3664 int ret = SA_OK; 3665 int err; 3666 3667 for (optionset = sa_get_optionset(group, NULL); 3668 optionset != NULL; 3669 optionset = sa_get_next_optionset(optionset)) { 3670 char *type; 3671 type = sa_get_optionset_attr(optionset, "type"); 3672 if (type != NULL) { 3673 err = sa_proto_rename_resource(handle, type, resource, 3674 newname); 3675 if (err != SA_OK) 3676 ret = err; 3677 sa_free_attr_string(type); 3678 } 3679 } 3680 return (ret); 3681 } 3682 3683 /* 3684 * sa_rename_resource(resource, newname) 3685 * 3686 * Rename the resource to the new name, if it is unique. 3687 */ 3688 3689 int 3690 sa_rename_resource(sa_resource_t resource, char *newname) 3691 { 3692 sa_share_t share; 3693 sa_group_t group = NULL; 3694 sa_resource_t target; 3695 int ret = SA_CONFIG_ERR; 3696 sa_handle_t handle = NULL; 3697 3698 share = sa_get_resource_parent(resource); 3699 if (share == NULL) 3700 return (ret); 3701 3702 group = sa_get_parent_group(share); 3703 if (group == NULL) 3704 return (ret); 3705 3706 handle = (sa_handle_impl_t)sa_find_group_handle(group); 3707 if (handle == NULL) 3708 return (ret); 3709 3710 target = sa_find_resource(handle, newname); 3711 if (target != NULL) { 3712 ret = SA_DUPLICATE_NAME; 3713 } else { 3714 /* 3715 * Everything appears to be valid at this 3716 * point. Change the name of the active share and then 3717 * update the share in the appropriate repository. 3718 */ 3719 ret = proto_rename_resource(handle, group, resource, newname); 3720 set_node_attr(resource, "name", newname); 3721 if (!sa_group_is_zfs(group) && 3722 sa_is_persistent((sa_group_t)share)) { 3723 sa_handle_impl_t ihandle = (sa_handle_impl_t)handle; 3724 ret = sa_commit_share(ihandle->scfhandle, group, 3725 share); 3726 } 3727 } 3728 return (ret); 3729 } 3730 3731 /* 3732 * sa_get_resource_attr(resource, tag) 3733 * 3734 * Get the named attribute of the resource. "name" and "id" are 3735 * currently defined. NULL if tag not defined. 3736 */ 3737 3738 char * 3739 sa_get_resource_attr(sa_resource_t resource, char *tag) 3740 { 3741 return (get_node_attr((void *)resource, tag)); 3742 } 3743 3744 /* 3745 * sa_set_resource_attr(resource, tag, value) 3746 * 3747 * Get the named attribute of the resource. "name" and "id" are 3748 * currently defined. NULL if tag not defined. Currently we don't do 3749 * much, but additional checking may be needed in the future. 3750 */ 3751 3752 int 3753 sa_set_resource_attr(sa_resource_t resource, char *tag, char *value) 3754 { 3755 set_node_attr((void *)resource, tag, value); 3756 return (SA_OK); 3757 } 3758 3759 /* 3760 * sa_get_resource_parent(resource_t) 3761 * 3762 * Returns the share associated with the resource. 3763 */ 3764 3765 sa_share_t 3766 sa_get_resource_parent(sa_resource_t resource) 3767 { 3768 sa_share_t share = NULL; 3769 3770 if (resource != NULL) 3771 share = (sa_share_t)((xmlNodePtr)resource)->parent; 3772 return (share); 3773 } 3774 3775 /* 3776 * find_resource(group, name) 3777 * 3778 * Find the resource within the group. 3779 */ 3780 3781 static sa_resource_t 3782 find_resource(sa_group_t group, char *resname) 3783 { 3784 sa_share_t share; 3785 sa_resource_t resource = NULL; 3786 char *name; 3787 3788 /* Iterate over all the shares and resources in the group. */ 3789 for (share = sa_get_share(group, NULL); 3790 share != NULL && resource == NULL; 3791 share = sa_get_next_share(share)) { 3792 for (resource = sa_get_share_resource(share, NULL); 3793 resource != NULL; 3794 resource = sa_get_next_resource(resource)) { 3795 name = sa_get_resource_attr(resource, "name"); 3796 if (name != NULL && xmlStrcasecmp((xmlChar*)name, 3797 (xmlChar*)resname) == 0) { 3798 sa_free_attr_string(name); 3799 break; 3800 } 3801 if (name != NULL) { 3802 sa_free_attr_string(name); 3803 } 3804 } 3805 } 3806 return (resource); 3807 } 3808 3809 /* 3810 * sa_find_resource(name) 3811 * 3812 * Find the named resource in the system. 3813 */ 3814 3815 sa_resource_t 3816 sa_find_resource(sa_handle_t handle, char *name) 3817 { 3818 sa_group_t group; 3819 sa_group_t zgroup; 3820 sa_resource_t resource = NULL; 3821 3822 /* 3823 * Iterate over all groups and zfs subgroups and check for 3824 * resource name in them. 3825 */ 3826 for (group = sa_get_group(handle, NULL); group != NULL; 3827 group = sa_get_next_group(group)) { 3828 3829 if (is_zfs_group(group)) { 3830 for (zgroup = 3831 (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 3832 (xmlChar *)"group"); 3833 zgroup != NULL && resource == NULL; 3834 zgroup = sa_get_next_group(zgroup)) { 3835 resource = find_resource(zgroup, name); 3836 } 3837 } else { 3838 resource = find_resource(group, name); 3839 } 3840 if (resource != NULL) 3841 break; 3842 } 3843 return (resource); 3844 } 3845 3846 /* 3847 * sa_get_resource(group, resource) 3848 * 3849 * Search all the shares in the specified group for a share with a 3850 * resource name matching the one specified. 3851 * 3852 * In the future, it may be advantageous to allow group to be NULL and 3853 * search all groups but that isn't needed at present. 3854 */ 3855 3856 sa_resource_t 3857 sa_get_resource(sa_group_t group, char *resource) 3858 { 3859 sa_share_t share = NULL; 3860 sa_resource_t res = NULL; 3861 3862 if (resource != NULL) { 3863 for (share = sa_get_share(group, NULL); 3864 share != NULL && res == NULL; 3865 share = sa_get_next_share(share)) { 3866 res = sa_get_share_resource(share, resource); 3867 } 3868 } 3869 return (res); 3870 } 3871 3872 /* 3873 * sa_enable_resource, protocol) 3874 * Disable the specified share to the specified protocol. 3875 * If protocol is NULL, then all protocols. 3876 */ 3877 int 3878 sa_enable_resource(sa_resource_t resource, char *protocol) 3879 { 3880 int ret = SA_OK; 3881 char **protocols; 3882 int numproto; 3883 3884 if (protocol != NULL) { 3885 ret = sa_proto_share_resource(protocol, resource); 3886 } else { 3887 /* need to do all protocols */ 3888 if ((numproto = sa_get_protocols(&protocols)) >= 0) { 3889 int i, err; 3890 for (i = 0; i < numproto; i++) { 3891 err = sa_proto_share_resource( 3892 protocols[i], resource); 3893 if (err != SA_OK) 3894 ret = err; 3895 } 3896 free(protocols); 3897 } 3898 } 3899 if (ret == SA_OK) 3900 (void) sa_set_resource_attr(resource, "shared", NULL); 3901 3902 return (ret); 3903 } 3904 3905 /* 3906 * sa_disable_resource(resource, protocol) 3907 * 3908 * Disable the specified share for the specified protocol. If 3909 * protocol is NULL, then all protocols. If the underlying 3910 * protocol doesn't implement disable at the resource level, we 3911 * disable at the share level. 3912 */ 3913 int 3914 sa_disable_resource(sa_resource_t resource, char *protocol) 3915 { 3916 int ret = SA_OK; 3917 char **protocols; 3918 int numproto; 3919 3920 if (protocol != NULL) { 3921 ret = sa_proto_unshare_resource(protocol, resource); 3922 if (ret == SA_NOT_IMPLEMENTED) { 3923 sa_share_t parent; 3924 /* 3925 * The protocol doesn't implement unshare 3926 * resource. That implies that resource names are 3927 * simple aliases for this protocol so we need to 3928 * unshare the share. 3929 */ 3930 parent = sa_get_resource_parent(resource); 3931 if (parent != NULL) 3932 ret = sa_disable_share(parent, protocol); 3933 else 3934 ret = SA_CONFIG_ERR; 3935 } 3936 } else { 3937 /* need to do all protocols */ 3938 if ((numproto = sa_get_protocols(&protocols)) >= 0) { 3939 int i, err; 3940 for (i = 0; i < numproto; i++) { 3941 err = sa_proto_unshare_resource(protocols[i], 3942 resource); 3943 if (err == SA_NOT_SUPPORTED) { 3944 sa_share_t parent; 3945 parent = sa_get_resource_parent( 3946 resource); 3947 if (parent != NULL) 3948 err = sa_disable_share(parent, 3949 protocols[i]); 3950 else 3951 err = SA_CONFIG_ERR; 3952 } 3953 if (err != SA_OK) 3954 ret = err; 3955 } 3956 free(protocols); 3957 } 3958 } 3959 if (ret == SA_OK) 3960 (void) sa_set_resource_attr(resource, "shared", NULL); 3961 3962 return (ret); 3963 } 3964 3965 /* 3966 * sa_set_resource_description(resource, content) 3967 * 3968 * Set the description of share to content. 3969 */ 3970 3971 int 3972 sa_set_resource_description(sa_resource_t resource, char *content) 3973 { 3974 xmlNodePtr node; 3975 sa_group_t group; 3976 sa_share_t share; 3977 int ret = SA_OK; 3978 3979 for (node = ((xmlNodePtr)resource)->children; 3980 node != NULL; 3981 node = node->next) { 3982 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 3983 break; 3984 } 3985 } 3986 3987 /* no existing description but want to add */ 3988 if (node == NULL && content != NULL) { 3989 /* add a description */ 3990 node = _sa_set_share_description(resource, content); 3991 } else if (node != NULL && content != NULL) { 3992 /* update a description */ 3993 xmlNodeSetContent(node, (xmlChar *)content); 3994 } else if (node != NULL && content == NULL) { 3995 /* remove an existing description */ 3996 xmlUnlinkNode(node); 3997 xmlFreeNode(node); 3998 } 3999 share = sa_get_resource_parent(resource); 4000 group = sa_get_parent_group(share); 4001 if (group != NULL && sa_is_persistent(share)) { 4002 sa_handle_impl_t impl_handle; 4003 impl_handle = (sa_handle_impl_t)sa_find_group_handle(group); 4004 if (impl_handle != NULL) 4005 ret = sa_commit_share(impl_handle->scfhandle, 4006 group, share); 4007 else 4008 ret = SA_SYSTEM_ERR; 4009 } 4010 return (ret); 4011 } 4012 4013 /* 4014 * sa_get_resource_description(share) 4015 * 4016 * Return the description text for the specified share if it 4017 * exists. NULL if no description exists. 4018 */ 4019 4020 char * 4021 sa_get_resource_description(sa_resource_t resource) 4022 { 4023 xmlChar *description = NULL; 4024 xmlNodePtr node; 4025 4026 for (node = ((xmlNodePtr)resource)->children; node != NULL; 4027 node = node->next) { 4028 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) 4029 break; 4030 } 4031 if (node != NULL) { 4032 description = xmlNodeGetContent(node); 4033 fixproblemchars((char *)description); 4034 } 4035 return ((char *)description); 4036 } 4037