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