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