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