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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Copyright 2023 Oxide Computer Company 27 */ 28 29 /* helper functions for using libscf with sharemgr */ 30 31 #include <libscf.h> 32 #include <libxml/parser.h> 33 #include <libxml/tree.h> 34 #include "libshare.h" 35 #include "libshare_impl.h" 36 #include "scfutil.h" 37 #include <string.h> 38 #include <ctype.h> 39 #include <errno.h> 40 #include <uuid/uuid.h> 41 #include <sys/param.h> 42 #include <signal.h> 43 #include <sys/time.h> 44 #include <libintl.h> 45 46 ssize_t scf_max_name_len; 47 extern struct sa_proto_plugin *sap_proto_list; 48 extern sa_handle_impl_t get_handle_for_root(xmlNodePtr); 49 static void set_transaction_tstamp(sa_handle_impl_t); 50 /* 51 * The SMF facility uses some properties that must exist. We want to 52 * skip over these when processing protocol options. 53 */ 54 static char *skip_props[] = { 55 "modify_authorization", 56 "action_authorization", 57 "value_authorization", 58 NULL 59 }; 60 61 /* 62 * sa_scf_fini(handle) 63 * 64 * Must be called when done. Called with the handle allocated in 65 * sa_scf_init(), it cleans up the state and frees any SCF resources 66 * still in use. Called by sa_fini(). 67 */ 68 69 void 70 sa_scf_fini(scfutilhandle_t *handle) 71 { 72 if (handle != NULL) { 73 int unbind = 0; 74 if (handle->scope != NULL) { 75 unbind = 1; 76 scf_scope_destroy(handle->scope); 77 } 78 if (handle->instance != NULL) 79 scf_instance_destroy(handle->instance); 80 if (handle->service != NULL) 81 scf_service_destroy(handle->service); 82 if (handle->pg != NULL) 83 scf_pg_destroy(handle->pg); 84 if (handle->handle != NULL) { 85 handle->scf_state = SCH_STATE_UNINIT; 86 if (unbind) 87 (void) scf_handle_unbind(handle->handle); 88 scf_handle_destroy(handle->handle); 89 } 90 free(handle); 91 } 92 } 93 94 /* 95 * sa_scf_init() 96 * 97 * Must be called before using any of the SCF functions. Called by 98 * sa_init() during the API setup. 99 */ 100 101 scfutilhandle_t * 102 sa_scf_init(sa_handle_impl_t ihandle) 103 { 104 scfutilhandle_t *handle; 105 106 scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 107 if (scf_max_name_len <= 0) 108 scf_max_name_len = SA_MAX_NAME_LEN + 1; 109 110 handle = calloc(1, sizeof (scfutilhandle_t)); 111 if (handle == NULL) 112 return (handle); 113 114 ihandle->scfhandle = handle; 115 handle->scf_state = SCH_STATE_INITIALIZING; 116 handle->handle = scf_handle_create(SCF_VERSION); 117 if (handle->handle == NULL) { 118 free(handle); 119 handle = NULL; 120 (void) printf("libshare could not access SMF repository: %s\n", 121 scf_strerror(scf_error())); 122 return (handle); 123 } 124 if (scf_handle_bind(handle->handle) != 0) 125 goto err; 126 127 handle->scope = scf_scope_create(handle->handle); 128 handle->service = scf_service_create(handle->handle); 129 handle->pg = scf_pg_create(handle->handle); 130 131 /* Make sure we have sufficient SMF running */ 132 handle->instance = scf_instance_create(handle->handle); 133 if (handle->scope == NULL || handle->service == NULL || 134 handle->pg == NULL || handle->instance == NULL) 135 goto err; 136 if (scf_handle_get_scope(handle->handle, 137 SCF_SCOPE_LOCAL, handle->scope) != 0) 138 goto err; 139 if (scf_scope_get_service(handle->scope, 140 SA_GROUP_SVC_NAME, handle->service) != 0) 141 goto err; 142 143 handle->scf_state = SCH_STATE_INIT; 144 if (sa_get_instance(handle, "default") != SA_OK) { 145 sa_group_t defgrp; 146 defgrp = sa_create_group((sa_handle_t)ihandle, "default", NULL); 147 /* Only NFS enabled for "default" group. */ 148 if (defgrp != NULL) 149 (void) sa_create_optionset(defgrp, "nfs"); 150 } 151 152 return (handle); 153 154 /* Error handling/unwinding */ 155 err: 156 (void) sa_scf_fini(handle); 157 if (scf_error() != SCF_ERROR_NOT_FOUND) { 158 (void) printf("libshare SMF initialization problem: %s\n", 159 scf_strerror(scf_error())); 160 } 161 return (NULL); 162 } 163 164 /* 165 * get_scf_limit(name) 166 * 167 * Since we use scf_limit a lot and do the same check and return the 168 * same value if it fails, implement as a function for code 169 * simplification. Basically, if name isn't found, return MAXPATHLEN 170 * (1024) so we have a reasonable default buffer size. 171 */ 172 static ssize_t 173 get_scf_limit(uint32_t name) 174 { 175 ssize_t vallen; 176 177 vallen = scf_limit(name); 178 if (vallen == (ssize_t)-1) 179 vallen = MAXPATHLEN; 180 return (vallen); 181 } 182 183 /* 184 * skip_property(name) 185 * 186 * Internal function to check to see if a property is an SMF magic 187 * property that needs to be skipped. 188 */ 189 static int 190 skip_property(char *name) 191 { 192 int i; 193 194 for (i = 0; skip_props[i] != NULL; i++) 195 if (strcmp(name, skip_props[i]) == 0) 196 return (1); 197 return (0); 198 } 199 200 /* 201 * generate_unique_sharename(sharename) 202 * 203 * Shares are represented in SMF as property groups. Due to share 204 * paths containing characters that are not allowed in SMF names and 205 * the need to be unique, we use UUIDs to construct a unique name. 206 */ 207 208 static void 209 generate_unique_sharename(char *sharename) 210 { 211 uuid_t uuid; 212 213 uuid_generate(uuid); 214 (void) strcpy(sharename, "S-"); 215 uuid_unparse(uuid, sharename + 2); 216 } 217 218 /* 219 * valid_protocol(proto) 220 * 221 * Check to see if the specified protocol is a valid one for the 222 * general sharemgr facility. We determine this by checking which 223 * plugin protocols were found. 224 */ 225 226 static int 227 valid_protocol(char *proto) 228 { 229 struct sa_proto_plugin *plugin; 230 for (plugin = sap_proto_list; plugin != NULL; 231 plugin = plugin->plugin_next) 232 if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0) 233 return (1); 234 return (0); 235 } 236 237 /* 238 * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype) 239 * 240 * Extract the name property group and create the specified type of 241 * node on the provided group. type will be optionset or security. 242 */ 243 244 static int 245 sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 246 scf_propertygroup_t *pg, char *nodetype, char *proto, char *sectype) 247 { 248 xmlNodePtr node; 249 scf_iter_t *iter; 250 scf_property_t *prop; 251 scf_value_t *value; 252 char *name; 253 char *valuestr; 254 ssize_t vallen; 255 int ret = SA_OK; 256 257 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 258 259 node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL); 260 if (node == NULL) 261 return (ret); 262 263 if (proto != NULL) 264 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 265 if (sectype != NULL) 266 (void) xmlSetProp(node, (xmlChar *)"sectype", 267 (xmlChar *)sectype); 268 /* 269 * Have node to work with so iterate over the properties 270 * in the pg and create option sub nodes. 271 */ 272 iter = scf_iter_create(handle->handle); 273 value = scf_value_create(handle->handle); 274 prop = scf_property_create(handle->handle); 275 name = malloc(scf_max_name_len); 276 valuestr = malloc(vallen); 277 /* 278 * Want to iterate through the properties and add them 279 * to the base optionset. 280 */ 281 if (iter == NULL || value == NULL || prop == NULL || 282 valuestr == NULL || name == NULL) { 283 ret = SA_NO_MEMORY; 284 goto out; 285 } 286 if (scf_iter_pg_properties(iter, pg) == 0) { 287 /* Now iterate the properties in the group */ 288 while (scf_iter_next_property(iter, prop) > 0) { 289 /* have a property */ 290 if (scf_property_get_name(prop, name, 291 scf_max_name_len) > 0) { 292 sa_property_t saprop; 293 /* Some properties are part of the framework */ 294 if (skip_property(name)) 295 continue; 296 if (scf_property_get_value(prop, value) != 0) 297 continue; 298 if (scf_value_get_astring(value, valuestr, 299 vallen) < 0) 300 continue; 301 saprop = sa_create_property(name, valuestr); 302 if (saprop != NULL) { 303 /* 304 * Since in SMF, don't 305 * recurse. Use xmlAddChild 306 * directly, instead. 307 */ 308 (void) xmlAddChild(node, 309 (xmlNodePtr) saprop); 310 } 311 } 312 } 313 } 314 out: 315 /* cleanup to avoid memory leaks */ 316 if (value != NULL) 317 scf_value_destroy(value); 318 if (iter != NULL) 319 scf_iter_destroy(iter); 320 if (prop != NULL) 321 scf_property_destroy(prop); 322 if (name != NULL) 323 free(name); 324 if (valuestr != NULL) 325 free(valuestr); 326 327 return (ret); 328 } 329 330 /* 331 * sa_extract_attrs(root, handle, instance) 332 * 333 * Local function to extract the actual attributes/properties from the 334 * property group of the service instance. These are the well known 335 * attributes of "state" and "zfs". If additional attributes are 336 * added, they should be added here. 337 */ 338 339 static void 340 sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle, 341 scf_instance_t *instance) 342 { 343 scf_property_t *prop; 344 scf_value_t *value; 345 char *valuestr; 346 ssize_t vallen; 347 348 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 349 prop = scf_property_create(handle->handle); 350 value = scf_value_create(handle->handle); 351 valuestr = malloc(vallen); 352 if (prop == NULL || value == NULL || valuestr == NULL || 353 scf_instance_get_pg(instance, "operation", handle->pg) != 0) { 354 goto out; 355 } 356 /* 357 * Have a property group with desired name so now get 358 * the known attributes. 359 */ 360 if (scf_pg_get_property(handle->pg, "state", prop) == 0) { 361 /* Found the property so get the value */ 362 if (scf_property_get_value(prop, value) == 0) { 363 if (scf_value_get_astring(value, valuestr, 364 vallen) >= 0) { 365 (void) xmlSetProp(root, (xmlChar *)"state", 366 (xmlChar *)valuestr); 367 } 368 } 369 } 370 if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) { 371 /* Found the property so get the value */ 372 if (scf_property_get_value(prop, value) == 0) { 373 if (scf_value_get_astring(value, valuestr, 374 vallen) > 0) { 375 (void) xmlSetProp(root, (xmlChar *)"zfs", 376 (xmlChar *)valuestr); 377 } 378 } 379 } 380 out: 381 if (valuestr != NULL) 382 free(valuestr); 383 if (value != NULL) 384 scf_value_destroy(value); 385 if (prop != NULL) 386 scf_property_destroy(prop); 387 } 388 389 /* 390 * List of known share attributes. 391 */ 392 393 static char *share_attr[] = { 394 "path", 395 "id", 396 "drive-letter", 397 "exclude", 398 NULL, 399 }; 400 401 static int 402 is_share_attr(char *name) 403 { 404 int i; 405 for (i = 0; share_attr[i] != NULL; i++) 406 if (strcmp(name, share_attr[i]) == 0) 407 return (1); 408 return (0); 409 } 410 411 /* 412 * _sa_make_resource(node, valuestr) 413 * 414 * Make a resource node on the share node. The valusestr will either 415 * be old format (SMF acceptable string) or new format (pretty much an 416 * arbitrary string with "nnn:" prefixing in order to persist 417 * mapping). The input valuestr will get modified in place. This is 418 * only used in SMF repository parsing. A possible third field will be 419 * a "description" string. 420 */ 421 422 static void 423 _sa_make_resource(xmlNodePtr node, char *valuestr) 424 { 425 char *idx; 426 char *name; 427 char *description = NULL; 428 429 idx = valuestr; 430 name = strchr(valuestr, ':'); 431 if (name == NULL) { 432 /* this is old form so give an index of "0" */ 433 idx = "0"; 434 name = valuestr; 435 } else { 436 /* NUL the ':' and move past it */ 437 *name++ = '\0'; 438 /* There could also be a description string */ 439 description = strchr(name, ':'); 440 if (description != NULL) 441 *description++ = '\0'; 442 } 443 node = xmlNewChild(node, NULL, (xmlChar *)"resource", NULL); 444 if (node != NULL) { 445 (void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)name); 446 (void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)idx); 447 /* SMF values are always persistent */ 448 (void) xmlSetProp(node, (xmlChar *)"type", 449 (xmlChar *)"persist"); 450 if (description != NULL && strlen(description) > 0) { 451 (void) xmlNewChild(node, NULL, (xmlChar *)"description", 452 (xmlChar *)description); 453 } 454 } 455 } 456 457 458 /* 459 * sa_share_from_pgroup 460 * 461 * Extract the share definition from the share property group. We do 462 * some sanity checking to avoid bad data. 463 * 464 * Since this is only constructing the internal data structures, we 465 * don't use the sa_* functions most of the time. 466 */ 467 void 468 sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 469 scf_propertygroup_t *pg, char *id) 470 { 471 xmlNodePtr node; 472 char *name; 473 scf_iter_t *iter; 474 scf_property_t *prop; 475 scf_value_t *value; 476 ssize_t vallen; 477 char *valuestr; 478 int ret = SA_OK; 479 int have_path = 0; 480 481 /* 482 * While preliminary check (starts with 'S') passed before 483 * getting here. Need to make sure it is in ID syntax 484 * (Snnnnnn). Note that shares with properties have similar 485 * pgroups. 486 */ 487 vallen = strlen(id); 488 if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) { 489 uuid_t uuid; 490 if (strncmp(id, SA_SHARE_PG_PREFIX, 491 SA_SHARE_PG_PREFIXLEN) != 0 || 492 uuid_parse(id + 2, uuid) < 0) 493 return; 494 } else { 495 return; 496 } 497 498 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 499 500 iter = scf_iter_create(handle->handle); 501 value = scf_value_create(handle->handle); 502 prop = scf_property_create(handle->handle); 503 name = malloc(scf_max_name_len); 504 valuestr = malloc(vallen); 505 506 /* 507 * Construct the share XML node. It is similar to sa_add_share 508 * but never changes the repository. Also, there won't be any 509 * ZFS or transient shares. Root will be the group it is 510 * associated with. 511 */ 512 node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL); 513 if (node != NULL) { 514 /* 515 * Make sure the UUID part of the property group is 516 * stored in the share "id" property. We use this 517 * later. 518 */ 519 (void) xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id); 520 (void) xmlSetProp(node, (xmlChar *)"type", 521 (xmlChar *)"persist"); 522 } 523 524 if (iter == NULL || value == NULL || prop == NULL || name == NULL) 525 goto out; 526 527 /* Iterate over the share pg properties */ 528 if (scf_iter_pg_properties(iter, pg) != 0) 529 goto out; 530 531 while (scf_iter_next_property(iter, prop) > 0) { 532 ret = SA_SYSTEM_ERR; /* assume the worst */ 533 if (scf_property_get_name(prop, name, scf_max_name_len) > 0) { 534 if (scf_property_get_value(prop, value) == 0) { 535 if (scf_value_get_astring(value, valuestr, 536 vallen) >= 0) { 537 ret = SA_OK; 538 } 539 } else if (strcmp(name, "resource") == 0) { 540 ret = SA_OK; 541 } 542 } 543 if (ret != SA_OK) 544 continue; 545 /* 546 * Check that we have the "path" property in 547 * name. The string in name will always be nul 548 * terminated if scf_property_get_name() 549 * succeeded. 550 */ 551 if (strcmp(name, "path") == 0) 552 have_path = 1; 553 if (is_share_attr(name)) { 554 /* 555 * If a share attr, then simple - 556 * usually path and id name 557 */ 558 (void) xmlSetProp(node, (xmlChar *)name, 559 (xmlChar *)valuestr); 560 } else if (strcmp(name, "resource") == 0) { 561 /* 562 * Resource names handled differently since 563 * there can be multiple on each share. The 564 * "resource" id must be preserved since this 565 * will be used by some protocols in mapping 566 * "property spaces" to names and is always 567 * used to create SMF property groups specific 568 * to resources. CIFS needs this. The first 569 * value is present so add and then loop for 570 * any additional. Since this is new and 571 * previous values may exist, handle 572 * conversions. 573 */ 574 scf_iter_t *viter; 575 viter = scf_iter_create(handle->handle); 576 if (viter != NULL && 577 scf_iter_property_values(viter, prop) == 0) { 578 while (scf_iter_next_value(viter, value) > 0) { 579 /* Have a value so process it */ 580 if (scf_value_get_ustring(value, 581 valuestr, vallen) >= 0) { 582 /* have a ustring */ 583 _sa_make_resource(node, 584 valuestr); 585 } else if (scf_value_get_astring(value, 586 valuestr, vallen) >= 0) { 587 /* have an astring */ 588 _sa_make_resource(node, 589 valuestr); 590 } 591 } 592 scf_iter_destroy(viter); 593 } 594 } else { 595 if (strcmp(name, "description") == 0) { 596 /* We have a description node */ 597 xmlNodePtr desc; 598 desc = xmlNewChild(node, NULL, 599 (xmlChar *)"description", NULL); 600 if (desc != NULL) 601 xmlNodeSetContent(desc, 602 (xmlChar *)valuestr); 603 } 604 } 605 } 606 out: 607 /* 608 * A share without a path is broken so we want to not include 609 * these. They shouldn't happen but if you kill a sharemgr in 610 * the process of creating a share, it could happen. They 611 * should be harmless. It is also possible that another 612 * sharemgr is running and in the process of creating a share. 613 */ 614 if (have_path == 0 && node != NULL) { 615 xmlUnlinkNode(node); 616 xmlFreeNode(node); 617 } 618 if (name != NULL) 619 free(name); 620 if (valuestr != NULL) 621 free(valuestr); 622 if (value != NULL) 623 scf_value_destroy(value); 624 if (iter != NULL) 625 scf_iter_destroy(iter); 626 if (prop != NULL) 627 scf_property_destroy(prop); 628 } 629 630 /* 631 * find_share_by_id(shareid) 632 * 633 * Search all shares in all groups until we find the share represented 634 * by "id". 635 */ 636 637 static sa_share_t 638 find_share_by_id(sa_handle_t handle, char *shareid) 639 { 640 sa_group_t group; 641 sa_share_t share = NULL; 642 char *id = NULL; 643 int done = 0; 644 645 for (group = sa_get_group(handle, NULL); 646 group != NULL && !done; 647 group = sa_get_next_group(group)) { 648 for (share = sa_get_share(group, NULL); 649 share != NULL; 650 share = sa_get_next_share(share)) { 651 id = sa_get_share_attr(share, "id"); 652 if (id != NULL && strcmp(id, shareid) == 0) { 653 sa_free_attr_string(id); 654 id = NULL; 655 done++; 656 break; 657 } 658 if (id != NULL) { 659 sa_free_attr_string(id); 660 id = NULL; 661 } 662 } 663 } 664 return (share); 665 } 666 667 /* 668 * find_resource_by_index(share, index) 669 * 670 * Search the resource records on the share for the id index. 671 */ 672 static sa_resource_t 673 find_resource_by_index(sa_share_t share, char *index) 674 { 675 sa_resource_t resource; 676 sa_resource_t found = NULL; 677 char *id; 678 679 for (resource = sa_get_share_resource(share, NULL); 680 resource != NULL && found == NULL; 681 resource = sa_get_next_resource(resource)) { 682 id = (char *)xmlGetProp((xmlNodePtr)resource, (xmlChar *)"id"); 683 if (id != NULL) { 684 if (strcmp(id, index) == 0) { 685 /* found it so save in "found" */ 686 found = resource; 687 } 688 sa_free_attr_string(id); 689 } 690 } 691 return (found); 692 } 693 694 /* 695 * sa_share_props_from_pgroup(root, handle, pg, id, sahandle) 696 * 697 * Extract share properties from the SMF property group. More sanity 698 * checks are done and the share object is created. We ignore some 699 * errors that could exist in the repository and only worry about 700 * property groups that validate in naming. 701 */ 702 703 static int 704 sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 705 scf_propertygroup_t *pg, char *id, sa_handle_t sahandle) 706 { 707 xmlNodePtr node; 708 char *name = NULL; 709 scf_iter_t *iter = NULL; 710 scf_property_t *prop = NULL; 711 scf_value_t *value = NULL; 712 ssize_t vallen; 713 char *valuestr = NULL; 714 int ret = SA_OK; 715 char *sectype = NULL; 716 char *proto = NULL; 717 sa_share_t share; 718 uuid_t uuid; 719 720 /* 721 * While preliminary check (starts with 'S') passed before 722 * getting here. Need to make sure it is in ID syntax 723 * (Snnnnnn). Note that shares with properties have similar 724 * pgroups. If the pg name is more than SA_SHARE_PG_LEN 725 * characters, it is likely one of the protocol/security 726 * versions. 727 */ 728 vallen = strlen(id); 729 if (*id != SA_SHARE_PG_PREFIX[0] || vallen <= SA_SHARE_PG_LEN) { 730 /* 731 * It is ok to not have what we thought since someone might 732 * have added a name via SMF. 733 */ 734 return (ret); 735 } 736 if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) { 737 proto = strchr(id, '_'); 738 if (proto == NULL) 739 return (ret); 740 *proto++ = '\0'; 741 if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0) 742 return (ret); 743 /* 744 * probably a legal optionset so check a few more 745 * syntax points below. 746 */ 747 if (*proto == '\0') { 748 /* not a valid proto (null) */ 749 return (ret); 750 } 751 752 sectype = strchr(proto, '_'); 753 if (sectype != NULL) 754 *sectype++ = '\0'; 755 if (!valid_protocol(proto)) 756 return (ret); 757 } 758 759 /* 760 * To get here, we have a valid protocol and possibly a 761 * security. We now have to find the share that it is really 762 * associated with. The "id" portion of the pgroup name will 763 * match. 764 */ 765 766 share = find_share_by_id(sahandle, id); 767 if (share == NULL) 768 return (SA_BAD_PATH); 769 770 root = (xmlNodePtr)share; 771 772 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 773 774 if (sectype == NULL) 775 node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL); 776 else { 777 if (isdigit((int)*sectype)) { 778 sa_resource_t resource; 779 /* 780 * If sectype[0] is a digit, then it is an index into 781 * the resource names. We need to find a resource 782 * record and then get the properties into an 783 * optionset. The optionset becomes the "node" and the 784 * rest is hung off of the share. 785 */ 786 resource = find_resource_by_index(share, sectype); 787 if (resource != NULL) { 788 node = xmlNewChild(resource, NULL, 789 (xmlChar *)"optionset", NULL); 790 } else { 791 /* This shouldn't happen. */ 792 ret = SA_SYSTEM_ERR; 793 goto out; 794 } 795 } else { 796 /* 797 * If not a digit, then it is a security type 798 * (alternate option space). Security types start with 799 * an alphabetic. 800 */ 801 node = xmlNewChild(root, NULL, (xmlChar *)"security", 802 NULL); 803 if (node != NULL) 804 (void) xmlSetProp(node, (xmlChar *)"sectype", 805 (xmlChar *)sectype); 806 } 807 } 808 if (node == NULL) { 809 ret = SA_NO_MEMORY; 810 goto out; 811 } 812 813 (void) xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 814 /* now find the properties */ 815 iter = scf_iter_create(handle->handle); 816 value = scf_value_create(handle->handle); 817 prop = scf_property_create(handle->handle); 818 name = malloc(scf_max_name_len); 819 valuestr = malloc(vallen); 820 821 if (iter == NULL || value == NULL || prop == NULL || name == NULL) 822 goto out; 823 824 /* iterate over the share pg properties */ 825 if (scf_iter_pg_properties(iter, pg) == 0) { 826 while (scf_iter_next_property(iter, prop) > 0) { 827 ret = SA_SYSTEM_ERR; /* assume the worst */ 828 if (scf_property_get_name(prop, name, 829 scf_max_name_len) > 0) { 830 if (scf_property_get_value(prop, value) == 0) { 831 if (scf_value_get_astring(value, 832 valuestr, vallen) >= 0) { 833 ret = SA_OK; 834 } 835 } 836 } else { 837 ret = SA_SYSTEM_ERR; 838 } 839 if (ret == SA_OK) { 840 sa_property_t prop; 841 prop = sa_create_property(name, valuestr); 842 if (prop != NULL) 843 prop = (sa_property_t)xmlAddChild(node, 844 (xmlNodePtr)prop); 845 else 846 ret = SA_NO_MEMORY; 847 } 848 } 849 } else { 850 ret = SA_SYSTEM_ERR; 851 } 852 out: 853 if (iter != NULL) 854 scf_iter_destroy(iter); 855 if (value != NULL) 856 scf_value_destroy(value); 857 if (prop != NULL) 858 scf_property_destroy(prop); 859 if (name != NULL) 860 free(name); 861 if (valuestr != NULL) 862 free(valuestr); 863 return (ret); 864 } 865 866 /* 867 * sa_extract_group(root, handle, instance) 868 * 869 * Get the config info for this instance of a group and create the XML 870 * subtree from it. 871 */ 872 873 static int 874 sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle, 875 scf_instance_t *instance, sa_handle_t sahandle) 876 { 877 char *buff; 878 xmlNodePtr node; 879 scf_iter_t *iter; 880 char *proto; 881 char *sectype; 882 boolean_t have_shares = B_FALSE; 883 boolean_t is_default = B_FALSE; 884 boolean_t is_nfs = B_FALSE; 885 int ret = SA_OK; 886 int err; 887 888 buff = malloc(scf_max_name_len); 889 if (buff == NULL) 890 return (SA_NO_MEMORY); 891 892 iter = scf_iter_create(handle->handle); 893 if (iter == NULL) { 894 ret = SA_NO_MEMORY; 895 goto out; 896 } 897 898 if (scf_instance_get_name(instance, buff, scf_max_name_len) > 0) { 899 node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL); 900 if (node == NULL) { 901 ret = SA_NO_MEMORY; 902 goto out; 903 } 904 (void) xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff); 905 if (strcmp(buff, "default") == 0) 906 is_default = B_TRUE; 907 908 sa_extract_attrs(node, handle, instance); 909 /* 910 * Iterate through all the property groups 911 * looking for those with security or 912 * optionset prefixes. The names of the 913 * matching pgroups are parsed to get the 914 * protocol, and for security, the sectype. 915 * Syntax is as follows: 916 * optionset | optionset_<proto> 917 * security_default | security_<proto>_<sectype> 918 * "operation" is handled by 919 * sa_extract_attrs(). 920 */ 921 if (scf_iter_instance_pgs(iter, instance) != 0) { 922 ret = SA_NO_MEMORY; 923 goto out; 924 } 925 while (scf_iter_next_pg(iter, handle->pg) > 0) { 926 /* Have a pgroup so sort it out */ 927 ret = scf_pg_get_name(handle->pg, buff, 928 scf_max_name_len); 929 if (ret <= 0) 930 continue; 931 is_nfs = B_FALSE; 932 933 if (buff[0] == SA_SHARE_PG_PREFIX[0]) { 934 sa_share_from_pgroup(node, handle, 935 handle->pg, buff); 936 have_shares = B_TRUE; 937 } else if (strncmp(buff, "optionset", 9) == 0) { 938 char *nodetype = "optionset"; 939 /* Have an optionset */ 940 sectype = NULL; 941 proto = strchr(buff, '_'); 942 if (proto != NULL) { 943 *proto++ = '\0'; 944 sectype = strchr(proto, '_'); 945 if (sectype != NULL) { 946 *sectype++ = '\0'; 947 nodetype = "security"; 948 } 949 is_nfs = strcmp(proto, "nfs") == 0; 950 } else if (strlen(buff) > 9) { 951 /* 952 * This can only occur if 953 * someone has made changes 954 * via an SMF command. Since 955 * this would be an unknown 956 * syntax, we just ignore it. 957 */ 958 continue; 959 } 960 /* 961 * If the group is not "default" or is 962 * "default" and is_nfs, then extract the 963 * pgroup. If it is_default and !is_nfs, 964 * then we have an error and should remove 965 * the extraneous protocols. We don't care 966 * about errors on scf_pg_delete since we 967 * might not have permission during an 968 * extract only. 969 */ 970 if (!is_default || is_nfs) { 971 ret = sa_extract_pgroup(node, handle, 972 handle->pg, nodetype, proto, 973 sectype); 974 } else { 975 err = scf_pg_delete(handle->pg); 976 if (err == 0) 977 (void) fprintf(stderr, 978 dgettext(TEXT_DOMAIN, 979 "Removed protocol \"%s\" " 980 "from group \"default\"\n"), 981 proto); 982 } 983 } else if (strncmp(buff, "security", 8) == 0) { 984 /* 985 * Have a security (note that 986 * this should change in the 987 * future) 988 */ 989 proto = strchr(buff, '_'); 990 sectype = NULL; 991 if (proto != NULL) { 992 *proto++ = '\0'; 993 sectype = strchr(proto, '_'); 994 if (sectype != NULL) 995 *sectype++ = '\0'; 996 if (strcmp(proto, "default") == 0) 997 proto = NULL; 998 } 999 ret = sa_extract_pgroup(node, handle, 1000 handle->pg, "security", proto, sectype); 1001 } 1002 /* Ignore everything else */ 1003 } 1004 /* 1005 * Make sure we have a valid default group. 1006 * On first boot, default won't have any 1007 * protocols defined and won't be enabled (but 1008 * should be). "default" only has NFS enabled on it. 1009 */ 1010 if (is_default) { 1011 char *state = sa_get_group_attr((sa_group_t)node, 1012 "state"); 1013 1014 if (state == NULL) { 1015 /* set attribute to enabled */ 1016 (void) sa_set_group_attr((sa_group_t)node, 1017 "state", "enabled"); 1018 (void) sa_create_optionset((sa_group_t)node, 1019 "nfs"); 1020 } else { 1021 sa_free_attr_string(state); 1022 } 1023 } 1024 /* Do a second pass if shares were found */ 1025 if (have_shares && scf_iter_instance_pgs(iter, instance) == 0) { 1026 while (scf_iter_next_pg(iter, handle->pg) > 0) { 1027 /* 1028 * Have a pgroup so see if it is a 1029 * share optionset 1030 */ 1031 err = scf_pg_get_name(handle->pg, buff, 1032 scf_max_name_len); 1033 if (err <= 0) 1034 continue; 1035 if (buff[0] == SA_SHARE_PG_PREFIX[0]) { 1036 ret = sa_share_props_from_pgroup(node, 1037 handle, handle->pg, buff, 1038 sahandle); 1039 } 1040 } 1041 } 1042 } 1043 out: 1044 if (iter != NULL) 1045 scf_iter_destroy(iter); 1046 if (buff != NULL) 1047 free(buff); 1048 return (ret); 1049 } 1050 1051 /* 1052 * sa_extract_defaults(root, handle, instance) 1053 * 1054 * Local function to find the default properties that live in the 1055 * default instance's "operation" property group. 1056 */ 1057 1058 static void 1059 sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle, 1060 scf_instance_t *instance) 1061 { 1062 xmlNodePtr node; 1063 scf_property_t *prop; 1064 scf_value_t *value; 1065 char *valuestr; 1066 ssize_t vallen; 1067 1068 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1069 prop = scf_property_create(handle->handle); 1070 value = scf_value_create(handle->handle); 1071 valuestr = malloc(vallen); 1072 1073 if (prop == NULL || value == NULL || vallen == 0 || 1074 scf_instance_get_pg(instance, "operation", handle->pg) != 0) 1075 goto out; 1076 1077 if (scf_pg_get_property(handle->pg, "legacy-timestamp", prop) != 0) 1078 goto out; 1079 1080 /* Found the property so get the value */ 1081 if (scf_property_get_value(prop, value) == 0) { 1082 if (scf_value_get_astring(value, valuestr, vallen) > 0) { 1083 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", 1084 NULL); 1085 if (node != NULL) { 1086 (void) xmlSetProp(node, (xmlChar *)"timestamp", 1087 (xmlChar *)valuestr); 1088 (void) xmlSetProp(node, (xmlChar *)"path", 1089 (xmlChar *)SA_LEGACY_DFSTAB); 1090 } 1091 } 1092 } 1093 out: 1094 if (valuestr != NULL) 1095 free(valuestr); 1096 if (value != NULL) 1097 scf_value_destroy(value); 1098 if (prop != NULL) 1099 scf_property_destroy(prop); 1100 } 1101 1102 1103 /* 1104 * sa_get_config(handle, root, doc, sahandle) 1105 * 1106 * Walk the SMF repository for /network/shares/group and find all the 1107 * instances. These become group names. Then add the XML structure 1108 * below the groups based on property groups and properties. 1109 */ 1110 int 1111 sa_get_config(scfutilhandle_t *handle, xmlNodePtr root, sa_handle_t sahandle) 1112 { 1113 int ret = SA_OK; 1114 scf_instance_t *instance; 1115 scf_iter_t *iter; 1116 char buff[BUFSIZ * 2]; 1117 1118 instance = scf_instance_create(handle->handle); 1119 iter = scf_iter_create(handle->handle); 1120 if (instance != NULL && iter != NULL) { 1121 if ((ret = scf_iter_service_instances(iter, 1122 handle->service)) == 0) { 1123 while ((ret = scf_iter_next_instance(iter, 1124 instance)) > 0) { 1125 if (scf_instance_get_name(instance, buff, 1126 sizeof (buff)) > 0) { 1127 if (strcmp(buff, "default") == 0) 1128 sa_extract_defaults(root, 1129 handle, instance); 1130 ret = sa_extract_group(root, handle, 1131 instance, sahandle); 1132 } 1133 } 1134 } 1135 } 1136 1137 /* Always cleanup these */ 1138 if (instance != NULL) 1139 scf_instance_destroy(instance); 1140 if (iter != NULL) 1141 scf_iter_destroy(iter); 1142 return (ret); 1143 } 1144 1145 /* 1146 * sa_get_instance(handle, instance) 1147 * 1148 * Get the instance of the group service. This is actually the 1149 * specific group name. The instance is needed for all property and 1150 * control operations. 1151 */ 1152 1153 int 1154 sa_get_instance(scfutilhandle_t *handle, char *instname) 1155 { 1156 if (scf_service_get_instance(handle->service, instname, 1157 handle->instance) != 0) { 1158 return (SA_NO_SUCH_GROUP); 1159 } 1160 return (SA_OK); 1161 } 1162 1163 /* 1164 * sa_create_instance(handle, instname) 1165 * 1166 * Create a new SMF service instance. There can only be one with a 1167 * given name. 1168 */ 1169 1170 int 1171 sa_create_instance(scfutilhandle_t *handle, char *instname) 1172 { 1173 int ret = SA_OK; 1174 char instance[SA_GROUP_INST_LEN]; 1175 if (scf_service_add_instance(handle->service, instname, 1176 handle->instance) != 0) { 1177 /* better error returns need to be added based on real error */ 1178 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) 1179 ret = SA_NO_PERMISSION; 1180 else 1181 ret = SA_DUPLICATE_NAME; 1182 } else { 1183 /* have the service created, so enable it */ 1184 (void) snprintf(instance, sizeof (instance), "%s:%s", 1185 SA_SVC_FMRI_BASE, instname); 1186 (void) smf_enable_instance(instance, 0); 1187 } 1188 return (ret); 1189 } 1190 1191 /* 1192 * sa_delete_instance(handle, instname) 1193 * 1194 * When a group goes away, we also remove the service instance. 1195 */ 1196 1197 int 1198 sa_delete_instance(scfutilhandle_t *handle, char *instname) 1199 { 1200 int ret; 1201 1202 if (strcmp(instname, "default") == 0) { 1203 ret = SA_NO_PERMISSION; 1204 } else { 1205 if ((ret = sa_get_instance(handle, instname)) == SA_OK) { 1206 if (scf_instance_delete(handle->instance) != 0) 1207 /* need better analysis */ 1208 ret = SA_NO_PERMISSION; 1209 } 1210 } 1211 return (ret); 1212 } 1213 1214 /* 1215 * sa_create_pgroup(handle, pgroup) 1216 * 1217 * create a new property group 1218 */ 1219 1220 int 1221 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup) 1222 { 1223 int ret = SA_OK; 1224 int persist = 0; 1225 1226 /* 1227 * Only create a handle if it doesn't exist. It is ok to exist 1228 * since the pg handle will be set as a side effect. 1229 */ 1230 if (handle->pg == NULL) 1231 handle->pg = scf_pg_create(handle->handle); 1232 1233 /* 1234 * Special case for a non-persistent property group. This is 1235 * internal use only. 1236 */ 1237 if (*pgroup == '*') { 1238 persist = SCF_PG_FLAG_NONPERSISTENT; 1239 pgroup++; 1240 } 1241 1242 /* 1243 * If the pgroup exists, we are done. If it doesn't, then we 1244 * need to actually add one to the service instance. 1245 */ 1246 if (scf_instance_get_pg(handle->instance, 1247 pgroup, handle->pg) != 0) { 1248 1249 /* Doesn't exist so create one */ 1250 if (scf_instance_add_pg(handle->instance, pgroup, 1251 SCF_GROUP_APPLICATION, persist, handle->pg) != 0) { 1252 switch (scf_error()) { 1253 case SCF_ERROR_PERMISSION_DENIED: 1254 ret = SA_NO_PERMISSION; 1255 break; 1256 default: 1257 ret = SA_SYSTEM_ERR; 1258 break; 1259 } 1260 } 1261 } 1262 return (ret); 1263 } 1264 1265 /* 1266 * sa_delete_pgroup(handle, pgroup) 1267 * 1268 * Remove the property group from the current instance of the service, 1269 * but only if it actually exists. 1270 */ 1271 1272 int 1273 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup) 1274 { 1275 int ret = SA_OK; 1276 /* 1277 * Only delete if it does exist. 1278 */ 1279 if (scf_instance_get_pg(handle->instance, pgroup, handle->pg) == 0) { 1280 /* does exist so delete it */ 1281 if (scf_pg_delete(handle->pg) != 0) 1282 ret = SA_SYSTEM_ERR; 1283 } else { 1284 ret = SA_SYSTEM_ERR; 1285 } 1286 if (ret == SA_SYSTEM_ERR && 1287 scf_error() == SCF_ERROR_PERMISSION_DENIED) { 1288 ret = SA_NO_PERMISSION; 1289 } 1290 return (ret); 1291 } 1292 1293 /* 1294 * sa_start_transaction(handle, pgroup) 1295 * 1296 * Start an SMF transaction so we can deal with properties. it would 1297 * be nice to not have to expose this, but we have to in order to 1298 * optimize. 1299 * 1300 * Basic model is to hold the transaction in the handle and allow 1301 * property adds/deletes/updates to be added then close the 1302 * transaction (or abort). There may eventually be a need to handle 1303 * other types of transaction mechanisms but we don't do that now. 1304 * 1305 * An sa_start_transaction must be followed by either an 1306 * sa_end_transaction or sa_abort_transaction before another 1307 * sa_start_transaction can be done. 1308 */ 1309 1310 int 1311 sa_start_transaction(scfutilhandle_t *handle, char *propgroup) 1312 { 1313 int ret = SA_OK; 1314 /* 1315 * Lookup the property group and create it if it doesn't already 1316 * exist. 1317 */ 1318 if (handle == NULL) 1319 return (SA_CONFIG_ERR); 1320 1321 if (handle->scf_state == SCH_STATE_INIT) { 1322 ret = sa_create_pgroup(handle, propgroup); 1323 if (ret == SA_OK) { 1324 handle->trans = scf_transaction_create(handle->handle); 1325 if (handle->trans != NULL) { 1326 if (scf_transaction_start(handle->trans, 1327 handle->pg) != 0) { 1328 ret = SA_SYSTEM_ERR; 1329 } 1330 if (ret != SA_OK) { 1331 scf_transaction_destroy(handle->trans); 1332 handle->trans = NULL; 1333 } 1334 } else { 1335 ret = SA_SYSTEM_ERR; 1336 } 1337 } 1338 } 1339 if (ret == SA_SYSTEM_ERR && 1340 scf_error() == SCF_ERROR_PERMISSION_DENIED) { 1341 ret = SA_NO_PERMISSION; 1342 } 1343 return (ret); 1344 } 1345 1346 1347 /* 1348 * sa_end_transaction(scfhandle, sahandle) 1349 * 1350 * Commit the changes that were added to the transaction in the 1351 * handle. Do all necessary cleanup. 1352 */ 1353 1354 int 1355 sa_end_transaction(scfutilhandle_t *handle, sa_handle_impl_t sahandle) 1356 { 1357 int ret = SA_OK; 1358 1359 if (handle == NULL || handle->trans == NULL || sahandle == NULL) { 1360 ret = SA_SYSTEM_ERR; 1361 } else { 1362 if (scf_transaction_commit(handle->trans) < 0) 1363 ret = SA_SYSTEM_ERR; 1364 scf_transaction_destroy_children(handle->trans); 1365 scf_transaction_destroy(handle->trans); 1366 if (ret == SA_OK) 1367 set_transaction_tstamp(sahandle); 1368 handle->trans = NULL; 1369 } 1370 return (ret); 1371 } 1372 1373 /* 1374 * sa_abort_transaction(handle) 1375 * 1376 * Abort the changes that were added to the transaction in the 1377 * handle. Do all necessary cleanup. 1378 */ 1379 1380 void 1381 sa_abort_transaction(scfutilhandle_t *handle) 1382 { 1383 if (handle->trans != NULL) { 1384 scf_transaction_reset_all(handle->trans); 1385 scf_transaction_destroy_children(handle->trans); 1386 scf_transaction_destroy(handle->trans); 1387 handle->trans = NULL; 1388 } 1389 } 1390 1391 /* 1392 * set_transaction_tstamp(sahandle) 1393 * 1394 * After a successful transaction commit, update the timestamp of the 1395 * last transaction. This lets us detect changes from other processes. 1396 */ 1397 static void 1398 set_transaction_tstamp(sa_handle_impl_t sahandle) 1399 { 1400 char tstring[32]; 1401 struct timeval tv; 1402 scfutilhandle_t *scfhandle; 1403 1404 if (sahandle == NULL || sahandle->scfhandle == NULL) 1405 return; 1406 1407 scfhandle = sahandle->scfhandle; 1408 1409 if (sa_get_instance(scfhandle, "default") != SA_OK) 1410 return; 1411 1412 if (gettimeofday(&tv, NULL) != 0) 1413 return; 1414 1415 if (sa_start_transaction(scfhandle, "*state") != SA_OK) 1416 return; 1417 1418 sahandle->tstrans = TSTAMP((*(timestruc_t *)&tv)); 1419 (void) snprintf(tstring, sizeof (tstring), "%lld", sahandle->tstrans); 1420 if (sa_set_property(sahandle->scfhandle, "lastupdate", tstring) == 1421 SA_OK) { 1422 /* 1423 * While best if it succeeds, a failure doesn't cause 1424 * problems and we will ignore it anyway. 1425 */ 1426 (void) scf_transaction_commit(scfhandle->trans); 1427 scf_transaction_destroy_children(scfhandle->trans); 1428 scf_transaction_destroy(scfhandle->trans); 1429 } else { 1430 sa_abort_transaction(scfhandle); 1431 } 1432 } 1433 1434 /* 1435 * sa_set_property(handle, prop, value) 1436 * 1437 * Set a property transaction entry into the pending SMF transaction. 1438 */ 1439 1440 int 1441 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr) 1442 { 1443 int ret = SA_OK; 1444 scf_value_t *value; 1445 scf_transaction_entry_t *entry; 1446 /* 1447 * Properties must be set in transactions and don't take 1448 * effect until the transaction has been ended/committed. 1449 */ 1450 value = scf_value_create(handle->handle); 1451 entry = scf_entry_create(handle->handle); 1452 if (value != NULL && entry != NULL) { 1453 if (scf_transaction_property_change(handle->trans, entry, 1454 propname, SCF_TYPE_ASTRING) == 0 || 1455 scf_transaction_property_new(handle->trans, entry, 1456 propname, SCF_TYPE_ASTRING) == 0) { 1457 if (scf_value_set_astring(value, valstr) == 0) { 1458 if (scf_entry_add_value(entry, value) != 0) { 1459 ret = SA_SYSTEM_ERR; 1460 scf_value_destroy(value); 1461 } 1462 /* The value is in the transaction */ 1463 value = NULL; 1464 } else { 1465 /* Value couldn't be constructed */ 1466 ret = SA_SYSTEM_ERR; 1467 } 1468 /* The entry is in the transaction */ 1469 entry = NULL; 1470 } else { 1471 ret = SA_SYSTEM_ERR; 1472 } 1473 } else { 1474 ret = SA_SYSTEM_ERR; 1475 } 1476 if (ret == SA_SYSTEM_ERR) { 1477 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { 1478 ret = SA_NO_PERMISSION; 1479 } 1480 } 1481 /* 1482 * Cleanup if there were any errors that didn't leave these 1483 * values where they would be cleaned up later. 1484 */ 1485 if (value != NULL) 1486 scf_value_destroy(value); 1487 if (entry != NULL) 1488 scf_entry_destroy(entry); 1489 return (ret); 1490 } 1491 1492 /* 1493 * check_resource(share) 1494 * 1495 * Check to see if share has any persistent resources. We don't want 1496 * to save if they are all transient. 1497 */ 1498 static int 1499 check_resource(sa_share_t share) 1500 { 1501 sa_resource_t resource; 1502 int ret = B_FALSE; 1503 1504 for (resource = sa_get_share_resource(share, NULL); 1505 resource != NULL && ret == B_FALSE; 1506 resource = sa_get_next_resource(resource)) { 1507 char *type; 1508 type = sa_get_resource_attr(resource, "type"); 1509 if (type != NULL) { 1510 if (strcmp(type, "transient") != 0) { 1511 ret = B_TRUE; 1512 } 1513 sa_free_attr_string(type); 1514 } 1515 } 1516 return (ret); 1517 } 1518 1519 /* 1520 * sa_set_resource_property(handle, prop, value) 1521 * 1522 * set a property transaction entry into the pending SMF 1523 * transaction. We don't want to include any transient resources 1524 */ 1525 1526 static int 1527 sa_set_resource_property(scfutilhandle_t *handle, sa_share_t share) 1528 { 1529 int ret = SA_OK; 1530 scf_value_t *value; 1531 scf_transaction_entry_t *entry; 1532 sa_resource_t resource; 1533 char *valstr = NULL; 1534 char *idstr = NULL; 1535 char *description = NULL; 1536 char *propstr = NULL; 1537 size_t strsize; 1538 1539 /* don't bother if no persistent resources */ 1540 if (check_resource(share) == B_FALSE) 1541 return (ret); 1542 1543 /* 1544 * properties must be set in transactions and don't take 1545 * effect until the transaction has been ended/committed. 1546 */ 1547 entry = scf_entry_create(handle->handle); 1548 if (entry == NULL) 1549 return (SA_SYSTEM_ERR); 1550 1551 if (scf_transaction_property_change(handle->trans, entry, 1552 "resource", SCF_TYPE_ASTRING) != 0 && 1553 scf_transaction_property_new(handle->trans, entry, 1554 "resource", SCF_TYPE_ASTRING) != 0) { 1555 scf_entry_destroy(entry); 1556 return (SA_SYSTEM_ERR); 1557 1558 } 1559 for (resource = sa_get_share_resource(share, NULL); 1560 resource != NULL; 1561 resource = sa_get_next_resource(resource)) { 1562 value = scf_value_create(handle->handle); 1563 if (value == NULL) { 1564 ret = SA_NO_MEMORY; 1565 break; 1566 } 1567 /* Get size of complete string */ 1568 valstr = sa_get_resource_attr(resource, "name"); 1569 idstr = sa_get_resource_attr(resource, "id"); 1570 description = sa_get_resource_description(resource); 1571 strsize = (valstr != NULL) ? strlen(valstr) : 0; 1572 strsize += (idstr != NULL) ? strlen(idstr) : 0; 1573 strsize += (description != NULL) ? strlen(description) : 0; 1574 if (strsize > 0) { 1575 strsize += 3; /* add nul and ':' */ 1576 propstr = (char *)malloc(strsize); 1577 if (propstr == NULL) { 1578 scf_value_destroy(value); 1579 ret = SA_NO_MEMORY; 1580 goto err; 1581 } 1582 if (idstr == NULL) 1583 (void) snprintf(propstr, strsize, "%s", 1584 valstr ? valstr : ""); 1585 else 1586 (void) snprintf(propstr, strsize, "%s:%s:%s", 1587 idstr, valstr ? valstr : "", 1588 description ? description : ""); 1589 if (scf_value_set_astring(value, propstr) != 0) { 1590 ret = SA_SYSTEM_ERR; 1591 free(propstr); 1592 scf_value_destroy(value); 1593 break; 1594 } 1595 if (scf_entry_add_value(entry, value) != 0) { 1596 ret = SA_SYSTEM_ERR; 1597 free(propstr); 1598 scf_value_destroy(value); 1599 break; 1600 } 1601 /* the value is in the transaction */ 1602 value = NULL; 1603 free(propstr); 1604 } 1605 err: 1606 if (valstr != NULL) { 1607 sa_free_attr_string(valstr); 1608 valstr = NULL; 1609 } 1610 if (idstr != NULL) { 1611 sa_free_attr_string(idstr); 1612 idstr = NULL; 1613 } 1614 if (description != NULL) { 1615 sa_free_share_description(description); 1616 description = NULL; 1617 } 1618 } 1619 /* the entry is in the transaction */ 1620 entry = NULL; 1621 1622 if (valstr != NULL) 1623 sa_free_attr_string(valstr); 1624 if (idstr != NULL) 1625 sa_free_attr_string(idstr); 1626 if (description != NULL) 1627 sa_free_share_description(description); 1628 1629 if (ret == SA_SYSTEM_ERR) { 1630 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) { 1631 ret = SA_NO_PERMISSION; 1632 } 1633 } 1634 /* 1635 * cleanup if there were any errors that didn't leave 1636 * these values where they would be cleaned up later. 1637 */ 1638 if (entry != NULL) 1639 scf_entry_destroy(entry); 1640 1641 return (ret); 1642 } 1643 1644 /* 1645 * sa_commit_share(handle, group, share) 1646 * 1647 * Commit this share to the repository. 1648 * properties are added if they exist but can be added later. 1649 * Need to add to dfstab and sharetab, if appropriate. 1650 */ 1651 int 1652 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) 1653 { 1654 int ret = SA_OK; 1655 char *groupname; 1656 char *name; 1657 char *description; 1658 char *sharename; 1659 ssize_t proplen; 1660 char *propstring; 1661 1662 /* 1663 * Don't commit in the zfs group. We do commit legacy 1664 * (default) and all other groups/shares. ZFS is handled 1665 * through the ZFS configuration rather than SMF. 1666 */ 1667 1668 groupname = sa_get_group_attr(group, "name"); 1669 if (groupname != NULL) { 1670 if (strcmp(groupname, "zfs") == 0) { 1671 /* 1672 * Adding to the ZFS group will result in the sharenfs 1673 * property being set but we don't want to do anything 1674 * SMF related at this point. 1675 */ 1676 sa_free_attr_string(groupname); 1677 return (ret); 1678 } 1679 } 1680 1681 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1682 propstring = malloc(proplen); 1683 if (propstring == NULL) 1684 ret = SA_NO_MEMORY; 1685 1686 if (groupname != NULL && ret == SA_OK) { 1687 ret = sa_get_instance(handle, groupname); 1688 sa_free_attr_string(groupname); 1689 groupname = NULL; 1690 sharename = sa_get_share_attr(share, "id"); 1691 if (sharename == NULL) { 1692 /* slipped by */ 1693 char shname[SA_SHARE_UUID_BUFLEN]; 1694 generate_unique_sharename(shname); 1695 (void) xmlSetProp((xmlNodePtr)share, (xmlChar *)"id", 1696 (xmlChar *)shname); 1697 sharename = strdup(shname); 1698 } 1699 if (sharename != NULL) { 1700 sigset_t old, new; 1701 /* 1702 * Have a share name allocated so create a pgroup for 1703 * it. It may already exist, but that is OK. In order 1704 * to avoid creating a share pgroup that doesn't have 1705 * a path property, block signals around the critical 1706 * region of creating the share pgroup and props. 1707 */ 1708 (void) sigprocmask(SIG_BLOCK, NULL, &new); 1709 (void) sigaddset(&new, SIGHUP); 1710 (void) sigaddset(&new, SIGINT); 1711 (void) sigaddset(&new, SIGQUIT); 1712 (void) sigaddset(&new, SIGTSTP); 1713 (void) sigprocmask(SIG_SETMASK, &new, &old); 1714 1715 ret = sa_create_pgroup(handle, sharename); 1716 if (ret == SA_OK) { 1717 /* 1718 * Now start the transaction for the 1719 * properties that define this share. They may 1720 * exist so attempt to update before create. 1721 */ 1722 ret = sa_start_transaction(handle, sharename); 1723 } 1724 if (ret == SA_OK) { 1725 name = sa_get_share_attr(share, "path"); 1726 if (name != NULL) { 1727 /* 1728 * There needs to be a path 1729 * for a share to exist. 1730 */ 1731 ret = sa_set_property(handle, "path", 1732 name); 1733 sa_free_attr_string(name); 1734 } else { 1735 ret = SA_NO_MEMORY; 1736 } 1737 } 1738 if (ret == SA_OK) { 1739 name = sa_get_share_attr(share, "drive-letter"); 1740 if (name != NULL) { 1741 /* A drive letter may exist for SMB */ 1742 ret = sa_set_property(handle, 1743 "drive-letter", name); 1744 sa_free_attr_string(name); 1745 } 1746 } 1747 if (ret == SA_OK) { 1748 name = sa_get_share_attr(share, "exclude"); 1749 if (name != NULL) { 1750 /* 1751 * In special cases need to 1752 * exclude proto enable. 1753 */ 1754 ret = sa_set_property(handle, 1755 "exclude", name); 1756 sa_free_attr_string(name); 1757 } 1758 } 1759 if (ret == SA_OK) { 1760 /* 1761 * If there are resource names, bundle them up 1762 * and save appropriately. 1763 */ 1764 ret = sa_set_resource_property(handle, share); 1765 } 1766 1767 if (ret == SA_OK) { 1768 description = sa_get_share_description(share); 1769 if (description != NULL) { 1770 ret = sa_set_property(handle, 1771 "description", 1772 description); 1773 sa_free_share_description(description); 1774 } 1775 } 1776 /* Make sure we cleanup the transaction */ 1777 if (ret == SA_OK) { 1778 sa_handle_impl_t sahandle; 1779 sahandle = (sa_handle_impl_t) 1780 sa_find_group_handle(group); 1781 if (sahandle != NULL) 1782 ret = sa_end_transaction(handle, 1783 sahandle); 1784 else 1785 ret = SA_SYSTEM_ERR; 1786 } else { 1787 sa_abort_transaction(handle); 1788 } 1789 1790 (void) sigprocmask(SIG_SETMASK, &old, NULL); 1791 1792 free(sharename); 1793 } 1794 } 1795 if (ret == SA_SYSTEM_ERR) { 1796 int err = scf_error(); 1797 if (err == SCF_ERROR_PERMISSION_DENIED) 1798 ret = SA_NO_PERMISSION; 1799 } 1800 if (propstring != NULL) 1801 free(propstring); 1802 if (groupname != NULL) 1803 sa_free_attr_string(groupname); 1804 1805 return (ret); 1806 } 1807 1808 /* 1809 * remove_resources(handle, share, shareid) 1810 * 1811 * If the share has resources, remove all of them and their 1812 * optionsets. 1813 */ 1814 static int 1815 remove_resources(scfutilhandle_t *handle, sa_share_t share, char *shareid) 1816 { 1817 sa_resource_t resource; 1818 sa_optionset_t opt; 1819 char *proto; 1820 char *id; 1821 ssize_t proplen; 1822 char *propstring; 1823 int ret = SA_OK; 1824 1825 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1826 propstring = malloc(proplen); 1827 if (propstring == NULL) 1828 return (SA_NO_MEMORY); 1829 1830 for (resource = sa_get_share_resource(share, NULL); 1831 resource != NULL; resource = sa_get_next_resource(resource)) { 1832 id = sa_get_resource_attr(resource, "id"); 1833 if (id == NULL) 1834 continue; 1835 for (opt = sa_get_optionset(resource, NULL); 1836 opt != NULL; opt = sa_get_next_optionset(resource)) { 1837 proto = sa_get_optionset_attr(opt, "type"); 1838 if (proto != NULL) { 1839 (void) snprintf(propstring, proplen, 1840 "%s_%s_%s", shareid, proto, id); 1841 ret = sa_delete_pgroup(handle, propstring); 1842 sa_free_attr_string(proto); 1843 } 1844 } 1845 sa_free_attr_string(id); 1846 } 1847 free(propstring); 1848 return (ret); 1849 } 1850 1851 /* 1852 * sa_delete_share(handle, group, share) 1853 * 1854 * Remove the specified share from the group (and service instance). 1855 */ 1856 1857 int 1858 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) 1859 { 1860 int ret = SA_OK; 1861 char *groupname = NULL; 1862 char *shareid = NULL; 1863 sa_optionset_t opt; 1864 sa_security_t sec; 1865 ssize_t proplen; 1866 char *propstring; 1867 1868 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1869 propstring = malloc(proplen); 1870 if (propstring == NULL) 1871 ret = SA_NO_MEMORY; 1872 1873 if (ret == SA_OK) { 1874 groupname = sa_get_group_attr(group, "name"); 1875 shareid = sa_get_share_attr(share, "id"); 1876 if (groupname == NULL || shareid == NULL) { 1877 ret = SA_CONFIG_ERR; 1878 goto out; 1879 } 1880 ret = sa_get_instance(handle, groupname); 1881 if (ret == SA_OK) { 1882 /* If a share has resources, remove them */ 1883 ret = remove_resources(handle, share, shareid); 1884 /* If a share has properties, remove them */ 1885 ret = sa_delete_pgroup(handle, shareid); 1886 for (opt = sa_get_optionset(share, NULL); 1887 opt != NULL; 1888 opt = sa_get_next_optionset(opt)) { 1889 char *proto; 1890 proto = sa_get_optionset_attr(opt, "type"); 1891 if (proto != NULL) { 1892 (void) snprintf(propstring, 1893 proplen, "%s_%s", shareid, 1894 proto); 1895 ret = sa_delete_pgroup(handle, 1896 propstring); 1897 sa_free_attr_string(proto); 1898 } else { 1899 ret = SA_NO_MEMORY; 1900 } 1901 } 1902 /* 1903 * If a share has security/negotiable 1904 * properties, remove them. 1905 */ 1906 for (sec = sa_get_security(share, NULL, NULL); 1907 sec != NULL; 1908 sec = sa_get_next_security(sec)) { 1909 char *proto; 1910 char *sectype; 1911 proto = sa_get_security_attr(sec, "type"); 1912 sectype = sa_get_security_attr(sec, "sectype"); 1913 if (proto != NULL && sectype != NULL) { 1914 (void) snprintf(propstring, proplen, 1915 "%s_%s_%s", shareid, proto, 1916 sectype); 1917 ret = sa_delete_pgroup(handle, 1918 propstring); 1919 } else { 1920 ret = SA_NO_MEMORY; 1921 } 1922 if (proto != NULL) 1923 sa_free_attr_string(proto); 1924 if (sectype != NULL) 1925 sa_free_attr_string(sectype); 1926 } 1927 } 1928 } 1929 out: 1930 if (groupname != NULL) 1931 sa_free_attr_string(groupname); 1932 if (shareid != NULL) 1933 sa_free_attr_string(shareid); 1934 if (propstring != NULL) 1935 free(propstring); 1936 1937 return (ret); 1938 } 1939