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