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 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * XML document manipulation routines 28 * 29 * These routines provide translation to and from the internal representation to 30 * XML. Directionally-oriented verbs are with respect to the external source, 31 * so lxml_get_service() fetches a service from the XML file into the 32 * internal representation. 33 */ 34 35 #include <libxml/parser.h> 36 #include <libxml/xinclude.h> 37 38 #include <assert.h> 39 #include <ctype.h> 40 #include <errno.h> 41 #include <libintl.h> 42 #include <libscf.h> 43 #include <libscf_priv.h> 44 #include <libuutil.h> 45 #include <sasl/saslutil.h> 46 #include <stdlib.h> 47 #include <string.h> 48 49 #include <sys/types.h> 50 #include <sys/stat.h> 51 #include <unistd.h> 52 53 #include <sys/param.h> 54 #include "manifest_hash.h" 55 56 #include "svccfg.h" 57 58 /* 59 * snprintf(3C) format strings for constructing property names that include 60 * the locale designation. Use %s to indicate where the locale should go. 61 * 62 * The VALUE_* symbols are an exception. The firs %s will be replaced with 63 * "value_". The second %s will be replaced by the name of the value and 64 * %%s will be replaced by the locale designation. These formats are 65 * processed twice by snprintf(3C). The first time captures the value name 66 * and the second time captures the locale. 67 */ 68 #define LOCALE_ONLY_FMT ("%s") 69 #define COMMON_NAME_FMT ("common_name_%s") 70 #define DESCRIPTION_FMT ("description_%s") 71 #define UNITS_FMT ("units_%s") 72 #define VALUE_COMMON_NAME_FMT ("%s%s_common_name_%%s") 73 #define VALUE_DESCRIPTION_FMT ("%s%s_description_%%s") 74 75 /* Attribute names */ 76 const char * const delete_attr = "delete"; 77 const char * const enabled_attr = "enabled"; 78 const char * const lang_attr = "lang"; 79 const char * const manpath_attr = "manpath"; 80 const char * const max_attr = "max"; 81 const char * const min_attr = "min"; 82 const char * const name_attr = "name"; 83 const char * const override_attr = "override"; 84 const char * const required_attr = "required"; 85 const char * const section_attr = "section"; 86 const char * const set_attr = "set"; 87 const char * const target_attr = "target"; 88 const char * const timeout_seconds_attr = "timeout_seconds"; 89 const char * const title_attr = "title"; 90 const char * const type_attr = "type"; 91 const char * const uri_attr = "uri"; 92 const char * const value_attr = "value"; 93 const char * const version_attr = "version"; 94 const char * const xml_lang_attr = "xml:lang"; 95 96 /* Attribute values */ 97 const char * const all_value = "all"; 98 99 const char * const true = "true"; 100 const char * const false = "false"; 101 102 /* 103 * The following list must be kept in the same order as that of 104 * element_t array 105 */ 106 static const char *lxml_elements[] = { 107 "astring_list", /* SC_ASTRING */ 108 "boolean_list", /* SC_BOOLEAN */ 109 "cardinality", /* SC_CARDINALITY */ 110 "choices", /* SC_CHOICES */ 111 "common_name", /* SC_COMMON_NAME */ 112 "constraints", /* SC_CONSTRAINTS */ 113 "count_list", /* SC_COUNT */ 114 "create_default_instance", /* SC_INSTANCE_CREATE_DEFAULT */ 115 "dependency", /* SC_DEPENDENCY */ 116 "dependent", /* SC_DEPENDENT */ 117 "description", /* SC_DESCRIPTION */ 118 "doc_link", /* SC_DOC_LINK */ 119 "documentation", /* SC_DOCUMENTATION */ 120 "enabled", /* SC_ENABLED */ 121 "exec_method", /* SC_EXEC_METHOD */ 122 "fmri_list", /* SC_FMRI */ 123 "host_list", /* SC_HOST */ 124 "hostname_list", /* SC_HOSTNAME */ 125 "include_values", /* SC_INCLUDE_VALUES */ 126 "instance", /* SC_INSTANCE */ 127 "integer_list", /* SC_INTEGER */ 128 "internal_separators", /* SC_INTERNAL_SEPARATORS */ 129 "loctext", /* SC_LOCTEXT */ 130 "manpage", /* SC_MANPAGE */ 131 "method_context", /* SC_METHOD_CONTEXT */ 132 "method_credential", /* SC_METHOD_CREDENTIAL */ 133 "method_profile", /* SC_METHOD_PROFILE */ 134 "method_environment", /* SC_METHOD_ENVIRONMENT */ 135 "envvar", /* SC_METHOD_ENVVAR */ 136 "net_address_v4_list", /* SC_NET_ADDR_V4 */ 137 "net_address_v6_list", /* SC_NET_ADDR_V6 */ 138 "opaque_list", /* SC_OPAQUE */ 139 "pg_pattern", /* SC_PG_PATTERN */ 140 "prop_pattern", /* SC_PROP_PATTERN */ 141 "property", /* SC_PROPERTY */ 142 "property_group", /* SC_PROPERTY_GROUP */ 143 "propval", /* SC_PROPVAL */ 144 "range", /* SC_RANGE */ 145 "restarter", /* SC_RESTARTER */ 146 "service", /* SC_SERVICE */ 147 "service_bundle", /* SC_SERVICE_BUNDLE */ 148 "service_fmri", /* SC_SERVICE_FMRI */ 149 "single_instance", /* SC_INSTANCE_SINGLE */ 150 "stability", /* SC_STABILITY */ 151 "template", /* SC_TEMPLATE */ 152 "time_list", /* SC_TIME */ 153 "units", /* SC_UNITS */ 154 "uri_list", /* SC_URI */ 155 "ustring_list", /* SC_USTRING */ 156 "value", /* SC_VALUE */ 157 "value_node", /* SC_VALUE_NODE */ 158 "values", /* SC_VALUES */ 159 "visibility", /* SC_VISIBILITY */ 160 "xi:fallback", /* SC_XI_FALLBACK */ 161 "xi:include" /* SC_XI_INCLUDE */ 162 }; 163 164 /* 165 * The following list must be kept in the same order as that of 166 * element_t array 167 */ 168 static const char *lxml_prop_types[] = { 169 "astring", /* SC_ASTRING */ 170 "boolean", /* SC_BOOLEAN */ 171 "", /* SC_CARDINALITY */ 172 "", /* SC_CHOICES */ 173 "", /* SC_COMMON_NAME */ 174 "", /* SC_CONSTRAINTS */ 175 "count", /* SC_COUNT */ 176 "", /* SC_INSTANCE_CREATE_DEFAULT */ 177 "", /* SC_DEPENDENCY */ 178 "", /* SC_DEPENDENT */ 179 "", /* SC_DESCRIPTION */ 180 "", /* SC_DOC_LINK */ 181 "", /* SC_DOCUMENTATION */ 182 "", /* SC_ENABLED */ 183 "", /* SC_EXEC_METHOD */ 184 "fmri", /* SC_FMRI */ 185 "host", /* SC_HOST */ 186 "hostname", /* SC_HOSTNAME */ 187 "", /* SC_INCLUDE_VALUES */ 188 "", /* SC_INSTANCE */ 189 "integer", /* SC_INTEGER */ 190 "", /* SC_INTERNAL_SEPARATORS */ 191 "", /* SC_LOCTEXT */ 192 "", /* SC_MANPAGE */ 193 "", /* SC_METHOD_CONTEXT */ 194 "", /* SC_METHOD_CREDENTIAL */ 195 "", /* SC_METHOD_PROFILE */ 196 "", /* SC_METHOD_ENVIRONMENT */ 197 "", /* SC_METHOD_ENVVAR */ 198 "net_address_v4", /* SC_NET_ADDR_V4 */ 199 "net_address_v6", /* SC_NET_ADDR_V6 */ 200 "opaque", /* SC_OPAQUE */ 201 "", /* SC_PG_PATTERN */ 202 "", /* SC_PROP_PATTERN */ 203 "", /* SC_PROPERTY */ 204 "", /* SC_PROPERTY_GROUP */ 205 "", /* SC_PROPVAL */ 206 "", /* SC_RANGE */ 207 "", /* SC_RESTARTER */ 208 "", /* SC_SERVICE */ 209 "", /* SC_SERVICE_BUNDLE */ 210 "", /* SC_SERVICE_FMRI */ 211 "", /* SC_INSTANCE_SINGLE */ 212 "", /* SC_STABILITY */ 213 "", /* SC_TEMPLATE */ 214 "time", /* SC_TIME */ 215 "", /* SC_UNITS */ 216 "uri", /* SC_URI */ 217 "ustring", /* SC_USTRING */ 218 "", /* SC_VALUE */ 219 "", /* SC_VALUE_NODE */ 220 "", /* SC_VALUES */ 221 "", /* SC_VISIBILITY */ 222 "", /* SC_XI_FALLBACK */ 223 "" /* SC_XI_INCLUDE */ 224 }; 225 226 int 227 lxml_init() 228 { 229 if (getenv("SVCCFG_NOVALIDATE") == NULL) { 230 /* 231 * DTD validation, with line numbers. 232 */ 233 (void) xmlLineNumbersDefault(1); 234 xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS; 235 xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS; 236 } 237 238 return (0); 239 } 240 241 static bundle_type_t 242 lxml_xlate_bundle_type(xmlChar *type) 243 { 244 if (xmlStrcmp(type, (const xmlChar *)"manifest") == 0) 245 return (SVCCFG_MANIFEST); 246 247 if (xmlStrcmp(type, (const xmlChar *)"profile") == 0) 248 return (SVCCFG_PROFILE); 249 250 if (xmlStrcmp(type, (const xmlChar *)"archive") == 0) 251 return (SVCCFG_ARCHIVE); 252 253 return (SVCCFG_UNKNOWN_BUNDLE); 254 } 255 256 static service_type_t 257 lxml_xlate_service_type(xmlChar *type) 258 { 259 if (xmlStrcmp(type, (const xmlChar *)"service") == 0) 260 return (SVCCFG_SERVICE); 261 262 if (xmlStrcmp(type, (const xmlChar *)"restarter") == 0) 263 return (SVCCFG_RESTARTER); 264 265 if (xmlStrcmp(type, (const xmlChar *)"milestone") == 0) 266 return (SVCCFG_MILESTONE); 267 268 return (SVCCFG_UNKNOWN_SERVICE); 269 } 270 271 static element_t 272 lxml_xlate_element(const xmlChar *tag) 273 { 274 int i; 275 276 for (i = 0; i < sizeof (lxml_elements) / sizeof (char *); i++) 277 if (xmlStrcmp(tag, (const xmlChar *)lxml_elements[i]) == 0) 278 return ((element_t)i); 279 280 return ((element_t)-1); 281 } 282 283 static uint_t 284 lxml_xlate_boolean(const xmlChar *value) 285 { 286 if (xmlStrcmp(value, (const xmlChar *)true) == 0) 287 return (1); 288 289 if (xmlStrcmp(value, (const xmlChar *)false) == 0) 290 return (0); 291 292 uu_die(gettext("illegal boolean value \"%s\"\n"), value); 293 294 /*NOTREACHED*/ 295 } 296 297 static scf_type_t 298 lxml_element_to_type(element_t type) 299 { 300 switch (type) { 301 case SC_ASTRING: return (SCF_TYPE_ASTRING); 302 case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN); 303 case SC_COUNT: return (SCF_TYPE_COUNT); 304 case SC_FMRI: return (SCF_TYPE_FMRI); 305 case SC_HOST: return (SCF_TYPE_HOST); 306 case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME); 307 case SC_INTEGER: return (SCF_TYPE_INTEGER); 308 case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4); 309 case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6); 310 case SC_OPAQUE: return (SCF_TYPE_OPAQUE); 311 case SC_TIME: return (SCF_TYPE_TIME); 312 case SC_URI: return (SCF_TYPE_URI); 313 case SC_USTRING: return (SCF_TYPE_USTRING); 314 315 default: 316 uu_die(gettext("unknown value type (%d)\n"), type); 317 } 318 319 /* NOTREACHED */ 320 } 321 322 static scf_type_t 323 lxml_element_to_scf_type(element_t type) 324 { 325 switch (type) { 326 case SC_ASTRING: return (SCF_TYPE_ASTRING); 327 case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN); 328 case SC_COUNT: return (SCF_TYPE_COUNT); 329 case SC_FMRI: return (SCF_TYPE_FMRI); 330 case SC_HOST: return (SCF_TYPE_HOST); 331 case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME); 332 case SC_INTEGER: return (SCF_TYPE_INTEGER); 333 case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4); 334 case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6); 335 case SC_OPAQUE: return (SCF_TYPE_OPAQUE); 336 case SC_TIME: return (SCF_TYPE_TIME); 337 case SC_URI: return (SCF_TYPE_URI); 338 case SC_USTRING: return (SCF_TYPE_USTRING); 339 default: 340 uu_die(gettext("unknown value type (%d)\n"), type); 341 } 342 343 /* NOTREACHED */ 344 } 345 346 /* 347 * Create a SCF_TYPE_BOOLEAN property name pname and attach it to the 348 * property group at pgrp. The value of the property will be set from the 349 * attribute named attr. attr must have a value of 0, 1, true or false. 350 * 351 * Zero is returned on success. An error is indicated by -1. It indicates 352 * that either the attribute had an invalid value or that we could not 353 * attach the property to pgrp. The attribute should not have an invalid 354 * value if the DTD is correctly written. 355 */ 356 static int 357 new_bool_prop_from_attr(pgroup_t *pgrp, const char *pname, xmlNodePtr n, 358 const char *attr) 359 { 360 uint64_t bool; 361 xmlChar *val; 362 property_t *p; 363 int r; 364 365 val = xmlGetProp(n, (xmlChar *)attr); 366 if (val == NULL) 367 return (0); 368 369 if ((xmlStrcmp(val, (xmlChar *)"0") == 0) || 370 (xmlStrcmp(val, (xmlChar *)"false") == 0)) { 371 bool = 0; 372 } else if ((xmlStrcmp(val, (xmlChar *)"1") == 0) || 373 (xmlStrcmp(val, (xmlChar *)"true") == 0)) { 374 bool = 1; 375 } else { 376 xmlFree(val); 377 return (-1); 378 } 379 xmlFree(val); 380 p = internal_property_create(pname, SCF_TYPE_BOOLEAN, 1, bool); 381 r = internal_attach_property(pgrp, p); 382 383 if (r != 0) 384 internal_property_free(p); 385 386 return (r); 387 } 388 389 static int 390 new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty, 391 xmlNodePtr n, const char *attr) 392 { 393 xmlChar *val; 394 property_t *p; 395 int r; 396 397 val = xmlGetProp(n, (xmlChar *)attr); 398 399 p = internal_property_create(pname, ty, 1, val); 400 r = internal_attach_property(pgrp, p); 401 402 if (r != 0) 403 internal_property_free(p); 404 405 return (r); 406 } 407 408 static int 409 new_opt_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty, 410 xmlNodePtr n, const char *attr, const char *dflt) 411 { 412 xmlChar *val; 413 property_t *p; 414 int r; 415 416 val = xmlGetProp(n, (xmlChar *)attr); 417 if (val == NULL) { 418 if (dflt == NULL) { 419 /* 420 * A missing attribute is considered to be a 421 * success in this function, because many of the 422 * attributes are optional. Missing non-optional 423 * attributes will be detected later when template 424 * validation is done. 425 */ 426 return (0); 427 } else { 428 val = (xmlChar *)dflt; 429 } 430 } 431 432 p = internal_property_create(pname, ty, 1, val); 433 r = internal_attach_property(pgrp, p); 434 435 if (r != 0) 436 internal_property_free(p); 437 438 return (r); 439 } 440 441 static int 442 lxml_ignorable_block(xmlNodePtr n) 443 { 444 return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 || 445 xmlStrcmp(n->name, (xmlChar *)"comment") == 0) ? 1 : 0); 446 } 447 448 static int 449 lxml_validate_string_value(scf_type_t type, const char *v) 450 { 451 static scf_value_t *scf_value = NULL; 452 static scf_handle_t *scf_hndl = NULL; 453 454 if (scf_hndl == NULL && (scf_hndl = scf_handle_create(SCF_VERSION)) == 455 NULL) 456 return (-1); 457 458 if (scf_value == NULL && (scf_value = scf_value_create(scf_hndl)) == 459 NULL) 460 return (-1); 461 462 return (scf_value_set_from_string(scf_value, type, v)); 463 } 464 465 static void 466 lxml_free_str(value_t *val) 467 { 468 free(val->sc_u.sc_string); 469 } 470 471 static value_t * 472 lxml_make_value(element_t type, const xmlChar *value) 473 { 474 value_t *v; 475 char *endptr; 476 scf_type_t scf_type = SCF_TYPE_INVALID; 477 478 v = internal_value_new(); 479 480 v->sc_type = lxml_element_to_type(type); 481 482 switch (type) { 483 case SC_COUNT: 484 /* 485 * Although an SC_COUNT represents a uint64_t the use 486 * of a negative value is acceptable due to the usage 487 * established by inetd(1M). 488 */ 489 errno = 0; 490 v->sc_u.sc_count = strtoull((char *)value, &endptr, 10); 491 if (errno != 0 || endptr == (char *)value || *endptr) 492 uu_die(gettext("illegal value \"%s\" for " 493 "%s (%s)\n"), (char *)value, 494 lxml_prop_types[type], 495 (errno) ? strerror(errno) : 496 gettext("Illegal character")); 497 break; 498 case SC_INTEGER: 499 errno = 0; 500 v->sc_u.sc_integer = strtoll((char *)value, &endptr, 10); 501 if (errno != 0 || *endptr) 502 uu_die(gettext("illegal value \"%s\" for " 503 "%s (%s)\n"), (char *)value, 504 lxml_prop_types[type], 505 (errno) ? strerror(errno) : "Illegal character"); 506 break; 507 case SC_OPAQUE: 508 case SC_HOST: 509 case SC_HOSTNAME: 510 case SC_NET_ADDR_V4: 511 case SC_NET_ADDR_V6: 512 case SC_FMRI: 513 case SC_URI: 514 case SC_TIME: 515 case SC_ASTRING: 516 case SC_USTRING: 517 scf_type = lxml_element_to_scf_type(type); 518 519 if ((v->sc_u.sc_string = strdup((char *)value)) == NULL) 520 uu_die(gettext("string duplication failed (%s)\n"), 521 strerror(errno)); 522 if (lxml_validate_string_value(scf_type, 523 v->sc_u.sc_string) != 0) 524 uu_die(gettext("illegal value \"%s\" for " 525 "%s (%s)\n"), (char *)value, 526 lxml_prop_types[type], 527 (scf_error()) ? scf_strerror(scf_error()) : 528 gettext("Illegal format")); 529 v->sc_free = lxml_free_str; 530 break; 531 case SC_BOOLEAN: 532 v->sc_u.sc_count = lxml_xlate_boolean(value); 533 break; 534 default: 535 uu_die(gettext("unknown value type (%d)\n"), type); 536 break; 537 } 538 539 return (v); 540 } 541 542 static int 543 lxml_get_value(property_t *prop, element_t vtype, xmlNodePtr value) 544 { 545 xmlNodePtr cursor; 546 547 for (cursor = value->xmlChildrenNode; cursor != NULL; 548 cursor = cursor->next) { 549 xmlChar *assigned_value; 550 value_t *v; 551 552 if (lxml_ignorable_block(cursor)) 553 continue; 554 555 switch (lxml_xlate_element(cursor->name)) { 556 case SC_VALUE_NODE: 557 if ((assigned_value = xmlGetProp(cursor, 558 (xmlChar *)value_attr)) == NULL) 559 uu_die(gettext("no value on value node?\n")); 560 break; 561 default: 562 uu_die(gettext("value list contains illegal element " 563 "\'%s\'\n"), cursor->name); 564 break; 565 } 566 567 v = lxml_make_value(vtype, assigned_value); 568 569 xmlFree(assigned_value); 570 571 internal_attach_value(prop, v); 572 } 573 574 return (0); 575 } 576 577 static int 578 lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval) 579 { 580 property_t *p; 581 element_t r; 582 value_t *v; 583 xmlChar *type, *val, *override; 584 585 p = internal_property_new(); 586 587 p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr); 588 if ((p->sc_property_name == NULL) || (*p->sc_property_name == 0)) 589 uu_die(gettext("property name missing in group '%s'\n"), 590 pgrp->sc_pgroup_name); 591 592 type = xmlGetProp(propval, (xmlChar *)type_attr); 593 if ((type == NULL) || (*type == 0)) 594 uu_die(gettext("property type missing for property '%s/%s'\n"), 595 pgrp->sc_pgroup_name, p->sc_property_name); 596 597 for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); ++r) { 598 if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0) 599 break; 600 } 601 xmlFree(type); 602 if (r >= sizeof (lxml_prop_types) / sizeof (char *)) 603 uu_die(gettext("property type invalid for property '%s/%s'\n"), 604 pgrp->sc_pgroup_name, p->sc_property_name); 605 606 p->sc_value_type = lxml_element_to_type(r); 607 608 val = xmlGetProp(propval, (xmlChar *)value_attr); 609 if (val == NULL) 610 uu_die(gettext("property value missing for property '%s/%s'\n"), 611 pgrp->sc_pgroup_name, p->sc_property_name); 612 613 v = lxml_make_value(r, val); 614 xmlFree(val); 615 internal_attach_value(p, v); 616 617 override = xmlGetProp(propval, (xmlChar *)override_attr); 618 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0); 619 xmlFree(override); 620 621 return (internal_attach_property(pgrp, p)); 622 } 623 624 static int 625 lxml_get_property(pgroup_t *pgrp, xmlNodePtr property) 626 { 627 property_t *p; 628 xmlNodePtr cursor; 629 element_t r; 630 xmlChar *type, *override; 631 632 p = internal_property_new(); 633 634 if (((p->sc_property_name = (char *)xmlGetProp(property, 635 (xmlChar *)name_attr)) == NULL) || (*p->sc_property_name == 0)) 636 uu_die(gettext("property name missing in group \'%s\'\n"), 637 pgrp->sc_pgroup_name); 638 639 if (((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL) || 640 (*type == 0)) { 641 uu_die(gettext("property type missing for " 642 "property \'%s/%s\'\n"), pgrp->sc_pgroup_name, 643 p->sc_property_name); 644 } 645 646 for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); r++) { 647 if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0) 648 break; 649 } 650 651 if (r >= sizeof (lxml_prop_types) / sizeof (char *)) { 652 uu_die(gettext("property type invalid for property '%s/%s'\n"), 653 pgrp->sc_pgroup_name, p->sc_property_name); 654 } 655 656 p->sc_value_type = lxml_element_to_type(r); 657 658 for (cursor = property->xmlChildrenNode; cursor != NULL; 659 cursor = cursor->next) { 660 if (lxml_ignorable_block(cursor)) 661 continue; 662 663 switch (r = lxml_xlate_element(cursor->name)) { 664 case SC_ASTRING: 665 case SC_BOOLEAN: 666 case SC_COUNT: 667 case SC_FMRI: 668 case SC_HOST: 669 case SC_HOSTNAME: 670 case SC_INTEGER: 671 case SC_NET_ADDR_V4: 672 case SC_NET_ADDR_V6: 673 case SC_OPAQUE: 674 case SC_TIME: 675 case SC_URI: 676 case SC_USTRING: 677 if (strcmp(lxml_prop_types[r], (const char *)type) != 0) 678 uu_die(gettext("property \'%s\' " 679 "type-to-list mismatch\n"), 680 p->sc_property_name); 681 682 (void) lxml_get_value(p, r, cursor); 683 break; 684 default: 685 uu_die(gettext("unknown value list type: %s\n"), 686 cursor->name); 687 break; 688 } 689 } 690 691 xmlFree(type); 692 693 override = xmlGetProp(property, (xmlChar *)override_attr); 694 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0); 695 xmlFree(override); 696 697 return (internal_attach_property(pgrp, p)); 698 } 699 700 static int 701 lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab) 702 { 703 return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY, 704 SCF_TYPE_ASTRING, stab, value_attr)); 705 } 706 707 /* 708 * Property groups can go on any of a service, an instance, or a template. 709 */ 710 static int 711 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup) 712 { 713 pgroup_t *pg; 714 xmlNodePtr cursor; 715 xmlChar *name, *type, *delete; 716 717 /* 718 * property group attributes: 719 * name: string 720 * type: string | framework | application 721 */ 722 name = xmlGetProp(pgroup, (xmlChar *)name_attr); 723 type = xmlGetProp(pgroup, (xmlChar *)type_attr); 724 pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type); 725 xmlFree(name); 726 xmlFree(type); 727 728 /* 729 * Walk the children of this lxml_elements, which are a stability 730 * element, property elements, or propval elements. 731 */ 732 for (cursor = pgroup->xmlChildrenNode; cursor != NULL; 733 cursor = cursor->next) { 734 if (lxml_ignorable_block(cursor)) 735 continue; 736 737 switch (lxml_xlate_element(cursor->name)) { 738 case SC_STABILITY: 739 (void) lxml_get_pgroup_stability(pg, cursor); 740 break; 741 case SC_PROPERTY: 742 (void) lxml_get_property(pg, cursor); 743 break; 744 case SC_PROPVAL: 745 (void) lxml_get_propval(pg, cursor); 746 break; 747 default: 748 abort(); 749 break; 750 } 751 } 752 753 delete = xmlGetProp(pgroup, (xmlChar *)delete_attr); 754 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 755 xmlFree(delete); 756 757 return (0); 758 } 759 760 761 /* 762 * Dependency groups, execution methods can go on either a service or an 763 * instance. 764 */ 765 766 static int 767 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile) 768 { 769 property_t *p; 770 771 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN, 772 1, (uint64_t)1); 773 if (internal_attach_property(pg, p) != 0) 774 return (-1); 775 776 return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE, 777 SCF_TYPE_ASTRING, profile, name_attr)); 778 } 779 780 static int 781 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred) 782 { 783 property_t *p; 784 785 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN, 786 1, (uint64_t)0); 787 if (internal_attach_property(pg, p) != 0) 788 return (-1); 789 790 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING, 791 cred, "user", NULL) != 0) 792 return (-1); 793 794 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING, 795 cred, "group", NULL) != 0) 796 return (-1); 797 798 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS, 799 SCF_TYPE_ASTRING, cred, "supp_groups", NULL) != 0) 800 return (-1); 801 802 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES, 803 SCF_TYPE_ASTRING, cred, "privileges", NULL) != 0) 804 return (-1); 805 806 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES, 807 SCF_TYPE_ASTRING, cred, "limit_privileges", NULL) != 0) 808 return (-1); 809 810 return (0); 811 } 812 813 static char * 814 lxml_get_envvar(xmlNodePtr envvar) 815 { 816 char *name; 817 char *value; 818 char *ret; 819 820 name = (char *)xmlGetProp(envvar, (xmlChar *)name_attr); 821 value = (char *)xmlGetProp(envvar, (xmlChar *)value_attr); 822 823 if (strlen(name) == 0 || strchr(name, '=') != NULL) 824 uu_die(gettext("Invalid environment variable " 825 "\"%s\".\n"), name); 826 if (strstr(name, "SMF_") == name) 827 uu_die(gettext("Invalid environment variable " 828 "\"%s\"; \"SMF_\" prefix is reserved.\n"), name); 829 830 ret = uu_msprintf("%s=%s", name, value); 831 xmlFree(name); 832 xmlFree(value); 833 return (ret); 834 } 835 836 static int 837 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment) 838 { 839 property_t *p; 840 xmlNodePtr cursor; 841 value_t *val; 842 843 p = internal_property_create(SCF_PROPERTY_ENVIRONMENT, 844 SCF_TYPE_ASTRING, 0); 845 846 for (cursor = environment->xmlChildrenNode; cursor != NULL; 847 cursor = cursor->next) { 848 char *tmp; 849 850 if (lxml_ignorable_block(cursor)) 851 continue; 852 853 if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR) 854 uu_die(gettext("illegal element \"%s\" on " 855 "method environment for \"%s\"\n"), 856 cursor->name, pg->sc_pgroup_name); 857 858 if ((tmp = lxml_get_envvar(cursor)) == NULL) 859 uu_die(gettext("Out of memory\n")); 860 861 val = internal_value_new(); 862 val->sc_u.sc_string = tmp; 863 val->sc_type = SCF_TYPE_ASTRING; 864 val->sc_free = lxml_free_str; 865 internal_attach_value(p, val); 866 } 867 868 if (internal_attach_property(pg, p) != 0) { 869 internal_property_free(p); 870 return (-1); 871 } 872 873 return (0); 874 } 875 876 static int 877 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx) 878 { 879 xmlNodePtr cursor; 880 881 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY, 882 SCF_TYPE_ASTRING, ctx, "working_directory", NULL) != 0) 883 return (-1); 884 885 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT, 886 SCF_TYPE_ASTRING, ctx, "project", NULL) != 0) 887 return (-1); 888 889 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL, 890 SCF_TYPE_ASTRING, ctx, "resource_pool", NULL) != 0) 891 return (-1); 892 893 for (cursor = ctx->xmlChildrenNode; cursor != NULL; 894 cursor = cursor->next) { 895 if (lxml_ignorable_block(cursor)) 896 continue; 897 898 switch (lxml_xlate_element(cursor->name)) { 899 case SC_METHOD_CREDENTIAL: 900 (void) lxml_get_method_credential(pg, cursor); 901 break; 902 case SC_METHOD_PROFILE: 903 (void) lxml_get_method_profile(pg, cursor); 904 break; 905 case SC_METHOD_ENVIRONMENT: 906 (void) lxml_get_method_environment(pg, cursor); 907 break; 908 default: 909 semerr(gettext("illegal element \'%s\' in method " 910 "context\n"), (char *)cursor); 911 break; 912 } 913 } 914 915 return (0); 916 } 917 918 static int 919 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx) 920 { 921 pgroup_t *pg; 922 923 pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT, 924 (char *)scf_group_framework); 925 926 return (lxml_get_method_context(pg, ctx)); 927 } 928 929 static int 930 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth) 931 { 932 pgroup_t *pg; 933 property_t *p; 934 xmlChar *name, *timeout, *delete; 935 xmlNodePtr cursor; 936 int r = 0; 937 938 name = xmlGetProp(emeth, (xmlChar *)name_attr); 939 pg = internal_pgroup_find_or_create(entity, (char *)name, 940 (char *)SCF_GROUP_METHOD); 941 xmlFree(name); 942 943 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 944 emeth, type_attr) != 0 || 945 new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING, 946 emeth, "exec") != 0) 947 return (-1); 948 949 timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr); 950 if (timeout != NULL) { 951 uint64_t u_timeout; 952 char *endptr; 953 /* 954 * Although an SC_COUNT represents a uint64_t the use 955 * of a negative value is acceptable due to the usage 956 * established by inetd(1M). 957 */ 958 errno = 0; 959 u_timeout = strtoull((char *)timeout, &endptr, 10); 960 if (errno != 0 || endptr == (char *)timeout || *endptr) 961 uu_die(gettext("illegal value \"%s\" for " 962 "timeout_seconds (%s)\n"), 963 (char *)timeout, (errno) ? strerror(errno): 964 gettext("Illegal character")); 965 p = internal_property_create(SCF_PROPERTY_TIMEOUT, 966 SCF_TYPE_COUNT, 1, u_timeout); 967 r = internal_attach_property(pg, p); 968 xmlFree(timeout); 969 } 970 if (r != 0) 971 return (-1); 972 973 /* 974 * There is a possibility that a method context also exists, in which 975 * case the following attributes are defined: project, resource_pool, 976 * working_directory, profile, user, group, privileges, limit_privileges 977 */ 978 for (cursor = emeth->xmlChildrenNode; cursor != NULL; 979 cursor = cursor->next) { 980 if (lxml_ignorable_block(cursor)) 981 continue; 982 983 switch (lxml_xlate_element(cursor->name)) { 984 case SC_STABILITY: 985 if (lxml_get_pgroup_stability(pg, cursor) != 0) 986 return (-1); 987 break; 988 989 case SC_METHOD_CONTEXT: 990 (void) lxml_get_method_context(pg, cursor); 991 break; 992 993 case SC_PROPVAL: 994 (void) lxml_get_propval(pg, cursor); 995 break; 996 997 case SC_PROPERTY: 998 (void) lxml_get_property(pg, cursor); 999 break; 1000 1001 default: 1002 uu_die(gettext("illegal element \"%s\" on " 1003 "execution method \"%s\"\n"), cursor->name, 1004 pg->sc_pgroup_name); 1005 break; 1006 } 1007 } 1008 1009 delete = xmlGetProp(emeth, (xmlChar *)delete_attr); 1010 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 1011 xmlFree(delete); 1012 1013 return (0); 1014 } 1015 1016 static int 1017 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency) 1018 { 1019 pgroup_t *pg; 1020 property_t *p; 1021 xmlNodePtr cursor; 1022 xmlChar *name; 1023 xmlChar *delete; 1024 1025 /* 1026 * dependency attributes: 1027 * name: string 1028 * grouping: require_all | require_any | exclude_all | optional_all 1029 * reset_on: string (error | restart | refresh | none) 1030 * type: service / path /host 1031 */ 1032 1033 name = xmlGetProp(dependency, (xmlChar *)name_attr); 1034 pg = internal_pgroup_find_or_create(entity, (char *)name, 1035 (char *)SCF_GROUP_DEPENDENCY); 1036 xmlFree(name); 1037 1038 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 1039 dependency, type_attr) != 0) 1040 return (-1); 1041 1042 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON, 1043 SCF_TYPE_ASTRING, dependency, "restart_on") != 0) 1044 return (-1); 1045 1046 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 1047 dependency, "grouping") != 0) 1048 return (-1); 1049 1050 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0); 1051 if (internal_attach_property(pg, p) != 0) 1052 return (-1); 1053 1054 for (cursor = dependency->xmlChildrenNode; cursor != NULL; 1055 cursor = cursor->next) { 1056 xmlChar *value; 1057 value_t *v; 1058 1059 if (lxml_ignorable_block(cursor)) 1060 continue; 1061 1062 switch (lxml_xlate_element(cursor->name)) { 1063 case SC_STABILITY: 1064 if (lxml_get_pgroup_stability(pg, cursor) != 0) 1065 return (-1); 1066 break; 1067 1068 case SC_SERVICE_FMRI: 1069 value = xmlGetProp(cursor, (xmlChar *)value_attr); 1070 if (value != NULL) { 1071 if (lxml_validate_string_value(SCF_TYPE_FMRI, 1072 (char *)value) != 0) 1073 uu_die(gettext("illegal value \"%s\" " 1074 "for %s (%s)\n"), (char *)value, 1075 lxml_prop_types[SC_FMRI], 1076 (scf_error()) ? 1077 scf_strerror(scf_error()) : 1078 gettext("Illegal format")); 1079 v = internal_value_new(); 1080 v->sc_type = SCF_TYPE_FMRI; 1081 v->sc_u.sc_string = (char *)value; 1082 internal_attach_value(p, v); 1083 } 1084 1085 break; 1086 1087 case SC_PROPVAL: 1088 (void) lxml_get_propval(pg, cursor); 1089 break; 1090 1091 case SC_PROPERTY: 1092 (void) lxml_get_property(pg, cursor); 1093 break; 1094 1095 default: 1096 uu_die(gettext("illegal element \"%s\" on " 1097 "dependency group \"%s\"\n"), cursor->name, name); 1098 break; 1099 } 1100 } 1101 1102 delete = xmlGetProp(dependency, (xmlChar *)delete_attr); 1103 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 1104 xmlFree(delete); 1105 1106 return (0); 1107 } 1108 1109 /* 1110 * Dependents are hairy. They should cause a dependency pg to be created in 1111 * another service, but we can't do that here; we'll have to wait until the 1112 * import routines. So for now we'll add the dependency group that should go 1113 * in the other service to the entity's dependent list. 1114 */ 1115 static int 1116 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent) 1117 { 1118 xmlChar *name, *or; 1119 xmlNodePtr sf; 1120 xmlChar *fmri, *delete; 1121 pgroup_t *pg; 1122 property_t *p; 1123 xmlNodePtr n; 1124 char *myfmri; 1125 1126 name = xmlGetProp(dependent, (xmlChar *)name_attr); 1127 1128 if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) { 1129 semerr(gettext("Property group and dependent of entity %s " 1130 "have same name \"%s\".\n"), entity->sc_name, name); 1131 xmlFree(name); 1132 return (-1); 1133 } 1134 1135 or = xmlGetProp(dependent, (xmlChar *)override_attr); 1136 1137 pg = internal_pgroup_new(); 1138 pg->sc_pgroup_name = (char *)name; 1139 pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY; 1140 pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0); 1141 xmlFree(or); 1142 if (internal_attach_dependent(entity, pg) != 0) { 1143 xmlFree(name); 1144 internal_pgroup_free(pg); 1145 return (-1); 1146 } 1147 1148 for (sf = dependent->children; sf != NULL; sf = sf->next) 1149 if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0) 1150 break; 1151 assert(sf != NULL); 1152 fmri = xmlGetProp(sf, (xmlChar *)value_attr); 1153 pg->sc_pgroup_fmri = (char *)fmri; 1154 1155 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON, 1156 SCF_TYPE_ASTRING, dependent, "restart_on") != 0) 1157 return (-1); 1158 1159 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 1160 dependent, "grouping") != 0) 1161 return (-1); 1162 1163 myfmri = safe_malloc(max_scf_fmri_len + 1); 1164 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) { 1165 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s", 1166 entity->sc_name) < 0) 1167 bad_error("snprintf", errno); 1168 } else { 1169 assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT); 1170 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s", 1171 entity->sc_parent->sc_name, entity->sc_name) < 0) 1172 bad_error("snprintf", errno); 1173 } 1174 1175 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1, 1176 myfmri); 1177 if (internal_attach_property(pg, p) != 0) 1178 return (-1); 1179 1180 /* Create a property to serve as a do-not-export flag. */ 1181 p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1, 1182 (uint64_t)1); 1183 if (internal_attach_property(pg, p) != 0) 1184 return (-1); 1185 1186 for (n = sf->next; n != NULL; n = n->next) { 1187 if (lxml_ignorable_block(n)) 1188 continue; 1189 1190 switch (lxml_xlate_element(n->name)) { 1191 case SC_STABILITY: 1192 if (new_str_prop_from_attr(pg, 1193 SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n, 1194 value_attr) != 0) 1195 return (-1); 1196 break; 1197 1198 case SC_PROPVAL: 1199 (void) lxml_get_propval(pg, n); 1200 break; 1201 1202 case SC_PROPERTY: 1203 (void) lxml_get_property(pg, n); 1204 break; 1205 1206 default: 1207 uu_die(gettext("unexpected element %s.\n"), n->name); 1208 } 1209 } 1210 1211 /* Go back and fill in defaults. */ 1212 if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) { 1213 p = internal_property_create(SCF_PROPERTY_TYPE, 1214 SCF_TYPE_ASTRING, 1, "service"); 1215 if (internal_attach_property(pg, p) != 0) 1216 return (-1); 1217 } 1218 1219 delete = xmlGetProp(dependent, (xmlChar *)delete_attr); 1220 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 1221 xmlFree(delete); 1222 1223 pg = internal_pgroup_find_or_create(entity, "dependents", 1224 (char *)scf_group_framework); 1225 p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri); 1226 if (internal_attach_property(pg, p) != 0) 1227 return (-1); 1228 1229 return (0); 1230 } 1231 1232 static int 1233 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr) 1234 { 1235 pgroup_t *pg; 1236 property_t *p; 1237 xmlChar *stabval; 1238 1239 if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) || 1240 (*stabval == 0)) { 1241 uu_warn(gettext("no stability value found\n")); 1242 stabval = (xmlChar *)strdup("External"); 1243 } 1244 1245 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1246 (char *)scf_group_framework); 1247 1248 p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY, 1249 SCF_TYPE_ASTRING, 1, stabval); 1250 1251 return (internal_attach_property(pg, p)); 1252 } 1253 1254 static int 1255 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr) 1256 { 1257 pgroup_t *pg; 1258 property_t *p; 1259 xmlChar *restarter; 1260 xmlNode *cursor; 1261 int r; 1262 1263 /* 1264 * Go find child. Child is a service_fmri element. value attribute 1265 * contains restarter FMRI. 1266 */ 1267 1268 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1269 (char *)scf_group_framework); 1270 1271 /* 1272 * Walk its child elements, as appropriate. 1273 */ 1274 for (cursor = rstr->xmlChildrenNode; cursor != NULL; 1275 cursor = cursor->next) { 1276 if (lxml_ignorable_block(cursor)) 1277 continue; 1278 1279 switch (lxml_xlate_element(cursor->name)) { 1280 case SC_SERVICE_FMRI: 1281 restarter = xmlGetProp(cursor, (xmlChar *)value_attr); 1282 break; 1283 default: 1284 uu_die(gettext("illegal element \"%s\" on restarter " 1285 "element for \"%s\"\n"), cursor->name, 1286 entity->sc_name); 1287 break; 1288 } 1289 } 1290 1291 p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1, 1292 restarter); 1293 1294 r = internal_attach_property(pg, p); 1295 if (r != 0) { 1296 internal_property_free(p); 1297 return (-1); 1298 } 1299 1300 return (0); 1301 } 1302 1303 /* 1304 * Add a property containing the localized text from the manifest. The 1305 * property is added to the property group at pg. The name of the created 1306 * property is based on the format at pn_format. This is an snprintf(3C) 1307 * format containing a single %s conversion specification. At conversion 1308 * time, the %s is replaced by the locale designation. 1309 * 1310 * source is the source element and it is only used for error messages. 1311 */ 1312 static int 1313 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext, 1314 const char *pn_format, const char *source) 1315 { 1316 int extra; 1317 xmlNodePtr cursor; 1318 xmlChar *val; 1319 char *stripped, *cp; 1320 property_t *p; 1321 char *prop_name; 1322 int r; 1323 1324 if (((val = xmlGetProp(loctext, (xmlChar *)xml_lang_attr)) == NULL) || 1325 (*val == 0)) { 1326 if (((val = xmlGetProp(loctext, 1327 (xmlChar *)lang_attr)) == NULL) || (*val == 0)) { 1328 val = (xmlChar *)"unknown"; 1329 } 1330 } 1331 1332 _scf_sanitize_locale((char *)val); 1333 prop_name = safe_malloc(max_scf_name_len + 1); 1334 if ((extra = snprintf(prop_name, max_scf_name_len + 1, pn_format, 1335 val)) >= max_scf_name_len + 1) { 1336 extra -= max_scf_name_len; 1337 uu_die(gettext("%s attribute is %d characters too long for " 1338 "%s in %s\n"), 1339 xml_lang_attr, extra, source, service->sc_name); 1340 } 1341 xmlFree(val); 1342 1343 for (cursor = loctext->xmlChildrenNode; cursor != NULL; 1344 cursor = cursor->next) { 1345 if (strcmp("text", (const char *)cursor->name) == 0) { 1346 break; 1347 } else if (strcmp("comment", (const char *)cursor->name) != 0) { 1348 uu_die(gettext("illegal element \"%s\" on loctext " 1349 "element for \"%s\"\n"), cursor->name, 1350 service->sc_name); 1351 } 1352 } 1353 1354 if (cursor == NULL) { 1355 uu_die(gettext("loctext element has no content for \"%s\"\n"), 1356 service->sc_name); 1357 } 1358 1359 /* 1360 * Remove leading and trailing whitespace. 1361 */ 1362 if ((stripped = strdup((const char *)cursor->content)) == NULL) 1363 uu_die(gettext("Out of memory\n")); 1364 1365 for (; isspace(*stripped); stripped++) 1366 ; 1367 for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--) 1368 ; 1369 *(cp + 1) = '\0'; 1370 1371 p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1, 1372 stripped); 1373 1374 r = internal_attach_property(pg, p); 1375 if (r != 0) { 1376 internal_property_free(p); 1377 free(prop_name); 1378 } 1379 1380 return (r); 1381 } 1382 1383 /* 1384 * This function processes all loctext elements in the current XML element 1385 * designated by container. A property is created for each loctext element 1386 * and added to the property group at pg. The name of the property is 1387 * derived from the loctext language designation using the format at 1388 * pn_format. pn_format should be an snprintf format string containing one 1389 * %s which is replaced by the language designation. 1390 * 1391 * The function returns 0 on success and -1 if it is unable to attach the 1392 * newly created property to pg. 1393 */ 1394 static int 1395 lxml_get_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container, 1396 const char *pn_format, const char *source) 1397 { 1398 xmlNodePtr cursor; 1399 1400 /* 1401 * Iterate through one or more loctext elements. The locale is 1402 * used to generate the property name; the contents are the ustring 1403 * value for the property. 1404 */ 1405 for (cursor = container->xmlChildrenNode; cursor != NULL; 1406 cursor = cursor->next) { 1407 if (lxml_ignorable_block(cursor)) 1408 continue; 1409 1410 switch (lxml_xlate_element(cursor->name)) { 1411 case SC_LOCTEXT: 1412 if (lxml_get_loctext(service, pg, cursor, pn_format, 1413 source)) 1414 return (-1); 1415 break; 1416 default: 1417 uu_die(gettext("illegal element \"%s\" on %s element " 1418 "for \"%s\"\n"), cursor->name, container->name, 1419 service->sc_name); 1420 break; 1421 } 1422 } 1423 1424 return (0); 1425 } 1426 1427 /* 1428 * Obtain the specified cardinality attribute and place it in a property 1429 * named prop_name. The converted attribute is placed at *value, and the 1430 * newly created property is returned to propp. NULL is returned to propp 1431 * if the attribute is not provided in the manifest. 1432 * 1433 * 0 is returned upon success, and -1 indicates that the manifest contained 1434 * an invalid cardinality value. 1435 */ 1436 static int 1437 lxml_get_cardinality_attribute(entity_t *service, xmlNodePtr cursor, 1438 const char *attr_name, const char *prop_name, uint64_t *value, 1439 property_t **propp) 1440 { 1441 char *c; 1442 property_t *p; 1443 xmlChar *val; 1444 uint64_t count; 1445 char *endptr; 1446 1447 *propp = NULL; 1448 val = xmlGetProp(cursor, (xmlChar *)attr_name); 1449 if (val == NULL) 1450 return (0); 1451 if (*val == 0) { 1452 xmlFree(val); 1453 return (0); 1454 } 1455 1456 /* 1457 * Make sure that the string at val doesn't have a leading minus 1458 * sign. The strtoull() call below does not catch this problem. 1459 */ 1460 for (c = (char *)val; *c != 0; c++) { 1461 if (isspace(*c)) 1462 continue; 1463 if (isdigit(*c)) 1464 break; 1465 semerr(gettext("\"%c\" is not a legal character in the %s " 1466 "attribute of the %s element in %s.\n"), *c, 1467 attr_name, prop_name, service->sc_name); 1468 xmlFree(val); 1469 return (-1); 1470 } 1471 errno = 0; 1472 count = strtoull((char *)val, &endptr, 10); 1473 if (errno != 0 || endptr == (char *)val || *endptr) { 1474 semerr(gettext("\"%s\" is not a legal number for the %s " 1475 "attribute of the %s element in %s.\n"), (char *)val, 1476 attr_name, prop_name, service->sc_name); 1477 xmlFree(val); 1478 return (-1); 1479 } 1480 1481 xmlFree(val); 1482 1483 /* Value is valid. Create the property. */ 1484 p = internal_property_create(prop_name, SCF_TYPE_COUNT, 1, count); 1485 *value = count; 1486 *propp = p; 1487 return (0); 1488 } 1489 1490 /* 1491 * The cardinality is specified by two attributes max and min at cursor. 1492 * Both are optional, but if present they must be unsigned integers. 1493 */ 1494 static int 1495 lxml_get_tm_cardinality(entity_t *service, pgroup_t *pg, xmlNodePtr cursor) 1496 { 1497 int min_attached = 0; 1498 int compare = 1; 1499 property_t *min_prop; 1500 property_t *max_prop; 1501 uint64_t max; 1502 uint64_t min; 1503 int r; 1504 1505 r = lxml_get_cardinality_attribute(service, cursor, min_attr, 1506 SCF_PROPERTY_TM_CARDINALITY_MIN, &min, &min_prop); 1507 if (r != 0) 1508 return (r); 1509 if (min_prop == NULL) 1510 compare = 0; 1511 r = lxml_get_cardinality_attribute(service, cursor, max_attr, 1512 SCF_PROPERTY_TM_CARDINALITY_MAX, &max, &max_prop); 1513 if (r != 0) 1514 goto errout; 1515 if ((max_prop != NULL) && (compare == 1)) { 1516 if (max < min) { 1517 semerr(gettext("Cardinality max is less than min for " 1518 "the %s element in %s.\n"), pg->sc_pgroup_name, 1519 service->sc_fmri); 1520 goto errout; 1521 } 1522 } 1523 1524 /* Attach the properties to the property group. */ 1525 if (min_prop) { 1526 if (internal_attach_property(pg, min_prop) == 0) { 1527 min_attached = 1; 1528 } else { 1529 goto errout; 1530 } 1531 } 1532 if (max_prop) { 1533 if (internal_attach_property(pg, max_prop) != 0) { 1534 if (min_attached) 1535 internal_detach_property(pg, min_prop); 1536 goto errout; 1537 } 1538 } 1539 return (0); 1540 1541 errout: 1542 if (min_prop) 1543 internal_property_free(min_prop); 1544 if (max_prop) 1545 internal_property_free(max_prop); 1546 return (-1); 1547 } 1548 1549 /* 1550 * Get the common_name which is present as localized text at common_name in 1551 * the manifest. The common_name is stored as the value of a property in 1552 * the property group whose name is SCF_PG_TM_COMMON_NAME and type is 1553 * SCF_GROUP_TEMPLATE. This property group will be created in service if 1554 * it is not already there. 1555 */ 1556 static int 1557 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name) 1558 { 1559 pgroup_t *pg; 1560 1561 /* 1562 * Create the property group, if absent. 1563 */ 1564 pg = internal_pgroup_find_or_create(service, SCF_PG_TM_COMMON_NAME, 1565 SCF_GROUP_TEMPLATE); 1566 1567 return (lxml_get_all_loctext(service, pg, common_name, LOCALE_ONLY_FMT, 1568 "common_name")); 1569 } 1570 1571 /* 1572 * Get the description which is present as localized text at description in 1573 * the manifest. The description is stored as the value of a property in 1574 * the property group whose name is SCF_PG_TM_DESCRIPTION and type is 1575 * SCF_GROUP_TEMPLATE. This property group will be created in service if 1576 * it is not already there. 1577 */ 1578 static int 1579 lxml_get_tm_description(entity_t *service, xmlNodePtr description) 1580 { 1581 pgroup_t *pg; 1582 1583 /* 1584 * Create the property group, if absent. 1585 */ 1586 pg = internal_pgroup_find_or_create(service, SCF_PG_TM_DESCRIPTION, 1587 SCF_GROUP_TEMPLATE); 1588 1589 return (lxml_get_all_loctext(service, pg, description, 1590 LOCALE_ONLY_FMT, "description")); 1591 } 1592 1593 static char * 1594 lxml_label_to_groupname(const char *prefix, const char *in) 1595 { 1596 char *out, *cp; 1597 size_t len, piece_len; 1598 1599 out = uu_zalloc(2 * scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1); 1600 if (out == NULL) 1601 return (NULL); 1602 1603 (void) strcpy(out, prefix); 1604 (void) strcat(out, in); 1605 1606 len = strlen(out); 1607 if (len > max_scf_name_len) { 1608 /* Use the first half and the second half. */ 1609 piece_len = (max_scf_name_len - 2) / 2; 1610 1611 (void) strncpy(out + piece_len, "..", 2); 1612 1613 (void) strcpy(out + piece_len + 2, out + (len - piece_len)); 1614 1615 len = strlen(out); 1616 } 1617 1618 /* 1619 * Translate non-property characters to '_'. 1620 */ 1621 for (cp = out; *cp != '\0'; ++cp) { 1622 if (!(isalnum(*cp) || *cp == '_' || *cp == '-')) 1623 *cp = '_'; 1624 } 1625 1626 *cp = '\0'; 1627 1628 return (out); 1629 } 1630 1631 /* 1632 * If *p is NULL, astring_prop_value() first creates a property with the 1633 * name specified in prop_name. The address of the newly created property 1634 * is placed in *p. 1635 * 1636 * In either case, newly created property or existing property, a new 1637 * SCF_TYPE_ASTRING value will created and attached to the property at *p. 1638 * The value of the newly created property is prop_value. 1639 * 1640 * free_flag is used to indicate whether or not the memory at prop_value 1641 * should be freed when the property is freed by a call to 1642 * internal_property_free(). 1643 */ 1644 static void 1645 astring_prop_value(property_t **p, const char *prop_name, char *prop_value, 1646 boolean_t free_flag) 1647 { 1648 value_t *v; 1649 1650 if (*p == NULL) { 1651 /* Create the property */ 1652 *p = internal_property_new(); 1653 (*p)->sc_property_name = (char *)prop_name; 1654 (*p)->sc_value_type = SCF_TYPE_ASTRING; 1655 } 1656 1657 /* Add the property value to the property's list of values. */ 1658 v = internal_value_new(); 1659 v->sc_type = SCF_TYPE_ASTRING; 1660 if (free_flag == B_TRUE) 1661 v->sc_free = lxml_free_str; 1662 v->sc_u.sc_string = prop_value; 1663 internal_attach_value(*p, v); 1664 } 1665 1666 /* 1667 * If p points to a null pointer, create an internal_separators property 1668 * saving the address at p. For each character at seps create a property 1669 * value and attach it to the property at p. 1670 */ 1671 static void 1672 seps_to_prop_values(property_t **p, xmlChar *seps) 1673 { 1674 value_t *v; 1675 char val_str[2]; 1676 1677 if (*p == NULL) { 1678 *p = internal_property_new(); 1679 (*p)->sc_property_name = 1680 (char *)SCF_PROPERTY_INTERNAL_SEPARATORS; 1681 (*p)->sc_value_type = SCF_TYPE_ASTRING; 1682 } 1683 1684 /* Add the values to the property's list. */ 1685 val_str[1] = 0; /* Terminate the string. */ 1686 for (; *seps != 0; seps++) { 1687 v = internal_value_new(); 1688 v->sc_type = (*p)->sc_value_type; 1689 v->sc_free = lxml_free_str; 1690 val_str[0] = *seps; 1691 v->sc_u.sc_string = strdup(val_str); 1692 if (v->sc_u.sc_string == NULL) 1693 uu_die(gettext("Out of memory\n")); 1694 internal_attach_value(*p, v); 1695 } 1696 } 1697 1698 /* 1699 * Create an internal_separators property and attach it to the property 1700 * group at pg. The separator characters are provided in the text nodes 1701 * that are the children of seps. Each separator character is stored as a 1702 * property value in the internal_separators property. 1703 */ 1704 static int 1705 lxml_get_tm_internal_seps(entity_t *service, pgroup_t *pg, xmlNodePtr seps) 1706 { 1707 xmlNodePtr cursor; 1708 property_t *prop = NULL; 1709 int r; 1710 1711 for (cursor = seps->xmlChildrenNode; cursor != NULL; 1712 cursor = cursor->next) { 1713 if (strcmp("text", (const char *)cursor->name) == 0) { 1714 seps_to_prop_values(&prop, cursor->content); 1715 } else if (strcmp("comment", (const char *)cursor->name) != 0) { 1716 uu_die(gettext("illegal element \"%s\" on %s element " 1717 "for \"%s\"\n"), cursor->name, seps->name, 1718 service->sc_name); 1719 } 1720 } 1721 if (prop == NULL) { 1722 semerr(gettext("The %s element in %s had an empty list of " 1723 "separators.\n"), (const char *)seps->name, 1724 service->sc_name); 1725 return (-1); 1726 } 1727 r = internal_attach_property(pg, prop); 1728 if (r != 0) 1729 internal_property_free(prop); 1730 return (r); 1731 } 1732 1733 static int 1734 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage) 1735 { 1736 pgroup_t *pg; 1737 char *pgname; 1738 xmlChar *title; 1739 1740 /* 1741 * Fetch title attribute, convert to something sanitized, and create 1742 * property group. 1743 */ 1744 title = xmlGetProp(manpage, (xmlChar *)title_attr); 1745 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, 1746 (const char *)title); 1747 xmlFree(title); 1748 1749 pg = internal_pgroup_find_or_create(service, pgname, 1750 (char *)SCF_GROUP_TEMPLATE); 1751 1752 /* 1753 * Each attribute is an astring property within the group. 1754 */ 1755 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_TITLE, 1756 SCF_TYPE_ASTRING, manpage, title_attr) != 0 || 1757 new_str_prop_from_attr(pg, SCF_PROPERTY_TM_SECTION, 1758 SCF_TYPE_ASTRING, manpage, section_attr) != 0 || 1759 new_str_prop_from_attr(pg, SCF_PROPERTY_TM_MANPATH, 1760 SCF_TYPE_ASTRING, manpage, manpath_attr) != 0) 1761 return (-1); 1762 1763 return (0); 1764 } 1765 1766 static int 1767 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link) 1768 { 1769 pgroup_t *pg; 1770 char *pgname; 1771 xmlChar *name; 1772 1773 /* 1774 * Fetch name attribute, convert name to something sanitized, and create 1775 * property group. 1776 */ 1777 name = xmlGetProp(doc_link, (xmlChar *)name_attr); 1778 1779 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX, 1780 (const char *)name); 1781 1782 pg = internal_pgroup_find_or_create(service, pgname, 1783 (char *)SCF_GROUP_TEMPLATE); 1784 xmlFree(name); 1785 1786 /* 1787 * Each attribute is an astring property within the group. 1788 */ 1789 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, SCF_TYPE_ASTRING, 1790 doc_link, name_attr) != 0 || 1791 new_str_prop_from_attr(pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING, 1792 doc_link, uri_attr) != 0) 1793 return (-1); 1794 1795 return (0); 1796 } 1797 1798 static int 1799 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation) 1800 { 1801 xmlNodePtr cursor; 1802 1803 for (cursor = documentation->xmlChildrenNode; cursor != NULL; 1804 cursor = cursor->next) { 1805 if (lxml_ignorable_block(cursor)) 1806 continue; 1807 1808 switch (lxml_xlate_element(cursor->name)) { 1809 case SC_MANPAGE: 1810 (void) lxml_get_tm_manpage(service, cursor); 1811 break; 1812 case SC_DOC_LINK: 1813 (void) lxml_get_tm_doclink(service, cursor); 1814 break; 1815 default: 1816 uu_die(gettext("illegal element \"%s\" on template " 1817 "for service \"%s\"\n"), 1818 cursor->name, service->sc_name); 1819 } 1820 } 1821 1822 return (0); 1823 } 1824 1825 static int 1826 lxml_get_prop_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor) 1827 { 1828 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, 1829 SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) { 1830 return (-1); 1831 } 1832 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE, 1833 SCF_TYPE_ASTRING, cursor, type_attr, "") != 0) { 1834 return (-1); 1835 } 1836 if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor, 1837 required_attr) != 0) 1838 return (-1); 1839 return (0); 1840 } 1841 1842 static int 1843 lxml_get_tm_include_values(entity_t *service, pgroup_t *pg, 1844 xmlNodePtr include_values, const char *prop_name) 1845 { 1846 boolean_t attach_to_pg = B_FALSE; 1847 property_t *p; 1848 int r = 0; 1849 char *type; 1850 1851 /* Get the type attribute of the include_values element. */ 1852 type = (char *)xmlGetProp(include_values, (const xmlChar *)type_attr); 1853 if ((type == NULL) || (*type == 0)) { 1854 uu_die(gettext("%s element requires a %s attribute in the %s " 1855 "service.\n"), include_values->name, type_attr, 1856 service->sc_name); 1857 } 1858 1859 /* Add the type to the values of the prop_name property. */ 1860 p = internal_property_find(pg, prop_name); 1861 if (p == NULL) 1862 attach_to_pg = B_TRUE; 1863 astring_prop_value(&p, prop_name, type, B_FALSE); 1864 if (attach_to_pg == B_TRUE) { 1865 r = internal_attach_property(pg, p); 1866 if (r != 0) 1867 internal_property_free(p); 1868 } 1869 return (r); 1870 } 1871 1872 #define RC_MIN 0 1873 #define RC_MAX 1 1874 #define RC_COUNT 2 1875 1876 /* 1877 * Verify that the strings at min and max are valid numeric strings. Also 1878 * verify that max is numerically >= min. 1879 * 1880 * 0 is returned if the range is valid, and -1 is returned if it is not. 1881 */ 1882 static int 1883 verify_range(entity_t *service, xmlNodePtr range, char *min, char *max) 1884 { 1885 char *c; 1886 int i; 1887 int is_signed = 0; 1888 int inverted = 0; 1889 const char *limit[RC_COUNT]; 1890 char *strings[RC_COUNT]; 1891 uint64_t urange[RC_COUNT]; /* unsigned range. */ 1892 int64_t srange[RC_COUNT]; /* signed range. */ 1893 1894 strings[RC_MIN] = min; 1895 strings[RC_MAX] = max; 1896 limit[RC_MIN] = min_attr; 1897 limit[RC_MAX] = max_attr; 1898 1899 /* See if the range is signed. */ 1900 for (i = 0; (i < RC_COUNT) && (is_signed == 0); i++) { 1901 c = strings[i]; 1902 while (isspace(*c)) { 1903 c++; 1904 } 1905 if (*c == '-') 1906 is_signed = 1; 1907 } 1908 1909 /* Attempt to convert the strings. */ 1910 for (i = 0; i < RC_COUNT; i++) { 1911 errno = 0; 1912 if (is_signed) { 1913 srange[i] = strtoll(strings[i], &c, 0); 1914 } else { 1915 urange[i] = strtoull(strings[i], &c, 0); 1916 } 1917 if ((errno != 0) || (c == strings[i]) || (*c != 0)) { 1918 /* Conversion failed. */ 1919 uu_die(gettext("Unable to convert %s for the %s " 1920 "element in service %s.\n"), limit[i], 1921 (char *)range->name, service->sc_name); 1922 } 1923 } 1924 1925 /* Make sure that min is <= max */ 1926 if (is_signed) { 1927 if (srange[RC_MAX] < srange[RC_MIN]) 1928 inverted = 1; 1929 } else { 1930 if (urange[RC_MAX] < urange[RC_MIN]) 1931 inverted = 1; 1932 } 1933 if (inverted != 0) { 1934 semerr(gettext("Maximum less than minimum for the %s element " 1935 "in service %s.\n"), (char *)range->name, 1936 service->sc_name); 1937 return (-1); 1938 } 1939 1940 return (0); 1941 } 1942 1943 /* 1944 * This, function creates a property named prop_name. The range element 1945 * should have two attributes -- min and max. The property value then 1946 * becomes the concatenation of their value separated by a comma. The 1947 * property is then attached to the property group at pg. 1948 * 1949 * If pg already contains a property with a name of prop_name, it is only 1950 * necessary to create a new value and attach it to the existing property. 1951 */ 1952 static int 1953 lxml_get_tm_range(entity_t *service, pgroup_t *pg, xmlNodePtr range, 1954 const char *prop_name) 1955 { 1956 boolean_t attach_to_pg = B_FALSE; 1957 char *max; 1958 char *min; 1959 property_t *p; 1960 char *prop_value; 1961 int r = 0; 1962 1963 /* Get max and min from the XML description. */ 1964 max = (char *)xmlGetProp(range, (xmlChar *)max_attr); 1965 if ((max == NULL) || (*max == 0)) { 1966 uu_die(gettext("%s element is missing the %s attribute in " 1967 "service %s.\n"), (char *)range->name, max_attr, 1968 service->sc_name); 1969 } 1970 min = (char *)xmlGetProp(range, (xmlChar *)min_attr); 1971 if ((min == NULL) || (*min == 0)) { 1972 uu_die(gettext("%s element is missing the %s attribute in " 1973 "service %s.\n"), (char *)range->name, min_attr, 1974 service->sc_name); 1975 } 1976 if (verify_range(service, range, min, max) != 0) { 1977 xmlFree(min); 1978 xmlFree(max); 1979 return (-1); 1980 } 1981 1982 /* Property value is concatenation of min and max. */ 1983 prop_value = safe_malloc(max_scf_value_len + 1); 1984 if (snprintf(prop_value, max_scf_value_len + 1, "%s,%s", min, max) >= 1985 max_scf_value_len + 1) { 1986 uu_die(gettext("min and max are too long for the %s element " 1987 "of %s.\n"), (char *)range->name, service->sc_name); 1988 } 1989 xmlFree(min); 1990 xmlFree(max); 1991 1992 /* 1993 * If necessary create the property and attach it to the property 1994 * group. 1995 */ 1996 p = internal_property_find(pg, prop_name); 1997 if (p == NULL) 1998 attach_to_pg = B_TRUE; 1999 astring_prop_value(&p, prop_name, prop_value, B_TRUE); 2000 if (attach_to_pg == B_TRUE) { 2001 r = internal_attach_property(pg, p); 2002 if (r != 0) { 2003 internal_property_free(p); 2004 } 2005 } 2006 return (r); 2007 } 2008 2009 /* 2010 * Determine how many plain characters are represented by count Base32 2011 * encoded characters. 5 plain text characters are converted to 8 Base32 2012 * characters. 2013 */ 2014 static size_t 2015 encoded_count_to_plain(size_t count) 2016 { 2017 return (5 * ((count + 7) / 8)); 2018 } 2019 2020 /* 2021 * The value element contains 0 or 1 common_name element followed by 0 or 1 2022 * description element. It also has a required attribute called "name". 2023 * The common_name and description are stored as property values in pg. 2024 * The property names are: 2025 * value_<name>_common_name_<lang> 2026 * value_<name>_description_<lang> 2027 * 2028 * The <name> portion of the preceeding proper names requires more 2029 * explanation. Ideally it would just the name attribute of this value 2030 * element. Unfortunately, the name attribute can contain characters that 2031 * are not legal in a property name. Thus, we base 32 encode the name 2032 * attribute and use that for <name>. 2033 * 2034 * There are cases where the caller needs to know the name, so it is 2035 * returned through the name_value pointer if it is not NULL. 2036 * 2037 * Parameters: 2038 * service - Information about the service that is being 2039 * processed. This function only uses this parameter 2040 * for producing error messages. 2041 * 2042 * pg - The property group to receive the newly created 2043 * properties. 2044 * 2045 * value - Pointer to the value element in the XML tree. 2046 * 2047 * name_value - Address to receive the value of the name attribute. 2048 * The caller must free the memory. 2049 */ 2050 static int 2051 lxml_get_tm_value_element(entity_t *service, pgroup_t *pg, xmlNodePtr value, 2052 char **name_value) 2053 { 2054 char *common_name_fmt; 2055 xmlNodePtr cursor; 2056 char *description_fmt; 2057 char *encoded_value = NULL; 2058 size_t extra; 2059 char *value_name; 2060 int r = 0; 2061 2062 common_name_fmt = safe_malloc(max_scf_name_len + 1); 2063 description_fmt = safe_malloc(max_scf_name_len + 1); 2064 2065 /* 2066 * Get the value of our name attribute, so that we can use it to 2067 * construct property names. 2068 */ 2069 value_name = (char *)xmlGetProp(value, (xmlChar *)name_attr); 2070 /* The value name must be present, but it can be empty. */ 2071 if (value_name == NULL) { 2072 uu_die(gettext("%s element requires a %s attribute in the %s " 2073 "service.\n"), (char *)value->name, name_attr, 2074 service->sc_name); 2075 } 2076 2077 /* 2078 * The value_name may contain characters that are not valid in in a 2079 * property name. So we will encode value_name and then use the 2080 * encoded value in the property name. 2081 */ 2082 encoded_value = safe_malloc(max_scf_name_len + 1); 2083 if (scf_encode32(value_name, strlen(value_name), encoded_value, 2084 max_scf_name_len + 1, &extra, SCF_ENCODE32_PAD) != 0) { 2085 extra = encoded_count_to_plain(extra - max_scf_name_len); 2086 uu_die(gettext("Constructed property name is %u characters " 2087 "too long for value \"%s\" in the %s service.\n"), 2088 extra, value_name, service->sc_name); 2089 } 2090 if ((extra = snprintf(common_name_fmt, max_scf_name_len + 1, 2091 VALUE_COMMON_NAME_FMT, SCF_PROPERTY_TM_VALUE_PREFIX, 2092 encoded_value)) >= max_scf_name_len + 1) { 2093 extra = encoded_count_to_plain(extra - max_scf_name_len); 2094 uu_die(gettext("Name attribute is " 2095 "%u characters too long for %s in service %s\n"), 2096 extra, (char *)value->name, service->sc_name); 2097 } 2098 if ((extra = snprintf(description_fmt, max_scf_name_len + 1, 2099 VALUE_DESCRIPTION_FMT, SCF_PROPERTY_TM_VALUE_PREFIX, 2100 encoded_value)) >= max_scf_name_len + 1) { 2101 extra = encoded_count_to_plain(extra - max_scf_name_len); 2102 uu_die(gettext("Name attribute is " 2103 "%u characters too long for %s in service %s\n"), 2104 extra, (char *)value->name, service->sc_name); 2105 } 2106 2107 for (cursor = value->xmlChildrenNode; 2108 cursor != NULL; 2109 cursor = cursor->next) { 2110 if (lxml_ignorable_block(cursor)) 2111 continue; 2112 switch (lxml_xlate_element(cursor->name)) { 2113 case SC_COMMON_NAME: 2114 r = lxml_get_all_loctext(service, pg, cursor, 2115 common_name_fmt, (const char *)cursor->name); 2116 break; 2117 case SC_DESCRIPTION: 2118 r = lxml_get_all_loctext(service, pg, cursor, 2119 description_fmt, (const char *)cursor->name); 2120 break; 2121 default: 2122 uu_die(gettext("\"%s\" is an illegal element in %s " 2123 "of service %s\n"), (char *)cursor->name, 2124 (char *)value->name, service->sc_name); 2125 } 2126 if (r != 0) 2127 break; 2128 } 2129 2130 free(description_fmt); 2131 free(common_name_fmt); 2132 if (r == 0) { 2133 *name_value = safe_strdup(value_name); 2134 } 2135 xmlFree(value_name); 2136 free(encoded_value); 2137 return (r); 2138 } 2139 2140 static int 2141 lxml_get_tm_choices(entity_t *service, pgroup_t *pg, xmlNodePtr choices) 2142 { 2143 xmlNodePtr cursor; 2144 char *name_value; 2145 property_t *name_prop = NULL; 2146 int r = 0; 2147 2148 for (cursor = choices->xmlChildrenNode; 2149 (cursor != NULL) && (r == 0); 2150 cursor = cursor->next) { 2151 if (lxml_ignorable_block(cursor)) 2152 continue; 2153 switch (lxml_xlate_element(cursor->name)) { 2154 case SC_INCLUDE_VALUES: 2155 (void) lxml_get_tm_include_values(service, pg, cursor, 2156 SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES); 2157 break; 2158 case SC_RANGE: 2159 r = lxml_get_tm_range(service, pg, cursor, 2160 SCF_PROPERTY_TM_CHOICES_RANGE); 2161 if (r != 0) 2162 goto out; 2163 break; 2164 case SC_VALUE: 2165 r = lxml_get_tm_value_element(service, pg, cursor, 2166 &name_value); 2167 if (r == 0) { 2168 /* 2169 * There is no need to free the memory 2170 * associated with name_value, because the 2171 * property value will end up pointing to 2172 * the memory. 2173 */ 2174 astring_prop_value(&name_prop, 2175 SCF_PROPERTY_TM_CHOICES_NAME, name_value, 2176 B_TRUE); 2177 } else { 2178 goto out; 2179 } 2180 break; 2181 default: 2182 uu_die(gettext("%s is an invalid element of " 2183 "choices for service %s.\n"), cursor->name, 2184 service->sc_name); 2185 } 2186 } 2187 2188 out: 2189 /* Attach the name property if we created one. */ 2190 if ((r == 0) && (name_prop != NULL)) { 2191 r = internal_attach_property(pg, name_prop); 2192 } 2193 if ((r != 0) && (name_prop != NULL)) { 2194 internal_property_free(name_prop); 2195 } 2196 2197 return (r); 2198 } 2199 2200 static int 2201 lxml_get_tm_constraints(entity_t *service, pgroup_t *pg, xmlNodePtr constraints) 2202 { 2203 xmlNodePtr cursor; 2204 char *name_value; 2205 property_t *name_prop = NULL; 2206 int r = 0; 2207 2208 for (cursor = constraints->xmlChildrenNode; 2209 (cursor != NULL) && (r == 0); 2210 cursor = cursor->next) { 2211 if (lxml_ignorable_block(cursor)) 2212 continue; 2213 switch (lxml_xlate_element(cursor->name)) { 2214 case SC_RANGE: 2215 r = lxml_get_tm_range(service, pg, cursor, 2216 SCF_PROPERTY_TM_CONSTRAINT_RANGE); 2217 if (r != 0) 2218 goto out; 2219 break; 2220 case SC_VALUE: 2221 r = lxml_get_tm_value_element(service, pg, cursor, 2222 &name_value); 2223 if (r == 0) { 2224 /* 2225 * There is no need to free the memory 2226 * associated with name_value, because the 2227 * property value will end up pointing to 2228 * the memory. 2229 */ 2230 astring_prop_value(&name_prop, 2231 SCF_PROPERTY_TM_CONSTRAINT_NAME, name_value, 2232 B_TRUE); 2233 } else { 2234 goto out; 2235 } 2236 break; 2237 default: 2238 uu_die(gettext("%s is an invalid element of " 2239 "constraints for service %s.\n"), cursor->name, 2240 service->sc_name); 2241 } 2242 } 2243 2244 out: 2245 /* Attach the name property if we created one. */ 2246 if ((r == 0) && (name_prop != NULL)) { 2247 r = internal_attach_property(pg, name_prop); 2248 } 2249 if ((r != 0) && (name_prop != NULL)) { 2250 internal_property_free(name_prop); 2251 } 2252 2253 return (r); 2254 } 2255 2256 /* 2257 * The values element contains one or more value elements. 2258 */ 2259 static int 2260 lxml_get_tm_values(entity_t *service, pgroup_t *pg, xmlNodePtr values) 2261 { 2262 xmlNodePtr cursor; 2263 char *name_value; 2264 property_t *name_prop = NULL; 2265 int r = 0; 2266 2267 for (cursor = values->xmlChildrenNode; 2268 (cursor != NULL) && (r == 0); 2269 cursor = cursor->next) { 2270 if (lxml_ignorable_block(cursor)) 2271 continue; 2272 if (lxml_xlate_element(cursor->name) != SC_VALUE) { 2273 uu_die(gettext("\"%s\" is an illegal element in the " 2274 "%s element of %s\n"), (char *)cursor->name, 2275 (char *)values->name, service->sc_name); 2276 } 2277 r = lxml_get_tm_value_element(service, pg, cursor, &name_value); 2278 if (r == 0) { 2279 /* 2280 * There is no need to free the memory 2281 * associated with name_value, because the 2282 * property value will end up pointing to 2283 * the memory. 2284 */ 2285 astring_prop_value(&name_prop, 2286 SCF_PROPERTY_TM_VALUES_NAME, name_value, 2287 B_TRUE); 2288 } 2289 } 2290 2291 /* Attach the name property if we created one. */ 2292 if ((r == 0) && (name_prop != NULL)) { 2293 r = internal_attach_property(pg, name_prop); 2294 } 2295 if ((r != 0) && (name_prop != NULL)) { 2296 internal_property_free(name_prop); 2297 } 2298 2299 return (r); 2300 } 2301 2302 /* 2303 * This function processes a prop_pattern element within a pg_pattern XML 2304 * element. First it creates a property group to hold the prop_pattern 2305 * information. The name of this property group is the concatenation of: 2306 * - SCF_PG_TM_PROP_PATTERN_PREFIX 2307 * - The unique part of the property group name of the enclosing 2308 * pg_pattern. The property group name of the enclosing pg_pattern 2309 * is passed to us in pgpat_name. The unique part, is the part 2310 * following SCF_PG_TM_PG_PATTERN_PREFIX. 2311 * - The name of this prop_pattern element. 2312 * 2313 * After creating the property group, the prop_pattern attributes are saved 2314 * as properties in the PG. Finally, the prop_pattern elements are 2315 * processed and added to the PG. 2316 */ 2317 static int 2318 lxml_get_tm_prop_pattern(entity_t *service, xmlNodePtr prop_pattern, 2319 const char *pgpat_name) 2320 { 2321 xmlNodePtr cursor; 2322 int extra; 2323 pgroup_t *pg; 2324 property_t *p; 2325 char *pg_name; 2326 size_t prefix_len; 2327 xmlChar *prop_pattern_name; 2328 int r; 2329 const char *unique; 2330 value_t *v; 2331 2332 /* Find the unique part of the pg_pattern property group name. */ 2333 prefix_len = strlen(SCF_PG_TM_PG_PAT_BASE); 2334 assert(strncmp(pgpat_name, SCF_PG_TM_PG_PAT_BASE, prefix_len) == 0); 2335 unique = pgpat_name + prefix_len; 2336 2337 /* 2338 * We need to get the value of the name attribute first. The 2339 * prop_pattern name as well as the name of the enclosing 2340 * pg_pattern both constitute part of the name of the property 2341 * group that we will create. 2342 */ 2343 prop_pattern_name = xmlGetProp(prop_pattern, (xmlChar *)name_attr); 2344 if ((prop_pattern_name == NULL) || (*prop_pattern_name == 0)) { 2345 semerr(gettext("prop_pattern name is missing for %s\n"), 2346 service->sc_name); 2347 return (-1); 2348 } 2349 if (uu_check_name((const char *)prop_pattern_name, 2350 UU_NAME_DOMAIN) != 0) { 2351 semerr(gettext("prop_pattern name, \"%s\", for %s is not " 2352 "valid.\n"), prop_pattern_name, service->sc_name); 2353 xmlFree(prop_pattern_name); 2354 return (-1); 2355 } 2356 pg_name = safe_malloc(max_scf_name_len + 1); 2357 if ((extra = snprintf(pg_name, max_scf_name_len + 1, "%s%s_%s", 2358 SCF_PG_TM_PROP_PATTERN_PREFIX, unique, 2359 (char *)prop_pattern_name)) >= max_scf_name_len + 1) { 2360 uu_die(gettext("prop_pattern name, \"%s\", for %s is %d " 2361 "characters too long\n"), (char *)prop_pattern_name, 2362 service->sc_name, extra - max_scf_name_len); 2363 } 2364 2365 /* 2366 * Create the property group, the property referencing the pg_pattern 2367 * name, and add the prop_pattern attributes to the property group. 2368 */ 2369 pg = internal_pgroup_create_strict(service, pg_name, 2370 SCF_GROUP_TEMPLATE_PROP_PATTERN); 2371 if (pg == NULL) { 2372 uu_die(gettext("Property group for prop_pattern, \"%s\", " 2373 "already exists in %s\n"), prop_pattern_name, 2374 service->sc_name); 2375 } 2376 2377 p = internal_property_create(SCF_PROPERTY_TM_PG_PATTERN, 2378 SCF_TYPE_ASTRING, 1, safe_strdup(pgpat_name)); 2379 /* 2380 * Unfortunately, internal_property_create() does not set the free 2381 * function for the value, so we'll set it now. 2382 */ 2383 v = uu_list_first(p->sc_property_values); 2384 v->sc_free = lxml_free_str; 2385 if (internal_attach_property(pg, p) != 0) 2386 internal_property_free(p); 2387 2388 2389 r = lxml_get_prop_pattern_attributes(pg, prop_pattern); 2390 if (r != 0) 2391 goto out; 2392 2393 /* 2394 * Now process the elements of prop_pattern 2395 */ 2396 for (cursor = prop_pattern->xmlChildrenNode; 2397 cursor != NULL; 2398 cursor = cursor->next) { 2399 if (lxml_ignorable_block(cursor)) 2400 continue; 2401 2402 switch (lxml_xlate_element(cursor->name)) { 2403 case SC_CARDINALITY: 2404 r = lxml_get_tm_cardinality(service, pg, cursor); 2405 if (r != 0) 2406 goto out; 2407 break; 2408 case SC_CHOICES: 2409 r = lxml_get_tm_choices(service, pg, cursor); 2410 if (r != 0) 2411 goto out; 2412 break; 2413 case SC_COMMON_NAME: 2414 (void) lxml_get_all_loctext(service, pg, cursor, 2415 COMMON_NAME_FMT, (const char *)cursor->name); 2416 break; 2417 case SC_CONSTRAINTS: 2418 r = lxml_get_tm_constraints(service, pg, cursor); 2419 if (r != 0) 2420 goto out; 2421 break; 2422 case SC_DESCRIPTION: 2423 (void) lxml_get_all_loctext(service, pg, cursor, 2424 DESCRIPTION_FMT, (const char *)cursor->name); 2425 break; 2426 case SC_INTERNAL_SEPARATORS: 2427 r = lxml_get_tm_internal_seps(service, pg, cursor); 2428 if (r != 0) 2429 goto out; 2430 break; 2431 case SC_UNITS: 2432 (void) lxml_get_all_loctext(service, pg, cursor, 2433 UNITS_FMT, "units"); 2434 break; 2435 case SC_VALUES: 2436 (void) lxml_get_tm_values(service, pg, cursor); 2437 break; 2438 case SC_VISIBILITY: 2439 /* 2440 * The visibility element is empty, so we only need 2441 * to proccess the value attribute. 2442 */ 2443 (void) new_str_prop_from_attr(pg, 2444 SCF_PROPERTY_TM_VISIBILITY, SCF_TYPE_ASTRING, 2445 cursor, value_attr); 2446 break; 2447 default: 2448 uu_die(gettext("illegal element \"%s\" in prop_pattern " 2449 "for service \"%s\"\n"), cursor->name, 2450 service->sc_name); 2451 } 2452 } 2453 2454 out: 2455 xmlFree(prop_pattern_name); 2456 free(pg_name); 2457 return (r); 2458 } 2459 2460 /* 2461 * Get the pg_pattern attributes and save them as properties in the 2462 * property group at pg. The pg_pattern element accepts four attributes -- 2463 * name, type, required and target. 2464 */ 2465 static int 2466 lxml_get_pg_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor) 2467 { 2468 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, 2469 SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) { 2470 return (-1); 2471 } 2472 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE, 2473 SCF_TYPE_ASTRING, cursor, type_attr, NULL) != 0) { 2474 return (-1); 2475 } 2476 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TARGET, 2477 SCF_TYPE_ASTRING, cursor, target_attr, NULL) != 0) { 2478 return (-1); 2479 } 2480 if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor, 2481 required_attr) != 0) 2482 return (-1); 2483 return (0); 2484 } 2485 2486 /* 2487 * There are several restrictions on the pg_pattern attributes that cannot 2488 * be specifed in the service bundle DTD. This function verifies that 2489 * those restrictions have been satisfied. The restrictions are: 2490 * 2491 * - The target attribute may have a value of "instance" only when the 2492 * template block is in a service declaration. 2493 * 2494 * - The target attribute may have a value of "delegate" only when the 2495 * template block applies to a restarter. 2496 * 2497 * - The target attribute may have a value of "all" only when the 2498 * template block applies to the master restarter. 2499 * 2500 * The function returns 0 on success and -1 on failure. 2501 */ 2502 static int 2503 verify_pg_pattern_attributes(entity_t *s, pgroup_t *pg) 2504 { 2505 int is_restarter; 2506 property_t *target; 2507 value_t *v; 2508 2509 /* Find the value of the target property. */ 2510 target = internal_property_find(pg, SCF_PROPERTY_TM_TARGET); 2511 if (target == NULL) { 2512 uu_die(gettext("pg_pattern is missing the %s attribute " 2513 "in %s\n"), target_attr, s->sc_name); 2514 return (-1); 2515 } 2516 v = uu_list_first(target->sc_property_values); 2517 assert(v != NULL); 2518 assert(v->sc_type == SCF_TYPE_ASTRING); 2519 2520 /* 2521 * If target has a value of instance, the template must be in a 2522 * service object. 2523 */ 2524 if (strcmp(v->sc_u.sc_string, "instance") == 0) { 2525 if (s->sc_etype != SVCCFG_SERVICE_OBJECT) { 2526 uu_warn(gettext("pg_pattern %s attribute may only " 2527 "have a value of \"instance\" when it is in a " 2528 "service declaration.\n"), target_attr); 2529 return (-1); 2530 } 2531 } 2532 2533 /* 2534 * If target has a value of "delegate", the template must be in a 2535 * restarter. 2536 */ 2537 if (strcmp(v->sc_u.sc_string, "delegate") == 0) { 2538 is_restarter = 0; 2539 if ((s->sc_etype == SVCCFG_SERVICE_OBJECT) && 2540 (s->sc_u.sc_service.sc_service_type == SVCCFG_RESTARTER)) { 2541 is_restarter = 1; 2542 } 2543 if ((s->sc_etype == SVCCFG_INSTANCE_OBJECT) && 2544 (s->sc_parent->sc_u.sc_service.sc_service_type == 2545 SVCCFG_RESTARTER)) { 2546 is_restarter = 1; 2547 } 2548 if (is_restarter == 0) { 2549 uu_warn(gettext("pg_pattern %s attribute has a " 2550 "value of \"delegate\" but is not in a " 2551 "restarter service\n"), target_attr); 2552 return (-1); 2553 } 2554 } 2555 2556 /* 2557 * If target has a value of "all", the template must be in the 2558 * global (SCF_SERVICE_GLOBAL) service. 2559 */ 2560 if (strcmp(v->sc_u.sc_string, all_value) == 0) { 2561 if (s->sc_etype != SVCCFG_SERVICE_OBJECT) { 2562 uu_warn(gettext("pg_pattern %s attribute has a " 2563 "value of \"%s\" but is not in a " 2564 "service entity.\n"), target_attr, all_value); 2565 return (-1); 2566 } 2567 if (strcmp(s->sc_fmri, SCF_SERVICE_GLOBAL) != 0) { 2568 uu_warn(gettext("pg_pattern %s attribute has a " 2569 "value of \"%s\" but is in the \"%s\" service. " 2570 "pg_patterns with target \"%s\" are only allowed " 2571 "in the global service.\n"), 2572 target_attr, all_value, s->sc_fmri, all_value); 2573 return (-1); 2574 } 2575 } 2576 2577 return (0); 2578 } 2579 2580 static int 2581 lxml_get_tm_pg_pattern(entity_t *service, xmlNodePtr pg_pattern) 2582 { 2583 xmlNodePtr cursor; 2584 int out_len; 2585 xmlChar *name; 2586 pgroup_t *pg = NULL; 2587 char *pg_name; 2588 int r = -1; 2589 xmlChar *type; 2590 2591 pg_name = safe_malloc(max_scf_name_len + 1); 2592 2593 /* 2594 * Get the name and type attributes. Their presence or absence 2595 * determines whcih prefix we will use for the property group name. 2596 * There are four cases -- neither attribute is present, both are 2597 * present, only name is present or only type is present. 2598 */ 2599 name = xmlGetProp(pg_pattern, (xmlChar *)name_attr); 2600 type = xmlGetProp(pg_pattern, (xmlChar *)type_attr); 2601 if ((name == NULL) || (*name == 0)) { 2602 if ((type == NULL) || (*type == 0)) { 2603 /* PG name contains only the prefix in this case */ 2604 if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX, 2605 max_scf_name_len + 1) >= max_scf_name_len + 1) { 2606 uu_die(gettext("Unable to create pg_pattern " 2607 "property for %s\n"), service->sc_name); 2608 } 2609 } else { 2610 /* 2611 * If we have a type and no name, the type becomes 2612 * part of the pg_pattern property group name. 2613 */ 2614 if ((out_len = snprintf(pg_name, max_scf_name_len + 1, 2615 "%s%s", SCF_PG_TM_PG_PATTERN_T_PREFIX, type)) >= 2616 max_scf_name_len + 1) { 2617 uu_die(gettext("pg_pattern type is for %s is " 2618 "%d bytes too long\n"), service->sc_name, 2619 out_len - max_scf_name_len); 2620 } 2621 } 2622 } else { 2623 const char *prefix; 2624 2625 /* Make sure that the name is valid. */ 2626 if (uu_check_name((const char *)name, UU_NAME_DOMAIN) != 0) { 2627 semerr(gettext("pg_pattern name attribute, \"%s\", " 2628 "for %s is invalid\n"), name, service->sc_name); 2629 goto out; 2630 } 2631 2632 /* 2633 * As long as the pg_pattern has a name, it becomes part of 2634 * the name of the pg_pattern property group name. We 2635 * merely need to pick the appropriate prefix. 2636 */ 2637 if ((type == NULL) || (*type == 0)) { 2638 prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX; 2639 } else { 2640 prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX; 2641 } 2642 if ((out_len = snprintf(pg_name, max_scf_name_len + 1, "%s%s", 2643 prefix, name)) >= max_scf_name_len + 1) { 2644 uu_die(gettext("pg_pattern property group name " 2645 "for %s is %d bytes too long\n"), service->sc_name, 2646 out_len - max_scf_name_len); 2647 } 2648 } 2649 2650 /* 2651 * Create the property group for holding this pg_pattern 2652 * information, and capture the pg_pattern attributes. 2653 */ 2654 pg = internal_pgroup_create_strict(service, pg_name, 2655 SCF_GROUP_TEMPLATE_PG_PATTERN); 2656 if (pg == NULL) { 2657 if ((name == NULL) || (*name == 0)) { 2658 if ((type == NULL) ||(*type == 0)) { 2659 semerr(gettext("pg_pattern with empty name and " 2660 "type is not unique in %s\n"), 2661 service->sc_name); 2662 } else { 2663 semerr(gettext("pg_pattern with empty name and " 2664 "type \"%s\" is not unique in %s\n"), 2665 type, service->sc_name); 2666 } 2667 } else { 2668 if ((type == NULL) || (*type == 0)) { 2669 semerr(gettext("pg_pattern with name \"%s\" " 2670 "and empty type is not unique in %s\n"), 2671 name, service->sc_name); 2672 } else { 2673 semerr(gettext("pg_pattern with name \"%s\" " 2674 "and type \"%s\" is not unique in %s\n"), 2675 name, type, service->sc_name); 2676 } 2677 } 2678 goto out; 2679 } 2680 2681 /* 2682 * Get the pg_pattern attributes from the manifest and verify 2683 * that they satisfy our restrictions. 2684 */ 2685 r = lxml_get_pg_pattern_attributes(pg, pg_pattern); 2686 if (r != 0) 2687 goto out; 2688 if (verify_pg_pattern_attributes(service, pg) != 0) { 2689 semerr(gettext("Invalid pg_pattern attributes in %s\n"), 2690 service->sc_name); 2691 r = -1; 2692 goto out; 2693 } 2694 2695 /* 2696 * Now process all of the elements of pg_pattern. 2697 */ 2698 for (cursor = pg_pattern->xmlChildrenNode; 2699 cursor != NULL; 2700 cursor = cursor->next) { 2701 if (lxml_ignorable_block(cursor)) 2702 continue; 2703 2704 switch (lxml_xlate_element(cursor->name)) { 2705 case SC_COMMON_NAME: 2706 (void) lxml_get_all_loctext(service, pg, cursor, 2707 COMMON_NAME_FMT, (const char *)cursor->name); 2708 break; 2709 case SC_DESCRIPTION: 2710 (void) lxml_get_all_loctext(service, pg, cursor, 2711 DESCRIPTION_FMT, (const char *)cursor->name); 2712 break; 2713 case SC_PROP_PATTERN: 2714 r = lxml_get_tm_prop_pattern(service, cursor, 2715 pg_name); 2716 if (r != 0) 2717 goto out; 2718 break; 2719 default: 2720 uu_die(gettext("illegal element \"%s\" in pg_pattern " 2721 "for service \"%s\"\n"), cursor->name, 2722 service->sc_name); 2723 } 2724 } 2725 2726 out: 2727 if ((r != 0) && (pg != NULL)) { 2728 internal_detach_pgroup(service, pg); 2729 internal_pgroup_free(pg); 2730 } 2731 free(pg_name); 2732 xmlFree(name); 2733 xmlFree(type); 2734 2735 return (r); 2736 } 2737 2738 static int 2739 lxml_get_template(entity_t *service, xmlNodePtr templ) 2740 { 2741 xmlNodePtr cursor; 2742 2743 for (cursor = templ->xmlChildrenNode; cursor != NULL; 2744 cursor = cursor->next) { 2745 if (lxml_ignorable_block(cursor)) 2746 continue; 2747 2748 switch (lxml_xlate_element(cursor->name)) { 2749 case SC_COMMON_NAME: 2750 (void) lxml_get_tm_common_name(service, cursor); 2751 break; 2752 case SC_DESCRIPTION: 2753 (void) lxml_get_tm_description(service, cursor); 2754 break; 2755 case SC_DOCUMENTATION: 2756 (void) lxml_get_tm_documentation(service, cursor); 2757 break; 2758 case SC_PG_PATTERN: 2759 if (lxml_get_tm_pg_pattern(service, cursor) != 0) 2760 return (-1); 2761 break; 2762 default: 2763 uu_die(gettext("illegal element \"%s\" on template " 2764 "for service \"%s\"\n"), 2765 cursor->name, service->sc_name); 2766 } 2767 } 2768 2769 return (0); 2770 } 2771 2772 static int 2773 lxml_get_default_instance(entity_t *service, xmlNodePtr definst) 2774 { 2775 entity_t *i; 2776 xmlChar *enabled; 2777 pgroup_t *pg; 2778 property_t *p; 2779 char *package; 2780 uint64_t enabled_val = 0; 2781 2782 i = internal_instance_new("default"); 2783 2784 if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) { 2785 enabled_val = (strcmp(true, (const char *)enabled) == 0) ? 2786 1 : 0; 2787 xmlFree(enabled); 2788 } 2789 2790 /* 2791 * New general property group with enabled boolean property set. 2792 */ 2793 2794 pg = internal_pgroup_new(); 2795 (void) internal_attach_pgroup(i, pg); 2796 2797 pg->sc_pgroup_name = (char *)scf_pg_general; 2798 pg->sc_pgroup_type = (char *)scf_group_framework; 2799 pg->sc_pgroup_flags = 0; 2800 2801 p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1, 2802 enabled_val); 2803 2804 (void) internal_attach_property(pg, p); 2805 2806 /* 2807 * Add general/package property if PKGINST is set. 2808 */ 2809 if ((package = getenv("PKGINST")) != NULL) { 2810 p = internal_property_create(SCF_PROPERTY_PACKAGE, 2811 SCF_TYPE_ASTRING, 1, package); 2812 2813 (void) internal_attach_property(pg, p); 2814 } 2815 2816 return (internal_attach_entity(service, i)); 2817 } 2818 2819 /* 2820 * Translate an instance element into an internal property tree, added to 2821 * service. If op is SVCCFG_OP_APPLY (i.e., apply a profile), set the 2822 * enabled property to override. 2823 */ 2824 static int 2825 lxml_get_instance(entity_t *service, xmlNodePtr inst, bundle_type_t bt, 2826 svccfg_op_t op) 2827 { 2828 entity_t *i; 2829 pgroup_t *pg; 2830 property_t *p; 2831 xmlNodePtr cursor; 2832 xmlChar *enabled; 2833 int r, e_val; 2834 2835 /* 2836 * Fetch its attributes, as appropriate. 2837 */ 2838 i = internal_instance_new((char *)xmlGetProp(inst, 2839 (xmlChar *)name_attr)); 2840 2841 /* 2842 * Note that this must be done before walking the children so that 2843 * sc_fmri is set in case we enter lxml_get_dependent(). 2844 */ 2845 r = internal_attach_entity(service, i); 2846 if (r != 0) 2847 return (r); 2848 2849 enabled = xmlGetProp(inst, (xmlChar *)enabled_attr); 2850 2851 if (enabled == NULL) { 2852 if (bt == SVCCFG_MANIFEST) { 2853 semerr(gettext("Instance \"%s\" missing attribute " 2854 "\"%s\".\n"), i->sc_name, enabled_attr); 2855 return (-1); 2856 } 2857 } else { /* enabled != NULL */ 2858 if (strcmp(true, (const char *)enabled) != 0 && 2859 strcmp(false, (const char *)enabled) != 0) { 2860 xmlFree(enabled); 2861 semerr(gettext("Invalid enabled value\n")); 2862 return (-1); 2863 } 2864 pg = internal_pgroup_new(); 2865 (void) internal_attach_pgroup(i, pg); 2866 2867 pg->sc_pgroup_name = (char *)scf_pg_general; 2868 pg->sc_pgroup_type = (char *)scf_group_framework; 2869 pg->sc_pgroup_flags = 0; 2870 2871 e_val = (strcmp(true, (const char *)enabled) == 0); 2872 p = internal_property_create(SCF_PROPERTY_ENABLED, 2873 SCF_TYPE_BOOLEAN, 1, (uint64_t)e_val); 2874 2875 p->sc_property_override = (op == SVCCFG_OP_APPLY); 2876 2877 (void) internal_attach_property(pg, p); 2878 2879 xmlFree(enabled); 2880 } 2881 2882 /* 2883 * Walk its child elements, as appropriate. 2884 */ 2885 for (cursor = inst->xmlChildrenNode; cursor != NULL; 2886 cursor = cursor->next) { 2887 if (lxml_ignorable_block(cursor)) 2888 continue; 2889 2890 switch (lxml_xlate_element(cursor->name)) { 2891 case SC_RESTARTER: 2892 (void) lxml_get_restarter(i, cursor); 2893 break; 2894 case SC_DEPENDENCY: 2895 (void) lxml_get_dependency(i, cursor); 2896 break; 2897 case SC_DEPENDENT: 2898 (void) lxml_get_dependent(i, cursor); 2899 break; 2900 case SC_METHOD_CONTEXT: 2901 (void) lxml_get_entity_method_context(i, cursor); 2902 break; 2903 case SC_EXEC_METHOD: 2904 (void) lxml_get_exec_method(i, cursor); 2905 break; 2906 case SC_PROPERTY_GROUP: 2907 (void) lxml_get_pgroup(i, cursor); 2908 break; 2909 case SC_TEMPLATE: 2910 if (lxml_get_template(i, cursor) != 0) 2911 return (-1); 2912 break; 2913 default: 2914 uu_die(gettext( 2915 "illegal element \"%s\" on instance \"%s\"\n"), 2916 cursor->name, i->sc_name); 2917 break; 2918 } 2919 } 2920 2921 return (0); 2922 } 2923 2924 /* ARGSUSED1 */ 2925 static int 2926 lxml_get_single_instance(entity_t *entity, xmlNodePtr si) 2927 { 2928 pgroup_t *pg; 2929 property_t *p; 2930 int r; 2931 2932 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 2933 (char *)scf_group_framework); 2934 2935 p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE, 2936 SCF_TYPE_BOOLEAN, 1, (uint64_t)1); 2937 2938 r = internal_attach_property(pg, p); 2939 if (r != 0) { 2940 internal_property_free(p); 2941 return (-1); 2942 } 2943 2944 return (0); 2945 } 2946 2947 /* 2948 * Check to see if the service should allow the upgrade 2949 * process to handle adding of the manifestfiles linkage. 2950 * 2951 * If the service exists and does not have a manifestfiles 2952 * property group then the upgrade process should handle 2953 * the service. 2954 * 2955 * If the service doesn't exist or the service exists 2956 * and has a manifestfiles property group then the import 2957 * process can handle the manifestfiles property group 2958 * work. 2959 * 2960 * This prevents potential cleanup of unaccounted for instances 2961 * in early manifest import due to upgrade process needing 2962 * information that has not yet been supplied by manifests 2963 * that are still located in the /var/svc manifests directory. 2964 */ 2965 static int 2966 lxml_check_upgrade(const char *service) { 2967 scf_handle_t *h = NULL; 2968 scf_scope_t *sc = NULL; 2969 scf_service_t *svc = NULL; 2970 scf_propertygroup_t *pg = NULL; 2971 int rc = SCF_FAILED; 2972 2973 if ((h = scf_handle_create(SCF_VERSION)) == NULL || 2974 (sc = scf_scope_create(h)) == NULL || 2975 (svc = scf_service_create(h)) == NULL || 2976 (pg = scf_pg_create(h)) == NULL) 2977 goto out; 2978 2979 if (scf_handle_bind(h) != 0) 2980 goto out; 2981 2982 if (scf_handle_get_scope(h, SCF_FMRI_LOCAL_SCOPE, sc) == -1) 2983 goto out; 2984 2985 if (scf_scope_get_service(sc, service, svc) != SCF_SUCCESS) { 2986 if (scf_error() == SCF_ERROR_NOT_FOUND) 2987 rc = SCF_SUCCESS; 2988 2989 goto out; 2990 } 2991 2992 if (scf_service_get_pg(svc, SCF_PG_MANIFESTFILES, pg) != SCF_SUCCESS) 2993 goto out; 2994 2995 rc = SCF_SUCCESS; 2996 out: 2997 scf_pg_destroy(pg); 2998 scf_service_destroy(svc); 2999 scf_scope_destroy(sc); 3000 scf_handle_destroy(h); 3001 3002 return (rc); 3003 } 3004 3005 /* 3006 * Translate a service element into an internal instance/property tree, added 3007 * to bundle. If op is SVCCFG_OP_APPLY, allow only instance subelements. 3008 */ 3009 static int 3010 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op) 3011 { 3012 pgroup_t *pg; 3013 property_t *p; 3014 entity_t *s; 3015 xmlNodePtr cursor; 3016 xmlChar *type; 3017 xmlChar *version; 3018 int e; 3019 3020 /* 3021 * Fetch attributes, as appropriate. 3022 */ 3023 s = internal_service_new((char *)xmlGetProp(svc, 3024 (xmlChar *)name_attr)); 3025 3026 version = xmlGetProp(svc, (xmlChar *)version_attr); 3027 s->sc_u.sc_service.sc_service_version = atol((const char *)version); 3028 xmlFree(version); 3029 3030 type = xmlGetProp(svc, (xmlChar *)type_attr); 3031 s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type); 3032 xmlFree(type); 3033 3034 /* 3035 * Now that the service is created create the manifest 3036 * property group and add the property value of the service. 3037 */ 3038 if (lxml_check_upgrade(s->sc_name) == SCF_SUCCESS && 3039 svc->doc->name != NULL && 3040 bundle->sc_bundle_type == SVCCFG_MANIFEST) { 3041 char *buf, *base, *fname; 3042 3043 pg = internal_pgroup_create_strict(s, SCF_PG_MANIFESTFILES, 3044 SCF_GROUP_FRAMEWORK); 3045 3046 if (pg == NULL) { 3047 uu_die(gettext("Property group for prop_pattern, " 3048 "\"%s\", already exists in %s\n"), 3049 SCF_PG_MANIFESTFILES, s->sc_name); 3050 } 3051 buf = mhash_filename_to_propname(svc->doc->name, B_FALSE); 3052 /* 3053 * Must remove the PKG_INSTALL_ROOT, point to the correct 3054 * directory after install 3055 */ 3056 base = getenv("PKG_INSTALL_ROOT"); 3057 fname = safe_strdup(svc->doc->name + 3058 ((base != NULL) ? strlen(base) : 0)); 3059 3060 p = internal_property_create(buf, SCF_TYPE_ASTRING, 1, fname); 3061 3062 (void) internal_attach_property(pg, p); 3063 } 3064 3065 /* 3066 * Walk its child elements, as appropriate. 3067 */ 3068 for (cursor = svc->xmlChildrenNode; cursor != NULL; 3069 cursor = cursor->next) { 3070 if (lxml_ignorable_block(cursor)) 3071 continue; 3072 3073 e = lxml_xlate_element(cursor->name); 3074 3075 switch (e) { 3076 case SC_INSTANCE: 3077 if (lxml_get_instance(s, cursor, 3078 bundle->sc_bundle_type, op) != 0) 3079 return (-1); 3080 break; 3081 case SC_TEMPLATE: 3082 if (lxml_get_template(s, cursor) != 0) 3083 return (-1); 3084 break; 3085 case SC_STABILITY: 3086 (void) lxml_get_entity_stability(s, cursor); 3087 break; 3088 case SC_DEPENDENCY: 3089 (void) lxml_get_dependency(s, cursor); 3090 break; 3091 case SC_DEPENDENT: 3092 (void) lxml_get_dependent(s, cursor); 3093 break; 3094 case SC_RESTARTER: 3095 (void) lxml_get_restarter(s, cursor); 3096 break; 3097 case SC_EXEC_METHOD: 3098 (void) lxml_get_exec_method(s, cursor); 3099 break; 3100 case SC_METHOD_CONTEXT: 3101 (void) lxml_get_entity_method_context(s, cursor); 3102 break; 3103 case SC_PROPERTY_GROUP: 3104 (void) lxml_get_pgroup(s, cursor); 3105 break; 3106 case SC_INSTANCE_CREATE_DEFAULT: 3107 (void) lxml_get_default_instance(s, cursor); 3108 break; 3109 case SC_INSTANCE_SINGLE: 3110 (void) lxml_get_single_instance(s, cursor); 3111 break; 3112 default: 3113 uu_die(gettext( 3114 "illegal element \"%s\" on service \"%s\"\n"), 3115 cursor->name, s->sc_name); 3116 break; 3117 } 3118 } 3119 3120 return (internal_attach_service(bundle, s)); 3121 } 3122 3123 #ifdef DEBUG 3124 void 3125 lxml_dump(int g, xmlNodePtr p) 3126 { 3127 if (p && p->name) { 3128 printf("%d %s\n", g, p->name); 3129 3130 for (p = p->xmlChildrenNode; p != NULL; p = p->next) 3131 lxml_dump(g + 1, p); 3132 } 3133 } 3134 #endif /* DEBUG */ 3135 3136 static int 3137 lxml_is_known_dtd(const xmlChar *dtdname) 3138 { 3139 if (dtdname == NULL || 3140 strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0) 3141 return (0); 3142 3143 return (1); 3144 } 3145 3146 static int 3147 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type, 3148 xmlNodePtr subbundle, svccfg_op_t op) 3149 { 3150 xmlNodePtr cursor; 3151 xmlChar *type; 3152 int e; 3153 3154 /* 3155 * 1. Get bundle attributes. 3156 */ 3157 type = xmlGetProp(subbundle, (xmlChar *)type_attr); 3158 bundle->sc_bundle_type = lxml_xlate_bundle_type(type); 3159 if (bundle->sc_bundle_type != bundle_type && 3160 bundle_type != SVCCFG_UNKNOWN_BUNDLE) { 3161 semerr(gettext("included bundle of different type.\n")); 3162 return (-1); 3163 } 3164 3165 xmlFree(type); 3166 3167 switch (op) { 3168 case SVCCFG_OP_IMPORT: 3169 if (bundle->sc_bundle_type != SVCCFG_MANIFEST) { 3170 semerr(gettext("document is not a manifest.\n")); 3171 return (-1); 3172 } 3173 break; 3174 case SVCCFG_OP_APPLY: 3175 if (bundle->sc_bundle_type != SVCCFG_PROFILE) { 3176 semerr(gettext("document is not a profile.\n")); 3177 return (-1); 3178 } 3179 break; 3180 case SVCCFG_OP_RESTORE: 3181 if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) { 3182 semerr(gettext("document is not an archive.\n")); 3183 return (-1); 3184 } 3185 break; 3186 } 3187 3188 if (((bundle->sc_bundle_name = xmlGetProp(subbundle, 3189 (xmlChar *)name_attr)) == NULL) || (*bundle->sc_bundle_name == 0)) { 3190 semerr(gettext("service bundle lacks name attribute\n")); 3191 return (-1); 3192 } 3193 3194 /* 3195 * 2. Get services, descend into each one and build state. 3196 */ 3197 for (cursor = subbundle->xmlChildrenNode; cursor != NULL; 3198 cursor = cursor->next) { 3199 if (lxml_ignorable_block(cursor)) 3200 continue; 3201 3202 e = lxml_xlate_element(cursor->name); 3203 3204 switch (e) { 3205 case SC_XI_INCLUDE: 3206 continue; 3207 3208 case SC_SERVICE_BUNDLE: 3209 if (lxml_get_bundle(bundle, bundle_type, cursor, op)) 3210 return (-1); 3211 break; 3212 case SC_SERVICE: 3213 if (lxml_get_service(bundle, cursor, op) != 0) 3214 return (-1); 3215 break; 3216 } 3217 } 3218 3219 return (0); 3220 } 3221 3222 /* 3223 * Load an XML tree from filename and translate it into an internal service 3224 * tree bundle. Require that the bundle be of appropriate type for the 3225 * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY. 3226 */ 3227 int 3228 lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op) 3229 { 3230 xmlDocPtr document; 3231 xmlNodePtr cursor; 3232 xmlDtdPtr dtd = NULL; 3233 xmlValidCtxtPtr vcp; 3234 boolean_t do_validate; 3235 char *dtdpath = NULL; 3236 int r; 3237 3238 /* 3239 * Verify we can read the file before we try to parse it. 3240 */ 3241 if (access(filename, R_OK | F_OK) == -1) { 3242 semerr(gettext("unable to open file: %s\n"), strerror(errno)); 3243 return (-1); 3244 } 3245 3246 /* 3247 * Until libxml2 addresses DTD-based validation with XInclude, we don't 3248 * validate service profiles (i.e. the apply path). 3249 */ 3250 do_validate = (op != SVCCFG_OP_APPLY) && 3251 (getenv("SVCCFG_NOVALIDATE") == NULL); 3252 if (do_validate) 3253 dtdpath = getenv("SVCCFG_DTD"); 3254 3255 if (dtdpath != NULL) 3256 xmlLoadExtDtdDefaultValue = 0; 3257 3258 if ((document = xmlReadFile(filename, NULL, 0)) == NULL) { 3259 semerr(gettext("couldn't parse document\n")); 3260 return (-1); 3261 } 3262 3263 document->name = strdup(filename); 3264 3265 /* 3266 * Verify that this is a document type we understand. 3267 */ 3268 if ((dtd = xmlGetIntSubset(document)) == NULL) { 3269 semerr(gettext("document has no DTD\n")); 3270 return (-1); 3271 } 3272 3273 if (!lxml_is_known_dtd(dtd->SystemID)) { 3274 semerr(gettext("document DTD unknown; not service bundle?\n")); 3275 return (-1); 3276 } 3277 3278 if ((cursor = xmlDocGetRootElement(document)) == NULL) { 3279 semerr(gettext("document is empty\n")); 3280 xmlFreeDoc(document); 3281 return (-1); 3282 } 3283 3284 if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) { 3285 semerr(gettext("document is not a service bundle\n")); 3286 xmlFreeDoc(document); 3287 return (-1); 3288 } 3289 3290 3291 if (dtdpath != NULL) { 3292 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath); 3293 if (dtd == NULL) { 3294 semerr(gettext("Could not parse DTD \"%s\".\n"), 3295 dtdpath); 3296 return (-1); 3297 } 3298 3299 if (document->extSubset != NULL) 3300 xmlFreeDtd(document->extSubset); 3301 3302 document->extSubset = dtd; 3303 } 3304 3305 if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) { 3306 semerr(gettext("couldn't handle XInclude statements " 3307 "in document\n")); 3308 return (-1); 3309 } 3310 3311 if (do_validate) { 3312 vcp = xmlNewValidCtxt(); 3313 if (vcp == NULL) 3314 uu_die(gettext("could not allocate memory")); 3315 vcp->warning = xmlParserValidityWarning; 3316 vcp->error = xmlParserValidityError; 3317 3318 r = xmlValidateDocument(vcp, document); 3319 3320 xmlFreeValidCtxt(vcp); 3321 3322 if (r == 0) { 3323 semerr(gettext("Document is not valid.\n")); 3324 xmlFreeDoc(document); 3325 return (-1); 3326 } 3327 } 3328 3329 3330 #ifdef DEBUG 3331 lxml_dump(0, cursor); 3332 #endif /* DEBUG */ 3333 3334 r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op); 3335 3336 xmlFreeDoc(document); 3337 3338 return (r); 3339 } 3340 3341 int 3342 lxml_inventory(const char *filename) 3343 { 3344 bundle_t *b; 3345 uu_list_walk_t *svcs, *insts; 3346 entity_t *svc, *inst; 3347 3348 b = internal_bundle_new(); 3349 3350 if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) { 3351 internal_bundle_free(b); 3352 return (-1); 3353 } 3354 3355 svcs = uu_list_walk_start(b->sc_bundle_services, 0); 3356 if (svcs == NULL) 3357 uu_die(gettext("Couldn't walk services")); 3358 3359 while ((svc = uu_list_walk_next(svcs)) != NULL) { 3360 uu_list_t *inst_list; 3361 3362 inst_list = svc->sc_u.sc_service.sc_service_instances; 3363 insts = uu_list_walk_start(inst_list, 0); 3364 if (insts == NULL) 3365 uu_die(gettext("Couldn't walk instances")); 3366 3367 while ((inst = uu_list_walk_next(insts)) != NULL) 3368 (void) printf("svc:/%s:%s\n", svc->sc_name, 3369 inst->sc_name); 3370 3371 uu_list_walk_end(insts); 3372 } 3373 3374 uu_list_walk_end(svcs); 3375 3376 svcs = uu_list_walk_start(b->sc_bundle_services, 0); 3377 while ((svc = uu_list_walk_next(svcs)) != NULL) { 3378 (void) fputs("svc:/", stdout); 3379 (void) puts(svc->sc_name); 3380 } 3381 uu_list_walk_end(svcs); 3382 3383 internal_bundle_free(b); 3384 3385 return (0); 3386 } 3387