1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <assert.h> 28 #include <errno.h> 29 #include <libintl.h> 30 #include <libuutil.h> 31 #include <stdarg.h> 32 #include <stddef.h> 33 #include <string.h> 34 #include <unistd.h> 35 #include <libscf_priv.h> 36 37 #include "svccfg.h" 38 39 /* 40 * Internal representation manipulation routines for svccfg(1) 41 */ 42 43 static uu_list_pool_t *entity_pool; 44 static uu_list_pool_t *pgroup_pool; 45 static uu_list_pool_t *property_pool; 46 static uu_list_pool_t *value_pool; 47 48 /* ARGSUSED */ 49 static int 50 entity_cmp(const void *a, const void *b, void *p) 51 { 52 entity_t *A = (entity_t *)a; 53 entity_t *B = (entity_t *)b; 54 55 return (strcmp(A->sc_name, B->sc_name)); 56 } 57 58 /*ARGSUSED*/ 59 static int 60 pgroup_cmp(const void *a, const void *b, void *p) 61 { 62 pgroup_t *A = (pgroup_t *)a; 63 pgroup_t *B = (pgroup_t *)b; 64 65 return (strcmp(A->sc_pgroup_name, B->sc_pgroup_name)); 66 } 67 68 /* ARGSUSED */ 69 static int 70 property_cmp(const void *a, const void *b, void *p) 71 { 72 property_t *A = (property_t *)a; 73 property_t *B = (property_t *)b; 74 75 return (strcmp(A->sc_property_name, B->sc_property_name)); 76 } 77 78 /* ARGSUSED */ 79 int 80 value_cmp(const void *a, const void *b, void *p) 81 { 82 const value_t *A = a; 83 const value_t *B = b; 84 85 if (A->sc_type != B->sc_type) 86 return (B->sc_type - A->sc_type); 87 88 switch (A->sc_type) { 89 case SCF_TYPE_BOOLEAN: 90 case SCF_TYPE_COUNT: 91 return (B->sc_u.sc_count - A->sc_u.sc_count); 92 93 case SCF_TYPE_INTEGER: 94 return (B->sc_u.sc_integer - A->sc_u.sc_integer); 95 96 default: 97 return (strcmp(A->sc_u.sc_string, B->sc_u.sc_string)); 98 } 99 } 100 101 void 102 internal_init() 103 { 104 if ((entity_pool = uu_list_pool_create("entities", sizeof (entity_t), 105 offsetof(entity_t, sc_node), entity_cmp, 0)) == NULL) 106 uu_die(gettext("entity list pool creation failed: %s\n"), 107 uu_strerror(uu_error())); 108 109 if ((pgroup_pool = uu_list_pool_create("property_groups", 110 sizeof (pgroup_t), offsetof(pgroup_t, sc_node), pgroup_cmp, 0)) == 111 NULL) 112 uu_die( 113 gettext("property group list pool creation failed: %s\n"), 114 uu_strerror(uu_error())); 115 116 if ((property_pool = uu_list_pool_create("properties", 117 sizeof (property_t), offsetof(property_t, sc_node), property_cmp, 118 0)) == NULL) 119 uu_die(gettext("property list pool creation failed: %s\n"), 120 uu_strerror(uu_error())); 121 122 if ((value_pool = uu_list_pool_create("property_values", 123 sizeof (value_t), offsetof(value_t, sc_node), value_cmp, 0)) == 124 NULL) 125 uu_die( 126 gettext("property value list pool creation failed: %s\n"), 127 uu_strerror(uu_error())); 128 } 129 130 /*ARGSUSED*/ 131 static int 132 internal_value_dump(void *v, void *pvt) 133 { 134 value_t *val = v; 135 136 switch (val->sc_type) { 137 case SCF_TYPE_BOOLEAN: 138 (void) printf(" value = %s\n", 139 val->sc_u.sc_count ? "true" : "false"); 140 break; 141 case SCF_TYPE_COUNT: 142 (void) printf(" value = %llu\n", val->sc_u.sc_count); 143 break; 144 case SCF_TYPE_INTEGER: 145 (void) printf(" value = %lld\n", val->sc_u.sc_integer); 146 break; 147 case SCF_TYPE_ASTRING: 148 case SCF_TYPE_FMRI: 149 case SCF_TYPE_HOST: 150 case SCF_TYPE_HOSTNAME: 151 case SCF_TYPE_NET_ADDR_V4: 152 case SCF_TYPE_NET_ADDR_V6: 153 case SCF_TYPE_OPAQUE: 154 case SCF_TYPE_TIME: 155 case SCF_TYPE_URI: 156 case SCF_TYPE_USTRING: 157 (void) printf(" value = %s\n", 158 val->sc_u.sc_string ? val->sc_u.sc_string : "(nil)"); 159 break; 160 default: 161 uu_die(gettext("unknown value type (%d)\n"), val->sc_type); 162 break; 163 } 164 165 return (UU_WALK_NEXT); 166 } 167 168 /*ARGSUSED*/ 169 static int 170 internal_property_dump(void *v, void *pvt) 171 { 172 property_t *p = v; 173 174 (void) printf("property\n name = %s\n", p->sc_property_name); 175 (void) printf(" type = %d\n", p->sc_value_type); 176 177 (void) uu_list_walk(p->sc_property_values, internal_value_dump, 178 NULL, UU_DEFAULT); 179 180 return (UU_WALK_NEXT); 181 } 182 183 /*ARGSUSED*/ 184 static int 185 internal_pgroup_dump(void *v, void *pvt) 186 { 187 pgroup_t *pg = v; 188 189 (void) printf("pgroup name = %s\n", pg->sc_pgroup_name); 190 (void) printf(" type = %s\n", pg->sc_pgroup_type); 191 192 (void) uu_list_walk(pg->sc_pgroup_props, internal_property_dump, 193 NULL, UU_DEFAULT); 194 195 return (UU_WALK_NEXT); 196 } 197 198 /*ARGSUSED*/ 199 static int 200 internal_instance_dump(void *v, void *pvt) 201 { 202 entity_t *i = v; 203 204 (void) printf("instance name = %s\n", i->sc_name); 205 206 (void) uu_list_walk(i->sc_pgroups, internal_pgroup_dump, NULL, 207 UU_DEFAULT); 208 209 return (UU_WALK_NEXT); 210 } 211 212 /*ARGSUSED*/ 213 static int 214 internal_service_dump(void *v, void *pvt) 215 { 216 entity_t *s = v; 217 218 (void) printf("service name = %s\n", s->sc_name); 219 (void) printf(" type = %x\n", s->sc_u.sc_service.sc_service_type); 220 (void) printf(" version = %u\n", s->sc_u.sc_service.sc_service_version); 221 222 (void) uu_list_walk(s->sc_pgroups, internal_pgroup_dump, NULL, 223 UU_DEFAULT); 224 225 (void) uu_list_walk(s->sc_u.sc_service.sc_service_instances, 226 internal_instance_dump, NULL, UU_DEFAULT); 227 228 return (UU_WALK_NEXT); 229 } 230 231 void 232 internal_dump(bundle_t *b) 233 { 234 (void) printf("bundle name = %s\n", b->sc_bundle_name); 235 (void) printf(" type = %x\n", b->sc_bundle_type); 236 237 (void) uu_list_walk(b->sc_bundle_services, internal_service_dump, 238 NULL, UU_DEFAULT); 239 } 240 241 bundle_t * 242 internal_bundle_new() 243 { 244 bundle_t *b; 245 246 if ((b = uu_zalloc(sizeof (bundle_t))) == NULL) 247 uu_die(gettext("couldn't allocate memory")); 248 249 b->sc_bundle_type = SVCCFG_UNKNOWN_BUNDLE; 250 b->sc_bundle_services = uu_list_create(entity_pool, b, 0); 251 if (b->sc_bundle_services == NULL) { 252 uu_die(gettext("Unable to create list for bundle services. " 253 "%s\n"), uu_strerror(uu_error())); 254 } 255 256 return (b); 257 } 258 259 void 260 internal_bundle_free(bundle_t *b) 261 { 262 void *cookie = NULL; 263 entity_t *service; 264 265 while ((service = uu_list_teardown(b->sc_bundle_services, &cookie)) != 266 NULL) 267 internal_service_free(service); 268 269 free(b); 270 } 271 272 entity_t * 273 internal_entity_new(entity_type_t entity) 274 { 275 entity_t *e; 276 277 if ((e = uu_zalloc(sizeof (entity_t))) == NULL) 278 uu_die(gettext("couldn't allocate memory")); 279 280 uu_list_node_init(e, &e->sc_node, entity_pool); 281 282 e->sc_etype = entity; 283 e->sc_pgroups = uu_list_create(pgroup_pool, e, 0); 284 if (e->sc_pgroups == NULL) { 285 uu_die(gettext("Unable to create list for entity property " 286 "groups. %s\n"), uu_strerror(uu_error())); 287 } 288 289 return (e); 290 } 291 292 entity_t * 293 internal_service_new(const char *name) 294 { 295 entity_t *s; 296 297 s = internal_entity_new(SVCCFG_SERVICE_OBJECT); 298 299 s->sc_name = name; 300 s->sc_fmri = uu_msprintf("svc:/%s", name); 301 if (s->sc_fmri == NULL) 302 uu_die(gettext("couldn't allocate memory")); 303 304 s->sc_dependents = uu_list_create(pgroup_pool, s, 0); 305 if (s->sc_dependents == NULL) { 306 uu_die(gettext("Unable to create list for service dependents. " 307 "%s\n"), uu_strerror(uu_error())); 308 } 309 310 s->sc_u.sc_service.sc_service_type = SVCCFG_UNKNOWN_SERVICE; 311 s->sc_u.sc_service.sc_service_instances = uu_list_create(entity_pool, s, 312 0); 313 if (s->sc_u.sc_service.sc_service_instances == NULL) { 314 uu_die(gettext("Unable to create list for service instances. " 315 "%s\n"), uu_strerror(uu_error())); 316 } 317 318 return (s); 319 } 320 321 void 322 internal_service_free(entity_t *s) 323 { 324 entity_t *inst; 325 pgroup_t *pg; 326 void *cookie; 327 328 if (s->sc_u.sc_service.sc_restarter != NULL) 329 internal_instance_free(s->sc_u.sc_service.sc_restarter); 330 if (s->sc_u.sc_service.sc_global != NULL) 331 internal_instance_free(s->sc_u.sc_service.sc_global); 332 333 cookie = NULL; 334 while ((pg = uu_list_teardown(s->sc_pgroups, &cookie)) != NULL) 335 internal_pgroup_free(pg); 336 337 cookie = NULL; 338 while ((pg = uu_list_teardown(s->sc_dependents, &cookie)) != NULL) 339 internal_pgroup_free(pg); 340 341 cookie = NULL; 342 while ((inst = uu_list_teardown(s->sc_u.sc_service.sc_service_instances, 343 &cookie)) != NULL) 344 internal_instance_free(inst); 345 uu_free((void *)s->sc_fmri); 346 347 free(s); 348 } 349 350 entity_t * 351 internal_instance_new(const char *name) 352 { 353 entity_t *i; 354 355 i = internal_entity_new(SVCCFG_INSTANCE_OBJECT); 356 i->sc_name = name; 357 /* Can't set i->sc_fmri until we're attached to a service. */ 358 i->sc_dependents = uu_list_create(pgroup_pool, i, 0); 359 if (i->sc_dependents == NULL) { 360 uu_die(gettext("Unable to create list for instance " 361 "dependents. %s\n"), uu_strerror(uu_error())); 362 } 363 364 return (i); 365 } 366 367 void 368 internal_instance_free(entity_t *i) 369 { 370 pgroup_t *pg; 371 void *cookie = NULL; 372 entity_t *rs; 373 374 rs = i->sc_u.sc_instance.sc_instance_restarter; 375 if (rs != NULL) 376 internal_instance_free(rs); 377 while ((pg = uu_list_teardown(i->sc_pgroups, &cookie)) != NULL) 378 internal_pgroup_free(pg); 379 380 cookie = NULL; 381 while ((pg = uu_list_teardown(i->sc_dependents, &cookie)) != NULL) 382 internal_pgroup_free(pg); 383 uu_free((void *)i->sc_fmri); 384 385 free(i); 386 } 387 388 pgroup_t * 389 internal_pgroup_new() 390 { 391 pgroup_t *p; 392 393 if ((p = uu_zalloc(sizeof (pgroup_t))) == NULL) 394 uu_die(gettext("couldn't allocate memory")); 395 396 uu_list_node_init(p, &p->sc_node, pgroup_pool); 397 398 p->sc_pgroup_props = uu_list_create(property_pool, p, UU_LIST_SORTED); 399 if (p->sc_pgroup_props == NULL) { 400 uu_die(gettext("Unable to create list for properties. %s\n"), 401 uu_strerror(uu_error())); 402 } 403 p->sc_pgroup_name = "<unset>"; 404 p->sc_pgroup_type = "<unset>"; 405 406 return (p); 407 } 408 409 void 410 internal_pgroup_free(pgroup_t *pg) 411 { 412 property_t *prop; 413 void *cookie = NULL; 414 415 /* 416 * Templates validation code should clean up this reference when 417 * the validation is finished. 418 */ 419 assert(pg->sc_pgroup_composed == NULL); 420 421 while ((prop = uu_list_teardown(pg->sc_pgroup_props, &cookie)) != NULL) 422 internal_property_free(prop); 423 424 uu_free(pg); 425 } 426 427 static pgroup_t * 428 find_pgroup(uu_list_t *list, const char *name, const char *type) 429 { 430 pgroup_t *pg; 431 432 for (pg = uu_list_first(list); 433 pg != NULL; 434 pg = uu_list_next(list, pg)) { 435 if (strcmp(pg->sc_pgroup_name, name) != 0) 436 continue; 437 438 if (type == NULL) 439 return (pg); 440 441 if (strcmp(pg->sc_pgroup_type, type) == 0) 442 return (pg); 443 } 444 445 return (NULL); 446 } 447 448 pgroup_t * 449 internal_dependent_find(entity_t *e, const char *name) 450 { 451 return (find_pgroup(e->sc_dependents, name, NULL)); 452 } 453 454 pgroup_t * 455 internal_pgroup_find(entity_t *e, const char *name, const char *type) 456 { 457 return (find_pgroup(e->sc_pgroups, name, type)); 458 } 459 460 static pgroup_t * 461 internal_pgroup_create_common(entity_t *e, const char *name, const char *type, 462 boolean_t unique) 463 { 464 pgroup_t *pg; 465 466 pg = internal_pgroup_find(e, name, type); 467 if (pg != NULL) { 468 if (unique == B_TRUE) { 469 return (NULL); 470 } else { 471 return (pg); 472 } 473 } 474 475 pg = internal_pgroup_new(); 476 (void) internal_attach_pgroup(e, pg); 477 pg->sc_pgroup_name = strdup(name); 478 pg->sc_pgroup_type = strdup(type); 479 pg->sc_pgroup_flags = 0; 480 481 if (pg->sc_pgroup_name == NULL || pg->sc_pgroup_type == NULL) 482 uu_die(gettext("Could not duplicate string")); 483 484 return (pg); 485 } 486 487 pgroup_t * 488 internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type) 489 { 490 return (internal_pgroup_create_common(e, name, type, B_FALSE)); 491 } 492 493 pgroup_t * 494 internal_pgroup_create_strict(entity_t *e, const char *name, const char *type) 495 { 496 return (internal_pgroup_create_common(e, name, type, B_TRUE)); 497 } 498 499 property_t * 500 internal_property_new() 501 { 502 property_t *p; 503 504 if ((p = uu_zalloc(sizeof (property_t))) == NULL) 505 uu_die(gettext("couldn't allocate memory")); 506 507 uu_list_node_init(p, &p->sc_node, property_pool); 508 509 p->sc_property_values = uu_list_create(value_pool, p, 0); 510 if (p->sc_property_values == NULL) { 511 uu_die(gettext("Unable to create list for property values. " 512 "%s\n"), uu_strerror(uu_error())); 513 } 514 p->sc_property_name = "<unset>"; 515 516 tmpl_property_init(p); 517 518 return (p); 519 } 520 521 void 522 internal_property_free(property_t *p) 523 { 524 value_t *val; 525 void *cookie = NULL; 526 527 tmpl_property_fini(p); 528 529 while ((val = uu_list_teardown(p->sc_property_values, &cookie)) != 530 NULL) { 531 if (val->sc_free != NULL) 532 val->sc_free(val); 533 free(val); 534 } 535 536 free(p); 537 } 538 539 property_t * 540 internal_property_find(pgroup_t *pg, const char *name) 541 { 542 property_t *p; 543 544 for (p = uu_list_first(pg->sc_pgroup_props); 545 p != NULL; 546 p = uu_list_next(pg->sc_pgroup_props, p)) 547 if (strcmp(p->sc_property_name, name) == 0) 548 return (p); 549 550 return (NULL); 551 } 552 553 value_t * 554 internal_value_new() 555 { 556 value_t *v; 557 558 if ((v = uu_zalloc(sizeof (value_t))) == NULL) 559 uu_die(gettext("couldn't allocate memory")); 560 561 uu_list_node_init(v, &v->sc_node, value_pool); 562 563 return (v); 564 } 565 566 static void 567 internal_value_free_str(value_t *v) 568 { 569 free(v->sc_u.sc_string); 570 } 571 572 property_t * 573 internal_property_create(const char *name, scf_type_t vtype, uint_t nvals, ...) 574 { 575 va_list args; 576 property_t *p; 577 value_t *v; 578 579 p = internal_property_new(); 580 581 p->sc_property_name = (char *)name; 582 p->sc_value_type = vtype; 583 584 va_start(args, nvals); 585 for (; nvals > 0; nvals--) { 586 587 v = internal_value_new(); 588 v->sc_type = vtype; 589 590 switch (vtype) { 591 case SCF_TYPE_BOOLEAN: 592 case SCF_TYPE_COUNT: 593 v->sc_u.sc_count = va_arg(args, uint64_t); 594 break; 595 case SCF_TYPE_INTEGER: 596 v->sc_u.sc_integer = va_arg(args, int64_t); 597 break; 598 case SCF_TYPE_ASTRING: 599 case SCF_TYPE_FMRI: 600 case SCF_TYPE_HOST: 601 case SCF_TYPE_HOSTNAME: 602 case SCF_TYPE_NET_ADDR_V4: 603 case SCF_TYPE_NET_ADDR_V6: 604 case SCF_TYPE_OPAQUE: 605 case SCF_TYPE_TIME: 606 case SCF_TYPE_URI: 607 case SCF_TYPE_USTRING: 608 v->sc_u.sc_string = (char *)va_arg(args, uchar_t *); 609 break; 610 default: 611 va_end(args); 612 uu_die(gettext("unknown property type (%d)\n"), vtype); 613 break; 614 } 615 616 internal_attach_value(p, v); 617 } 618 va_end(args); 619 620 return (p); 621 } 622 623 /* 624 * Some of these attach functions use uu_list_append() to maintain the 625 * same order across import/export, whereas others are always sorted 626 * anyway, or the order is irrelevant. 627 */ 628 629 int 630 internal_attach_service(bundle_t *bndl, entity_t *svc) 631 { 632 if (uu_list_find(bndl->sc_bundle_services, svc, NULL, NULL) != NULL) { 633 semerr(gettext("Multiple definitions for service %s in " 634 "bundle %s.\n"), svc->sc_name, bndl->sc_bundle_name); 635 return (-1); 636 } 637 638 (void) uu_list_append(bndl->sc_bundle_services, svc); 639 640 return (0); 641 } 642 643 int 644 internal_attach_entity(entity_t *svc, entity_t *ent) 645 { 646 if (svc->sc_etype != SVCCFG_SERVICE_OBJECT) 647 uu_die(gettext("bad entity attach: %s is not a service\n"), 648 svc->sc_name); 649 650 if (uu_list_find(svc->sc_u.sc_service.sc_service_instances, ent, NULL, 651 NULL) != NULL) { 652 semerr(gettext("Multiple definitions of entity %s in service " 653 "%s.\n"), ent->sc_name, svc->sc_name); 654 return (-1); 655 } 656 657 (void) uu_list_prepend(svc->sc_u.sc_service.sc_service_instances, ent); 658 ent->sc_parent = svc; 659 ent->sc_fmri = uu_msprintf("%s:%s", svc->sc_fmri, ent->sc_name); 660 if (ent->sc_fmri == NULL) 661 uu_die(gettext("couldn't allocate memory")); 662 663 return (0); 664 } 665 666 int 667 internal_attach_pgroup(entity_t *ent, pgroup_t *pgrp) 668 { 669 if (uu_list_find(ent->sc_pgroups, pgrp, NULL, NULL) != NULL) { 670 semerr(gettext("Multiple definitions of property group %s in " 671 "entity %s.\n"), pgrp->sc_pgroup_name, ent->sc_name); 672 return (-1); 673 } 674 675 (void) uu_list_append(ent->sc_pgroups, pgrp); 676 677 pgrp->sc_parent = ent; 678 679 return (0); 680 } 681 682 void 683 internal_detach_pgroup(entity_t *ent, pgroup_t *pgrp) 684 { 685 uu_list_remove(ent->sc_pgroups, pgrp); 686 } 687 688 int 689 internal_attach_dependent(entity_t *ent, pgroup_t *pg) 690 { 691 if (uu_list_find(ent->sc_dependents, pg, NULL, NULL) != NULL) { 692 semerr(gettext("Multiple definitions of dependent %s in " 693 "entity %s.\n"), pg->sc_pgroup_name, ent->sc_name); 694 return (-1); 695 } 696 697 (void) uu_list_append(ent->sc_dependents, pg); 698 699 pg->sc_parent = ent; 700 701 return (0); 702 } 703 704 /* 705 * Returns 706 * 0 - success 707 * -1 - prop already exists in pgrp 708 */ 709 int 710 internal_attach_property(pgroup_t *pgrp, property_t *prop) 711 { 712 uu_list_index_t idx; 713 714 if (uu_list_find(pgrp->sc_pgroup_props, prop, NULL, &idx) != NULL) { 715 semerr(gettext("Multiple definitions for property %s in " 716 "property group %s.\n"), prop->sc_property_name, 717 pgrp->sc_pgroup_name); 718 return (-1); 719 } 720 721 uu_list_insert(pgrp->sc_pgroup_props, prop, idx); 722 723 return (0); 724 } 725 726 void 727 internal_detach_property(pgroup_t *pgrp, property_t *prop) 728 { 729 uu_list_remove(pgrp->sc_pgroup_props, prop); 730 } 731 732 void 733 internal_attach_value(property_t *prop, value_t *val) 734 { 735 (void) uu_list_append(prop->sc_property_values, val); 736 } 737 738 /* 739 * These functions create an internal representation of a property group 740 * (pgroup_t) from the repository (scf_propertygroup_t). They are used by the 741 * import functions in svccfg_libscf.c . 742 * 743 * load_init() must be called first to initialize these globals, and 744 * load_fini() should be called afterwards to destroy them. 745 */ 746 747 static char *loadbuf = NULL; 748 static size_t loadbuf_sz; 749 static scf_propertygroup_t *load_pgroup = NULL; 750 static scf_property_t *load_prop = NULL; 751 static scf_value_t *load_val = NULL; 752 static scf_iter_t *load_propiter = NULL, *load_valiter = NULL; 753 static scf_iter_t *load_pgiter = NULL; 754 755 /* 756 * Initialize the global state for the load_*() routines. 757 * Returns 758 * 0 - success 759 * ENOMEM - out of memory 760 */ 761 int 762 load_init(void) 763 { 764 loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ? 765 max_scf_value_len : max_scf_pg_type_len) + 1; 766 767 loadbuf = malloc(loadbuf_sz); 768 if (loadbuf == NULL) 769 return (ENOMEM); 770 771 if ((load_prop = scf_property_create(g_hndl)) == NULL || 772 (load_val = scf_value_create(g_hndl)) == NULL || 773 (load_pgroup = scf_pg_create(g_hndl)) == NULL || 774 (load_pgiter = scf_iter_create(g_hndl)) == NULL || 775 (load_propiter = scf_iter_create(g_hndl)) == NULL || 776 (load_valiter = scf_iter_create(g_hndl)) == NULL) { 777 load_fini(); 778 return (ENOMEM); 779 } 780 781 return (0); 782 } 783 784 void 785 load_fini(void) 786 { 787 scf_iter_destroy(load_propiter); 788 load_propiter = NULL; 789 scf_iter_destroy(load_valiter); 790 load_valiter = NULL; 791 scf_iter_destroy(load_pgiter); 792 load_pgiter = NULL; 793 scf_pg_destroy(load_pgroup); 794 load_pgroup = NULL; 795 scf_value_destroy(load_val); 796 load_val = NULL; 797 scf_property_destroy(load_prop); 798 load_prop = NULL; 799 free(loadbuf); 800 loadbuf = NULL; 801 } 802 803 /* 804 * Create a property_t which represents an scf_property_t. Returns 805 * 0 - success 806 * ECANCELED - prop's pg was deleted 807 * ECONNABORTED - repository disconnected 808 * ENOMEM - out of memory 809 * EACCES - permission denied when reading property 810 */ 811 static int 812 load_property(scf_property_t *prop, property_t **ipp) 813 { 814 property_t *iprop; 815 int r; 816 ssize_t ssz; 817 818 /* get name */ 819 if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) { 820 switch (scf_error()) { 821 case SCF_ERROR_DELETED: 822 return (ECANCELED); 823 824 case SCF_ERROR_CONNECTION_BROKEN: 825 return (ECONNABORTED); 826 827 case SCF_ERROR_NOT_BOUND: 828 case SCF_ERROR_NOT_SET: 829 default: 830 bad_error("scf_property_get_name", scf_error()); 831 } 832 } 833 834 iprop = internal_property_new(); 835 iprop->sc_property_name = strdup(loadbuf); 836 if (iprop->sc_property_name == NULL) { 837 internal_property_free(iprop); 838 return (ENOMEM); 839 } 840 841 /* get type */ 842 if (scf_property_type(prop, &iprop->sc_value_type) != 0) { 843 switch (scf_error()) { 844 case SCF_ERROR_DELETED: 845 r = ECANCELED; 846 goto out; 847 848 case SCF_ERROR_CONNECTION_BROKEN: 849 r = ECONNABORTED; 850 goto out; 851 852 case SCF_ERROR_NOT_BOUND: 853 case SCF_ERROR_NOT_SET: 854 default: 855 bad_error("scf_property_type", scf_error()); 856 } 857 } 858 859 /* get values */ 860 if (scf_iter_property_values(load_valiter, prop) != 0) { 861 switch (scf_error()) { 862 case SCF_ERROR_DELETED: 863 r = ECANCELED; 864 goto out; 865 866 case SCF_ERROR_CONNECTION_BROKEN: 867 r = ECONNABORTED; 868 goto out; 869 870 case SCF_ERROR_HANDLE_MISMATCH: 871 case SCF_ERROR_NOT_BOUND: 872 case SCF_ERROR_NOT_SET: 873 default: 874 bad_error("scf_iter_property_values", scf_error()); 875 } 876 } 877 878 for (;;) { 879 value_t *ival; 880 881 r = scf_iter_next_value(load_valiter, load_val); 882 if (r == 0) 883 break; 884 if (r != 1) { 885 switch (scf_error()) { 886 case SCF_ERROR_DELETED: 887 r = ECANCELED; 888 goto out; 889 890 case SCF_ERROR_CONNECTION_BROKEN: 891 r = ECONNABORTED; 892 goto out; 893 894 case SCF_ERROR_PERMISSION_DENIED: 895 r = EACCES; 896 goto out; 897 898 case SCF_ERROR_HANDLE_MISMATCH: 899 case SCF_ERROR_NOT_BOUND: 900 case SCF_ERROR_NOT_SET: 901 case SCF_ERROR_INVALID_ARGUMENT: 902 default: 903 bad_error("scf_iter_next_value", scf_error()); 904 } 905 } 906 907 ival = internal_value_new(); 908 ival->sc_type = scf_value_type(load_val); 909 assert(ival->sc_type != SCF_TYPE_INVALID); 910 911 switch (ival->sc_type) { 912 case SCF_TYPE_BOOLEAN: { 913 uint8_t b; 914 915 r = scf_value_get_boolean(load_val, &b); 916 if (r != 0) 917 bad_error("scf_value_get_boolean", scf_error()); 918 ival->sc_u.sc_count = b; 919 break; 920 } 921 922 case SCF_TYPE_COUNT: 923 r = scf_value_get_count(load_val, &ival->sc_u.sc_count); 924 if (r != 0) 925 bad_error("scf_value_get_count", scf_error()); 926 break; 927 928 case SCF_TYPE_INTEGER: 929 r = scf_value_get_integer(load_val, 930 &ival->sc_u.sc_integer); 931 if (r != 0) 932 bad_error("scf_value_get_integer", scf_error()); 933 break; 934 935 default: 936 ssz = scf_value_get_as_string(load_val, loadbuf, 937 loadbuf_sz); 938 if (ssz < 0) 939 bad_error("scf_value_get_as_string", 940 scf_error()); 941 942 ival->sc_u.sc_string = strdup(loadbuf); 943 if (ival->sc_u.sc_string == NULL) { 944 r = ENOMEM; 945 goto out; 946 } 947 948 ival->sc_free = internal_value_free_str; 949 } 950 951 internal_attach_value(iprop, ival); 952 } 953 954 *ipp = iprop; 955 return (0); 956 957 out: 958 free(iprop->sc_property_name); 959 internal_property_free(iprop); 960 return (r); 961 } 962 963 /* 964 * Returns 965 * 0 - success 966 * ECANCELED - pg was deleted 967 * ECONNABORTED - repository disconnected 968 * ENOMEM - out of memory 969 */ 970 int 971 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp) 972 { 973 pgroup_t *ipg; 974 975 ipg = internal_pgroup_new(); 976 977 if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) { 978 switch (scf_error()) { 979 case SCF_ERROR_DELETED: 980 internal_pgroup_free(ipg); 981 return (ECANCELED); 982 983 case SCF_ERROR_CONNECTION_BROKEN: 984 internal_pgroup_free(ipg); 985 return (ECONNABORTED); 986 987 case SCF_ERROR_NOT_SET: 988 case SCF_ERROR_NOT_BOUND: 989 default: 990 bad_error("scf_pg_get_name", scf_error()); 991 } 992 } 993 994 if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) { 995 switch (scf_error()) { 996 case SCF_ERROR_DELETED: 997 internal_pgroup_free(ipg); 998 return (ECANCELED); 999 1000 case SCF_ERROR_CONNECTION_BROKEN: 1001 internal_pgroup_free(ipg); 1002 return (ECONNABORTED); 1003 1004 case SCF_ERROR_NOT_SET: 1005 case SCF_ERROR_NOT_BOUND: 1006 default: 1007 bad_error("scf_pg_get_name", scf_error()); 1008 } 1009 } 1010 1011 ipg->sc_pgroup_name = strdup(loadbuf); 1012 if (ipg->sc_pgroup_name == NULL) { 1013 internal_pgroup_free(ipg); 1014 return (ENOMEM); 1015 } 1016 1017 if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) { 1018 switch (scf_error()) { 1019 case SCF_ERROR_DELETED: 1020 free((char *)ipg->sc_pgroup_name); 1021 internal_pgroup_free(ipg); 1022 return (ECANCELED); 1023 1024 case SCF_ERROR_CONNECTION_BROKEN: 1025 free((char *)ipg->sc_pgroup_name); 1026 internal_pgroup_free(ipg); 1027 return (ECONNABORTED); 1028 1029 case SCF_ERROR_NOT_SET: 1030 case SCF_ERROR_NOT_BOUND: 1031 default: 1032 bad_error("scf_pg_get_name", scf_error()); 1033 } 1034 } 1035 1036 ipg->sc_pgroup_type = strdup(loadbuf); 1037 if (ipg->sc_pgroup_type == NULL) { 1038 free((char *)ipg->sc_pgroup_name); 1039 internal_pgroup_free(ipg); 1040 return (ENOMEM); 1041 } 1042 1043 *ipgp = ipg; 1044 return (0); 1045 } 1046 1047 /* 1048 * Load a property group into a pgroup_t. Returns 1049 * 0 - success 1050 * ECANCELED - pg was deleted 1051 * ECONNABORTED - repository disconnected 1052 * EBADF - pg is corrupt (error printed if fmri is given) 1053 * ENOMEM - out of memory 1054 * EACCES - permission denied when reading property 1055 */ 1056 int 1057 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri, 1058 const char *snapname) 1059 { 1060 pgroup_t *ipg; 1061 int r; 1062 1063 if (scf_iter_pg_properties(load_propiter, pg) != 0) { 1064 switch (scf_error()) { 1065 case SCF_ERROR_DELETED: 1066 return (ECANCELED); 1067 1068 case SCF_ERROR_CONNECTION_BROKEN: 1069 return (ECONNABORTED); 1070 1071 case SCF_ERROR_HANDLE_MISMATCH: 1072 case SCF_ERROR_NOT_SET: 1073 case SCF_ERROR_NOT_BOUND: 1074 default: 1075 bad_error("scf_iter_pg_properties", scf_error()); 1076 } 1077 } 1078 1079 r = load_pg_attrs(pg, &ipg); 1080 switch (r) { 1081 case 0: 1082 break; 1083 1084 case ECANCELED: 1085 case ECONNABORTED: 1086 case ENOMEM: 1087 return (r); 1088 1089 default: 1090 bad_error("load_pg_attrs", r); 1091 } 1092 1093 for (;;) { 1094 property_t *iprop; 1095 1096 r = scf_iter_next_property(load_propiter, load_prop); 1097 if (r == 0) 1098 break; 1099 if (r != 1) { 1100 switch (scf_error()) { 1101 case SCF_ERROR_DELETED: 1102 r = ECANCELED; 1103 goto out; 1104 1105 case SCF_ERROR_CONNECTION_BROKEN: 1106 r = ECONNABORTED; 1107 goto out; 1108 1109 case SCF_ERROR_HANDLE_MISMATCH: 1110 case SCF_ERROR_NOT_BOUND: 1111 case SCF_ERROR_NOT_SET: 1112 case SCF_ERROR_INVALID_ARGUMENT: 1113 default: 1114 bad_error("scf_iter_next_property", 1115 scf_error()); 1116 } 1117 } 1118 1119 r = load_property(load_prop, &iprop); 1120 switch (r) { 1121 case 0: 1122 break; 1123 1124 case ECANCELED: 1125 case ECONNABORTED: 1126 case ENOMEM: 1127 case EACCES: 1128 goto out; 1129 1130 default: 1131 bad_error("load_property", r); 1132 } 1133 1134 r = internal_attach_property(ipg, iprop); 1135 if (r != 0) { 1136 if (fmri != NULL) { 1137 if (snapname == NULL) 1138 warn(gettext("Property group \"%s\" of " 1139 "%s has multiple definitions of " 1140 "property \"%s\".\n"), 1141 ipg->sc_pgroup_name, fmri, 1142 iprop->sc_property_name); 1143 else 1144 warn(gettext("Property group \"%s\" of " 1145 "the \"%s\" snapshot of %s has " 1146 "multiple definitions of property " 1147 "\"%s\".\n"), 1148 ipg->sc_pgroup_name, snapname, fmri, 1149 iprop->sc_property_name); 1150 } 1151 r = EBADF; 1152 goto out; 1153 } 1154 } 1155 1156 *ipgp = ipg; 1157 return (0); 1158 1159 out: 1160 internal_pgroup_free(ipg); 1161 return (r); 1162 } 1163 1164 /* 1165 * Load the instance for fmri from the repository into memory. The 1166 * property groups that define the instances pg_patterns and prop_patterns 1167 * are also loaded. 1168 * 1169 * Returns 0 on success and non-zero on failure. 1170 */ 1171 int 1172 load_instance(const char *fmri, const char *name, entity_t **inst_ptr) 1173 { 1174 entity_t *e = NULL; 1175 scf_instance_t *inst; 1176 pgroup_t *ipg; 1177 int rc; 1178 char *type = NULL; 1179 ssize_t tsize; 1180 1181 assert(inst_ptr != NULL); 1182 1183 if ((inst = scf_instance_create(g_hndl)) == NULL) { 1184 switch (scf_error()) { 1185 case SCF_ERROR_NO_MEMORY: 1186 case SCF_ERROR_NO_RESOURCES: 1187 rc = EAGAIN; 1188 goto errout; 1189 default: 1190 bad_error("scf_instance_create", scf_error()); 1191 } 1192 } 1193 if (scf_handle_decode_fmri(g_hndl, fmri, NULL, NULL, inst, NULL, NULL, 1194 SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { 1195 switch (scf_error()) { 1196 case SCF_ERROR_CONNECTION_BROKEN: 1197 rc = ECONNABORTED; 1198 goto errout; 1199 case SCF_ERROR_DELETED: 1200 case SCF_ERROR_NOT_FOUND: 1201 rc = ENOENT; 1202 goto errout; 1203 case SCF_ERROR_INVALID_ARGUMENT: 1204 rc = EINVAL; 1205 goto errout; 1206 case SCF_ERROR_CONSTRAINT_VIOLATED: 1207 rc = ENOTSUP; 1208 goto errout; 1209 default: 1210 bad_error("scf_handle_decode_fmri", scf_error()); 1211 } 1212 } 1213 if (scf_iter_instance_pgs_composed(load_pgiter, inst, NULL) != 0) { 1214 switch (scf_error()) { 1215 case SCF_ERROR_DELETED: 1216 rc = ECANCELED; 1217 goto errout; 1218 case SCF_ERROR_CONNECTION_BROKEN: 1219 rc = ECONNABORTED; 1220 goto errout; 1221 default: 1222 bad_error("scf_iter_instance_pgs_composed", 1223 scf_error()); 1224 } 1225 } 1226 1227 tsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH); 1228 type = uu_zalloc(tsize); 1229 if (type == NULL) { 1230 rc = ENOMEM; 1231 goto errout; 1232 } 1233 1234 /* 1235 * Initialize our entity structure. 1236 */ 1237 e = internal_instance_new(name); 1238 if (e == NULL) { 1239 rc = ENOMEM; 1240 goto errout; 1241 } 1242 e->sc_fmri = uu_strdup(fmri); 1243 if (e->sc_fmri == NULL) { 1244 rc = ENOMEM; 1245 goto errout; 1246 } 1247 1248 /* 1249 * Walk through the property group's of the instance and capture 1250 * the property groups that are of type 1251 * SCF_GROUP_TEMPLATE_PG_PATTERN and 1252 * SCF_GROUP_TEMPLATE_PROP_PATTERN. In other words grab the 1253 * pg_pattern and prop_pattern property groups. 1254 */ 1255 while ((rc = scf_iter_next_pg(load_pgiter, load_pgroup)) == 1) { 1256 if (scf_pg_get_type(load_pgroup, type, tsize) <= 0) { 1257 switch (scf_error()) { 1258 case SCF_ERROR_DELETED: 1259 rc = ENOENT; 1260 break; 1261 case SCF_ERROR_CONNECTION_BROKEN: 1262 rc = ECONNABORTED; 1263 break; 1264 default: 1265 bad_error("scf_pg_get_type", scf_error()); 1266 } 1267 goto errout; 1268 } 1269 if ((strcmp(type, SCF_GROUP_TEMPLATE_PG_PATTERN) != 0) && 1270 (strcmp(type, SCF_GROUP_TEMPLATE_PROP_PATTERN) != 0)) { 1271 continue; 1272 } 1273 if ((rc = load_pg(load_pgroup, &ipg, fmri, NULL)) != 0) { 1274 switch (rc) { 1275 case ECANCELED: 1276 case ECONNABORTED: 1277 case EACCES: 1278 case ENOMEM: 1279 break; 1280 default: 1281 bad_error("load_pg", rc); 1282 } 1283 goto errout; 1284 } 1285 if (internal_attach_pgroup(e, ipg) != 0) { 1286 rc = EBADF; 1287 goto errout; 1288 } 1289 } 1290 if (rc == -1) { 1291 /* Error in iteration. */ 1292 switch (scf_error()) { 1293 case SCF_ERROR_CONNECTION_BROKEN: 1294 rc = ECONNABORTED; 1295 break; 1296 case SCF_ERROR_DELETED: 1297 rc = ENOENT; 1298 break; 1299 case SCF_ERROR_NO_RESOURCES: 1300 rc = EAGAIN; 1301 break; 1302 default: 1303 bad_error("scf_iter_next_pg", scf_error()); 1304 } 1305 goto errout; 1306 } 1307 1308 *inst_ptr = e; 1309 scf_instance_destroy(inst); 1310 return (0); 1311 1312 errout: 1313 if (type != NULL) 1314 uu_free(type); 1315 if (inst != NULL) 1316 scf_instance_destroy(inst); 1317 if (e != NULL) 1318 internal_instance_free(e); 1319 return (rc); 1320 } 1321 1322 /* 1323 * These functions compare internal property groups and properties (pgroup_t 1324 * & property_t). They return 1 if the given structures are equal and 1325 * 0 otherwise. Some will report the differences between the two structures. 1326 * They are used by the import functions in svccfg_libscf.c . 1327 */ 1328 1329 int 1330 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname, 1331 int new) 1332 { 1333 value_t *v1, *v2; 1334 1335 const char * const values_diff = gettext("Conflict upgrading %s " 1336 "(property \"%s/%s\" has different values).\n"); 1337 const char * const values_diff_new = gettext("Conflict upgrading %s " 1338 "(new property \"%s/%s\" has different values).\n"); 1339 1340 assert((fmri == NULL) == (pgname == NULL)); 1341 1342 if (fmri != NULL) { 1343 /* 1344 * If we find any differences, we'll report conflicts. But 1345 * conflict messages won't make any sense if the names don't 1346 * match. If the caller supplied fmri, assert that the names 1347 * match. 1348 */ 1349 assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0); 1350 } else { 1351 if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0) 1352 return (0); 1353 } 1354 1355 if (p1->sc_value_type != p2->sc_value_type) { 1356 if (fmri != NULL) { 1357 if (new) 1358 warn(gettext("Conflict upgrading %s " 1359 "(new property \"%s/%s\" has different " 1360 "type).\n"), fmri, pgname, 1361 p1->sc_property_name); 1362 else 1363 warn(gettext("Conflict upgrading %s " 1364 "(property \"%s/%s\" has different " 1365 "type).\n"), fmri, pgname, 1366 p1->sc_property_name); 1367 } 1368 return (0); 1369 } 1370 1371 if (uu_list_numnodes(p1->sc_property_values) != 1372 uu_list_numnodes(p2->sc_property_values)) { 1373 if (fmri != NULL) 1374 warn(new ? values_diff_new : values_diff, fmri, 1375 pgname, p1->sc_property_name); 1376 return (0); 1377 } 1378 1379 v1 = uu_list_first(p1->sc_property_values); 1380 v2 = uu_list_first(p2->sc_property_values); 1381 1382 while (v1 != NULL) { 1383 assert(v2 != NULL); 1384 1385 if (value_cmp(v1, v2, NULL) != 0) { 1386 if (fmri != NULL) 1387 warn(new ? values_diff_new : values_diff, 1388 fmri, pgname, p1->sc_property_name); 1389 return (0); 1390 } 1391 1392 v1 = uu_list_next(p1->sc_property_values, v1); 1393 v2 = uu_list_next(p2->sc_property_values, v2); 1394 } 1395 1396 return (1); 1397 } 1398 1399 int 1400 pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri, 1401 int new) 1402 { 1403 if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) { 1404 assert(fmri == NULL); 1405 return (0); 1406 } 1407 1408 if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) { 1409 if (fmri) { 1410 if (new) 1411 warn(gettext("Conflict upgrading %s " 1412 "(new property group \"%s\" has different " 1413 "flags).\n"), fmri, pg1->sc_pgroup_name); 1414 else 1415 warn(gettext("Conflict upgrading %s " 1416 "(property group \"%s\" has different " 1417 "flags).\n"), fmri, pg1->sc_pgroup_name); 1418 } 1419 return (0); 1420 } 1421 1422 if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) { 1423 if (fmri) { 1424 if (new) 1425 warn(gettext("Conflict upgrading %s " 1426 "(new property group \"%s\" has different " 1427 "type).\n"), fmri, pg1->sc_pgroup_name); 1428 else 1429 warn(gettext("Conflict upgrading %s " 1430 "(property group \"%s\" has different " 1431 "type).\n"), fmri, pg1->sc_pgroup_name); 1432 } 1433 return (0); 1434 } 1435 1436 return (1); 1437 } 1438 1439 int 1440 pg_equal(pgroup_t *pg1, pgroup_t *pg2) 1441 { 1442 property_t *p1, *p2; 1443 1444 if (!pg_attrs_equal(pg1, pg2, NULL, 0)) 1445 return (0); 1446 1447 if (uu_list_numnodes(pg1->sc_pgroup_props) != 1448 uu_list_numnodes(pg2->sc_pgroup_props)) 1449 return (0); 1450 1451 p1 = uu_list_first(pg1->sc_pgroup_props); 1452 p2 = uu_list_first(pg2->sc_pgroup_props); 1453 1454 while (p1 != NULL) { 1455 assert(p2 != NULL); 1456 1457 if (!prop_equal(p1, p2, NULL, NULL, 0)) 1458 return (0); 1459 1460 p1 = uu_list_next(pg1->sc_pgroup_props, p1); 1461 p2 = uu_list_next(pg2->sc_pgroup_props, p2); 1462 } 1463 1464 return (1); 1465 } 1466