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