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 2006 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 <errno.h> 39 #include <uuid/uuid.h> 40 #include <sys/param.h> 41 42 ssize_t scf_max_name_len; 43 extern struct sa_proto_plugin *sap_proto_list; 44 45 /* 46 * The SMF facility uses some properties that must exist. We want to 47 * skip over these when processing protocol options. 48 */ 49 static char *skip_props[] = { 50 "modify_authorization", 51 "action_authorization", 52 "value_authorization", 53 NULL 54 }; 55 56 /* 57 * sa_scf_fini(handle) 58 * 59 * must be called when done. Called with the handle allocated in 60 * sa_scf_init(), it cleans up the state and frees any SCF resources 61 * still in use. Called by sa_fini(). 62 */ 63 64 void 65 sa_scf_fini(scfutilhandle_t *handle) 66 { 67 if (handle != NULL) { 68 int unbind = 0; 69 if (handle->scope != NULL) { 70 unbind = 1; 71 scf_scope_destroy(handle->scope); 72 } 73 if (handle->service != NULL) 74 scf_service_destroy(handle->service); 75 if (handle->pg != NULL) 76 scf_pg_destroy(handle->pg); 77 if (handle->handle != NULL) { 78 handle->scf_state = SCH_STATE_UNINIT; 79 if (unbind) 80 (void) scf_handle_unbind(handle->handle); 81 scf_handle_destroy(handle->handle); 82 } 83 free(handle); 84 } 85 } 86 87 /* 88 * sa_scf_init() 89 * 90 * must be called before using any of the SCF functions. Called by 91 * sa_init() during the API setup. 92 */ 93 94 scfutilhandle_t * 95 sa_scf_init() 96 { 97 scfutilhandle_t *handle; 98 99 scf_max_name_len = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH); 100 if (scf_max_name_len <= 0) 101 scf_max_name_len = SA_MAX_NAME_LEN + 1; 102 103 handle = calloc(1, sizeof (scfutilhandle_t)); 104 if (handle != NULL) { 105 handle->scf_state = SCH_STATE_INITIALIZING; 106 handle->handle = scf_handle_create(SCF_VERSION); 107 if (handle->handle != NULL) { 108 if (scf_handle_bind(handle->handle) == 0) { 109 handle->scope = scf_scope_create(handle->handle); 110 handle->service = scf_service_create(handle->handle); 111 handle->pg = scf_pg_create(handle->handle); 112 handle->instance = scf_instance_create(handle->handle); 113 if (scf_handle_get_scope(handle->handle, 114 SCF_SCOPE_LOCAL, handle->scope) == 0) { 115 if (scf_scope_get_service(handle->scope, 116 SA_GROUP_SVC_NAME, 117 handle->service) != 0) { 118 goto err; 119 } 120 handle->scf_state = SCH_STATE_INIT; 121 if (sa_get_instance(handle, "default") != SA_OK) { 122 char **protolist; 123 int numprotos, i; 124 sa_group_t defgrp; 125 defgrp = sa_create_group("default", NULL); 126 if (defgrp != NULL) { 127 numprotos = sa_get_protocols(&protolist); 128 for (i = 0; i < numprotos; i++) { 129 (void) sa_create_optionset(defgrp, 130 protolist[i]); 131 } 132 if (protolist != NULL) 133 free(protolist); 134 } 135 } 136 } else { 137 goto err; 138 } 139 } else { 140 goto err; 141 } 142 } else { 143 free(handle); 144 handle = NULL; 145 (void) printf("libshare could not access SMF repository: %s\n", 146 scf_strerror(scf_error())); 147 } 148 } 149 return (handle); 150 151 /* error handling/unwinding */ 152 err: 153 (void) sa_scf_fini(handle); 154 (void) printf("libshare SMF initialization problem: %s\n", 155 scf_strerror(scf_error())); 156 return (NULL); 157 } 158 159 /* 160 * get_scf_limit(name) 161 * 162 * Since we use scf_limit a lot and do the same check and return the 163 * same value if it fails, implement as a function for code 164 * simplification. Basically, if name isn't found, return MAXPATHLEN 165 * (1024) so we have a reasonable default buffer size. 166 */ 167 static ssize_t 168 get_scf_limit(uint32_t name) 169 { 170 ssize_t vallen; 171 172 vallen = scf_limit(name); 173 if (vallen == (ssize_t)-1) 174 vallen = MAXPATHLEN; 175 return (vallen); 176 } 177 178 /* 179 * skip_property(name) 180 * 181 * internal function to check to see if a property is an SMF magic 182 * property that needs to be skipped. 183 */ 184 static int 185 skip_property(char *name) 186 { 187 int i; 188 189 for (i = 0; skip_props[i] != NULL; i++) 190 if (strcmp(name, skip_props[i]) == 0) 191 return (1); 192 return (0); 193 } 194 195 /* 196 * generate_unique_sharename(sharename) 197 * 198 * Shares are represented in SMF as property groups. Due to share 199 * paths containing characters that are not allowed in SMF names and 200 * the need to be unique, we use UUIDs to construct a unique name. 201 */ 202 203 static void 204 generate_unique_sharename(char *sharename) 205 { 206 uuid_t uuid; 207 208 uuid_generate(uuid); 209 (void) strcpy(sharename, "S-"); 210 uuid_unparse(uuid, sharename + 2); 211 } 212 213 /* 214 * valid_protocol(proto) 215 * 216 * check to see if the specified protocol is a valid one for the 217 * general sharemgr facility. We determine this by checking which 218 * plugin protocols were found. 219 */ 220 221 static int 222 valid_protocol(char *proto) 223 { 224 struct sa_proto_plugin *plugin; 225 for (plugin = sap_proto_list; plugin != NULL; 226 plugin = plugin->plugin_next) 227 if (strcmp(proto, plugin->plugin_ops->sa_protocol) == 0) 228 return (1); 229 return (0); 230 } 231 232 /* 233 * sa_extract_pgroup(root, handle, pg, nodetype, proto, sectype) 234 * 235 * extract the name property group and create the specified type of 236 * node on the provided group. type will be optionset or security. 237 */ 238 239 static int 240 sa_extract_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 241 scf_propertygroup_t *pg, 242 char *nodetype, char *proto, char *sectype) 243 { 244 xmlNodePtr node; 245 scf_iter_t *iter; 246 scf_property_t *prop; 247 scf_value_t *value; 248 char *name; 249 char *valuestr; 250 ssize_t vallen; 251 int ret = SA_OK; 252 253 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 254 255 node = xmlNewChild(root, NULL, (xmlChar *)nodetype, NULL); 256 if (node != NULL) { 257 if (proto != NULL) 258 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 259 if (sectype != NULL) 260 xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype); 261 /* 262 * have node to work with so iterate over the properties 263 * in the pg and create option sub nodes. 264 */ 265 iter = scf_iter_create(handle->handle); 266 value = scf_value_create(handle->handle); 267 prop = scf_property_create(handle->handle); 268 name = malloc(scf_max_name_len); 269 valuestr = malloc(vallen); 270 /* 271 * want to iterate through the properties and add them 272 * to the base optionset. 273 */ 274 if (iter != NULL && value != NULL && prop != NULL && 275 valuestr != NULL && name != NULL) { 276 if (scf_iter_pg_properties(iter, pg) == 0) { 277 /* now iterate the properties in the group */ 278 while (scf_iter_next_property(iter, prop) > 0) { 279 /* have a property */ 280 if (scf_property_get_name(prop, name, 281 scf_max_name_len) > 0) { 282 /* some properties are part of the framework */ 283 if (skip_property(name)) 284 continue; 285 if (scf_property_get_value(prop, value) == 0) { 286 if (scf_value_get_astring(value, valuestr, 287 vallen) >= 0) { 288 sa_property_t saprop; 289 saprop = sa_create_property(name, 290 valuestr); 291 if (saprop != NULL) { 292 /* 293 * since in SMF, don't 294 * recurse. Use xmlAddChild 295 * directly, instead. 296 */ 297 xmlAddChild(node, 298 (xmlNodePtr) saprop); 299 } 300 } 301 } 302 } 303 } 304 } 305 } else { 306 ret = SA_NO_MEMORY; 307 } 308 /* cleanup to avoid memory leaks */ 309 if (value != NULL) 310 scf_value_destroy(value); 311 if (iter != NULL) 312 scf_iter_destroy(iter); 313 if (prop != NULL) 314 scf_property_destroy(prop); 315 if (name != NULL) 316 free(name); 317 if (valuestr != NULL) 318 free(valuestr); 319 } 320 return (ret); 321 } 322 323 /* 324 * sa_extract_attrs(root, handle, instance) 325 * 326 * local function to extract the actual attributes/properties from the 327 * property group of the service instance. These are the well known 328 * attributes of "state" and "zfs". If additional attributes are 329 * added, they should be added here. 330 */ 331 332 static void 333 sa_extract_attrs(xmlNodePtr root, scfutilhandle_t *handle, 334 scf_instance_t *instance) 335 { 336 scf_property_t *prop; 337 scf_value_t *value; 338 char *valuestr; 339 ssize_t vallen; 340 341 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 342 prop = scf_property_create(handle->handle); 343 value = scf_value_create(handle->handle); 344 valuestr = malloc(vallen); 345 if (prop != NULL && value != NULL && valuestr != NULL && 346 scf_instance_get_pg(instance, "operation", 347 handle->pg) == 0) { 348 /* 349 * have a property group with desired name so now get 350 * the known attributes. 351 */ 352 if (scf_pg_get_property(handle->pg, "state", prop) == 0) { 353 /* found the property so get the value */ 354 if (scf_property_get_value(prop, value) == 0) { 355 if (scf_value_get_astring(value, valuestr, vallen) >= 0) { 356 xmlSetProp(root, (xmlChar *)"state", 357 (xmlChar *)valuestr); 358 } 359 } 360 } 361 if (scf_pg_get_property(handle->pg, "zfs", prop) == 0) { 362 /* found the property so get the value */ 363 if (scf_property_get_value(prop, value) == 0) { 364 if (scf_value_get_astring(value, valuestr, vallen) > 0) { 365 xmlSetProp(root, (xmlChar *)"zfs", 366 (xmlChar *)valuestr); 367 } 368 } 369 } 370 } 371 if (valuestr != NULL) 372 free(valuestr); 373 if (value != NULL) 374 scf_value_destroy(value); 375 if (prop != NULL) 376 scf_property_destroy(prop); 377 } 378 379 /* 380 * list of known share attributes. 381 */ 382 383 static char *share_attr[] = { 384 "path", 385 "id", 386 "resource", 387 NULL, 388 }; 389 390 static int 391 is_share_attr(char *name) 392 { 393 int i; 394 for (i = 0; share_attr[i] != NULL; i++) 395 if (strcmp(name, share_attr[i]) == 0) 396 return (1); 397 return (0); 398 } 399 400 /* 401 * sa_share_from_pgroup 402 * 403 * extract the share definition from the share property group. We do 404 * some sanity checking to avoid bad data. 405 * 406 * Since this is only constructing the internal data structures, we 407 * don't use the sa_* functions most of the time. 408 */ 409 void 410 sa_share_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 411 scf_propertygroup_t *pg, char *id) 412 { 413 xmlNodePtr node; 414 char *name; 415 scf_iter_t *iter; 416 scf_property_t *prop; 417 scf_value_t *value; 418 ssize_t vallen; 419 char *valuestr; 420 int ret = SA_OK; 421 422 /* 423 * While preliminary check (starts with 'S') passed before 424 * getting here. Need to make sure it is in ID syntax 425 * (Snnnnnn). Note that shares with properties have similar 426 * pgroups. 427 */ 428 vallen = strlen(id); 429 if (*id == SA_SHARE_PG_PREFIX[0] && vallen == SA_SHARE_PG_LEN) { 430 uuid_t uuid; 431 if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) != 0 || 432 uuid_parse(id + 2, uuid) < 0) 433 return; 434 } else { 435 return; 436 } 437 438 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 439 440 iter = scf_iter_create(handle->handle); 441 value = scf_value_create(handle->handle); 442 prop = scf_property_create(handle->handle); 443 name = malloc(scf_max_name_len); 444 valuestr = malloc(vallen); 445 446 /* 447 * construct the share XML node. It is similar to sa_add_share 448 * but never changes the repository. Also, there won't be any 449 * ZFS or transient shares. Root will be the group it is 450 * associated with. 451 */ 452 node = xmlNewChild(root, NULL, (xmlChar *)"share", NULL); 453 if (node != NULL) { 454 /* 455 * make sure the UUID part of the property group is 456 * stored in the share "id" property. We use this 457 * later. 458 */ 459 xmlSetProp(node, (xmlChar *)"id", (xmlChar *)id); 460 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)"persist"); 461 } 462 463 if (iter != NULL && value != NULL && prop != NULL && name != NULL) { 464 /* iterate over the share pg properties */ 465 if (scf_iter_pg_properties(iter, pg) == 0) { 466 while (scf_iter_next_property(iter, prop) > 0) { 467 ret = SA_SYSTEM_ERR; /* assume the worst */ 468 if (scf_property_get_name(prop, name, 469 scf_max_name_len) > 0) { 470 if (scf_property_get_value(prop, value) == 0) { 471 if (scf_value_get_astring(value, valuestr, 472 vallen) >= 0) { 473 ret = SA_OK; 474 } 475 } 476 } 477 if (ret == SA_OK) { 478 if (is_share_attr(name)) { 479 /* 480 * if a share attr, then simple - 481 * usually path and resource name 482 */ 483 xmlSetProp(node, (xmlChar *)name, 484 (xmlChar *)valuestr); 485 } else { 486 if (strcmp(name, "description") == 0) { 487 /* we have a description node */ 488 xmlNodePtr desc; 489 desc = xmlNewChild(node, NULL, 490 (xmlChar *)"description", 491 NULL); 492 if (desc != NULL) 493 xmlNodeSetContent(desc, 494 (xmlChar *)valuestr); 495 } 496 } 497 } 498 } 499 } 500 } 501 if (name != NULL) 502 free(name); 503 if (valuestr != NULL) 504 free(valuestr); 505 if (value != NULL) 506 scf_value_destroy(value); 507 if (iter != NULL) 508 scf_iter_destroy(iter); 509 if (prop != NULL) 510 scf_property_destroy(prop); 511 } 512 513 /* 514 * find_share_by_id(shareid) 515 * 516 * Search all shares in all groups until we find the share represented 517 * by "id". 518 */ 519 520 static sa_share_t 521 find_share_by_id(char *shareid) 522 { 523 sa_group_t group; 524 sa_share_t share = NULL; 525 char *id = NULL; 526 int done = 0; 527 528 for (group = sa_get_group(NULL); group != NULL && !done; 529 group = sa_get_next_group(group)) { 530 for (share = sa_get_share(group, NULL); share != NULL; 531 share = sa_get_next_share(share)) { 532 id = sa_get_share_attr(share, "id"); 533 if (id != NULL && strcmp(id, shareid) == 0) { 534 sa_free_attr_string(id); 535 id = NULL; 536 done++; 537 break; 538 } 539 if (id != NULL) { 540 sa_free_attr_string(id); 541 id = NULL; 542 } 543 } 544 } 545 return (share); 546 } 547 548 /* 549 * sa_share_props_from_pgroup(root, handle, pg, id) 550 * 551 * extract share properties from the SMF property group. More sanity 552 * checks are done and the share object is created. We ignore some 553 * errors that could exist in the repository and only worry about 554 * property groups that validate in naming. 555 */ 556 557 static int 558 sa_share_props_from_pgroup(xmlNodePtr root, scfutilhandle_t *handle, 559 scf_propertygroup_t *pg, char *id) 560 { 561 xmlNodePtr node; 562 char *name; 563 scf_iter_t *iter; 564 scf_property_t *prop; 565 scf_value_t *value; 566 ssize_t vallen; 567 char *valuestr; 568 int ret = SA_OK; 569 char *sectype = NULL; 570 char *proto; 571 sa_share_t share; 572 573 /* 574 * While preliminary check (starts with 'S') passed before 575 * getting here. Need to make sure it is in ID syntax 576 * (Snnnnnn). Note that shares with properties have similar 577 * pgroups. If the pg name is more than SA_SHARE_PG_LEN 578 * characters, it is likely one of the protocol/security 579 * versions. 580 */ 581 vallen = strlen(id); 582 if (*id == SA_SHARE_PG_PREFIX[0] && vallen > SA_SHARE_PG_LEN) { 583 uuid_t uuid; 584 if (strncmp(id, SA_SHARE_PG_PREFIX, SA_SHARE_PG_PREFIXLEN) == 0) { 585 proto = strchr(id, '_'); 586 if (proto == NULL) 587 return (ret); 588 *proto++ = '\0'; 589 if (uuid_parse(id + SA_SHARE_PG_PREFIXLEN, uuid) < 0) 590 return (ret); 591 /* 592 * probably a legal optionset so check a few more 593 * syntax points below. 594 */ 595 if (*proto == '\0') { 596 /* not a valid proto (null) */ 597 return (ret); 598 } 599 sectype = strchr(proto, '_'); 600 if (sectype != NULL) 601 *sectype++ = '\0'; 602 if (!valid_protocol(proto)) 603 return (ret); 604 } 605 } else { 606 /* 607 * it is ok to not have what we thought since someone might 608 * have added a name via SMF. 609 */ 610 return (ret); 611 } 612 613 /* 614 * to get here, we have a valid protocol and possibly a 615 * security. We now have to find the share that it is really 616 * associated with. The "id" portion of the pgroup name will 617 * match. 618 */ 619 620 share = find_share_by_id(id); 621 if (share == NULL) 622 return (SA_BAD_PATH); 623 624 root = (xmlNodePtr)share; 625 626 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 627 628 iter = scf_iter_create(handle->handle); 629 value = scf_value_create(handle->handle); 630 prop = scf_property_create(handle->handle); 631 name = malloc(scf_max_name_len); 632 valuestr = malloc(vallen); 633 634 if (sectype == NULL) 635 node = xmlNewChild(root, NULL, (xmlChar *)"optionset", NULL); 636 else { 637 node = xmlNewChild(root, NULL, (xmlChar *)"security", NULL); 638 if (node != NULL) 639 xmlSetProp(node, (xmlChar *)"sectype", (xmlChar *)sectype); 640 } 641 if (node != NULL) { 642 xmlSetProp(node, (xmlChar *)"type", (xmlChar *)proto); 643 /* now find the properties */ 644 if (iter != NULL && value != NULL && prop != NULL && name != NULL) { 645 /* iterate over the share pg properties */ 646 if (scf_iter_pg_properties(iter, pg) == 0) { 647 while (scf_iter_next_property(iter, prop) > 0) { 648 ret = SA_SYSTEM_ERR; /* assume the worst */ 649 if (scf_property_get_name(prop, name, 650 scf_max_name_len) > 0) { 651 if (scf_property_get_value(prop, value) == 0) { 652 if (scf_value_get_astring(value, valuestr, 653 vallen) >= 0) { 654 ret = SA_OK; 655 } 656 } 657 } else { 658 ret = SA_SYSTEM_ERR; 659 } 660 if (ret == SA_OK) { 661 sa_property_t prop; 662 prop = sa_create_property(name, valuestr); 663 if (prop != NULL) 664 prop = (sa_property_t)xmlAddChild(node, 665 (xmlNodePtr)prop); 666 else 667 ret = SA_NO_MEMORY; 668 } 669 } 670 } else { 671 ret = SA_SYSTEM_ERR; 672 } 673 } 674 } else { 675 ret = SA_NO_MEMORY; 676 } 677 if (iter != NULL) 678 scf_iter_destroy(iter); 679 if (value != NULL) 680 scf_value_destroy(value); 681 if (prop != NULL) 682 scf_property_destroy(prop); 683 if (name != NULL) 684 free(name); 685 if (valuestr != NULL) 686 free(valuestr); 687 return (ret); 688 } 689 690 /* 691 * sa_extract_group(root, handle, instance) 692 * 693 * get the config info for this instance of a group and create the XML 694 * subtree from it. 695 */ 696 697 static int 698 sa_extract_group(xmlNodePtr root, scfutilhandle_t *handle, 699 scf_instance_t *instance) 700 { 701 char *buff; 702 xmlNodePtr node; 703 scf_iter_t *iter; 704 char *proto; 705 char *sectype; 706 int have_shares = 0; 707 int has_proto = 0; 708 int is_default = 0; 709 int ret = SA_OK; 710 int err; 711 712 buff = malloc(scf_max_name_len); 713 iter = scf_iter_create(handle->handle); 714 if (buff != NULL) { 715 if (scf_instance_get_name(instance, buff, 716 scf_max_name_len) > 0) { 717 node = xmlNewChild(root, NULL, (xmlChar *)"group", NULL); 718 if (node != NULL) { 719 xmlSetProp(node, (xmlChar *)"name", (xmlChar *)buff); 720 if (strcmp(buff, "default") == 0) 721 is_default++; 722 sa_extract_attrs(node, handle, instance); 723 /* 724 * Iterate through all the property groups 725 * looking for those with security or 726 * optionset prefixes. The names of the 727 * matching pgroups are parsed to get the 728 * protocol, and for security, the sectype. 729 * Syntax is as follows: 730 * optionset | optionset_<proto> 731 * security_default | security_<proto>_<sectype> 732 * "operation" is handled by 733 * sa_extract_attrs(). 734 */ 735 if (iter != NULL) { 736 if (scf_iter_instance_pgs(iter, instance) == 0) { 737 while (scf_iter_next_pg(iter, handle->pg) > 0) { 738 /* have a pgroup so sort it out */ 739 ret = scf_pg_get_name(handle->pg, buff, 740 scf_max_name_len); 741 if (ret > 0) { 742 if (buff[0] == SA_SHARE_PG_PREFIX[0]) { 743 sa_share_from_pgroup(node, handle, 744 handle->pg, 745 buff); 746 have_shares++; 747 } else if (strncmp(buff, "optionset", 9) == 748 0) { 749 char *nodetype = "optionset"; 750 /* have an optionset */ 751 sectype = NULL; 752 proto = strchr(buff, '_'); 753 if (proto != NULL) { 754 *proto++ = '\0'; 755 sectype = strchr(proto, '_'); 756 if (sectype != NULL) { 757 *sectype++ = '\0'; 758 nodetype = "security"; 759 } 760 } 761 ret = sa_extract_pgroup(node, handle, 762 handle->pg, 763 nodetype, 764 proto, sectype); 765 has_proto++; 766 } else if (strncmp(buff, 767 "security", 8) == 0) { 768 /* 769 * have a security (note that 770 * this should change in the 771 * future) 772 */ 773 proto = strchr(buff, '_'); 774 sectype = NULL; 775 if (proto != NULL) { 776 *proto++ = '\0'; 777 sectype = strchr(proto, '_'); 778 if (sectype != NULL) 779 *sectype++ = '\0'; 780 if (strcmp(proto, "default") == 0) 781 proto = NULL; 782 } 783 ret = sa_extract_pgroup(node, handle, 784 handle->pg, 785 "security", proto, 786 sectype); 787 has_proto++; 788 } 789 /* ignore everything else */ 790 } 791 } 792 } else { 793 ret = SA_NO_MEMORY; 794 } 795 /* 796 * Make sure we have a valid default group. 797 * On first boot, default won't have any 798 * protocols defined and won't be enabled (but 799 * should be). 800 */ 801 if (is_default) { 802 char *state = sa_get_group_attr((sa_group_t)node, 803 "state"); 804 char **protos; 805 int numprotos; 806 int i; 807 808 if (state == NULL) { 809 /* set attribute to enabled */ 810 (void) sa_set_group_attr((sa_group_t)node, 811 "state", 812 "enabled"); 813 /* we can assume no protocols */ 814 numprotos = sa_get_protocols(&protos); 815 for (i = 0; i < numprotos; i++) 816 (void) sa_create_optionset((sa_group_t)node, 817 protos[i]); 818 if (numprotos > 0) 819 free(protos); 820 } else { 821 sa_free_attr_string(state); 822 } 823 } 824 /* do a second pass if shares were found */ 825 if (have_shares && 826 scf_iter_instance_pgs(iter, instance) == 0) { 827 while (scf_iter_next_pg(iter, handle->pg) > 0) { 828 /* 829 * have a pgroup so see if it is a 830 * share optionset 831 */ 832 err = scf_pg_get_name(handle->pg, buff, 833 scf_max_name_len); 834 if (err > 0) { 835 if (buff[0] == SA_SHARE_PG_PREFIX[0]) { 836 ret = sa_share_props_from_pgroup(node, 837 handle, 838 handle->pg, 839 buff); 840 } 841 } 842 } 843 } 844 } 845 } 846 } 847 } 848 if (iter != NULL) 849 scf_iter_destroy(iter); 850 if (buff != NULL) 851 free(buff); 852 return (ret); 853 } 854 855 /* 856 * sa_extract_defaults(root, handle, instance) 857 * 858 * local function to find the default properties that live in the 859 * default instance's "operation" proprerty group. 860 */ 861 862 static void 863 sa_extract_defaults(xmlNodePtr root, scfutilhandle_t *handle, 864 scf_instance_t *instance) 865 { 866 xmlNodePtr node; 867 scf_property_t *prop; 868 scf_value_t *value; 869 char *valuestr; 870 ssize_t vallen; 871 872 vallen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 873 prop = scf_property_create(handle->handle); 874 value = scf_value_create(handle->handle); 875 valuestr = malloc(vallen); 876 if (prop != NULL && value != NULL && vallen != NULL && 877 scf_instance_get_pg(instance, "operation", 878 handle->pg) == 0) { 879 if (scf_pg_get_property(handle->pg, 880 "legacy-timestamp", prop) == 0) { 881 /* found the property so get the value */ 882 if (scf_property_get_value(prop, value) == 0) { 883 if (scf_value_get_astring(value, valuestr, vallen) > 0) { 884 node = xmlNewChild(root, NULL, (xmlChar *)"legacy", 885 NULL); 886 if (node != NULL) { 887 xmlSetProp(node, (xmlChar *)"timestamp", 888 (xmlChar *)valuestr); 889 xmlSetProp(node, (xmlChar *)"path", 890 (xmlChar *)SA_LEGACY_DFSTAB); 891 } 892 } 893 } 894 } 895 } 896 if (valuestr != NULL) 897 free(valuestr); 898 if (value != NULL) 899 scf_value_destroy(value); 900 if (prop != NULL) 901 scf_property_destroy(prop); 902 } 903 904 905 /* 906 * sa_get_config(handle, root, doc) 907 * 908 * walk the SMF repository for /network/shares/group and find all the 909 * instances. These become group names. Then add the XML structure 910 * below the groups based on property groups and properties. 911 */ 912 int 913 sa_get_config(scfutilhandle_t *handle, xmlNodePtr *root, xmlDocPtr *doc) 914 { 915 int ret = SA_OK; 916 scf_instance_t *instance; 917 scf_iter_t *iter; 918 char buff[BUFSIZ * 2]; 919 920 *doc = xmlNewDoc((xmlChar *)"1.0"); 921 *root = xmlNewNode(NULL, (xmlChar *)"sharecfg"); 922 instance = scf_instance_create(handle->handle); 923 iter = scf_iter_create(handle->handle); 924 if (*doc != NULL && *root != NULL && instance != NULL && iter != NULL) { 925 xmlDocSetRootElement(*doc, *root); 926 if ((ret = scf_iter_service_instances(iter, 927 handle->service)) == 0) { 928 while ((ret = scf_iter_next_instance(iter, 929 instance)) > 0) { 930 if (scf_instance_get_name(instance, buff, 931 sizeof (buff)) > 0) { 932 if (strcmp(buff, "default") == 0) 933 sa_extract_defaults(*root, handle, instance); 934 ret = sa_extract_group(*root, handle, instance); 935 } 936 } 937 } 938 } else { 939 /* if we can't create the document, cleanup */ 940 if (*doc != NULL) 941 xmlFreeDoc(*doc); 942 if (*root != NULL) 943 xmlFreeNode(*root); 944 *doc = NULL; 945 *root = NULL; 946 } 947 /* always cleanup these */ 948 if (instance != NULL) 949 scf_instance_destroy(instance); 950 if (iter != NULL) 951 scf_iter_destroy(iter); 952 return (ret); 953 } 954 955 /* 956 * sa_get_instance(handle, instance) 957 * 958 * get the instance of the group service. This is actually the 959 * specific group name. The instance is needed for all property and 960 * control operations. 961 */ 962 963 int 964 sa_get_instance(scfutilhandle_t *handle, char *instname) 965 { 966 if (scf_service_get_instance(handle->service, instname, 967 handle->instance) != 0) { 968 return (SA_NO_SUCH_GROUP); 969 } 970 return (SA_OK); 971 } 972 973 /* 974 * sa_create_instance(handle, instname) 975 * 976 * Create a new SMF service instance. There can only be one with a 977 * given name. 978 */ 979 980 int 981 sa_create_instance(scfutilhandle_t *handle, char *instname) 982 { 983 int ret = SA_OK; 984 char instance[SA_GROUP_INST_LEN]; 985 if (scf_service_add_instance(handle->service, instname, 986 handle->instance) != 0) { 987 /* better error returns need to be added based on real error */ 988 if (scf_error() == SCF_ERROR_PERMISSION_DENIED) 989 ret = SA_NO_PERMISSION; 990 else 991 ret = SA_DUPLICATE_NAME; 992 } else { 993 /* have the service created, so enable it */ 994 (void) snprintf(instance, sizeof (instance), "%s:%s", 995 SA_SVC_FMRI_BASE, instname); 996 (void) smf_enable_instance(instance, 0); 997 } 998 return (ret); 999 } 1000 1001 /* 1002 * sa_delete_instance(handle, instname) 1003 * 1004 * When a group goes away, we also remove the service instance. 1005 */ 1006 1007 int 1008 sa_delete_instance(scfutilhandle_t *handle, char *instname) 1009 { 1010 int ret; 1011 1012 if (strcmp(instname, "default") == 0) { 1013 ret = SA_NO_PERMISSION; 1014 } else { 1015 if ((ret = sa_get_instance(handle, instname)) == SA_OK) { 1016 if (scf_instance_delete(handle->instance) != 0) 1017 /* need better analysis */ 1018 ret = SA_NO_PERMISSION; 1019 } 1020 } 1021 return (ret); 1022 } 1023 1024 /* 1025 * sa_create_pgroup(handle, pgroup) 1026 * 1027 * create a new property group 1028 */ 1029 1030 int 1031 sa_create_pgroup(scfutilhandle_t *handle, char *pgroup) 1032 { 1033 int ret = SA_OK; 1034 /* 1035 * only create a handle if it doesn't exist. It is ok to exist 1036 * since the pg handle will be set as a side effect. 1037 */ 1038 if (handle->pg == NULL) { 1039 handle->pg = scf_pg_create(handle->handle); 1040 } 1041 /* 1042 * if the pgroup exists, we are done. If it doesn't, then we 1043 * need to actually add one to the service instance. 1044 */ 1045 if (scf_instance_get_pg(handle->instance, 1046 pgroup, handle->pg) != 0) { 1047 /* doesn't exist so create one */ 1048 if (scf_instance_add_pg(handle->instance, pgroup, 1049 SCF_GROUP_APPLICATION, 0, 1050 handle->pg) != 0) { 1051 switch (scf_error()) { 1052 case SCF_ERROR_PERMISSION_DENIED: 1053 ret = SA_NO_PERMISSION; 1054 break; 1055 default: 1056 ret = SA_SYSTEM_ERR; 1057 break; 1058 } 1059 } 1060 } 1061 return (ret); 1062 } 1063 1064 /* 1065 * sa_delete_pgroup(handle, pgroup) 1066 * 1067 * remove the property group from the current instance of the service, 1068 * but only if it actually exists. 1069 */ 1070 1071 int 1072 sa_delete_pgroup(scfutilhandle_t *handle, char *pgroup) 1073 { 1074 int ret = SA_OK; 1075 /* 1076 * only delete if it does exist. 1077 */ 1078 if (scf_instance_get_pg(handle->instance, 1079 pgroup, handle->pg) == 0) { 1080 /* does exist so delete it */ 1081 if (scf_pg_delete(handle->pg) != 0) { 1082 ret = SA_SYSTEM_ERR; 1083 } 1084 } else { 1085 ret = SA_SYSTEM_ERR; 1086 } 1087 if (ret == SA_SYSTEM_ERR && 1088 scf_error() == SCF_ERROR_PERMISSION_DENIED) { 1089 ret = SA_NO_PERMISSION; 1090 } 1091 return (ret); 1092 } 1093 1094 /* 1095 * sa_start_transaction(handle, pgroup) 1096 * 1097 * Start an SMF transaction so we can deal with properties. it would 1098 * be nice to not have to expose this, but we have to in order to 1099 * optimize. 1100 * 1101 * Basic model is to hold the transaction in the handle and allow 1102 * property adds/deletes/updates to be added then close the 1103 * transaction (or abort). There may eventually be a need to handle 1104 * other types of transaction mechanisms but we don't do that now. 1105 * 1106 * An sa_start_transaction must be followed by either an 1107 * sa_end_transaction or sa_abort_transaction before another 1108 * sa_start_transaction can be done. 1109 */ 1110 1111 int 1112 sa_start_transaction(scfutilhandle_t *handle, char *propgroup) 1113 { 1114 int ret = SA_OK; 1115 /* 1116 * lookup the property group and create it if it doesn't already 1117 * exist. 1118 */ 1119 if (handle->scf_state == SCH_STATE_INIT) { 1120 ret = sa_create_pgroup(handle, propgroup); 1121 if (ret == SA_OK) { 1122 handle->trans = scf_transaction_create(handle->handle); 1123 if (handle->trans != NULL) { 1124 if (scf_transaction_start(handle->trans, handle->pg) != 0) { 1125 ret = SA_SYSTEM_ERR; 1126 } 1127 if (ret != SA_OK) { 1128 scf_transaction_destroy(handle->trans); 1129 handle->trans = NULL; 1130 } 1131 } else { 1132 ret = SA_SYSTEM_ERR; 1133 } 1134 } 1135 } 1136 if (ret == SA_SYSTEM_ERR && 1137 scf_error() == SCF_ERROR_PERMISSION_DENIED) { 1138 ret = SA_NO_PERMISSION; 1139 } 1140 return (ret); 1141 } 1142 1143 /* 1144 * sa_end_transaction(handle) 1145 * 1146 * Commit the changes that were added to the transaction in the 1147 * handle. Do all necessary cleanup. 1148 */ 1149 1150 int 1151 sa_end_transaction(scfutilhandle_t *handle) 1152 { 1153 int ret = SA_OK; 1154 1155 if (handle->trans == NULL) { 1156 ret = SA_SYSTEM_ERR; 1157 } else { 1158 if (scf_transaction_commit(handle->trans) < 0) 1159 ret = SA_SYSTEM_ERR; 1160 scf_transaction_destroy_children(handle->trans); 1161 scf_transaction_destroy(handle->trans); 1162 handle->trans = NULL; 1163 } 1164 return (ret); 1165 } 1166 1167 /* 1168 * sa_abort_transaction(handle) 1169 * 1170 * Abort the changes that were added to the transaction in the 1171 * handle. Do all necessary cleanup. 1172 */ 1173 1174 void 1175 sa_abort_transaction(scfutilhandle_t *handle) 1176 { 1177 if (handle->trans != NULL) { 1178 scf_transaction_reset_all(handle->trans); 1179 scf_transaction_destroy_children(handle->trans); 1180 scf_transaction_destroy(handle->trans); 1181 handle->trans = NULL; 1182 } 1183 } 1184 1185 /* 1186 * sa_set_property(handle, prop, value) 1187 * 1188 * set a property transaction entry into the pending SMF transaction. 1189 */ 1190 1191 int 1192 sa_set_property(scfutilhandle_t *handle, char *propname, char *valstr) 1193 { 1194 int ret = SA_OK; 1195 scf_value_t *value; 1196 scf_transaction_entry_t *entry; 1197 /* 1198 * properties must be set in transactions and don't take 1199 * effect until the transaction has been ended/committed. 1200 */ 1201 value = scf_value_create(handle->handle); 1202 entry = scf_entry_create(handle->handle); 1203 if (value != NULL && entry != NULL) { 1204 if (scf_transaction_property_change(handle->trans, entry, 1205 propname, 1206 SCF_TYPE_ASTRING) == 0 || 1207 scf_transaction_property_new(handle->trans, entry, 1208 propname, 1209 SCF_TYPE_ASTRING) == 0) { 1210 if (scf_value_set_astring(value, valstr) == 0) { 1211 if (scf_entry_add_value(entry, value) != 0) { 1212 ret = SA_SYSTEM_ERR; 1213 scf_value_destroy(value); 1214 } 1215 /* the value is in the transaction */ 1216 value = NULL; 1217 } else { 1218 /* value couldn't be constructed */ 1219 ret = SA_SYSTEM_ERR; 1220 } 1221 /* the entry is in the transaction */ 1222 entry = NULL; 1223 } else { 1224 ret = SA_SYSTEM_ERR; 1225 } 1226 } else { 1227 ret = SA_SYSTEM_ERR; 1228 } 1229 if (ret == SA_SYSTEM_ERR) { 1230 switch (scf_error()) { 1231 case SCF_ERROR_PERMISSION_DENIED: 1232 ret = SA_NO_PERMISSION; 1233 break; 1234 } 1235 } 1236 /* 1237 * cleanup if there were any errors that didn't leave these 1238 * values where they would be cleaned up later. 1239 */ 1240 if (value != NULL) 1241 scf_value_destroy(value); 1242 if (entry != NULL) 1243 scf_entry_destroy(entry); 1244 return (ret); 1245 } 1246 1247 /* 1248 * sa_commit_share(handle, group, share) 1249 * 1250 * commit this share to the repository. 1251 * properties are added if they exist but can be added later. 1252 * Need to add to dfstab and sharetab, if appropriate. 1253 */ 1254 int 1255 sa_commit_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) 1256 { 1257 int ret = SA_OK; 1258 char *groupname; 1259 char *name; 1260 char *resource; 1261 char *description; 1262 char *sharename; 1263 ssize_t proplen; 1264 char *propstring; 1265 1266 /* 1267 * don't commit in the zfs group. We do commit legacy 1268 * (default) and all other groups/shares. ZFS is handled 1269 * through the ZFS configuration rather than SMF. 1270 */ 1271 1272 groupname = sa_get_group_attr(group, "name"); 1273 if (groupname != NULL) { 1274 if (strcmp(groupname, "zfs") == 0) { 1275 /* 1276 * adding to the ZFS group will result in the sharenfs 1277 * property being set but we don't want to do anything 1278 * SMF related at this point. 1279 */ 1280 sa_free_attr_string(groupname); 1281 return (ret); 1282 } 1283 } 1284 1285 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1286 propstring = malloc(proplen); 1287 if (propstring == NULL) 1288 ret = SA_NO_MEMORY; 1289 1290 if (groupname != NULL && ret == SA_OK) { 1291 ret = sa_get_instance(handle, groupname); 1292 sa_free_attr_string(groupname); 1293 groupname = NULL; 1294 sharename = sa_get_share_attr(share, "id"); 1295 if (sharename == NULL) { 1296 /* slipped by */ 1297 char shname[SA_SHARE_UUID_BUFLEN]; 1298 generate_unique_sharename(shname); 1299 xmlSetProp((xmlNodePtr)share, (xmlChar *)"id", 1300 (xmlChar *)shname); 1301 sharename = strdup(shname); 1302 } 1303 if (sharename != NULL) { 1304 /* 1305 * have a share name allocated so create a pgroup 1306 * for it. It may already exist, but that is OK. 1307 */ 1308 ret = sa_create_pgroup(handle, sharename); 1309 if (ret == SA_OK) { 1310 /* 1311 * now start the transaction for the 1312 * properties that define this share. They may 1313 * exist so attempt to update before create. 1314 */ 1315 ret = sa_start_transaction(handle, sharename); 1316 } 1317 if (ret == SA_OK) { 1318 name = sa_get_share_attr(share, "path"); 1319 if (name != NULL) { 1320 /* there needs to be a path for a share to exist */ 1321 ret = sa_set_property(handle, "path", name); 1322 sa_free_attr_string(name); 1323 } else { 1324 ret = SA_NO_MEMORY; 1325 } 1326 } 1327 if (ret == SA_OK) { 1328 resource = sa_get_share_attr(share, "resource"); 1329 if (resource != NULL) { 1330 ret = sa_set_property(handle, "resource", resource); 1331 sa_free_attr_string(resource); 1332 } 1333 } 1334 if (ret == SA_OK) { 1335 description = sa_get_share_description(share); 1336 if (description != NULL) { 1337 ret = sa_set_property(handle, "description", 1338 description); 1339 sa_free_share_description(description); 1340 } 1341 } 1342 /* make sure we cleanup the transaction */ 1343 if (ret == SA_OK) { 1344 ret = sa_end_transaction(handle); 1345 } else { 1346 sa_abort_transaction(handle); 1347 } 1348 free(sharename); 1349 } 1350 } 1351 if (ret == SA_SYSTEM_ERR) { 1352 int err = scf_error(); 1353 if (err == SCF_ERROR_PERMISSION_DENIED) 1354 ret = SA_NO_PERMISSION; 1355 } 1356 if (propstring != NULL) 1357 free(propstring); 1358 if (groupname != NULL) 1359 sa_free_attr_string(groupname); 1360 1361 return (ret); 1362 } 1363 1364 /* 1365 * sa_delete_share(handle, group, share) 1366 * 1367 * remove the specified share from the group (and service instance). 1368 */ 1369 1370 int 1371 sa_delete_share(scfutilhandle_t *handle, sa_group_t group, sa_share_t share) 1372 { 1373 int ret = SA_OK; 1374 char *groupname = NULL; 1375 char *shareid = NULL; 1376 sa_optionset_t opt; 1377 sa_security_t sec; 1378 ssize_t proplen; 1379 char *propstring; 1380 1381 proplen = get_scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH); 1382 propstring = malloc(proplen); 1383 if (propstring == NULL) 1384 ret = SA_NO_MEMORY; 1385 1386 if (ret == SA_OK) { 1387 groupname = sa_get_group_attr(group, "name"); 1388 shareid = sa_get_share_attr(share, "id"); 1389 if (groupname != NULL && shareid != NULL) { 1390 ret = sa_get_instance(handle, groupname); 1391 if (ret == SA_OK) { 1392 /* if a share has properties, remove them */ 1393 ret = sa_delete_pgroup(handle, shareid); 1394 for (opt = sa_get_optionset(share, NULL); opt != NULL; 1395 opt = sa_get_next_optionset(opt)) { 1396 char *proto; 1397 proto = sa_get_optionset_attr(opt, "type"); 1398 if (proto != NULL) { 1399 (void) snprintf(propstring, proplen, "%s_%s", 1400 shareid, proto); 1401 ret = sa_delete_pgroup(handle, propstring); 1402 sa_free_attr_string(proto); 1403 } else { 1404 ret = SA_NO_MEMORY; 1405 } 1406 } 1407 /* 1408 * if a share has security/negotiable 1409 * properties, remove them. 1410 */ 1411 for (sec = sa_get_security(share, NULL, NULL); sec != NULL; 1412 sec = sa_get_next_security(sec)) { 1413 char *proto; 1414 char *sectype; 1415 proto = sa_get_security_attr(sec, "type"); 1416 sectype = sa_get_security_attr(sec, "sectype"); 1417 if (proto != NULL && sectype != NULL) { 1418 (void) snprintf(propstring, proplen, "%s_%s_%s", 1419 shareid, 1420 proto, sectype); 1421 ret = sa_delete_pgroup(handle, propstring); 1422 } else { 1423 ret = SA_NO_MEMORY; 1424 } 1425 if (proto != NULL) 1426 sa_free_attr_string(proto); 1427 if (sectype != NULL) 1428 sa_free_attr_string(sectype); 1429 } 1430 } 1431 } else { 1432 ret = SA_CONFIG_ERR; 1433 } 1434 } 1435 if (groupname != NULL) 1436 sa_free_attr_string(groupname); 1437 if (shareid != NULL) 1438 sa_free_attr_string(shareid); 1439 if (propstring != NULL) 1440 free(propstring); 1441 1442 return (ret); 1443 } 1444