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