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