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 <unistd.h> 38 #include <libxml/parser.h> 39 #include <libxml/tree.h> 40 #include "libshare.h" 41 #include "libshare_impl.h" 42 #include <libscf.h> 43 #include "scfutil.h" 44 #include <ctype.h> 45 #include <libintl.h> 46 47 #if _NOT_SMF 48 #define CONFIG_FILE "/var/tmp/share.cfg" 49 #define CONFIG_FILE_TMP "/var/tmp/share.cfg.tmp" 50 #endif 51 #define TSTAMP(tm) (uint64_t)(((uint64_t)tm.tv_sec << 32) | \ 52 (tm.tv_nsec & 0xffffffff)) 53 54 /* 55 * internal data structures 56 */ 57 58 static xmlNodePtr sa_config_tree; /* the current config */ 59 static xmlDocPtr sa_config_doc = NULL; /* current config document */ 60 extern struct sa_proto_plugin *sap_proto_list; 61 62 /* current SMF/SVC repository handle */ 63 static scfutilhandle_t *scf_handle = NULL; 64 extern void getlegacyconfig(char *, xmlNodePtr *); 65 extern int gettransients(xmlNodePtr *); 66 extern int sa_valid_property(void *, char *, sa_property_t); 67 extern char *sa_fstype(char *); 68 extern int sa_is_share(void *); 69 extern ssize_t scf_max_name_len; /* defined in scfutil during initialization */ 70 extern int sa_group_is_zfs(sa_group_t); 71 extern int sa_path_is_zfs(char *); 72 extern int sa_zfs_set_sharenfs(sa_group_t, char *, int); 73 extern void update_legacy_config(void); 74 extern int issubdir(char *, char *); 75 extern void sa_zfs_init(void); 76 extern void sa_zfs_fini(void); 77 78 static int sa_initialized = 0; 79 80 /* helper functions */ 81 82 char * 83 sa_errorstr(int err) 84 { 85 static char errstr[32]; 86 char *ret = NULL; 87 88 switch (err) { 89 case SA_OK: 90 ret = gettext("ok"); 91 break; 92 case SA_NO_SUCH_PATH: 93 ret = gettext("path doesn't exist"); 94 break; 95 case SA_NO_MEMORY: 96 ret = gettext("no memory"); 97 break; 98 case SA_DUPLICATE_NAME: 99 ret = gettext("name in use"); 100 break; 101 case SA_BAD_PATH: 102 ret = gettext("bad path"); 103 break; 104 case SA_NO_SUCH_GROUP: 105 ret = gettext("no such group"); 106 break; 107 case SA_CONFIG_ERR: 108 ret = gettext("configuration error"); 109 break; 110 case SA_SYSTEM_ERR: 111 ret = gettext("system error"); 112 break; 113 case SA_SYNTAX_ERR: 114 ret = gettext("syntax error"); 115 break; 116 case SA_NO_PERMISSION: 117 ret = gettext("no permission"); 118 break; 119 case SA_BUSY: 120 ret = gettext("busy"); 121 break; 122 case SA_NO_SUCH_PROP: 123 ret = gettext("no such property"); 124 break; 125 case SA_INVALID_NAME: 126 ret = gettext("invalid name"); 127 break; 128 case SA_INVALID_PROTOCOL: 129 ret = gettext("invalid protocol"); 130 break; 131 case SA_NOT_ALLOWED: 132 ret = gettext("operation not allowed"); 133 break; 134 case SA_BAD_VALUE: 135 ret = gettext("bad property value"); 136 break; 137 case SA_INVALID_SECURITY: 138 ret = gettext("invalid security type"); 139 break; 140 case SA_NO_SUCH_SECURITY: 141 ret = gettext("security type not found"); 142 break; 143 case SA_VALUE_CONFLICT: 144 ret = gettext("property value conflict"); 145 break; 146 case SA_NOT_IMPLEMENTED: 147 ret = gettext("not implemented"); 148 break; 149 case SA_INVALID_PATH: 150 ret = gettext("invalid path"); 151 break; 152 case SA_NOT_SUPPORTED: 153 ret = gettext("operation not supported"); 154 break; 155 case SA_PROP_SHARE_ONLY: 156 ret = gettext("property not valid for group"); 157 break; 158 case SA_NOT_SHARED: 159 ret = gettext("not shared"); 160 break; 161 default: 162 (void) snprintf(errstr, sizeof (errstr), 163 gettext("unknown %d"), err); 164 ret = errstr; 165 } 166 return (ret); 167 } 168 169 /* 170 * get_legacy_timestamp(root, path) 171 * gets the timestamp of the last time sharemgr updated the legacy 172 * files. This is used to determine if someone has modified them by 173 * hand. 174 */ 175 static uint64_t 176 get_legacy_timestamp(xmlNodePtr root, char *path) 177 { 178 uint64_t tval = 0; 179 xmlNodePtr node; 180 xmlChar *lpath = NULL; 181 xmlChar *timestamp = NULL; 182 183 for (node = root->xmlChildrenNode; node != NULL; 184 node = node->next) { 185 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 186 /* a possible legacy node for this path */ 187 lpath = xmlGetProp(node, (xmlChar *)"path"); 188 if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) { 189 /* now have the node, extract the data */ 190 timestamp = xmlGetProp(node, (xmlChar *)"timestamp"); 191 if (timestamp != NULL) { 192 tval = strtoull((char *)timestamp, NULL, 0); 193 break; 194 } 195 } 196 if (lpath != NULL) { 197 xmlFree(lpath); 198 lpath = NULL; 199 } 200 } 201 } 202 if (lpath != NULL) 203 xmlFree(lpath); 204 if (timestamp != NULL) 205 xmlFree(timestamp); 206 return (tval); 207 } 208 209 /* 210 * set_legacy_timestamp(root, path, timevalue) 211 * 212 * add the current timestamp value to the configuration for use in 213 * determining when to update the legacy files. For SMF, this 214 * property is kept in default/operation/legacy_timestamp 215 */ 216 217 static void 218 set_legacy_timestamp(xmlNodePtr root, char *path, uint64_t tval) 219 { 220 xmlNodePtr node; 221 xmlChar *lpath = NULL; 222 223 for (node = root->xmlChildrenNode; node != NULL; 224 node = node->next) { 225 if (xmlStrcmp(node->name, (xmlChar *)"legacy") == 0) { 226 /* a possible legacy node for this path */ 227 lpath = xmlGetProp(node, (xmlChar *)"path"); 228 if (lpath != NULL && xmlStrcmp(lpath, (xmlChar *)path) == 0) { 229 xmlFree(lpath); 230 break; 231 } 232 if (lpath != NULL) 233 xmlFree(lpath); 234 } 235 } 236 if (node == NULL) { 237 /* need to create the first legacy timestamp node */ 238 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", NULL); 239 } 240 if (node != NULL) { 241 char tstring[32]; 242 int ret; 243 244 (void) snprintf(tstring, sizeof (tstring), "%lld", tval); 245 xmlSetProp(node, (xmlChar *)"timestamp", (xmlChar *)tstring); 246 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)path); 247 /* now commit to SMF */ 248 ret = sa_get_instance(scf_handle, "default"); 249 if (ret == SA_OK) { 250 ret = sa_start_transaction(scf_handle, "operation"); 251 if (ret == SA_OK) { 252 ret = sa_set_property(scf_handle, "legacy-timestamp", 253 tstring); 254 if (ret == SA_OK) { 255 (void) sa_end_transaction(scf_handle); 256 } else { 257 sa_abort_transaction(scf_handle); 258 } 259 } 260 } 261 } 262 } 263 264 /* 265 * is_shared(share) 266 * 267 * determine if the specified share is currently shared or not. 268 */ 269 static int 270 is_shared(sa_share_t share) 271 { 272 char *shared; 273 int result = 0; /* assume not */ 274 275 shared = sa_get_share_attr(share, "shared"); 276 if (shared != NULL) { 277 if (strcmp(shared, "true") == 0) 278 result = 1; 279 sa_free_attr_string(shared); 280 } 281 return (result); 282 } 283 284 /* 285 * checksubdir(newpath, strictness) 286 * 287 * checksubdir determines if the specified path (newpath) is a 288 * subdirectory of another share. It calls issubdir() from the old 289 * share implementation to do the complicated work. The strictness 290 * parameter determines how strict a check to make against the 291 * path. The strictness values mean: 292 * SA_CHECK_NORMAL == only check newpath against shares that are active 293 * SA_CHECK_STRICT == check newpath against both active shares and those 294 * stored in the repository 295 */ 296 static int 297 checksubdir(char *newpath, int strictness) 298 { 299 sa_group_t group; 300 sa_share_t share; 301 int issub; 302 char *path = NULL; 303 304 for (issub = 0, group = sa_get_group(NULL); 305 group != NULL && !issub; 306 group = sa_get_next_group(group)) { 307 for (share = sa_get_share(group, NULL); share != NULL; 308 share = sa_get_next_share(share)) { 309 /* 310 * The original behavior of share never checked 311 * against the permanent configuration 312 * (/etc/dfs/dfstab). PIT has a number of cases where 313 * it depends on this older behavior even though it 314 * could be considered incorrect. We may tighten this 315 * up in the future. 316 */ 317 if (strictness == SA_CHECK_NORMAL && !is_shared(share)) 318 continue; 319 320 path = sa_get_share_attr(share, "path"); 321 /* 322 * If path is NULL, then a share is in the process of 323 * construction or someone has modified the property 324 * group inappropriately. It should be ignored. 325 */ 326 if (path == NULL) 327 continue; 328 if (newpath != NULL && 329 (strcmp(path, newpath) == 0 || issubdir(newpath, path) || 330 issubdir(path, newpath))) { 331 sa_free_attr_string(path); 332 path = NULL; 333 issub = SA_INVALID_PATH; 334 break; 335 } 336 sa_free_attr_string(path); 337 path = NULL; 338 } 339 } 340 if (path != NULL) 341 sa_free_attr_string(path); 342 return (issub); 343 } 344 345 /* 346 * validpath(path, strictness) 347 * determine if the provided path is valid for a share. It shouldn't 348 * be a sub-dir of an already shared path or the parent directory of a 349 * share path. 350 */ 351 static int 352 validpath(char *path, int strictness) 353 { 354 int error = SA_OK; 355 struct stat st; 356 sa_share_t share; 357 char *fstype; 358 359 if (*path != '/') { 360 return (SA_BAD_PATH); 361 } 362 if (stat(path, &st) < 0) { 363 error = SA_NO_SUCH_PATH; 364 } else { 365 share = sa_find_share(path); 366 if (share != NULL) { 367 error = SA_DUPLICATE_NAME; 368 } 369 if (error == SA_OK) { 370 /* 371 * check for special case with file system that might 372 * have restrictions. For now, ZFS is the only case 373 * since it has its own idea of how to configure 374 * shares. We do this before subdir checking since 375 * things like ZFS will do that for us. This should 376 * also be done via plugin interface. 377 */ 378 fstype = sa_fstype(path); 379 if (fstype != NULL && strcmp(fstype, "zfs") == 0) { 380 if (sa_zfs_is_shared(path)) 381 error = SA_DUPLICATE_NAME; 382 } 383 if (fstype != NULL) 384 sa_free_fstype(fstype); 385 } 386 if (error == SA_OK) { 387 error = checksubdir(path, strictness); 388 } 389 } 390 return (error); 391 } 392 393 /* 394 * check to see if group/share is persistent. 395 */ 396 static int 397 is_persistent(sa_group_t group) 398 { 399 char *type; 400 int persist = 1; 401 402 type = sa_get_group_attr(group, "type"); 403 if (type != NULL && strcmp(type, "transient") == 0) 404 persist = 0; 405 if (type != NULL) 406 sa_free_attr_string(type); 407 return (persist); 408 } 409 410 /* 411 * sa_valid_group_name(name) 412 * 413 * check that the "name" contains only valid characters and otherwise 414 * fits the required naming conventions. Valid names must start with 415 * an alphabetic and the remainder may consist of only alphanumeric 416 * plus the '-' and '_' characters. This name limitation comes from 417 * inherent limitations in SMF. 418 */ 419 420 int 421 sa_valid_group_name(char *name) 422 { 423 int ret = 1; 424 ssize_t len; 425 426 if (name != NULL && isalpha(*name)) { 427 char c; 428 len = strlen(name); 429 if (len < (scf_max_name_len - sizeof ("group:"))) { 430 for (c = *name++; c != '\0' && ret != 0; c = *name++) { 431 if (!isalnum(c) && c != '-' && c != '_') 432 ret = 0; 433 } 434 } else { 435 ret = 0; 436 } 437 } else { 438 ret = 0; 439 } 440 return (ret); 441 } 442 443 444 /* 445 * is_zfs_group(group) 446 * Determine if the specified group is a ZFS sharenfs group 447 */ 448 static int 449 is_zfs_group(sa_group_t group) 450 { 451 int ret = 0; 452 xmlNodePtr parent; 453 xmlChar *zfs; 454 455 if (strcmp((char *)((xmlNodePtr)group)->name, "share") == 0) { 456 parent = (xmlNodePtr)sa_get_parent_group(group); 457 } else { 458 parent = (xmlNodePtr)group; 459 } 460 zfs = xmlGetProp(parent, (xmlChar *)"zfs"); 461 if (zfs != NULL) { 462 xmlFree(zfs); 463 ret = 1; 464 } 465 return (ret); 466 } 467 468 /* 469 * sa_optionset_name(optionset, oname, len, id) 470 * return the SMF name for the optionset. If id is not NULL, it 471 * will have the GUID value for a share and should be used 472 * instead of the keyword "optionset" which is used for 473 * groups. If the optionset doesn't have a protocol type 474 * associated with it, "default" is used. This shouldn't happen 475 * at this point but may be desirable in the future if there are 476 * protocol independent properties added. The name is returned in 477 * oname. 478 */ 479 480 static int 481 sa_optionset_name(sa_optionset_t optionset, char *oname, size_t len, char *id) 482 { 483 char *proto; 484 485 if (id == NULL) 486 id = "optionset"; 487 488 proto = sa_get_optionset_attr(optionset, "type"); 489 len = snprintf(oname, len, "%s_%s", id, proto ? proto : "default"); 490 491 if (proto != NULL) 492 sa_free_attr_string(proto); 493 return (len); 494 } 495 496 /* 497 * sa_security_name(optionset, oname, len, id) 498 * 499 * return the SMF name for the security. If id is not NULL, it will 500 * have the GUID value for a share and should be used instead of the 501 * keyword "optionset" which is used for groups. If the optionset 502 * doesn't have a protocol type associated with it, "default" is 503 * used. This shouldn't happen at this point but may be desirable in 504 * the future if there are protocol independent properties added. The 505 * name is returned in oname. The security type is also encoded into 506 * the name. In the future, this wil *be handled a bit differently. 507 */ 508 509 static int 510 sa_security_name(sa_security_t security, char *oname, size_t len, char *id) 511 { 512 char *proto; 513 char *sectype; 514 515 if (id == NULL) 516 id = "optionset"; 517 518 proto = sa_get_security_attr(security, "type"); 519 sectype = sa_get_security_attr(security, "sectype"); 520 len = snprintf(oname, len, "%s_%s_%s", id, 521 proto ? proto : "default", 522 sectype ? sectype : "default"); 523 if (proto != NULL) 524 sa_free_attr_string(proto); 525 if (sectype != NULL) 526 sa_free_attr_string(sectype); 527 return (len); 528 } 529 530 /* 531 * sa_init(init_service) 532 * Initialize the API 533 * find all the shared objects 534 * init the tables with all objects 535 * read in the current configuration 536 */ 537 538 void 539 sa_init(int init_service) 540 { 541 struct stat st; 542 int legacy = 0; 543 uint64_t tval = 0; 544 545 if (!sa_initialized) { 546 /* get protocol specific structures */ 547 (void) proto_plugin_init(); 548 if (init_service & SA_INIT_SHARE_API) { 549 /* 550 * initialize access into libzfs. We use this when 551 * collecting info about ZFS datasets and shares. 552 */ 553 sa_zfs_init(); 554 /* 555 * since we want to use SMF, initialize an svc handle 556 * and find out what is there. 557 */ 558 scf_handle = sa_scf_init(); 559 if (scf_handle != NULL) { 560 (void) sa_get_config(scf_handle, &sa_config_tree, 561 &sa_config_doc); 562 tval = get_legacy_timestamp(sa_config_tree, 563 SA_LEGACY_DFSTAB); 564 if (tval == 0) { 565 /* first time so make sure default is setup */ 566 sa_group_t defgrp; 567 sa_optionset_t opt; 568 defgrp = sa_get_group("default"); 569 if (defgrp != NULL) { 570 opt = sa_get_optionset(defgrp, NULL); 571 if (opt == NULL) 572 /* NFS is the default for default */ 573 opt = sa_create_optionset(defgrp, "nfs"); 574 } 575 } 576 if (stat(SA_LEGACY_DFSTAB, &st) >= 0 && 577 tval != TSTAMP(st.st_ctim)) { 578 getlegacyconfig(SA_LEGACY_DFSTAB, &sa_config_tree); 579 if (stat(SA_LEGACY_DFSTAB, &st) >= 0) 580 set_legacy_timestamp(sa_config_tree, 581 SA_LEGACY_DFSTAB, 582 TSTAMP(st.st_ctim)); 583 } 584 legacy |= sa_get_zfs_shares("zfs"); 585 legacy |= gettransients(&sa_config_tree); 586 } 587 } 588 } 589 } 590 591 /* 592 * sa_fini() 593 * Uninitialize the API structures including the configuration 594 * data structures and ZFS related data. 595 */ 596 597 void 598 sa_fini() 599 { 600 if (sa_initialized) { 601 /* free the config trees */ 602 sa_initialized = 0; 603 if (sa_config_doc != NULL) 604 xmlFreeDoc(sa_config_doc); 605 sa_config_tree = NULL; 606 sa_config_doc = NULL; 607 sa_scf_fini(scf_handle); 608 sa_zfs_fini(); 609 (void) proto_plugin_init(); 610 } 611 } 612 613 /* 614 * sa_get_protocols(char **protocol) 615 * Get array of protocols that are supported 616 * Returns pointer to an allocated and NULL terminated 617 * array of strings. Caller must free. 618 * This really should be determined dynamically. 619 * If there aren't any defined, return -1. 620 * Use free() to return memory. 621 */ 622 623 int 624 sa_get_protocols(char ***protocols) 625 { 626 int numproto = -1; 627 628 if (protocols != NULL) { 629 struct sa_proto_plugin *plug; 630 for (numproto = 0, plug = sap_proto_list; plug != NULL; 631 plug = plug->plugin_next) { 632 numproto++; 633 } 634 635 *protocols = calloc(numproto + 1, sizeof (char *)); 636 if (*protocols != NULL) { 637 int ret = 0; 638 for (plug = sap_proto_list; plug != NULL; 639 plug = plug->plugin_next) { 640 /* faking for now */ 641 (*protocols)[ret++] = plug->plugin_ops->sa_protocol; 642 } 643 } else { 644 numproto = -1; 645 } 646 } 647 return (numproto); 648 } 649 650 /* 651 * find_group_by_name(node, group) 652 * 653 * search the XML document subtree specified by node to find the group 654 * specified by group. Searching subtree allows subgroups to be 655 * searched for. 656 */ 657 658 static xmlNodePtr 659 find_group_by_name(xmlNodePtr node, xmlChar *group) 660 { 661 xmlChar *name = NULL; 662 663 for (node = node->xmlChildrenNode; node != NULL; 664 node = node->next) { 665 if (xmlStrcmp(node->name, (xmlChar *)"group") == 0) { 666 /* if no groupname, return the first found */ 667 if (group == NULL) 668 break; 669 name = xmlGetProp(node, (xmlChar *)"name"); 670 if (name != NULL && 671 xmlStrcmp(name, group) == 0) { 672 break; 673 } 674 if (name != NULL) { 675 xmlFree(name); 676 name = NULL; 677 } 678 } 679 } 680 if (name != NULL) 681 xmlFree(name); 682 return (node); 683 } 684 685 /* 686 * sa_get_group(groupname) 687 * Return the "group" specified. If groupname is NULL, 688 * return the first group of the list of groups. 689 */ 690 sa_group_t 691 sa_get_group(char *groupname) 692 { 693 xmlNodePtr node = NULL; 694 char *subgroup = NULL; 695 char *group = NULL; 696 697 if (sa_config_tree != NULL) { 698 if (groupname != NULL) { 699 group = strdup(groupname); 700 subgroup = strchr(group, '/'); 701 if (subgroup != NULL) 702 *subgroup++ = '\0'; 703 } 704 node = find_group_by_name(sa_config_tree, (xmlChar *)group); 705 /* if a subgroup, find it before returning */ 706 if (subgroup != NULL && node != NULL) { 707 node = find_group_by_name(node, (xmlChar *)subgroup); 708 } 709 } 710 if (node != NULL && (char *)group != NULL) 711 (void) sa_get_instance(scf_handle, (char *)group); 712 if (group != NULL) 713 free(group); 714 return ((sa_group_t)(node)); 715 } 716 717 /* 718 * sa_get_next_group(group) 719 * Return the "next" group after the specified group from 720 * the internal group list. NULL if there are no more. 721 */ 722 sa_group_t 723 sa_get_next_group(sa_group_t group) 724 { 725 xmlNodePtr ngroup = NULL; 726 if (group != NULL) { 727 for (ngroup = ((xmlNodePtr)group)->next; ngroup != NULL; 728 ngroup = ngroup->next) { 729 if (xmlStrcmp(ngroup->name, (xmlChar *)"group") == 0) 730 break; 731 } 732 } 733 return ((sa_group_t)ngroup); 734 } 735 736 /* 737 * sa_get_share(group, sharepath) 738 * Return the share object for the share specified. The share 739 * must be in the specified group. Return NULL if not found. 740 */ 741 sa_share_t 742 sa_get_share(sa_group_t group, char *sharepath) 743 { 744 xmlNodePtr node = NULL; 745 xmlChar *path; 746 747 /* 748 * For future scalability, this should end up building a cache 749 * since it will get called regularly by the mountd and info 750 * services. 751 */ 752 if (group != NULL) { 753 for (node = ((xmlNodePtr)group)->children; node != NULL; 754 node = node->next) { 755 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 756 if (sharepath == NULL) { 757 break; 758 } else { 759 /* is it the correct share? */ 760 path = xmlGetProp(node, (xmlChar *)"path"); 761 if (path != NULL && 762 xmlStrcmp(path, (xmlChar *)sharepath) == 0) { 763 xmlFree(path); 764 break; 765 } 766 xmlFree(path); 767 } 768 } 769 } 770 } 771 return ((sa_share_t)node); 772 } 773 774 /* 775 * sa_get_next_share(share) 776 * Return the next share following the specified share 777 * from the internal list of shares. Returns NULL if there 778 * are no more shares. The list is relative to the same 779 * group. 780 */ 781 sa_share_t 782 sa_get_next_share(sa_share_t share) 783 { 784 xmlNodePtr node = NULL; 785 786 if (share != NULL) { 787 for (node = ((xmlNodePtr)share)->next; node != NULL; 788 node = node->next) { 789 if (xmlStrcmp(node->name, (xmlChar *)"share") == 0) { 790 break; 791 } 792 } 793 } 794 return ((sa_share_t)node); 795 } 796 797 /* 798 * _sa_get_child_node(node, type) 799 * 800 * find the child node of the specified node that has "type". This is 801 * used to implement several internal functions. 802 */ 803 804 static xmlNodePtr 805 _sa_get_child_node(xmlNodePtr node, xmlChar *type) 806 { 807 xmlNodePtr child; 808 for (child = node->xmlChildrenNode; child != NULL; 809 child = child->next) 810 if (xmlStrcmp(child->name, type) == 0) 811 return (child); 812 return ((xmlNodePtr)NULL); 813 } 814 815 /* 816 * find_share(group, path) 817 * 818 * Search all the shares in the specified group for one that has the 819 * specified path. 820 */ 821 822 static sa_share_t 823 find_share(sa_group_t group, char *sharepath) 824 { 825 sa_share_t share; 826 char *path; 827 828 for (share = sa_get_share(group, NULL); share != NULL; 829 share = sa_get_next_share(share)) { 830 path = sa_get_share_attr(share, "path"); 831 if (path != NULL && strcmp(path, sharepath) == 0) { 832 sa_free_attr_string(path); 833 break; 834 } 835 if (path != NULL) 836 sa_free_attr_string(path); 837 } 838 return (share); 839 } 840 841 /* 842 * sa_get_sub_group(group) 843 * 844 * Get the first sub-group of group. The sa_get_next_group() function 845 * can be used to get the rest. This is currently only used for ZFS 846 * sub-groups but could be used to implement a more general mechanism. 847 */ 848 849 sa_group_t 850 sa_get_sub_group(sa_group_t group) 851 { 852 return ((sa_group_t)_sa_get_child_node((xmlNodePtr)group, 853 (xmlChar *)"group")); 854 } 855 856 /* 857 * sa_find_share(sharepath) 858 * Finds a share regardless of group. In the future, this 859 * function should utilize a cache and hash table of some kind. 860 * The current assumption is that a path will only be shared 861 * once. In the future, this may change as implementation of 862 * resource names comes into being. 863 */ 864 sa_share_t 865 sa_find_share(char *sharepath) 866 { 867 sa_group_t group; 868 sa_group_t zgroup; 869 sa_share_t share = NULL; 870 int done = 0; 871 872 for (group = sa_get_group(NULL); group != NULL && !done; 873 group = sa_get_next_group(group)) { 874 if (is_zfs_group(group)) { 875 for (zgroup = (sa_group_t)_sa_get_child_node((xmlNodePtr)group, 876 (xmlChar *)"group"); 877 zgroup != NULL; zgroup = sa_get_next_group(zgroup)) { 878 share = find_share(zgroup, sharepath); 879 if (share != NULL) 880 break; 881 } 882 } else { 883 share = find_share(group, sharepath); 884 } 885 if (share != NULL) 886 break; 887 } 888 return (share); 889 } 890 891 /* 892 * sa_check_path(group, path, strictness) 893 * 894 * check that path is a valid path relative to the group. Currently, 895 * we are ignoring the group and checking only the NFS rules. Later, 896 * we may want to use the group to then check against the protocols 897 * enabled on the group. The strictness values mean: 898 * SA_CHECK_NORMAL == only check newpath against shares that are active 899 * SA_CHECK_STRICT == check newpath against both active shares and those 900 * stored in the repository 901 */ 902 903 int 904 sa_check_path(sa_group_t group, char *path, int strictness) 905 { 906 #ifdef lint 907 group = group; 908 #endif 909 return (validpath(path, strictness)); 910 } 911 912 /* 913 * _sa_add_share(group, sharepath, persist, *error) 914 * 915 * common code for all types of add_share. sa_add_share() is the 916 * public API, we also need to be able to do this when parsing legacy 917 * files and construction of the internal configuration while 918 * extracting config info from SMF. 919 */ 920 921 sa_share_t 922 _sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 923 { 924 xmlNodePtr node = NULL; 925 int err; 926 927 err = SA_OK; /* assume success */ 928 929 node = xmlNewChild((xmlNodePtr)group, NULL, 930 (xmlChar *)"share", NULL); 931 if (node != NULL) { 932 xmlSetProp(node, (xmlChar *)"path", (xmlChar *)sharepath); 933 xmlSetProp(node, (xmlChar *)"type", persist ? 934 (xmlChar *)"persist" : (xmlChar *)"transient"); 935 if (persist != SA_SHARE_TRANSIENT) { 936 /* 937 * persistent shares come in two flavors: SMF and 938 * ZFS. Sort this one out based on target group and 939 * path type. Currently, only NFS is supported in the 940 * ZFS group and it is always on. 941 */ 942 if (sa_group_is_zfs(group) && sa_path_is_zfs(sharepath)) { 943 err = sa_zfs_set_sharenfs(group, sharepath, 1); 944 } else { 945 err = sa_commit_share(scf_handle, group, 946 (sa_share_t)node); 947 } 948 } 949 if (err == SA_NO_PERMISSION && persist & SA_SHARE_PARSER) { 950 /* called by the dfstab parser so could be a show */ 951 err = SA_OK; 952 } 953 if (err != SA_OK) { 954 /* 955 * we couldn't commit to the repository so undo 956 * our internal state to reflect reality. 957 */ 958 xmlUnlinkNode(node); 959 xmlFreeNode(node); 960 node = NULL; 961 } 962 } else { 963 err = SA_NO_MEMORY; 964 } 965 if (error != NULL) 966 *error = err; 967 return (node); 968 } 969 970 /* 971 * sa_add_share(group, sharepath, persist, *error) 972 * 973 * Add a new share object to the specified group. The share will 974 * have the specified sharepath and will only be constructed if 975 * it is a valid path to be shared. NULL is returned on error 976 * and a detailed error value will be returned via the error 977 * pointer. 978 */ 979 sa_share_t 980 sa_add_share(sa_group_t group, char *sharepath, int persist, int *error) 981 { 982 xmlNodePtr node = NULL; 983 sa_share_t dup; 984 int strictness = SA_CHECK_NORMAL; 985 986 /* 987 * If the share is to be permanent, use strict checking so a 988 * bad config doesn't get created. Transient shares only need 989 * to check against the currently active 990 * shares. SA_SHARE_PARSER is a modifier used internally to 991 * indicate that we are being called by the dfstab parser and 992 * that we need strict checking in all cases. Normally persist 993 * is in integer value but SA_SHARE_PARSER may be or'd into 994 * it as an override. 995 */ 996 if (persist & SA_SHARE_PARSER || persist == SA_SHARE_PERMANENT) 997 strictness = SA_CHECK_STRICT; 998 999 if ((dup = sa_find_share(sharepath)) == NULL && 1000 (*error = sa_check_path(group, sharepath, strictness)) == 1001 SA_OK) { 1002 node = _sa_add_share(group, sharepath, persist, error); 1003 } 1004 if (dup != NULL) 1005 *error = SA_DUPLICATE_NAME; 1006 1007 return ((sa_share_t)node); 1008 } 1009 1010 /* 1011 * sa_enable_share(share, protocol) 1012 * Enable the specified share to the specified protocol. 1013 * If protocol is NULL, then all protocols. 1014 */ 1015 int 1016 sa_enable_share(sa_share_t share, char *protocol) 1017 { 1018 char *sharepath; 1019 struct stat st; 1020 int err = 0; 1021 1022 sharepath = sa_get_share_attr(share, "path"); 1023 if (stat(sharepath, &st) < 0) { 1024 err = SA_NO_SUCH_PATH; 1025 } else { 1026 /* tell the server about the share */ 1027 if (protocol != NULL) { 1028 /* lookup protocol specific handler */ 1029 err = sa_proto_share(protocol, share); 1030 if (err == SA_OK) 1031 (void) sa_set_share_attr(share, "shared", "true"); 1032 } else { 1033 /* tell all protocols */ 1034 err = sa_proto_share("nfs", share); /* only NFS for now */ 1035 (void) sa_set_share_attr(share, "shared", "true"); 1036 } 1037 } 1038 if (sharepath != NULL) 1039 sa_free_attr_string(sharepath); 1040 return (err); 1041 } 1042 1043 /* 1044 * sa_disable_share(share, protocol) 1045 * Disable the specified share to the specified protocol. 1046 * If protocol is NULL, then all protocols. 1047 */ 1048 int 1049 sa_disable_share(sa_share_t share, char *protocol) 1050 { 1051 char *path; 1052 char *shared; 1053 int ret = SA_OK; 1054 1055 path = sa_get_share_attr(share, "path"); 1056 shared = sa_get_share_attr(share, "shared"); 1057 1058 if (protocol != NULL) { 1059 ret = sa_proto_unshare(protocol, path); 1060 } else { 1061 /* need to do all protocols */ 1062 ret = sa_proto_unshare("nfs", path); 1063 } 1064 if (ret == SA_OK) 1065 (void) sa_set_share_attr(share, "shared", NULL); 1066 if (path != NULL) 1067 sa_free_attr_string(path); 1068 if (shared != NULL) 1069 sa_free_attr_string(shared); 1070 return (ret); 1071 } 1072 1073 /* 1074 * sa_remove_share(share) 1075 * 1076 * remove the specified share from its containing group. 1077 * Remove from the SMF or ZFS configuration space. 1078 */ 1079 1080 int 1081 sa_remove_share(sa_share_t share) 1082 { 1083 sa_group_t group; 1084 int ret = SA_OK; 1085 char *type; 1086 int transient = 0; 1087 char *groupname; 1088 char *zfs; 1089 1090 type = sa_get_share_attr(share, "type"); 1091 group = sa_get_parent_group(share); 1092 zfs = sa_get_group_attr(group, "zfs"); 1093 groupname = sa_get_group_attr(group, "name"); 1094 if (type != NULL && strcmp(type, "persist") != 0) 1095 transient = 1; 1096 if (type != NULL) 1097 sa_free_attr_string(type); 1098 1099 /* remove the node from its group then free the memory */ 1100 1101 /* 1102 * need to test if "busy" 1103 */ 1104 /* only do SMF action if permanent */ 1105 if (!transient || zfs != NULL) { 1106 /* remove from legacy dfstab as well as possible SMF */ 1107 ret = sa_delete_legacy(share); 1108 if (ret == SA_OK) { 1109 if (!sa_group_is_zfs(group)) { 1110 ret = sa_delete_share(scf_handle, group, share); 1111 } else { 1112 char *sharepath = sa_get_share_attr(share, "path"); 1113 if (sharepath != NULL) { 1114 ret = sa_zfs_set_sharenfs(group, sharepath, 0); 1115 sa_free_attr_string(sharepath); 1116 } 1117 } 1118 } 1119 } 1120 if (groupname != NULL) 1121 sa_free_attr_string(groupname); 1122 if (zfs != NULL) 1123 sa_free_attr_string(zfs); 1124 1125 xmlUnlinkNode((xmlNodePtr)share); 1126 xmlFreeNode((xmlNodePtr)share); 1127 return (ret); 1128 } 1129 1130 /* 1131 * sa_move_share(group, share) 1132 * 1133 * move the specified share to the specified group. Update SMF 1134 * appropriately. 1135 */ 1136 1137 int 1138 sa_move_share(sa_group_t group, sa_share_t share) 1139 { 1140 sa_group_t oldgroup; 1141 int ret = SA_OK; 1142 1143 /* remove the node from its group then free the memory */ 1144 1145 oldgroup = sa_get_parent_group(share); 1146 if (oldgroup != group) { 1147 xmlUnlinkNode((xmlNodePtr)share); 1148 /* now that the share isn't in its old group, add to the new one */ 1149 xmlAddChild((xmlNodePtr)group, (xmlNodePtr)share); 1150 /* need to deal with SMF */ 1151 if (ret == SA_OK) { 1152 /* 1153 * need to remove from old group first and then add to 1154 * new group. Ideally, we would do the other order but 1155 * need to avoid having the share in two groups at the 1156 * same time. 1157 */ 1158 ret = sa_delete_share(scf_handle, oldgroup, share); 1159 } 1160 ret = sa_commit_share(scf_handle, group, share); 1161 } 1162 return (ret); 1163 } 1164 1165 /* 1166 * sa_get_parent_group(share) 1167 * 1168 * Return the containg group for the share. If a group was actually 1169 * passed in, we don't want a parent so return NULL. 1170 */ 1171 1172 sa_group_t 1173 sa_get_parent_group(sa_share_t share) 1174 { 1175 xmlNodePtr node = NULL; 1176 if (share != NULL) { 1177 node = ((xmlNodePtr)share)->parent; 1178 /* 1179 * make sure parent is a group and not sharecfg since 1180 * we may be cheating and passing in a group. 1181 * Eventually, groups of groups might come into being. 1182 */ 1183 if (node == NULL || 1184 xmlStrcmp(node->name, (xmlChar *)"sharecfg") == 0) 1185 node = NULL; 1186 } 1187 return ((sa_group_t)node); 1188 } 1189 1190 /* 1191 * _sa_create_group(groupname) 1192 * 1193 * Create a group in the document. The caller will need to deal with 1194 * configuration store and activation. 1195 */ 1196 1197 sa_group_t 1198 _sa_create_group(char *groupname) 1199 { 1200 xmlNodePtr node = NULL; 1201 1202 if (sa_valid_group_name(groupname)) { 1203 node = xmlNewChild(sa_config_tree, NULL, 1204 (xmlChar *)"group", NULL); 1205 if (node != NULL) { 1206 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1207 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1208 } 1209 } 1210 return ((sa_group_t)node); 1211 } 1212 1213 /* 1214 * _sa_create_zfs_group(group, groupname) 1215 * 1216 * Create a ZFS subgroup under the specified group. This may 1217 * eventually form the basis of general sub-groups, but is currently 1218 * restricted to ZFS. 1219 */ 1220 sa_group_t 1221 _sa_create_zfs_group(sa_group_t group, char *groupname) 1222 { 1223 xmlNodePtr node = NULL; 1224 1225 node = xmlNewChild((xmlNodePtr)group, NULL, 1226 (xmlChar *)"group", NULL); 1227 if (node != NULL) { 1228 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1229 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1230 } 1231 1232 return ((sa_group_t)node); 1233 } 1234 1235 /* 1236 * sa_create_group(groupname, *error) 1237 * 1238 * Create a new group with groupname. Need to validate that it is a 1239 * legal name for SMF and the construct the SMF service instance of 1240 * svc:/network/shares/group to implement the group. All necessary 1241 * operational properties must be added to the group at this point 1242 * (via the SMF transaction model). 1243 */ 1244 sa_group_t 1245 sa_create_group(char *groupname, int *error) 1246 { 1247 xmlNodePtr node = NULL; 1248 sa_group_t group; 1249 int ret; 1250 char rbacstr[256]; 1251 1252 ret = SA_OK; 1253 1254 if (scf_handle == NULL) { 1255 ret = SA_SYSTEM_ERR; 1256 goto err; 1257 } 1258 1259 group = sa_get_group(groupname); 1260 if (group != NULL) { 1261 ret = SA_DUPLICATE_NAME; 1262 } else { 1263 if (sa_valid_group_name(groupname)) { 1264 node = xmlNewChild(sa_config_tree, NULL, 1265 (xmlChar *)"group", NULL); 1266 if (node != NULL) { 1267 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)groupname); 1268 /* default to the group being enabled */ 1269 xmlSetProp(node, (xmlChar *)"state", (xmlChar *)"enabled"); 1270 ret = sa_create_instance(scf_handle, groupname); 1271 if (ret == SA_OK) { 1272 ret = sa_start_transaction(scf_handle, "operation"); 1273 } 1274 if (ret == SA_OK) { 1275 ret = sa_set_property(scf_handle, "state", "enabled"); 1276 if (ret == SA_OK) { 1277 ret = sa_end_transaction(scf_handle); 1278 } else { 1279 sa_abort_transaction(scf_handle); 1280 } 1281 } 1282 if (ret == SA_OK) { 1283 /* initialize the RBAC strings */ 1284 ret = sa_start_transaction(scf_handle, "general"); 1285 if (ret == SA_OK) { 1286 (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 1287 SA_RBAC_MANAGE, groupname); 1288 ret = sa_set_property(scf_handle, 1289 "action_authorization", 1290 rbacstr); 1291 } 1292 if (ret == SA_OK) { 1293 (void) snprintf(rbacstr, sizeof (rbacstr), "%s.%s", 1294 SA_RBAC_VALUE, groupname); 1295 ret = sa_set_property(scf_handle, 1296 "value_authorization", 1297 rbacstr); 1298 } 1299 if (ret == SA_OK) { 1300 ret = sa_end_transaction(scf_handle); 1301 } else { 1302 sa_abort_transaction(scf_handle); 1303 } 1304 } 1305 if (ret != SA_OK) { 1306 /* 1307 * Couldn't commit the group so we need to 1308 * undo internally. 1309 */ 1310 xmlUnlinkNode(node); 1311 xmlFreeNode(node); 1312 node = NULL; 1313 } 1314 } else { 1315 ret = SA_NO_MEMORY; 1316 } 1317 } else { 1318 ret = SA_INVALID_NAME; 1319 } 1320 } 1321 err: 1322 if (error != NULL) 1323 *error = ret; 1324 return ((sa_group_t)node); 1325 } 1326 1327 /* 1328 * sa_remove_group(group) 1329 * 1330 * Remove the specified group. This deletes from the SMF repository. 1331 * All property groups and properties are removed. 1332 */ 1333 1334 int 1335 sa_remove_group(sa_group_t group) 1336 { 1337 char *name; 1338 int ret = SA_OK; 1339 1340 name = sa_get_group_attr(group, "name"); 1341 if (name != NULL) { 1342 ret = sa_delete_instance(scf_handle, name); 1343 sa_free_attr_string(name); 1344 } 1345 xmlUnlinkNode((xmlNodePtr)group); /* make sure unlinked */ 1346 xmlFreeNode((xmlNodePtr)group); /* now it is gone */ 1347 return (ret); 1348 } 1349 1350 /* 1351 * sa_update_config() 1352 * 1353 * Used to update legacy files that need to be updated in bulk 1354 * Currently, this is a placeholder and will go away in a future 1355 * release. 1356 */ 1357 1358 int 1359 sa_update_config() 1360 { 1361 /* 1362 * do legacy files first so we can tell when they change. 1363 * This will go away when we start updating individual records 1364 * rather than the whole file. 1365 */ 1366 update_legacy_config(); 1367 return (SA_OK); 1368 } 1369 1370 /* 1371 * get_node_attr(node, tag) 1372 * 1373 * Get the speficied tag(attribute) if it exists on the node. This is 1374 * used internally by a number of attribute oriented functions. 1375 */ 1376 1377 static char * 1378 get_node_attr(void *nodehdl, char *tag) 1379 { 1380 xmlNodePtr node = (xmlNodePtr)nodehdl; 1381 xmlChar *name = NULL; 1382 1383 if (node != NULL) { 1384 name = xmlGetProp(node, (xmlChar *)tag); 1385 } 1386 return ((char *)name); 1387 } 1388 1389 /* 1390 * get_node_attr(node, tag) 1391 * 1392 * Set the speficied tag(attribute) to the specified value This is 1393 * used internally by a number of attribute oriented functions. It 1394 * doesn't update the repository, only the internal document state. 1395 */ 1396 1397 void 1398 set_node_attr(void *nodehdl, char *tag, char *value) 1399 { 1400 xmlNodePtr node = (xmlNodePtr)nodehdl; 1401 if (node != NULL && tag != NULL) { 1402 if (value != NULL) { 1403 xmlSetProp(node, (xmlChar *)tag, (xmlChar *)value); 1404 } else { 1405 xmlUnsetProp(node, (xmlChar *)tag); 1406 } 1407 } 1408 } 1409 1410 /* 1411 * sa_get_group_attr(group, tag) 1412 * 1413 * Get the specied attribute, if defined, for the group. 1414 */ 1415 1416 char * 1417 sa_get_group_attr(sa_group_t group, char *tag) 1418 { 1419 return (get_node_attr((void *)group, tag)); 1420 } 1421 1422 /* 1423 * sa_set_group_attr(group, tag, value) 1424 * 1425 * set the specified tag/attribute on the group using value as its 1426 * value. 1427 * 1428 * This will result in setting the property in the SMF repository as 1429 * well as in the internal document. 1430 */ 1431 1432 int 1433 sa_set_group_attr(sa_group_t group, char *tag, char *value) 1434 { 1435 int ret; 1436 char *groupname; 1437 1438 groupname = sa_get_group_attr(group, "name"); 1439 ret = sa_get_instance(scf_handle, groupname); 1440 if (ret == SA_OK) { 1441 set_node_attr((void *)group, tag, value); 1442 ret = sa_start_transaction(scf_handle, "operation"); 1443 if (ret == SA_OK) { 1444 ret = sa_set_property(scf_handle, tag, value); 1445 if (ret == SA_OK) 1446 (void) sa_end_transaction(scf_handle); 1447 else { 1448 sa_abort_transaction(scf_handle); 1449 } 1450 } 1451 } 1452 if (groupname != NULL) 1453 sa_free_attr_string(groupname); 1454 return (ret); 1455 } 1456 1457 /* 1458 * sa_get_share_attr(share, tag) 1459 * 1460 * Return the value of the tag/attribute set on the specified 1461 * share. Returns NULL if the tag doesn't exist. 1462 */ 1463 1464 char * 1465 sa_get_share_attr(sa_share_t share, char *tag) 1466 { 1467 return (get_node_attr((void *)share, tag)); 1468 } 1469 1470 /* 1471 * sa_get_resource(group, resource) 1472 * 1473 * Search all the shares in the speified group for a share with a 1474 * resource name matching the one specified. 1475 * 1476 * In the future, it may be advantageous to allow group to be NULL and 1477 * search all groups but that isn't needed at present. 1478 */ 1479 1480 sa_share_t 1481 sa_get_resource(sa_group_t group, char *resource) 1482 { 1483 sa_share_t share = NULL; 1484 char *name = NULL; 1485 1486 if (resource != NULL) { 1487 for (share = sa_get_share(group, NULL); share != NULL; 1488 share = sa_get_next_share(share)) { 1489 name = sa_get_share_attr(share, "resource"); 1490 if (name != NULL) { 1491 if (strcmp(name, resource) == 0) 1492 break; 1493 sa_free_attr_string(name); 1494 name = NULL; 1495 } 1496 } 1497 if (name != NULL) 1498 sa_free_attr_string(name); 1499 } 1500 return ((sa_share_t)share); 1501 } 1502 1503 /* 1504 * _sa_set_share_description(share, description) 1505 * 1506 * Add a description tag with text contents to the specified share. 1507 * A separate XML tag is used rather than a property. 1508 */ 1509 1510 xmlNodePtr 1511 _sa_set_share_description(sa_share_t share, char *content) 1512 { 1513 xmlNodePtr node; 1514 node = xmlNewChild((xmlNodePtr)share, 1515 NULL, (xmlChar *)"description", NULL); 1516 xmlNodeSetContent(node, (xmlChar *)content); 1517 return (node); 1518 } 1519 1520 /* 1521 * sa_set_share_attr(share, tag, value) 1522 * 1523 * Set the share attribute specified by tag to the specified value. In 1524 * the case of "resource", enforce a no duplicates in a group rule. If 1525 * the share is not transient, commit the changes to the repository 1526 * else just update the share internally. 1527 */ 1528 1529 int 1530 sa_set_share_attr(sa_share_t share, char *tag, char *value) 1531 { 1532 sa_group_t group; 1533 sa_share_t resource; 1534 int ret = SA_OK; 1535 1536 group = sa_get_parent_group(share); 1537 1538 /* 1539 * There are some attributes that may have specific 1540 * restrictions on them. Initially, only "resource" has 1541 * special meaning that needs to be checked. Only one instance 1542 * of a resource name may exist within a group. 1543 */ 1544 1545 if (strcmp(tag, "resource") == 0) { 1546 resource = sa_get_resource(group, value); 1547 if (resource != share && resource != NULL) 1548 ret = SA_DUPLICATE_NAME; 1549 } 1550 if (ret == SA_OK) { 1551 set_node_attr((void *)share, tag, value); 1552 if (group != NULL) { 1553 char *type; 1554 /* we can probably optimize this some */ 1555 type = sa_get_share_attr(share, "type"); 1556 if (type == NULL || strcmp(type, "transient") != 0) 1557 ret = sa_commit_share(scf_handle, group, share); 1558 if (type != NULL) 1559 sa_free_attr_string(type); 1560 } 1561 } 1562 return (ret); 1563 } 1564 1565 /* 1566 * sa_get_property_attr(prop, tag) 1567 * 1568 * Get the value of the specified property attribute. Standard 1569 * attributes are "type" and "value". 1570 */ 1571 1572 char * 1573 sa_get_property_attr(sa_property_t prop, char *tag) 1574 { 1575 return (get_node_attr((void *)prop, tag)); 1576 } 1577 1578 /* 1579 * sa_get_optionset_attr(prop, tag) 1580 * 1581 * Get the value of the specified property attribute. Standard 1582 * attribute is "type". 1583 */ 1584 1585 char * 1586 sa_get_optionset_attr(sa_property_t optionset, char *tag) 1587 { 1588 return (get_node_attr((void *)optionset, tag)); 1589 1590 } 1591 1592 /* 1593 * sa_set_optionset_attr(optionset, tag, value) 1594 * 1595 * Set the specified attribute(tag) to the specified value on the 1596 * optionset. 1597 */ 1598 1599 void 1600 sa_set_optionset_attr(sa_group_t optionset, char *tag, char *value) 1601 { 1602 set_node_attr((void *)optionset, tag, value); 1603 } 1604 1605 /* 1606 * sa_free_attr_string(string) 1607 * 1608 * Free the string that was returned in one of the sa_get_*_attr() 1609 * functions. 1610 */ 1611 1612 void 1613 sa_free_attr_string(char *string) 1614 { 1615 xmlFree((xmlChar *)string); 1616 } 1617 1618 /* 1619 * sa_get_optionset(group, proto) 1620 * 1621 * Return the optionset, if it exists, that is associated with the 1622 * specified protocol. 1623 */ 1624 1625 sa_optionset_t 1626 sa_get_optionset(void *group, char *proto) 1627 { 1628 xmlNodePtr node; 1629 xmlChar *value = NULL; 1630 1631 for (node = ((xmlNodePtr)group)->children; node != NULL; 1632 node = node->next) { 1633 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 1634 value = xmlGetProp(node, (xmlChar *)"type"); 1635 if (proto != NULL) { 1636 if (value != NULL && 1637 xmlStrcmp(value, (xmlChar *)proto) == 0) { 1638 break; 1639 } 1640 if (value != NULL) { 1641 xmlFree(value); 1642 value = NULL; 1643 } 1644 } else { 1645 break; 1646 } 1647 } 1648 } 1649 if (value != NULL) 1650 xmlFree(value); 1651 return ((sa_optionset_t)node); 1652 } 1653 1654 /* 1655 * sa_get_next_optionset(optionset) 1656 * 1657 * Return the next optionset in the group. NULL if this was the last. 1658 */ 1659 1660 sa_optionset_t 1661 sa_get_next_optionset(sa_optionset_t optionset) 1662 { 1663 xmlNodePtr node; 1664 1665 for (node = ((xmlNodePtr)optionset)->next; node != NULL; 1666 node = node->next) { 1667 if (xmlStrcmp(node->name, (xmlChar *)"optionset") == 0) { 1668 break; 1669 } 1670 } 1671 return ((sa_optionset_t)node); 1672 } 1673 1674 /* 1675 * sa_get_security(group, sectype, proto) 1676 * 1677 * Return the security optionset. The internal name is a hold over 1678 * from the implementation and will be changed before the API is 1679 * finalized. This is really a named optionset that can be negotiated 1680 * as a group of properties (like NFS security options). 1681 */ 1682 1683 sa_security_t 1684 sa_get_security(sa_group_t group, char *sectype, char *proto) 1685 { 1686 xmlNodePtr node; 1687 xmlChar *value = NULL; 1688 1689 for (node = ((xmlNodePtr)group)->children; node != NULL; 1690 node = node->next) { 1691 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 1692 if (proto != NULL) { 1693 value = xmlGetProp(node, (xmlChar *)"type"); 1694 if (value == NULL || 1695 (value != NULL && 1696 xmlStrcmp(value, (xmlChar *)proto) != 0)) { 1697 /* it doesn't match so continue */ 1698 xmlFree(value); 1699 value = NULL; 1700 continue; 1701 } 1702 } 1703 if (value != NULL) { 1704 xmlFree(value); 1705 value = NULL; 1706 } 1707 /* potential match */ 1708 if (sectype != NULL) { 1709 value = xmlGetProp(node, (xmlChar *)"sectype"); 1710 if (value != NULL && 1711 xmlStrcmp(value, (xmlChar *)sectype) == 0) { 1712 break; 1713 } 1714 } else { 1715 break; 1716 } 1717 } 1718 if (value != NULL) { 1719 xmlFree(value); 1720 value = NULL; 1721 } 1722 } 1723 if (value != NULL) 1724 xmlFree(value); 1725 return ((sa_security_t)node); 1726 } 1727 1728 /* 1729 * sa_get_next_security(security) 1730 * 1731 * Get the next security optionset if one exists. 1732 */ 1733 1734 sa_security_t 1735 sa_get_next_security(sa_security_t security) 1736 { 1737 xmlNodePtr node; 1738 1739 for (node = ((xmlNodePtr)security)->next; node != NULL; 1740 node = node->next) { 1741 if (xmlStrcmp(node->name, (xmlChar *)"security") == 0) { 1742 break; 1743 } 1744 } 1745 return ((sa_security_t)node); 1746 } 1747 1748 /* 1749 * sa_get_property(optionset, prop) 1750 * 1751 * Get the property object with the name specified in prop from the 1752 * optionset. 1753 */ 1754 1755 sa_property_t 1756 sa_get_property(sa_optionset_t optionset, char *prop) 1757 { 1758 xmlNodePtr node = (xmlNodePtr)optionset; 1759 xmlChar *value = NULL; 1760 1761 if (optionset == NULL) 1762 return (NULL); 1763 1764 for (node = node->children; node != NULL; 1765 node = node->next) { 1766 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 1767 if (prop == NULL) 1768 break; 1769 value = xmlGetProp(node, (xmlChar *)"type"); 1770 if (value != NULL && xmlStrcmp(value, (xmlChar *)prop) == 0) { 1771 break; 1772 } 1773 if (value != NULL) { 1774 xmlFree(value); 1775 value = NULL; 1776 } 1777 } 1778 } 1779 if (value != NULL) 1780 xmlFree(value); 1781 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 1782 /* avoid a non option node -- it is possible to be a text node */ 1783 node = NULL; 1784 } 1785 return ((sa_property_t)node); 1786 } 1787 1788 /* 1789 * sa_get_next_property(property) 1790 * 1791 * Get the next property following the specified property. NULL if 1792 * this was the last. 1793 */ 1794 1795 sa_property_t 1796 sa_get_next_property(sa_property_t property) 1797 { 1798 xmlNodePtr node; 1799 1800 for (node = ((xmlNodePtr)property)->next; node != NULL; 1801 node = node->next) { 1802 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 1803 break; 1804 } 1805 } 1806 return ((sa_property_t)node); 1807 } 1808 1809 /* 1810 * sa_set_share_description(share, content) 1811 * 1812 * Set the description of share to content. 1813 */ 1814 1815 int 1816 sa_set_share_description(sa_share_t share, char *content) 1817 { 1818 xmlNodePtr node; 1819 sa_group_t group; 1820 int ret = SA_OK; 1821 1822 for (node = ((xmlNodePtr)share)->children; node != NULL; 1823 node = node->next) { 1824 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 1825 break; 1826 } 1827 } 1828 group = sa_get_parent_group(share); 1829 /* no existing description but want to add */ 1830 if (node == NULL && content != NULL) { 1831 /* add a description */ 1832 node = _sa_set_share_description(share, content); 1833 } else if (node != NULL && content != NULL) { 1834 /* update a description */ 1835 xmlNodeSetContent(node, (xmlChar *)content); 1836 } else if (node != NULL && content == NULL) { 1837 /* remove an existing description */ 1838 xmlUnlinkNode(node); 1839 xmlFreeNode(node); 1840 } 1841 if (group != NULL && is_persistent((sa_group_t)share)) 1842 ret = sa_commit_share(scf_handle, group, share); 1843 return (ret); 1844 } 1845 1846 /* 1847 * fixproblemchars(string) 1848 * 1849 * don't want any newline or tab characters in the text since these 1850 * could break display of data and legacy file formats. 1851 */ 1852 static void 1853 fixproblemchars(char *str) 1854 { 1855 int c; 1856 for (c = *str; c != '\0'; c = *++str) { 1857 if (c == '\t' || c == '\n') 1858 *str = ' '; 1859 else if (c == '"') 1860 *str = '\''; 1861 } 1862 } 1863 1864 /* 1865 * sa_get_share_description(share) 1866 * 1867 * Return the description text for the specified share if it 1868 * exists. NULL if no description exists. 1869 */ 1870 1871 char * 1872 sa_get_share_description(sa_share_t share) 1873 { 1874 xmlChar *description = NULL; 1875 xmlNodePtr node; 1876 1877 for (node = ((xmlNodePtr)share)->children; node != NULL; 1878 node = node->next) { 1879 if (xmlStrcmp(node->name, (xmlChar *)"description") == 0) { 1880 break; 1881 } 1882 } 1883 if (node != NULL) { 1884 description = xmlNodeGetContent((xmlNodePtr)share); 1885 fixproblemchars((char *)description); 1886 } 1887 return ((char *)description); 1888 } 1889 1890 /* 1891 * sa_free(share_description(description) 1892 * 1893 * Free the description string. 1894 */ 1895 1896 void 1897 sa_free_share_description(char *description) 1898 { 1899 xmlFree((xmlChar *)description); 1900 } 1901 1902 /* 1903 * sa_create_optionset(group, proto) 1904 * 1905 * Create an optionset for the specified protocol in the specied 1906 * group. This is manifested as a property group within SMF. 1907 */ 1908 1909 sa_optionset_t 1910 sa_create_optionset(sa_group_t group, char *proto) 1911 { 1912 sa_optionset_t optionset; 1913 sa_group_t parent = group; 1914 1915 optionset = sa_get_optionset(group, proto); 1916 if (optionset != NULL) { 1917 /* can't have a duplicate protocol */ 1918 optionset = NULL; 1919 } else { 1920 optionset = (sa_optionset_t)xmlNewChild((xmlNodePtr)group, 1921 NULL, 1922 (xmlChar *)"optionset", 1923 NULL); 1924 /* 1925 * only put to repository if on a group and we were 1926 * able to create an optionset. 1927 */ 1928 if (optionset != NULL) { 1929 char oname[256]; 1930 char *groupname; 1931 char *id = NULL; 1932 1933 if (sa_is_share(group)) 1934 parent = sa_get_parent_group((sa_share_t)group); 1935 1936 sa_set_optionset_attr(optionset, "type", proto); 1937 1938 if (sa_is_share(group)) { 1939 id = sa_get_share_attr((sa_share_t)group, "id"); 1940 } 1941 (void) sa_optionset_name(optionset, oname, 1942 sizeof (oname), id); 1943 groupname = sa_get_group_attr(parent, "name"); 1944 if (groupname != NULL && is_persistent(group)) { 1945 (void) sa_get_instance(scf_handle, groupname); 1946 sa_free_attr_string(groupname); 1947 (void) sa_create_pgroup(scf_handle, oname); 1948 } 1949 if (id != NULL) 1950 sa_free_attr_string(id); 1951 } 1952 } 1953 return (optionset); 1954 } 1955 1956 /* 1957 * sa_get_property_parent(property) 1958 * 1959 * Given a property, return the object it is a property of. This will 1960 * be an optionset of some type. 1961 */ 1962 1963 static sa_optionset_t 1964 sa_get_property_parent(sa_property_t property) 1965 { 1966 xmlNodePtr node = NULL; 1967 1968 if (property != NULL) { 1969 node = ((xmlNodePtr)property)->parent; 1970 } 1971 return ((sa_optionset_t)node); 1972 } 1973 1974 /* 1975 * sa_get_optionset_parent(optionset) 1976 * 1977 * Return the parent of the specified optionset. This could be a group 1978 * or a share. 1979 */ 1980 1981 static sa_group_t 1982 sa_get_optionset_parent(sa_optionset_t optionset) 1983 { 1984 xmlNodePtr node = NULL; 1985 1986 if (optionset != NULL) { 1987 node = ((xmlNodePtr)optionset)->parent; 1988 } 1989 return ((sa_group_t)node); 1990 } 1991 1992 /* 1993 * zfs_needs_update(share) 1994 * 1995 * In order to avoid making multiple updates to a ZFS share when 1996 * setting properties, the share attribute "changed" will be set to 1997 * true when a property is added or modifed. When done adding 1998 * properties, we can then detect that an update is needed. We then 1999 * clear the state here to detect additional changes. 2000 */ 2001 2002 static int 2003 zfs_needs_update(sa_share_t share) 2004 { 2005 char *attr; 2006 int result = 0; 2007 2008 attr = sa_get_share_attr(share, "changed"); 2009 if (attr != NULL) { 2010 sa_free_attr_string(attr); 2011 result = 1; 2012 } 2013 set_node_attr((void *)share, "changed", NULL); 2014 return (result); 2015 } 2016 2017 /* 2018 * zfs_set_update(share) 2019 * 2020 * Set the changed attribute of the share to true. 2021 */ 2022 2023 static void 2024 zfs_set_update(sa_share_t share) 2025 { 2026 set_node_attr((void *)share, "changed", "true"); 2027 } 2028 2029 /* 2030 * sa_commit_properties(optionset, clear) 2031 * 2032 * Check if SMF or ZFS config and either update or abort the pending 2033 * changes. 2034 */ 2035 2036 int 2037 sa_commit_properties(sa_optionset_t optionset, int clear) 2038 { 2039 sa_group_t group; 2040 sa_group_t parent; 2041 int zfs = 0; 2042 int needsupdate = 0; 2043 int ret = SA_OK; 2044 2045 group = sa_get_optionset_parent(optionset); 2046 if (group != NULL && (sa_is_share(group) || is_zfs_group(group))) { 2047 /* only update ZFS if on a share */ 2048 parent = sa_get_parent_group(group); 2049 zfs++; 2050 if (parent != NULL && is_zfs_group(parent)) { 2051 needsupdate = zfs_needs_update(group); 2052 } else { 2053 zfs = 0; 2054 } 2055 } 2056 if (zfs) { 2057 if (!clear && needsupdate) 2058 ret = sa_zfs_update((sa_share_t)group); 2059 } else { 2060 if (clear) 2061 (void) sa_abort_transaction(scf_handle); 2062 else 2063 ret = sa_end_transaction(scf_handle); 2064 } 2065 return (ret); 2066 } 2067 2068 /* 2069 * sa_destroy_optionset(optionset) 2070 * 2071 * Remove the optionset from its group. Update the repostory to 2072 * reflect this change. 2073 */ 2074 2075 int 2076 sa_destroy_optionset(sa_optionset_t optionset) 2077 { 2078 char name[256]; 2079 int len; 2080 int ret; 2081 char *id = NULL; 2082 sa_group_t group; 2083 int ispersist = 1; 2084 2085 /* now delete the prop group */ 2086 group = sa_get_optionset_parent(optionset); 2087 if (group != NULL && sa_is_share(group)) { 2088 ispersist = is_persistent(group); 2089 id = sa_get_share_attr((sa_share_t)group, "id"); 2090 } 2091 if (ispersist) { 2092 len = sa_optionset_name(optionset, name, sizeof (name), id); 2093 if (len > 0) { 2094 ret = sa_delete_pgroup(scf_handle, name); 2095 } 2096 } 2097 xmlUnlinkNode((xmlNodePtr)optionset); 2098 xmlFreeNode((xmlNodePtr)optionset); 2099 if (id != NULL) 2100 sa_free_attr_string(id); 2101 return (ret); 2102 } 2103 2104 /* private to the implementation */ 2105 int 2106 _sa_remove_optionset(sa_optionset_t optionset) 2107 { 2108 int ret = SA_OK; 2109 2110 xmlUnlinkNode((xmlNodePtr)optionset); 2111 xmlFreeNode((xmlNodePtr)optionset); 2112 return (ret); 2113 } 2114 2115 /* 2116 * sa_create_security(group, sectype, proto) 2117 * 2118 * Create a security optionset (one that has a type name and a 2119 * proto). Security is left over from a pure NFS implementation. The 2120 * naming will change in the future when the API is released. 2121 */ 2122 sa_security_t 2123 sa_create_security(sa_group_t group, char *sectype, char *proto) 2124 { 2125 sa_security_t security; 2126 char *id = NULL; 2127 sa_group_t parent; 2128 char *groupname = NULL; 2129 2130 if (group != NULL && sa_is_share(group)) { 2131 id = sa_get_share_attr((sa_share_t)group, "id"); 2132 parent = sa_get_parent_group(group); 2133 if (parent != NULL) 2134 groupname = sa_get_group_attr(parent, "name"); 2135 } else if (group != NULL) { 2136 groupname = sa_get_group_attr(group, "name"); 2137 } 2138 2139 security = sa_get_security(group, sectype, proto); 2140 if (security != NULL) { 2141 /* can't have a duplicate security option */ 2142 security = NULL; 2143 } else { 2144 security = (sa_security_t)xmlNewChild((xmlNodePtr)group, 2145 NULL, 2146 (xmlChar *)"security", 2147 NULL); 2148 if (security != NULL) { 2149 char oname[256]; 2150 sa_set_security_attr(security, "type", proto); 2151 2152 sa_set_security_attr(security, "sectype", sectype); 2153 (void) sa_security_name(security, oname, 2154 sizeof (oname), id); 2155 if (groupname != NULL && is_persistent(group)) { 2156 (void) sa_get_instance(scf_handle, groupname); 2157 (void) sa_create_pgroup(scf_handle, oname); 2158 } 2159 } 2160 } 2161 if (groupname != NULL) 2162 sa_free_attr_string(groupname); 2163 return (security); 2164 } 2165 2166 /* 2167 * sa_destroy_security(security) 2168 * 2169 * Remove the specified optionset from the document and the 2170 * configuration. 2171 */ 2172 2173 int 2174 sa_destroy_security(sa_security_t security) 2175 { 2176 char name[256]; 2177 int len; 2178 int ret = SA_OK; 2179 char *id = NULL; 2180 sa_group_t group; 2181 int iszfs = 0; 2182 int ispersist = 1; 2183 2184 group = sa_get_optionset_parent(security); 2185 2186 if (group != NULL) 2187 iszfs = sa_group_is_zfs(group); 2188 2189 if (group != NULL && !iszfs) { 2190 if (sa_is_share(group)) 2191 ispersist = is_persistent(group); 2192 id = sa_get_share_attr((sa_share_t)group, "id"); 2193 } 2194 if (ispersist) { 2195 len = sa_security_name(security, name, sizeof (name), id); 2196 if (!iszfs && len > 0) { 2197 ret = sa_delete_pgroup(scf_handle, name); 2198 } 2199 } 2200 xmlUnlinkNode((xmlNodePtr)security); 2201 xmlFreeNode((xmlNodePtr)security); 2202 if (iszfs) { 2203 ret = sa_zfs_update(group); 2204 } 2205 if (id != NULL) 2206 sa_free_attr_string(id); 2207 return (ret); 2208 } 2209 2210 /* 2211 * sa_get_security_attr(optionset, tag) 2212 * 2213 * Return the specified attribute value from the optionset. 2214 */ 2215 2216 char * 2217 sa_get_security_attr(sa_property_t optionset, char *tag) 2218 { 2219 return (get_node_attr((void *)optionset, tag)); 2220 2221 } 2222 2223 /* 2224 * sa_set_security_attr(optionset, tag, value) 2225 * 2226 * Set the optioset attribute specied by tag to the specified value. 2227 */ 2228 2229 void 2230 sa_set_security_attr(sa_group_t optionset, char *tag, char *value) 2231 { 2232 set_node_attr((void *)optionset, tag, value); 2233 } 2234 2235 /* 2236 * is_nodetype(node, type) 2237 * 2238 * Check to see if node is of the type specified. 2239 */ 2240 2241 static int 2242 is_nodetype(void *node, char *type) 2243 { 2244 return (strcmp((char *)((xmlNodePtr)node)->name, type) == 0); 2245 } 2246 2247 /* 2248 * sa_set_prop_by_prop(optionset, group, prop, type) 2249 * 2250 * Add/remove/update the specified property prop into the optionset or 2251 * share. If a share, sort out which property group based on GUID. In 2252 * all cases, the appropriate transaction is set (or ZFS share is 2253 * marked as needing an update) 2254 */ 2255 2256 #define SA_PROP_OP_REMOVE 1 2257 #define SA_PROP_OP_ADD 2 2258 #define SA_PROP_OP_UPDATE 3 2259 static int 2260 sa_set_prop_by_prop(sa_optionset_t optionset, sa_group_t group, 2261 sa_property_t prop, int type) 2262 { 2263 char *name; 2264 char *valstr; 2265 int ret = SA_OK; 2266 scf_transaction_entry_t *entry; 2267 scf_value_t *value; 2268 int opttype; /* 1 == optionset, 0 == security */ 2269 char *id = NULL; 2270 int iszfs = 0; 2271 int isshare = 0; 2272 sa_group_t parent = NULL; 2273 2274 if (!is_persistent(group)) { 2275 /* 2276 * if the group/share is not persistent we don't need 2277 * to do anything here 2278 */ 2279 return (SA_OK); 2280 } 2281 name = sa_get_property_attr(prop, "type"); 2282 valstr = sa_get_property_attr(prop, "value"); 2283 entry = scf_entry_create(scf_handle->handle); 2284 opttype = is_nodetype((void *)optionset, "optionset"); 2285 2286 if (valstr != NULL && entry != NULL) { 2287 if (sa_is_share(group)) { 2288 isshare = 1; 2289 parent = sa_get_parent_group(group); 2290 if (parent != NULL) { 2291 iszfs = is_zfs_group(parent); 2292 } 2293 } else { 2294 iszfs = is_zfs_group(group); 2295 } 2296 if (!iszfs) { 2297 if (scf_handle->trans == NULL) { 2298 char oname[256]; 2299 char *groupname = NULL; 2300 if (isshare) { 2301 if (parent != NULL) { 2302 groupname = sa_get_group_attr(parent, "name"); 2303 } 2304 id = sa_get_share_attr((sa_share_t)group, "id"); 2305 } else { 2306 groupname = sa_get_group_attr(group, "name"); 2307 } 2308 if (groupname != NULL) { 2309 ret = sa_get_instance(scf_handle, groupname); 2310 sa_free_attr_string(groupname); 2311 } 2312 if (opttype) 2313 (void) sa_optionset_name(optionset, oname, 2314 sizeof (oname), id); 2315 else 2316 (void) sa_security_name(optionset, oname, 2317 sizeof (oname), id); 2318 ret = sa_start_transaction(scf_handle, oname); 2319 } 2320 if (ret == SA_OK) { 2321 switch (type) { 2322 case SA_PROP_OP_REMOVE: 2323 ret = scf_transaction_property_delete(scf_handle->trans, 2324 entry, 2325 name); 2326 break; 2327 case SA_PROP_OP_ADD: 2328 case SA_PROP_OP_UPDATE: 2329 value = scf_value_create(scf_handle->handle); 2330 if (value != NULL) { 2331 if (type == SA_PROP_OP_ADD) 2332 ret = scf_transaction_property_new( 2333 scf_handle->trans, 2334 entry, 2335 name, 2336 SCF_TYPE_ASTRING); 2337 else 2338 ret = scf_transaction_property_change( 2339 scf_handle->trans, 2340 entry, 2341 name, 2342 SCF_TYPE_ASTRING); 2343 if (ret == 0) { 2344 ret = scf_value_set_astring(value, valstr); 2345 if (ret == 0) 2346 ret = scf_entry_add_value(entry, value); 2347 if (ret != 0) { 2348 scf_value_destroy(value); 2349 ret = SA_SYSTEM_ERR; 2350 } 2351 } else { 2352 scf_entry_destroy(entry); 2353 ret = SA_SYSTEM_ERR; 2354 } 2355 break; 2356 } 2357 } 2358 } 2359 } else { 2360 /* 2361 * ZFS update. The calling function would have updated 2362 * the internal XML structure. Just need to flag it as 2363 * changed for ZFS. 2364 */ 2365 zfs_set_update((sa_share_t)group); 2366 } 2367 } 2368 2369 if (name != NULL) 2370 sa_free_attr_string(name); 2371 if (valstr != NULL) 2372 sa_free_attr_string(valstr); 2373 else if (entry != NULL) 2374 scf_entry_destroy(entry); 2375 2376 if (ret == -1) 2377 ret = SA_SYSTEM_ERR; 2378 2379 return (ret); 2380 } 2381 2382 /* 2383 * sa_create_property(name, value) 2384 * 2385 * Create a new property with the specified name and value. 2386 */ 2387 2388 sa_property_t 2389 sa_create_property(char *name, char *value) 2390 { 2391 xmlNodePtr node; 2392 2393 node = xmlNewNode(NULL, (xmlChar *)"option"); 2394 if (node != NULL) { 2395 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)name); 2396 xmlSetProp(node, (xmlChar *)"value", (xmlChar *)value); 2397 } 2398 return ((sa_property_t)node); 2399 } 2400 2401 /* 2402 * sa_add_property(object, property) 2403 * 2404 * Add the specified property to the object. Issue the appropriate 2405 * transaction or mark a ZFS object as needing an update. 2406 */ 2407 2408 int 2409 sa_add_property(void *object, sa_property_t property) 2410 { 2411 int ret = SA_OK; 2412 sa_group_t parent; 2413 sa_group_t group; 2414 char *proto; 2415 2416 proto = sa_get_optionset_attr(object, "type"); 2417 if (property != NULL) { 2418 if ((ret = sa_valid_property(object, proto, property)) == SA_OK) { 2419 property = (sa_property_t)xmlAddChild((xmlNodePtr)object, 2420 (xmlNodePtr)property); 2421 } else { 2422 if (proto != NULL) 2423 sa_free_attr_string(proto); 2424 return (ret); 2425 } 2426 } 2427 2428 if (proto != NULL) 2429 sa_free_attr_string(proto); 2430 2431 parent = sa_get_parent_group(object); 2432 if (!is_persistent(parent)) { 2433 return (ret); 2434 } 2435 2436 if (sa_is_share(parent)) 2437 group = sa_get_parent_group(parent); 2438 else 2439 group = parent; 2440 2441 if (property == NULL) 2442 ret = SA_NO_MEMORY; 2443 else { 2444 char oname[256]; 2445 2446 if (!is_zfs_group(group)) { 2447 char *id = NULL; 2448 if (sa_is_share((sa_group_t)parent)) { 2449 id = sa_get_share_attr((sa_share_t)parent, "id"); 2450 } 2451 if (scf_handle->trans == NULL) { 2452 if (is_nodetype(object, "optionset")) 2453 (void) sa_optionset_name((sa_optionset_t)object, 2454 oname, sizeof (oname), id); 2455 else 2456 (void) sa_security_name((sa_optionset_t)object, 2457 oname, sizeof (oname), id); 2458 ret = sa_start_transaction(scf_handle, oname); 2459 } 2460 if (ret == SA_OK) { 2461 char *name; 2462 char *value; 2463 name = sa_get_property_attr(property, "type"); 2464 value = sa_get_property_attr(property, "value"); 2465 if (name != NULL && value != NULL) { 2466 if (scf_handle->scf_state == SCH_STATE_INIT) 2467 ret = sa_set_property(scf_handle, name, value); 2468 } else 2469 ret = SA_CONFIG_ERR; 2470 if (name != NULL) 2471 sa_free_attr_string(name); 2472 if (value != NULL) 2473 sa_free_attr_string(value); 2474 } 2475 if (id != NULL) 2476 sa_free_attr_string(id); 2477 } else { 2478 /* 2479 * ZFS is a special case. We do want to allow editing 2480 * property/security lists since we can have a better 2481 * syntax and we also want to keep things consistent 2482 * when possible. 2483 * 2484 * Right now, we defer until the sa_commit_properties 2485 * so we can get them all at once. We do need to mark 2486 * the share as "changed" 2487 */ 2488 zfs_set_update((sa_share_t)parent); 2489 } 2490 } 2491 return (ret); 2492 } 2493 2494 /* 2495 * sa_remove_property(property) 2496 * 2497 * Remove the specied property from its containing object. Update the 2498 * repository as appropriate. 2499 */ 2500 2501 int 2502 sa_remove_property(sa_property_t property) 2503 { 2504 int ret = SA_OK; 2505 2506 if (property != NULL) { 2507 sa_optionset_t optionset; 2508 sa_group_t group; 2509 optionset = sa_get_property_parent(property); 2510 if (optionset != NULL) { 2511 group = sa_get_optionset_parent(optionset); 2512 if (group != NULL) { 2513 ret = sa_set_prop_by_prop(optionset, group, property, 2514 SA_PROP_OP_REMOVE); 2515 } 2516 } 2517 xmlUnlinkNode((xmlNodePtr)property); 2518 xmlFreeNode((xmlNodePtr)property); 2519 } else { 2520 ret = SA_NO_SUCH_PROP; 2521 } 2522 return (ret); 2523 } 2524 2525 /* 2526 * sa_update_property(property, value) 2527 * 2528 * Update the specified property to the new value. If value is NULL, 2529 * we currently treat this as a remove. 2530 */ 2531 2532 int 2533 sa_update_property(sa_property_t property, char *value) 2534 { 2535 int ret = SA_OK; 2536 if (value == NULL) { 2537 return (sa_remove_property(property)); 2538 } else { 2539 sa_optionset_t optionset; 2540 sa_group_t group; 2541 set_node_attr((void *)property, "value", value); 2542 optionset = sa_get_property_parent(property); 2543 if (optionset != NULL) { 2544 group = sa_get_optionset_parent(optionset); 2545 if (group != NULL) { 2546 ret = sa_set_prop_by_prop(optionset, group, property, 2547 SA_PROP_OP_UPDATE); 2548 } 2549 } else { 2550 ret = SA_NO_SUCH_PROP; 2551 } 2552 } 2553 return (ret); 2554 } 2555 2556 /* 2557 * _sa_get_next_error(node) 2558 * 2559 * Get the next (first if node==NULL) error node in the 2560 * document. "error" nodes are added if there were syntax errors 2561 * during parsing of the /etc/dfs/dfstab file. They are preserved in 2562 * comments and recreated in the doc on the next parse. 2563 */ 2564 2565 xmlNodePtr 2566 _sa_get_next_error(xmlNodePtr node) 2567 { 2568 if (node == NULL) { 2569 for (node = sa_config_tree->xmlChildrenNode; 2570 node != NULL; node = node->next) 2571 if (xmlStrcmp(node->name, (xmlChar *)"error") == 0) 2572 return (node); 2573 } else { 2574 for (node = node->next; node != NULL; node = node->next) 2575 if (xmlStrcmp(node->name, (xmlChar *)"error") == 0) 2576 return (node); 2577 } 2578 return (node); 2579 } 2580 2581 /* 2582 * sa_get_protocol_property(propset, prop) 2583 * 2584 * Get the specified protocol specific property. These are global to 2585 * the protocol and not specific to a group or share. 2586 */ 2587 2588 sa_property_t 2589 sa_get_protocol_property(sa_protocol_properties_t propset, char *prop) 2590 { 2591 xmlNodePtr node = (xmlNodePtr)propset; 2592 xmlChar *value = NULL; 2593 2594 for (node = node->children; node != NULL; 2595 node = node->next) { 2596 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2597 if (prop == NULL) 2598 break; 2599 value = xmlGetProp(node, (xmlChar *)"type"); 2600 if (value != NULL && 2601 xmlStrcasecmp(value, (xmlChar *)prop) == 0) { 2602 break; 2603 } 2604 if (value != NULL) { 2605 xmlFree(value); 2606 value = NULL; 2607 } 2608 } 2609 } 2610 if (value != NULL) 2611 xmlFree(value); 2612 if (node != NULL && xmlStrcmp(node->name, (xmlChar *)"option") != 0) { 2613 /* avoid a non option node -- it is possible to be a text node */ 2614 node = NULL; 2615 } 2616 return ((sa_property_t)node); 2617 } 2618 2619 /* 2620 * sa_get_next_protocol_property(prop) 2621 * 2622 * Get the next protocol specific property in the list. 2623 */ 2624 2625 sa_property_t 2626 sa_get_next_protocol_property(sa_property_t prop) 2627 { 2628 xmlNodePtr node; 2629 2630 for (node = ((xmlNodePtr)prop)->next; node != NULL; 2631 node = node->next) { 2632 if (xmlStrcmp(node->name, (xmlChar *)"option") == 0) { 2633 break; 2634 } 2635 } 2636 return ((sa_property_t)node); 2637 } 2638 2639 /* 2640 * sa_set_protocol_property(prop, value) 2641 * 2642 * Set the specified property to have the new value. The protocol 2643 * specific plugin will then be called to update the property. 2644 */ 2645 2646 int 2647 sa_set_protocol_property(sa_property_t prop, char *value) 2648 { 2649 sa_protocol_properties_t propset; 2650 char *proto; 2651 int ret = SA_INVALID_PROTOCOL; 2652 2653 propset = ((xmlNodePtr)prop)->parent; 2654 if (propset != NULL) { 2655 proto = sa_get_optionset_attr(propset, "type"); 2656 if (proto != NULL) { 2657 set_node_attr((xmlNodePtr)prop, "value", value); 2658 ret = sa_proto_set_property(proto, prop); 2659 sa_free_attr_string(prop); 2660 } 2661 } 2662 return (ret); 2663 } 2664 2665 /* 2666 * sa_add_protocol_property(propset, prop) 2667 * 2668 * Add a new property to the protocol sepcific property set. 2669 */ 2670 2671 int 2672 sa_add_protocol_property(sa_protocol_properties_t propset, sa_property_t prop) 2673 { 2674 xmlNodePtr node; 2675 2676 /* should check for legitimacy */ 2677 node = xmlAddChild((xmlNodePtr)propset, (xmlNodePtr)prop); 2678 if (node != NULL) 2679 return (SA_OK); 2680 return (SA_NO_MEMORY); 2681 } 2682 2683 /* 2684 * sa_create_protocol_properties(proto) 2685 * 2686 * Create a protocol specifity property set. 2687 */ 2688 2689 sa_protocol_properties_t 2690 sa_create_protocol_properties(char *proto) 2691 { 2692 xmlNodePtr node; 2693 node = xmlNewNode(NULL, (xmlChar *)"propertyset"); 2694 if (node != NULL) { 2695 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 2696 } 2697 return (node); 2698 } 2699