1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #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 uu_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, 0); 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 (void) uu_list_append(prop->sc_property_values, val); 673 } 674 675 /* 676 * These functions create an internal representation of a property group 677 * (pgroup_t) from the repository (scf_propertygroup_t). They are used by the 678 * import functions in svccfg_libscf.c . 679 * 680 * load_init() must be called first to initialize these globals, and 681 * load_fini() should be called afterwards to destroy them. 682 */ 683 684 static char *loadbuf = NULL; 685 static size_t loadbuf_sz; 686 static scf_property_t *load_prop = NULL; 687 static scf_value_t *load_val = NULL; 688 static scf_iter_t *load_propiter = NULL, *load_valiter = NULL; 689 690 /* 691 * Initialize the global state for the load_*() routines. 692 * Returns 693 * 0 - success 694 * ENOMEM - out of memory 695 */ 696 int 697 load_init(void) 698 { 699 loadbuf_sz = ((max_scf_value_len > max_scf_pg_type_len) ? 700 max_scf_value_len : max_scf_pg_type_len) + 1; 701 702 loadbuf = malloc(loadbuf_sz); 703 if (loadbuf == NULL) 704 return (ENOMEM); 705 706 if ((load_prop = scf_property_create(g_hndl)) == NULL || 707 (load_val = scf_value_create(g_hndl)) == NULL || 708 (load_propiter = scf_iter_create(g_hndl)) == NULL || 709 (load_valiter = scf_iter_create(g_hndl)) == NULL) { 710 load_fini(); 711 return (ENOMEM); 712 } 713 714 return (0); 715 } 716 717 void 718 load_fini(void) 719 { 720 scf_iter_destroy(load_propiter); 721 load_propiter = NULL; 722 scf_iter_destroy(load_valiter); 723 load_valiter = NULL; 724 scf_value_destroy(load_val); 725 load_val = NULL; 726 scf_property_destroy(load_prop); 727 load_prop = NULL; 728 free(loadbuf); 729 loadbuf = NULL; 730 } 731 732 /* 733 * Create a property_t which represents an scf_property_t. Returns 734 * 0 - success 735 * ECANCELED - prop's pg was deleted 736 * ECONNABORTED - repository disconnected 737 * ENOMEM - out of memory 738 * EACCES - permission denied when reading property 739 */ 740 static int 741 load_property(scf_property_t *prop, property_t **ipp) 742 { 743 property_t *iprop; 744 int r; 745 ssize_t ssz; 746 747 /* get name */ 748 if (scf_property_get_name(prop, loadbuf, loadbuf_sz) < 0) { 749 switch (scf_error()) { 750 case SCF_ERROR_DELETED: 751 return (ECANCELED); 752 753 case SCF_ERROR_CONNECTION_BROKEN: 754 return (ECONNABORTED); 755 756 case SCF_ERROR_NOT_BOUND: 757 case SCF_ERROR_NOT_SET: 758 default: 759 bad_error("scf_property_get_name", scf_error()); 760 } 761 } 762 763 iprop = internal_property_new(); 764 iprop->sc_property_name = strdup(loadbuf); 765 if (iprop->sc_property_name == NULL) { 766 internal_property_free(iprop); 767 return (ENOMEM); 768 } 769 770 /* get type */ 771 if (scf_property_type(prop, &iprop->sc_value_type) != 0) { 772 switch (scf_error()) { 773 case SCF_ERROR_DELETED: 774 r = ECANCELED; 775 goto out; 776 777 case SCF_ERROR_CONNECTION_BROKEN: 778 r = ECONNABORTED; 779 goto out; 780 781 case SCF_ERROR_NOT_BOUND: 782 case SCF_ERROR_NOT_SET: 783 default: 784 bad_error("scf_property_type", scf_error()); 785 } 786 } 787 788 /* get values */ 789 if (scf_iter_property_values(load_valiter, prop) != 0) { 790 switch (scf_error()) { 791 case SCF_ERROR_DELETED: 792 r = ECANCELED; 793 goto out; 794 795 case SCF_ERROR_CONNECTION_BROKEN: 796 r = ECONNABORTED; 797 goto out; 798 799 case SCF_ERROR_HANDLE_MISMATCH: 800 case SCF_ERROR_NOT_BOUND: 801 case SCF_ERROR_NOT_SET: 802 default: 803 bad_error("scf_iter_property_values", scf_error()); 804 } 805 } 806 807 for (;;) { 808 value_t *ival; 809 810 r = scf_iter_next_value(load_valiter, load_val); 811 if (r == 0) 812 break; 813 if (r != 1) { 814 switch (scf_error()) { 815 case SCF_ERROR_DELETED: 816 r = ECANCELED; 817 goto out; 818 819 case SCF_ERROR_CONNECTION_BROKEN: 820 r = ECONNABORTED; 821 goto out; 822 823 case SCF_ERROR_PERMISSION_DENIED: 824 r = EACCES; 825 goto out; 826 827 case SCF_ERROR_HANDLE_MISMATCH: 828 case SCF_ERROR_NOT_BOUND: 829 case SCF_ERROR_NOT_SET: 830 case SCF_ERROR_INVALID_ARGUMENT: 831 default: 832 bad_error("scf_iter_next_value", scf_error()); 833 } 834 } 835 836 ival = internal_value_new(); 837 ival->sc_type = scf_value_type(load_val); 838 assert(ival->sc_type != SCF_TYPE_INVALID); 839 840 switch (ival->sc_type) { 841 case SCF_TYPE_BOOLEAN: { 842 uint8_t b; 843 844 r = scf_value_get_boolean(load_val, &b); 845 if (r != 0) 846 bad_error("scf_value_get_boolean", scf_error()); 847 ival->sc_u.sc_count = b; 848 break; 849 } 850 851 case SCF_TYPE_COUNT: 852 r = scf_value_get_count(load_val, &ival->sc_u.sc_count); 853 if (r != 0) 854 bad_error("scf_value_get_count", scf_error()); 855 break; 856 857 case SCF_TYPE_INTEGER: 858 r = scf_value_get_integer(load_val, 859 &ival->sc_u.sc_integer); 860 if (r != 0) 861 bad_error("scf_value_get_integer", scf_error()); 862 break; 863 864 default: 865 ssz = scf_value_get_as_string(load_val, loadbuf, 866 loadbuf_sz); 867 if (ssz < 0) 868 bad_error("scf_value_get_as_string", 869 scf_error()); 870 871 ival->sc_u.sc_string = strdup(loadbuf); 872 if (ival->sc_u.sc_string == NULL) { 873 r = ENOMEM; 874 goto out; 875 } 876 877 ival->sc_free = internal_value_free_str; 878 } 879 880 internal_attach_value(iprop, ival); 881 } 882 883 *ipp = iprop; 884 return (0); 885 886 out: 887 free(iprop->sc_property_name); 888 internal_property_free(iprop); 889 return (r); 890 } 891 892 /* 893 * Returns 894 * 0 - success 895 * ECANCELED - pg was deleted 896 * ECONNABORTED - repository disconnected 897 * ENOMEM - out of memory 898 */ 899 int 900 load_pg_attrs(const scf_propertygroup_t *pg, pgroup_t **ipgp) 901 { 902 pgroup_t *ipg; 903 904 ipg = internal_pgroup_new(); 905 906 if (scf_pg_get_flags(pg, &ipg->sc_pgroup_flags) != 0) { 907 switch (scf_error()) { 908 case SCF_ERROR_DELETED: 909 internal_pgroup_free(ipg); 910 return (ECANCELED); 911 912 case SCF_ERROR_CONNECTION_BROKEN: 913 internal_pgroup_free(ipg); 914 return (ECONNABORTED); 915 916 case SCF_ERROR_NOT_SET: 917 case SCF_ERROR_NOT_BOUND: 918 default: 919 bad_error("scf_pg_get_name", scf_error()); 920 } 921 } 922 923 if (scf_pg_get_name(pg, loadbuf, loadbuf_sz) < 0) { 924 switch (scf_error()) { 925 case SCF_ERROR_DELETED: 926 internal_pgroup_free(ipg); 927 return (ECANCELED); 928 929 case SCF_ERROR_CONNECTION_BROKEN: 930 internal_pgroup_free(ipg); 931 return (ECONNABORTED); 932 933 case SCF_ERROR_NOT_SET: 934 case SCF_ERROR_NOT_BOUND: 935 default: 936 bad_error("scf_pg_get_name", scf_error()); 937 } 938 } 939 940 ipg->sc_pgroup_name = strdup(loadbuf); 941 if (ipg->sc_pgroup_name == NULL) { 942 internal_pgroup_free(ipg); 943 return (ENOMEM); 944 } 945 946 if (scf_pg_get_type(pg, loadbuf, loadbuf_sz) < 0) { 947 switch (scf_error()) { 948 case SCF_ERROR_DELETED: 949 free((char *)ipg->sc_pgroup_name); 950 internal_pgroup_free(ipg); 951 return (ECANCELED); 952 953 case SCF_ERROR_CONNECTION_BROKEN: 954 free((char *)ipg->sc_pgroup_name); 955 internal_pgroup_free(ipg); 956 return (ECONNABORTED); 957 958 case SCF_ERROR_NOT_SET: 959 case SCF_ERROR_NOT_BOUND: 960 default: 961 bad_error("scf_pg_get_name", scf_error()); 962 } 963 } 964 965 ipg->sc_pgroup_type = strdup(loadbuf); 966 if (ipg->sc_pgroup_type == NULL) { 967 free((char *)ipg->sc_pgroup_name); 968 internal_pgroup_free(ipg); 969 return (ENOMEM); 970 } 971 972 *ipgp = ipg; 973 return (0); 974 } 975 976 /* 977 * Load a property group into a pgroup_t. Returns 978 * 0 - success 979 * ECANCELED - pg was deleted 980 * ECONNABORTED - repository disconnected 981 * EBADF - pg is corrupt (error printed if fmri is given) 982 * ENOMEM - out of memory 983 * EACCES - permission denied when reading property 984 */ 985 int 986 load_pg(const scf_propertygroup_t *pg, pgroup_t **ipgp, const char *fmri, 987 const char *snapname) 988 { 989 pgroup_t *ipg; 990 int r; 991 992 if (scf_iter_pg_properties(load_propiter, pg) != 0) { 993 switch (scf_error()) { 994 case SCF_ERROR_DELETED: 995 return (ECANCELED); 996 997 case SCF_ERROR_CONNECTION_BROKEN: 998 return (ECONNABORTED); 999 1000 case SCF_ERROR_HANDLE_MISMATCH: 1001 case SCF_ERROR_NOT_SET: 1002 case SCF_ERROR_NOT_BOUND: 1003 default: 1004 bad_error("scf_iter_pg_properties", scf_error()); 1005 } 1006 } 1007 1008 r = load_pg_attrs(pg, &ipg); 1009 switch (r) { 1010 case 0: 1011 break; 1012 1013 case ECANCELED: 1014 case ECONNABORTED: 1015 case ENOMEM: 1016 return (r); 1017 1018 default: 1019 bad_error("load_pg_attrs", r); 1020 } 1021 1022 for (;;) { 1023 property_t *iprop; 1024 1025 r = scf_iter_next_property(load_propiter, load_prop); 1026 if (r == 0) 1027 break; 1028 if (r != 1) { 1029 switch (scf_error()) { 1030 case SCF_ERROR_DELETED: 1031 r = ECANCELED; 1032 goto out; 1033 1034 case SCF_ERROR_CONNECTION_BROKEN: 1035 r = ECONNABORTED; 1036 goto out; 1037 1038 case SCF_ERROR_HANDLE_MISMATCH: 1039 case SCF_ERROR_NOT_BOUND: 1040 case SCF_ERROR_NOT_SET: 1041 case SCF_ERROR_INVALID_ARGUMENT: 1042 default: 1043 bad_error("scf_iter_next_property", 1044 scf_error()); 1045 } 1046 } 1047 1048 r = load_property(load_prop, &iprop); 1049 switch (r) { 1050 case 0: 1051 break; 1052 1053 case ECANCELED: 1054 case ECONNABORTED: 1055 case ENOMEM: 1056 case EACCES: 1057 goto out; 1058 1059 default: 1060 bad_error("load_property", r); 1061 } 1062 1063 r = internal_attach_property(ipg, iprop); 1064 if (r != 0) { 1065 if (fmri != NULL) { 1066 if (snapname == NULL) 1067 warn(gettext("Property group \"%s\" of " 1068 "%s has multiple definitions of " 1069 "property \"%s\".\n"), 1070 ipg->sc_pgroup_name, fmri, 1071 iprop->sc_property_name); 1072 else 1073 warn(gettext("Property group \"%s\" of " 1074 "the \"%s\" snapshot of %s has " 1075 "multiple definitions of property " 1076 "\"%s\".\n"), 1077 ipg->sc_pgroup_name, snapname, fmri, 1078 iprop->sc_property_name); 1079 } 1080 r = EBADF; 1081 goto out; 1082 } 1083 } 1084 1085 *ipgp = ipg; 1086 return (0); 1087 1088 out: 1089 internal_pgroup_free(ipg); 1090 return (r); 1091 } 1092 1093 /* 1094 * These functions compare internal property groups and properties (pgroup_t 1095 * & property_t). They return 1 if the given structures are equal and 1096 * 0 otherwise. Some will report the differences between the two structures. 1097 * They are used by the import functions in svccfg_libscf.c . 1098 */ 1099 1100 int 1101 prop_equal(property_t *p1, property_t *p2, const char *fmri, const char *pgname, 1102 int new) 1103 { 1104 value_t *v1, *v2; 1105 1106 const char * const values_diff = gettext("Conflict upgrading %s " 1107 "(property \"%s/%s\" has different values).\n"); 1108 const char * const values_diff_new = gettext("Conflict upgrading %s " 1109 "(new property \"%s/%s\" has different values).\n"); 1110 1111 assert((fmri == NULL) == (pgname == NULL)); 1112 1113 if (fmri != NULL) { 1114 /* 1115 * If we find any differences, we'll report conflicts. But 1116 * conflict messages won't make any sense if the names don't 1117 * match. If the caller supplied fmri, assert that the names 1118 * match. 1119 */ 1120 assert(strcmp(p1->sc_property_name, p2->sc_property_name) == 0); 1121 } else { 1122 if (strcmp(p1->sc_property_name, p2->sc_property_name) != 0) 1123 return (0); 1124 } 1125 1126 if (p1->sc_value_type != p2->sc_value_type) { 1127 if (fmri != NULL) { 1128 if (new) 1129 warn(gettext("Conflict upgrading %s " 1130 "(new property \"%s/%s\" has different " 1131 "type).\n"), fmri, pgname, 1132 p1->sc_property_name); 1133 else 1134 warn(gettext("Conflict upgrading %s " 1135 "(property \"%s/%s\" has different " 1136 "type).\n"), fmri, pgname, 1137 p1->sc_property_name); 1138 } 1139 return (0); 1140 } 1141 1142 if (uu_list_numnodes(p1->sc_property_values) != 1143 uu_list_numnodes(p2->sc_property_values)) { 1144 if (fmri != NULL) 1145 warn(new ? values_diff_new : values_diff, fmri, 1146 pgname, p1->sc_property_name); 1147 return (0); 1148 } 1149 1150 v1 = uu_list_first(p1->sc_property_values); 1151 v2 = uu_list_first(p2->sc_property_values); 1152 1153 while (v1 != NULL) { 1154 assert(v2 != NULL); 1155 1156 if (value_cmp(v1, v2, NULL) != 0) { 1157 if (fmri != NULL) 1158 warn(new ? values_diff_new : values_diff, 1159 fmri, pgname, p1->sc_property_name); 1160 return (0); 1161 } 1162 1163 v1 = uu_list_next(p1->sc_property_values, v1); 1164 v2 = uu_list_next(p2->sc_property_values, v2); 1165 } 1166 1167 return (1); 1168 } 1169 1170 int 1171 pg_attrs_equal(const pgroup_t *pg1, const pgroup_t *pg2, const char *fmri, 1172 int new) 1173 { 1174 if (strcmp(pg1->sc_pgroup_name, pg2->sc_pgroup_name) != 0) { 1175 assert(fmri == NULL); 1176 return (0); 1177 } 1178 1179 if (pg1->sc_pgroup_flags != pg2->sc_pgroup_flags) { 1180 if (fmri) { 1181 if (new) 1182 warn(gettext("Conflict upgrading %s " 1183 "(new property group \"%s\" has different " 1184 "flags).\n"), fmri, pg1->sc_pgroup_name); 1185 else 1186 warn(gettext("Conflict upgrading %s " 1187 "(property group \"%s\" has different " 1188 "flags).\n"), fmri, pg1->sc_pgroup_name); 1189 } 1190 return (0); 1191 } 1192 1193 if (strcmp(pg1->sc_pgroup_type, pg2->sc_pgroup_type) != 0) { 1194 if (fmri) { 1195 if (new) 1196 warn(gettext("Conflict upgrading %s " 1197 "(new property group \"%s\" has different " 1198 "type).\n"), fmri, pg1->sc_pgroup_name); 1199 else 1200 warn(gettext("Conflict upgrading %s " 1201 "(property group \"%s\" has different " 1202 "type).\n"), fmri, pg1->sc_pgroup_name); 1203 } 1204 return (0); 1205 } 1206 1207 return (1); 1208 } 1209 1210 int 1211 pg_equal(pgroup_t *pg1, pgroup_t *pg2) 1212 { 1213 property_t *p1, *p2; 1214 1215 if (!pg_attrs_equal(pg1, pg2, NULL, 0)) 1216 return (0); 1217 1218 if (uu_list_numnodes(pg1->sc_pgroup_props) != 1219 uu_list_numnodes(pg2->sc_pgroup_props)) 1220 return (0); 1221 1222 p1 = uu_list_first(pg1->sc_pgroup_props); 1223 p2 = uu_list_first(pg2->sc_pgroup_props); 1224 1225 while (p1 != NULL) { 1226 assert(p2 != NULL); 1227 1228 if (!prop_equal(p1, p2, NULL, NULL, 0)) 1229 return (0); 1230 1231 p1 = uu_list_next(pg1->sc_pgroup_props, p1); 1232 p2 = uu_list_next(pg2->sc_pgroup_props, p2); 1233 } 1234 1235 return (1); 1236 } 1237