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