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