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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 #include <assert.h> 30 #include <errno.h> 31 #include <libintl.h> 32 #include <libuutil.h> 33 #include <stdarg.h> 34 #include <stddef.h> 35 #include <string.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 252 return (b); 253 } 254 255 void 256 internal_bundle_free(bundle_t *b) 257 { 258 void *cookie = NULL; 259 entity_t *service; 260 261 while ((service = uu_list_teardown(b->sc_bundle_services, &cookie)) != 262 NULL) 263 internal_service_free(service); 264 265 free(b); 266 } 267 268 entity_t * 269 internal_service_new(const char *name) 270 { 271 entity_t *s; 272 273 if ((s = uu_zalloc(sizeof (entity_t))) == NULL) 274 uu_die(gettext("couldn't allocate memory")); 275 276 uu_list_node_init(s, &s->sc_node, entity_pool); 277 278 s->sc_name = name; 279 s->sc_fmri = uu_msprintf("svc:/%s", name); 280 if (s->sc_fmri == NULL) 281 uu_die(gettext("couldn't allocate memory")); 282 283 s->sc_etype = SVCCFG_SERVICE_OBJECT; 284 s->sc_pgroups = uu_list_create(pgroup_pool, s, 0); 285 s->sc_dependents = uu_list_create(pgroup_pool, s, 0); 286 287 s->sc_u.sc_service.sc_service_type = SVCCFG_UNKNOWN_SERVICE; 288 s->sc_u.sc_service.sc_service_instances = uu_list_create(entity_pool, s, 289 0); 290 291 return (s); 292 } 293 294 void 295 internal_service_free(entity_t *s) 296 { 297 entity_t *inst; 298 pgroup_t *pg; 299 void *cookie; 300 301 cookie = NULL; 302 while ((pg = uu_list_teardown(s->sc_pgroups, &cookie)) != NULL) 303 internal_pgroup_free(pg); 304 305 cookie = NULL; 306 while ((pg = uu_list_teardown(s->sc_dependents, &cookie)) != NULL) 307 internal_pgroup_free(pg); 308 309 cookie = NULL; 310 while ((inst = uu_list_teardown(s->sc_u.sc_service.sc_service_instances, 311 &cookie)) != NULL) 312 internal_instance_free(inst); 313 314 free(s); 315 } 316 317 entity_t * 318 internal_instance_new(const char *name) 319 { 320 entity_t *i; 321 322 if ((i = uu_zalloc(sizeof (entity_t))) == NULL) 323 uu_die(gettext("couldn't allocate memory")); 324 325 uu_list_node_init(i, &i->sc_node, entity_pool); 326 327 i->sc_name = name; 328 /* Can't set i->sc_fmri until we're attached to a service. */ 329 i->sc_etype = SVCCFG_INSTANCE_OBJECT; 330 i->sc_pgroups = uu_list_create(pgroup_pool, i, 0); 331 i->sc_dependents = uu_list_create(pgroup_pool, i, 0); 332 333 return (i); 334 } 335 336 void 337 internal_instance_free(entity_t *i) 338 { 339 pgroup_t *pg; 340 void *cookie = NULL; 341 342 while ((pg = uu_list_teardown(i->sc_pgroups, &cookie)) != NULL) 343 internal_pgroup_free(pg); 344 345 cookie = NULL; 346 while ((pg = uu_list_teardown(i->sc_dependents, &cookie)) != NULL) 347 internal_pgroup_free(pg); 348 349 free(i); 350 } 351 352 entity_t * 353 internal_template_new() 354 { 355 entity_t *t; 356 357 if ((t = uu_zalloc(sizeof (entity_t))) == NULL) 358 uu_die(gettext("couldn't allocate memory")); 359 360 uu_list_node_init(t, &t->sc_node, entity_pool); 361 362 t->sc_etype = SVCCFG_TEMPLATE_OBJECT; 363 t->sc_pgroups = uu_list_create(pgroup_pool, t, 0); 364 365 return (t); 366 } 367 368 pgroup_t * 369 internal_pgroup_new() 370 { 371 pgroup_t *p; 372 373 if ((p = uu_zalloc(sizeof (pgroup_t))) == NULL) 374 uu_die(gettext("couldn't allocate memory")); 375 376 uu_list_node_init(p, &p->sc_node, pgroup_pool); 377 378 p->sc_pgroup_props = uu_list_create(property_pool, p, UU_LIST_SORTED); 379 p->sc_pgroup_name = "<unset>"; 380 p->sc_pgroup_type = "<unset>"; 381 382 return (p); 383 } 384 385 void 386 internal_pgroup_free(pgroup_t *pg) 387 { 388 property_t *prop; 389 void *cookie = NULL; 390 391 while ((prop = uu_list_teardown(pg->sc_pgroup_props, &cookie)) != NULL) 392 internal_property_free(prop); 393 394 free(pg); 395 } 396 397 static pgroup_t * 398 find_pgroup(uu_list_t *list, const char *name, const char *type) 399 { 400 pgroup_t *pg; 401 402 for (pg = uu_list_first(list); 403 pg != NULL; 404 pg = uu_list_next(list, pg)) { 405 if (strcmp(pg->sc_pgroup_name, name) != 0) 406 continue; 407 408 if (type == NULL) 409 return (pg); 410 411 if (strcmp(pg->sc_pgroup_type, type) == 0) 412 return (pg); 413 } 414 415 return (NULL); 416 } 417 418 pgroup_t * 419 internal_dependent_find(entity_t *e, const char *name) 420 { 421 return (find_pgroup(e->sc_dependents, name, NULL)); 422 } 423 424 pgroup_t * 425 internal_pgroup_find(entity_t *e, const char *name, const char *type) 426 { 427 return (find_pgroup(e->sc_pgroups, name, type)); 428 } 429 430 pgroup_t * 431 internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type) 432 { 433 pgroup_t *pg; 434 435 pg = internal_pgroup_find(e, name, type); 436 if (pg != NULL) 437 return (pg); 438 439 pg = internal_pgroup_new(); 440 (void) internal_attach_pgroup(e, pg); 441 pg->sc_pgroup_name = strdup(name); 442 pg->sc_pgroup_type = strdup(type); 443 pg->sc_pgroup_flags = 0; 444 445 if (pg->sc_pgroup_name == NULL || pg->sc_pgroup_type == NULL) 446 uu_die(gettext("Could not duplicate string")); 447 448 return (pg); 449 } 450 451 property_t * 452 internal_property_new() 453 { 454 property_t *p; 455 456 if ((p = uu_zalloc(sizeof (property_t))) == NULL) 457 uu_die(gettext("couldn't allocate memory")); 458 459 uu_list_node_init(p, &p->sc_node, property_pool); 460 461 p->sc_property_values = uu_list_create(value_pool, p, UU_LIST_SORTED); 462 p->sc_property_name = "<unset>"; 463 464 return (p); 465 } 466 467 void 468 internal_property_free(property_t *p) 469 { 470 value_t *val; 471 void *cookie = NULL; 472 473 while ((val = uu_list_teardown(p->sc_property_values, &cookie)) != 474 NULL) { 475 if (val->sc_free != NULL) 476 val->sc_free(val); 477 free(val); 478 } 479 480 free(p); 481 } 482 483 property_t * 484 internal_property_find(pgroup_t *pg, const char *name) 485 { 486 property_t *p; 487 488 for (p = uu_list_first(pg->sc_pgroup_props); 489 p != NULL; 490 p = uu_list_next(pg->sc_pgroup_props, p)) 491 if (strcmp(p->sc_property_name, name) == 0) 492 return (p); 493 494 return (NULL); 495 } 496 497 value_t * 498 internal_value_new() 499 { 500 value_t *v; 501 502 if ((v = uu_zalloc(sizeof (value_t))) == NULL) 503 uu_die(gettext("couldn't allocate memory")); 504 505 uu_list_node_init(v, &v->sc_node, value_pool); 506 507 return (v); 508 } 509 510 static void 511 internal_value_free_str(value_t *v) 512 { 513 free(v->sc_u.sc_string); 514 } 515 516 property_t * 517 internal_property_create(const char *name, scf_type_t vtype, uint_t nvals, ...) 518 { 519 va_list args; 520 property_t *p; 521 value_t *v; 522 523 p = internal_property_new(); 524 525 p->sc_property_name = (char *)name; 526 p->sc_value_type = vtype; 527 528 va_start(args, nvals); 529 for (; nvals > 0; nvals--) { 530 531 v = internal_value_new(); 532 v->sc_type = vtype; 533 534 switch (vtype) { 535 case SCF_TYPE_BOOLEAN: 536 case SCF_TYPE_COUNT: 537 v->sc_u.sc_count = va_arg(args, uint64_t); 538 break; 539 case SCF_TYPE_INTEGER: 540 v->sc_u.sc_integer = va_arg(args, int64_t); 541 break; 542 case SCF_TYPE_ASTRING: 543 case SCF_TYPE_FMRI: 544 case SCF_TYPE_HOST: 545 case SCF_TYPE_HOSTNAME: 546 case SCF_TYPE_NET_ADDR_V4: 547 case SCF_TYPE_NET_ADDR_V6: 548 case SCF_TYPE_OPAQUE: 549 case SCF_TYPE_TIME: 550 case SCF_TYPE_URI: 551 case SCF_TYPE_USTRING: 552 v->sc_u.sc_string = (char *)va_arg(args, uchar_t *); 553 break; 554 default: 555 va_end(args); 556 uu_die(gettext("unknown property type (%d)\n"), vtype); 557 break; 558 } 559 560 internal_attach_value(p, v); 561 } 562 va_end(args); 563 564 return (p); 565 } 566 567 /* 568 * Some of these attach functions use uu_list_append() to maintain the 569 * same order across import/export, whereas others are always sorted 570 * anyway, or the order is irrelevant. 571 */ 572 573 int 574 internal_attach_service(bundle_t *bndl, entity_t *svc) 575 { 576 if (uu_list_find(bndl->sc_bundle_services, svc, NULL, NULL) != NULL) { 577 semerr(gettext("Multiple definitions for service %s in " 578 "bundle %s.\n"), svc->sc_name, bndl->sc_bundle_name); 579 return (-1); 580 } 581 582 (void) uu_list_append(bndl->sc_bundle_services, svc); 583 584 return (0); 585 } 586 587 int 588 internal_attach_entity(entity_t *svc, entity_t *ent) 589 { 590 if (ent->sc_etype == SVCCFG_TEMPLATE_OBJECT) { 591 svc->sc_u.sc_service.sc_service_template = ent; 592 return (0); 593 } 594 595 if (svc->sc_etype != SVCCFG_SERVICE_OBJECT) 596 uu_die(gettext("bad entity attach: %s is not a service\n"), 597 svc->sc_name); 598 599 if (uu_list_find(svc->sc_u.sc_service.sc_service_instances, ent, NULL, 600 NULL) != NULL) { 601 semerr(gettext("Multiple definitions of entity %s in service " 602 "%s.\n"), ent->sc_name, svc->sc_name); 603 return (-1); 604 } 605 606 (void) uu_list_prepend(svc->sc_u.sc_service.sc_service_instances, ent); 607 ent->sc_parent = svc; 608 ent->sc_fmri = uu_msprintf("%s:%s", svc->sc_fmri, ent->sc_name); 609 if (ent->sc_fmri == NULL) 610 uu_die(gettext("couldn't allocate memory")); 611 612 return (0); 613 } 614 615 int 616 internal_attach_pgroup(entity_t *ent, pgroup_t *pgrp) 617 { 618 if (uu_list_find(ent->sc_pgroups, pgrp, NULL, NULL) != NULL) { 619 semerr(gettext("Multiple definitions of property group %s in " 620 "entity %s.\n"), pgrp->sc_pgroup_name, ent->sc_name); 621 return (-1); 622 } 623 624 (void) uu_list_append(ent->sc_pgroups, pgrp); 625 626 pgrp->sc_parent = ent; 627 628 return (0); 629 } 630 631 int 632 internal_attach_dependent(entity_t *ent, pgroup_t *pg) 633 { 634 if (uu_list_find(ent->sc_dependents, pg, NULL, NULL) != NULL) { 635 semerr(gettext("Multiple definitions of dependent %s in " 636 "entity %s.\n"), pg->sc_pgroup_name, ent->sc_name); 637 return (-1); 638 } 639 640 (void) uu_list_append(ent->sc_dependents, pg); 641 642 pg->sc_parent = ent; 643 644 return (0); 645 } 646 647 /* 648 * Returns 649 * 0 - success 650 * -1 - prop already exists in pgrp 651 */ 652 int 653 internal_attach_property(pgroup_t *pgrp, property_t *prop) 654 { 655 uu_list_index_t idx; 656 657 if (uu_list_find(pgrp->sc_pgroup_props, prop, NULL, &idx) != NULL) { 658 semerr(gettext("Multiple definitions for property %s in " 659 "property group %s.\n"), prop->sc_property_name, 660 pgrp->sc_pgroup_name); 661 return (-1); 662 } 663 664 uu_list_insert(pgrp->sc_pgroup_props, prop, idx); 665 666 return (0); 667 } 668 669 void 670 internal_attach_value(property_t *prop, value_t *val) 671 { 672 uu_list_index_t idx; 673 674 (void) uu_list_find(prop->sc_property_values, val, NULL, &idx); 675 uu_list_insert(prop->sc_property_values, val, idx); 676 } 677 678 /* 679 * These functions create an internal representation of a property group 680 * (pgroup_t) from the repository (scf_propertygroup_t). They are used by the 681 * import functions in svccfg_libscf.c . 682 * 683 * load_init() must be called first to initialize these globals, and 684 * load_fini() should be called afterwards to destroy them. 685 */ 686 687 static char *loadbuf = NULL; 688 static size_t loadbuf_sz; 689 static scf_property_t *load_prop = NULL; 690 static scf_value_t *load_val = NULL; 691 static scf_iter_t *load_propiter = NULL, *load_valiter = NULL; 692 693 /* 694 * Initialize the global state for the load_*() routines. 695 * Returns 696 * 0 - success 697 * ENOMEM - out of memory 698 */ 699 int 700 load_init(void) 701 { 702 loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ? 703 max_scf_value_len : max_scf_pg_type_len) + 1; 704 705 loadbuf = malloc(loadbuf_sz); 706 if (loadbuf == NULL) 707 return (ENOMEM); 708 709 if ((load_prop = scf_property_create(g_hndl)) == NULL || 710 (load_val = scf_value_create(g_hndl)) == NULL || 711 (load_propiter = scf_iter_create(g_hndl)) == NULL || 712 (load_valiter = scf_iter_create(g_hndl)) == NULL) { 713 load_fini(); 714 return (ENOMEM); 715 } 716 717 return (0); 718 } 719 720 void 721 load_fini(void) 722 { 723 scf_iter_destroy(load_propiter); 724 load_propiter = NULL; 725 scf_iter_destroy(load_valiter); 726 load_valiter = NULL; 727 scf_value_destroy(load_val); 728 load_val = NULL; 729 scf_property_destroy(load_prop); 730 load_prop = NULL; 731 free(loadbuf); 732 loadbuf = NULL; 733 } 734 735 /* 736 * Create a property_t which represents an scf_property_t. Returns 737 * 0 - success 738 * ECANCELED - prop's pg was deleted 739 * ECONNABORTED - repository disconnected 740 * ENOMEM - out of memory 741 */ 742 static int 743 load_property(scf_property_t *prop, property_t **ipp) 744 { 745 property_t *iprop; 746 int r; 747 ssize_t ssz; 748 749 /* get name */ 750 if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) { 751 switch (scf_error()) { 752 case SCF_ERROR_DELETED: 753 return (ECANCELED); 754 755 case SCF_ERROR_CONNECTION_BROKEN: 756 return (ECONNABORTED); 757 758 case SCF_ERROR_NOT_BOUND: 759 case SCF_ERROR_NOT_SET: 760 default: 761 bad_error("scf_property_get_name", scf_error()); 762 } 763 } 764 765 iprop = internal_property_new(); 766 iprop->sc_property_name = strdup(loadbuf); 767 if (iprop->sc_property_name == NULL) { 768 internal_property_free(iprop); 769 return (ENOMEM); 770 } 771 772 /* get type */ 773 if (scf_property_type(prop, &iprop->sc_value_type) != 0) { 774 switch (scf_error()) { 775 case SCF_ERROR_DELETED: 776 r = ECANCELED; 777 goto out; 778 779 case SCF_ERROR_CONNECTION_BROKEN: 780 r = ECONNABORTED; 781 goto out; 782 783 case SCF_ERROR_NOT_BOUND: 784 case SCF_ERROR_NOT_SET: 785 default: 786 bad_error("scf_property_type", scf_error()); 787 } 788 } 789 790 /* get values */ 791 if (scf_iter_property_values(load_valiter, prop) != 0) { 792 switch (scf_error()) { 793 case SCF_ERROR_DELETED: 794 r = ECANCELED; 795 goto out; 796 797 case SCF_ERROR_CONNECTION_BROKEN: 798 r = ECONNABORTED; 799 goto out; 800 801 case SCF_ERROR_HANDLE_MISMATCH: 802 case SCF_ERROR_NOT_BOUND: 803 case SCF_ERROR_NOT_SET: 804 default: 805 bad_error("scf_iter_property_values", scf_error()); 806 } 807 } 808 809 for (;;) { 810 value_t *ival; 811 812 r = scf_iter_next_value(load_valiter, load_val); 813 if (r == 0) 814 break; 815 if (r != 1) { 816 switch (scf_error()) { 817 case SCF_ERROR_DELETED: 818 r = ECANCELED; 819 goto out; 820 821 case SCF_ERROR_CONNECTION_BROKEN: 822 r = ECONNABORTED; 823 goto out; 824 825 case SCF_ERROR_HANDLE_MISMATCH: 826 case SCF_ERROR_NOT_BOUND: 827 case SCF_ERROR_NOT_SET: 828 case SCF_ERROR_INVALID_ARGUMENT: 829 default: 830 bad_error("scf_iter_next_value", scf_error()); 831 } 832 } 833 834 ival = internal_value_new(); 835 ival->sc_type = scf_value_type(load_val); 836 assert(ival->sc_type != SCF_TYPE_INVALID); 837 838 switch (ival->sc_type) { 839 case SCF_TYPE_BOOLEAN: { 840 uint8_t b; 841 842 r = scf_value_get_boolean(load_val, &b); 843 if (r != 0) 844 bad_error("scf_value_get_boolean", scf_error()); 845 ival->sc_u.sc_count = b; 846 break; 847 } 848 849 case SCF_TYPE_COUNT: 850 r = scf_value_get_count(load_val, &ival->sc_u.sc_count); 851 if (r != 0) 852 bad_error("scf_value_get_count", scf_error()); 853 break; 854 855 case SCF_TYPE_INTEGER: 856 r = scf_value_get_integer(load_val, 857 &ival->sc_u.sc_integer); 858 if (r != 0) 859 bad_error("scf_value_get_integer", scf_error()); 860 break; 861 862 default: 863 ssz = scf_value_get_as_string(load_val, loadbuf, 864 loadbuf_sz); 865 if (ssz < 0) 866 bad_error("scf_value_get_as_string", 867 scf_error()); 868 869 ival->sc_u.sc_string = strdup(loadbuf); 870 if (ival->sc_u.sc_string == NULL) { 871 r = ENOMEM; 872 goto out; 873 } 874 875 ival->sc_free = internal_value_free_str; 876 } 877 878 internal_attach_value(iprop, ival); 879 } 880 881 *ipp = iprop; 882 return (0); 883 884 out: 885 free(iprop->sc_property_name); 886 internal_property_free(iprop); 887 return (r); 888 } 889 890 /* 891 * Returns 892 * 0 - success 893 * ECANCELED - pg was deleted 894 * ECONNABORTED - repository disconnected 895 * ENOMEM - out of memory 896 */ 897 int 898 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp) 899 { 900 pgroup_t *ipg; 901 902 ipg = internal_pgroup_new(); 903 904 if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) { 905 switch (scf_error()) { 906 case SCF_ERROR_DELETED: 907 internal_pgroup_free(ipg); 908 return (ECANCELED); 909 910 case SCF_ERROR_CONNECTION_BROKEN: 911 internal_pgroup_free(ipg); 912 return (ECONNABORTED); 913 914 case SCF_ERROR_NOT_SET: 915 case SCF_ERROR_NOT_BOUND: 916 default: 917 bad_error("scf_pg_get_name", scf_error()); 918 } 919 } 920 921 if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) { 922 switch (scf_error()) { 923 case SCF_ERROR_DELETED: 924 internal_pgroup_free(ipg); 925 return (ECANCELED); 926 927 case SCF_ERROR_CONNECTION_BROKEN: 928 internal_pgroup_free(ipg); 929 return (ECONNABORTED); 930 931 case SCF_ERROR_NOT_SET: 932 case SCF_ERROR_NOT_BOUND: 933 default: 934 bad_error("scf_pg_get_name", scf_error()); 935 } 936 } 937 938 ipg->sc_pgroup_name = strdup(loadbuf); 939 if (ipg->sc_pgroup_name == NULL) { 940 internal_pgroup_free(ipg); 941 return (ENOMEM); 942 } 943 944 if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) { 945 switch (scf_error()) { 946 case SCF_ERROR_DELETED: 947 free((char *)ipg->sc_pgroup_name); 948 internal_pgroup_free(ipg); 949 return (ECANCELED); 950 951 case SCF_ERROR_CONNECTION_BROKEN: 952 free((char *)ipg->sc_pgroup_name); 953 internal_pgroup_free(ipg); 954 return (ECONNABORTED); 955 956 case SCF_ERROR_NOT_SET: 957 case SCF_ERROR_NOT_BOUND: 958 default: 959 bad_error("scf_pg_get_name", scf_error()); 960 } 961 } 962 963 ipg->sc_pgroup_type = strdup(loadbuf); 964 if (ipg->sc_pgroup_type == NULL) { 965 free((char *)ipg->sc_pgroup_name); 966 internal_pgroup_free(ipg); 967 return (ENOMEM); 968 } 969 970 *ipgp = ipg; 971 return (0); 972 } 973 974 /* 975 * Load a property group into a pgroup_t. Returns 976 * 0 - success 977 * ECANCELED - pg was deleted 978 * ECONNABORTED - repository disconnected 979 * EBADF - pg is corrupt (error printed if fmri is given) 980 * ENOMEM - out of memory 981 */ 982 int 983 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri, 984 const char *snapname) 985 { 986 pgroup_t *ipg; 987 int r; 988 989 if (scf_iter_pg_properties(load_propiter, pg) != 0) { 990 switch (scf_error()) { 991 case SCF_ERROR_DELETED: 992 return (ECANCELED); 993 994 case SCF_ERROR_CONNECTION_BROKEN: 995 return (ECONNABORTED); 996 997 case SCF_ERROR_HANDLE_MISMATCH: 998 case SCF_ERROR_NOT_SET: 999 case SCF_ERROR_NOT_BOUND: 1000 default: 1001 bad_error("scf_iter_pg_properties", scf_error()); 1002 } 1003 } 1004 1005 r = load_pg_attrs(pg, &ipg); 1006 switch (r) { 1007 case 0: 1008 break; 1009 1010 case ECANCELED: 1011 case ECONNABORTED: 1012 case ENOMEM: 1013 return (r); 1014 1015 default: 1016 bad_error("load_pg_attrs", r); 1017 } 1018 1019 for (;;) { 1020 property_t *iprop; 1021 1022 r = scf_iter_next_property(load_propiter, load_prop); 1023 if (r == 0) 1024 break; 1025 if (r != 1) { 1026 switch (scf_error()) { 1027 case SCF_ERROR_DELETED: 1028 r = ECANCELED; 1029 goto out; 1030 1031 case SCF_ERROR_CONNECTION_BROKEN: 1032 r = ECONNABORTED; 1033 goto out; 1034 1035 case SCF_ERROR_HANDLE_MISMATCH: 1036 case SCF_ERROR_NOT_BOUND: 1037 case SCF_ERROR_NOT_SET: 1038 case SCF_ERROR_INVALID_ARGUMENT: 1039 default: 1040 bad_error("scf_iter_next_property", 1041 scf_error()); 1042 } 1043 } 1044 1045 r = load_property(load_prop, &iprop); 1046 switch (r) { 1047 case 0: 1048 break; 1049 1050 case ECANCELED: 1051 case ECONNABORTED: 1052 case ENOMEM: 1053 goto out; 1054 1055 default: 1056 bad_error("load_property", r); 1057 } 1058 1059 r = internal_attach_property(ipg, iprop); 1060 if (r != 0) { 1061 if (fmri != NULL) { 1062 if (snapname == NULL) 1063 warn(gettext("Property group \"%s\" of " 1064 "%s has multiple definitions of " 1065 "property \"%s\".\n"), 1066 ipg->sc_pgroup_name, fmri, 1067 iprop->sc_property_name); 1068 else 1069 warn(gettext("Property group \"%s\" of " 1070 "the \"%s\" snapshot of %s has " 1071 "multiple definitions of property " 1072 "\"%s\".\n"), 1073 ipg->sc_pgroup_name, snapname, fmri, 1074 iprop->sc_property_name); 1075 } 1076 r = EBADF; 1077 goto out; 1078 } 1079 } 1080 1081 *ipgp = ipg; 1082 return (0); 1083 1084 out: 1085 internal_pgroup_free(ipg); 1086 return (r); 1087 } 1088 1089 /* 1090 * These functions compare internal property groups and properties (pgroup_t 1091 * & property_t). They return 1 if the given structures are equal and 1092 * 0 otherwise. Some will report the differences between the two structures. 1093 * They are used by the import functions in svccfg_libscf.c . 1094 */ 1095 1096 int 1097 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname, 1098 int new) 1099 { 1100 value_t *v1, *v2; 1101 1102 const char * const values_diff = gettext("Conflict upgrading %s " 1103 "(property \"%s/%s\" has different values).\n"); 1104 const char * const values_diff_new = gettext("Conflict upgrading %s " 1105 "(new property \"%s/%s\" has different values).\n"); 1106 1107 assert((fmri == NULL) == (pgname == NULL)); 1108 1109 if (fmri != NULL) { 1110 /* 1111 * If we find any differences, we'll report conflicts. But 1112 * conflict messages won't make any sense if the names don't 1113 * match. If the caller supplied fmri, assert that the names 1114 * match. 1115 */ 1116 assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0); 1117 } else { 1118 if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0) 1119 return (0); 1120 } 1121 1122 if (p1->sc_value_type != p2->sc_value_type) { 1123 if (fmri != NULL) { 1124 if (new) 1125 warn(gettext("Conflict upgrading %s " 1126 "(new property \"%s/%s\" has different " 1127 "type).\n"), fmri, pgname, 1128 p1->sc_property_name); 1129 else 1130 warn(gettext("Conflict upgrading %s " 1131 "(property \"%s/%s\" has different " 1132 "type).\n"), fmri, pgname, 1133 p1->sc_property_name); 1134 } 1135 return (0); 1136 } 1137 1138 if (uu_list_numnodes(p1->sc_property_values) != 1139 uu_list_numnodes(p2->sc_property_values)) { 1140 if (fmri != NULL) 1141 warn(new ? values_diff_new : values_diff, fmri, 1142 pgname, p1->sc_property_name); 1143 return (0); 1144 } 1145 1146 v1 = uu_list_first(p1->sc_property_values); 1147 v2 = uu_list_first(p2->sc_property_values); 1148 1149 while (v1 != NULL) { 1150 assert(v2 != NULL); 1151 1152 if (value_cmp(v1, v2, NULL) != 0) { 1153 if (fmri != NULL) 1154 warn(new ? values_diff_new : values_diff, 1155 fmri, pgname, p1->sc_property_name); 1156 return (0); 1157 } 1158 1159 v1 = uu_list_next(p1->sc_property_values, v1); 1160 v2 = uu_list_next(p2->sc_property_values, v2); 1161 } 1162 1163 return (1); 1164 } 1165 1166 int 1167 pg_attrs_equal(pgroup_t *pg1, pgroup_t *pg2, const char *fmri, int new) 1168 { 1169 if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) { 1170 assert(fmri == NULL); 1171 return (0); 1172 } 1173 1174 if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) { 1175 if (fmri) { 1176 if (new) 1177 warn(gettext("Conflict upgrading %s " 1178 "(new property group \"%s\" has different " 1179 "flags).\n"), fmri, pg1->sc_pgroup_name); 1180 else 1181 warn(gettext("Conflict upgrading %s " 1182 "(property group \"%s\" has different " 1183 "flags).\n"), fmri, pg1->sc_pgroup_name); 1184 } 1185 return (0); 1186 } 1187 1188 if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) { 1189 if (fmri) { 1190 if (new) 1191 warn(gettext("Conflict upgrading %s " 1192 "(new property group \"%s\" has different " 1193 "type).\n"), fmri, pg1->sc_pgroup_name); 1194 else 1195 warn(gettext("Conflict upgrading %s " 1196 "(property group \"%s\" has different " 1197 "type).\n"), fmri, pg1->sc_pgroup_name); 1198 } 1199 return (0); 1200 } 1201 1202 return (1); 1203 } 1204 1205 int 1206 pg_equal(pgroup_t *pg1, pgroup_t *pg2) 1207 { 1208 property_t *p1, *p2; 1209 1210 if (!pg_attrs_equal(pg1, pg2, NULL, 0)) 1211 return (0); 1212 1213 if (uu_list_numnodes(pg1->sc_pgroup_props) != 1214 uu_list_numnodes(pg2->sc_pgroup_props)) 1215 return (0); 1216 1217 p1 = uu_list_first(pg1->sc_pgroup_props); 1218 p2 = uu_list_first(pg2->sc_pgroup_props); 1219 1220 while (p1 != NULL) { 1221 assert(p2 != NULL); 1222 1223 if (!prop_equal(p1, p2, NULL, NULL, 0)) 1224 return (0); 1225 1226 p1 = uu_list_next(pg1->sc_pgroup_props, p1); 1227 p2 = uu_list_next(pg2->sc_pgroup_props, p2); 1228 } 1229 1230 return (1); 1231 } 1232