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