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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <libxml/parser.h> 30 #include <libxml/xinclude.h> 31 32 #include <assert.h> 33 #include <ctype.h> 34 #include <errno.h> 35 #include <libintl.h> 36 #include <libuutil.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #include "svccfg.h" 41 42 /* 43 * XML document manipulation routines 44 * 45 * These routines provide translation to and from the internal representation to 46 * XML. Directionally-oriented verbs are with respect to the external source, 47 * so lxml_get_service() fetches a service from the XML file into the 48 * internal representation. 49 */ 50 51 const char * const delete_attr = "delete"; 52 const char * const enabled_attr = "enabled"; 53 const char * const name_attr = "name"; 54 const char * const override_attr = "override"; 55 const char * const type_attr = "type"; 56 const char * const value_attr = "value"; 57 const char * const true = "true"; 58 const char * const false = "false"; 59 60 /* 61 * The following list must be kept in the same order as that of 62 * element_t array 63 */ 64 static const char *lxml_elements[] = { 65 "astring_list", /* SC_ASTRING */ 66 "boolean_list", /* SC_BOOLEAN */ 67 "common_name", /* SC_COMMON_NAME */ 68 "count_list", /* SC_COUNT */ 69 "create_default_instance", /* SC_INSTANCE_CREATE_DEFAULT */ 70 "dependency", /* SC_DEPENDENCY */ 71 "dependent", /* SC_DEPENDENT */ 72 "description", /* SC_DESCRIPTION */ 73 "doc_link", /* SC_DOC_LINK */ 74 "documentation", /* SC_DOCUMENTATION */ 75 "enabled", /* SC_ENABLED */ 76 "exec_method", /* SC_EXEC_METHOD */ 77 "fmri_list", /* SC_FMRI */ 78 "host_list", /* SC_HOST */ 79 "hostname_list", /* SC_HOSTNAME */ 80 "instance", /* SC_INSTANCE */ 81 "integer_list", /* SC_INTEGER */ 82 "loctext", /* SC_LOCTEXT */ 83 "manpage", /* SC_MANPAGE */ 84 "method_context", /* SC_METHOD_CONTEXT */ 85 "method_credential", /* SC_METHOD_CREDENTIAL */ 86 "method_profile", /* SC_METHOD_PROFILE */ 87 "method_environment", /* SC_METHOD_ENVIRONMENT */ 88 "envvar", /* SC_METHOD_ENVVAR */ 89 "net_address_v4_list", /* SC_NET_ADDR_V4 */ 90 "net_address_v6_list", /* SC_NET_ADDR_V6 */ 91 "opaque_list", /* SC_OPAQUE */ 92 "property", /* SC_PROPERTY */ 93 "property_group", /* SC_PROPERTY_GROUP */ 94 "propval", /* SC_PROPVAL */ 95 "restarter", /* SC_RESTARTER */ 96 "service", /* SC_SERVICE */ 97 "service_bundle", /* SC_SERVICE_BUNDLE */ 98 "service_fmri", /* SC_SERVICE_FMRI */ 99 "single_instance", /* SC_INSTANCE_SINGLE */ 100 "stability", /* SC_STABILITY */ 101 "template", /* SC_TEMPLATE */ 102 "time_list", /* SC_TIME */ 103 "uri_list", /* SC_URI */ 104 "ustring_list", /* SC_USTRING */ 105 "value_node", /* SC_VALUE_NODE */ 106 "xi:fallback", /* SC_XI_FALLBACK */ 107 "xi:include" /* SC_XI_INCLUDE */ 108 }; 109 110 /* 111 * The following list must be kept in the same order as that of 112 * element_t array 113 */ 114 static const char *lxml_prop_types[] = { 115 "astring", /* SC_ASTRING */ 116 "boolean", /* SC_BOOLEAN */ 117 "", /* SC_COMMON_NAME */ 118 "count", /* SC_COUNT */ 119 "", /* SC_INSTANCE_CREATE_DEFAULT */ 120 "", /* SC_DEPENDENCY */ 121 "", /* SC_DEPENDENT */ 122 "", /* SC_DESCRIPTION */ 123 "", /* SC_DOC_LINK */ 124 "", /* SC_DOCUMENTATION */ 125 "", /* SC_ENABLED */ 126 "", /* SC_EXEC_METHOD */ 127 "fmri", /* SC_FMRI */ 128 "host", /* SC_HOST */ 129 "hostname", /* SC_HOSTNAME */ 130 "", /* SC_INSTANCE */ 131 "integer", /* SC_INTEGER */ 132 "", /* SC_LOCTEXT */ 133 "", /* SC_MANPAGE */ 134 "", /* SC_METHOD_CONTEXT */ 135 "", /* SC_METHOD_CREDENTIAL */ 136 "", /* SC_METHOD_PROFILE */ 137 "", /* SC_METHOD_ENVIRONMENT */ 138 "", /* SC_METHOD_ENVVAR */ 139 "net_address_v4", /* SC_NET_ADDR_V4 */ 140 "net_address_v6", /* SC_NET_ADDR_V6 */ 141 "opaque", /* SC_OPAQUE */ 142 "", /* SC_PROPERTY */ 143 "", /* SC_PROPERTY_GROUP */ 144 "", /* SC_PROPVAL */ 145 "", /* SC_RESTARTER */ 146 "", /* SC_SERVICE */ 147 "", /* SC_SERVICE_BUNDLE */ 148 "", /* SC_SERVICE_FMRI */ 149 "", /* SC_INSTANCE_SINGLE */ 150 "", /* SC_STABILITY */ 151 "", /* SC_TEMPLATE */ 152 "time", /* SC_TIME */ 153 "uri", /* SC_URI */ 154 "ustring", /* SC_USTRING */ 155 "" /* SC_VALUE_NODE */ 156 "" /* SC_XI_FALLBACK */ 157 "" /* SC_XI_INCLUDE */ 158 }; 159 160 int 161 lxml_init() 162 { 163 if (getenv("SVCCFG_NOVALIDATE") == NULL) { 164 /* 165 * DTD validation, with line numbers. 166 */ 167 xmlLineNumbersDefault(1); 168 xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS; 169 xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS; 170 } 171 172 return (0); 173 } 174 175 static bundle_type_t 176 lxml_xlate_bundle_type(xmlChar *type) 177 { 178 if (xmlStrcmp(type, (const xmlChar *)"manifest") == 0) 179 return (SVCCFG_MANIFEST); 180 181 if (xmlStrcmp(type, (const xmlChar *)"profile") == 0) 182 return (SVCCFG_PROFILE); 183 184 if (xmlStrcmp(type, (const xmlChar *)"archive") == 0) 185 return (SVCCFG_ARCHIVE); 186 187 return (SVCCFG_UNKNOWN_BUNDLE); 188 } 189 190 static service_type_t 191 lxml_xlate_service_type(xmlChar *type) 192 { 193 if (xmlStrcmp(type, (const xmlChar *)"service") == 0) 194 return (SVCCFG_SERVICE); 195 196 if (xmlStrcmp(type, (const xmlChar *)"restarter") == 0) 197 return (SVCCFG_RESTARTER); 198 199 if (xmlStrcmp(type, (const xmlChar *)"milestone") == 0) 200 return (SVCCFG_MILESTONE); 201 202 return (SVCCFG_UNKNOWN_SERVICE); 203 } 204 205 static element_t 206 lxml_xlate_element(const xmlChar *tag) 207 { 208 int i; 209 210 for (i = 0; i < sizeof (lxml_elements) / sizeof (char *); i++) 211 if (xmlStrcmp(tag, (const xmlChar *)lxml_elements[i]) == 0) 212 return ((element_t)i); 213 214 return ((element_t)-1); 215 } 216 217 static uint_t 218 lxml_xlate_boolean(const xmlChar *value) 219 { 220 if (xmlStrcmp(value, (const xmlChar *)true) == 0) 221 return (1); 222 223 if (xmlStrcmp(value, (const xmlChar *)false) == 0) 224 return (0); 225 226 uu_die(gettext("illegal boolean value \"%s\"\n"), value); 227 228 /*NOTREACHED*/ 229 } 230 231 static scf_type_t 232 lxml_element_to_type(element_t type) 233 { 234 switch (type) { 235 case SC_ASTRING: return (SCF_TYPE_ASTRING); 236 case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN); 237 case SC_COUNT: return (SCF_TYPE_COUNT); 238 case SC_FMRI: return (SCF_TYPE_FMRI); 239 case SC_HOST: return (SCF_TYPE_HOST); 240 case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME); 241 case SC_INTEGER: return (SCF_TYPE_INTEGER); 242 case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4); 243 case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6); 244 case SC_OPAQUE: return (SCF_TYPE_OPAQUE); 245 case SC_TIME: return (SCF_TYPE_TIME); 246 case SC_URI: return (SCF_TYPE_URI); 247 case SC_USTRING: return (SCF_TYPE_USTRING); 248 249 default: 250 uu_die(gettext("unknown value type (%d)\n"), type); 251 } 252 253 /* NOTREACHED */ 254 } 255 256 static scf_type_t 257 lxml_element_to_scf_type(element_t type) 258 { 259 switch (type) { 260 case SC_ASTRING: return (SCF_TYPE_ASTRING); 261 case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN); 262 case SC_COUNT: return (SCF_TYPE_COUNT); 263 case SC_FMRI: return (SCF_TYPE_FMRI); 264 case SC_HOST: return (SCF_TYPE_HOST); 265 case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME); 266 case SC_INTEGER: return (SCF_TYPE_INTEGER); 267 case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4); 268 case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6); 269 case SC_OPAQUE: return (SCF_TYPE_OPAQUE); 270 case SC_TIME: return (SCF_TYPE_TIME); 271 case SC_URI: return (SCF_TYPE_URI); 272 case SC_USTRING: return (SCF_TYPE_USTRING); 273 default: 274 uu_die(gettext("unknown value type (%d)\n"), type); 275 } 276 277 /* NOTREACHED */ 278 } 279 280 static int 281 new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty, 282 xmlNodePtr n, const char *attr) 283 { 284 xmlChar *val; 285 property_t *p; 286 int r; 287 288 val = xmlGetProp(n, (xmlChar *)attr); 289 290 p = internal_property_create(pname, ty, 1, val); 291 r = internal_attach_property(pgrp, p); 292 293 if (r != 0) 294 internal_property_free(p); 295 296 return (r); 297 } 298 299 static int 300 lxml_ignorable_block(xmlNodePtr n) 301 { 302 return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 || 303 xmlStrcmp(n->name, (xmlChar *)"comment") == 0) ? 1 : 0); 304 } 305 306 static int 307 lxml_validate_string_value(scf_type_t type, const char *v) 308 { 309 static scf_value_t *scf_value = NULL; 310 static scf_handle_t *scf_hndl = NULL; 311 312 if (scf_hndl == NULL && (scf_hndl = scf_handle_create(SCF_VERSION)) == 313 NULL) 314 return (-1); 315 316 if (scf_value == NULL && (scf_value = scf_value_create(scf_hndl)) == 317 NULL) 318 return (-1); 319 320 return (scf_value_set_from_string(scf_value, type, v)); 321 } 322 323 static void 324 lxml_free_str(value_t *val) 325 { 326 free(val->sc_u.sc_string); 327 } 328 329 static value_t * 330 lxml_make_value(element_t type, const xmlChar *value) 331 { 332 value_t *v; 333 char *endptr; 334 scf_type_t scf_type = SCF_TYPE_INVALID; 335 336 v = internal_value_new(); 337 338 v->sc_type = lxml_element_to_type(type); 339 340 switch (type) { 341 case SC_COUNT: 342 /* 343 * Although an SC_COUNT represents a uint64_t the use 344 * of a negative value is acceptable due to the usage 345 * established by inetd(1M). 346 */ 347 errno = 0; 348 v->sc_u.sc_count = strtoull((char *)value, &endptr, 10); 349 if (errno != 0 || endptr == (char *)value || *endptr) 350 uu_die(gettext("illegal value \"%s\" for " 351 "%s (%s)\n"), (char *)value, 352 lxml_prop_types[type], 353 (errno) ? strerror(errno) : 354 gettext("Illegal character")); 355 break; 356 case SC_INTEGER: 357 errno = 0; 358 v->sc_u.sc_integer = strtoll((char *)value, &endptr, 10); 359 if (errno != 0 || *endptr) 360 uu_die(gettext("illegal value \"%s\" for " 361 "%s (%s)\n"), (char *)value, 362 lxml_prop_types[type], 363 (errno) ? strerror(errno) : "Illegal character"); 364 break; 365 case SC_OPAQUE: 366 case SC_HOST: 367 case SC_HOSTNAME: 368 case SC_NET_ADDR_V4: 369 case SC_NET_ADDR_V6: 370 case SC_FMRI: 371 case SC_URI: 372 case SC_TIME: 373 case SC_ASTRING: 374 case SC_USTRING: 375 scf_type = lxml_element_to_scf_type(type); 376 377 if ((v->sc_u.sc_string = strdup((char *)value)) == NULL) 378 uu_die(gettext("string duplication failed (%s)\n"), 379 strerror(errno)); 380 if (lxml_validate_string_value(scf_type, 381 v->sc_u.sc_string) != 0) 382 uu_die(gettext("illegal value \"%s\" for " 383 "%s (%s)\n"), (char *)value, 384 lxml_prop_types[type], 385 (scf_error()) ? scf_strerror(scf_error()) : 386 gettext("Illegal format")); 387 v->sc_free = lxml_free_str; 388 break; 389 case SC_BOOLEAN: 390 v->sc_u.sc_count = lxml_xlate_boolean(value); 391 break; 392 default: 393 uu_die(gettext("unknown value type (%d)\n"), type); 394 break; 395 } 396 397 return (v); 398 } 399 400 static int 401 lxml_get_value(property_t *prop, element_t vtype, xmlNodePtr value) 402 { 403 xmlNodePtr cursor; 404 405 for (cursor = value->xmlChildrenNode; cursor != NULL; 406 cursor = cursor->next) { 407 xmlChar *assigned_value; 408 value_t *v; 409 410 if (lxml_ignorable_block(cursor)) 411 continue; 412 413 switch (lxml_xlate_element(cursor->name)) { 414 case SC_VALUE_NODE: 415 if ((assigned_value = xmlGetProp(cursor, 416 (xmlChar *)value_attr)) == NULL) 417 uu_die(gettext("no value on value node?\n")); 418 break; 419 default: 420 uu_die(gettext("value list contains illegal element " 421 "\'%s\'\n"), cursor->name); 422 break; 423 } 424 425 v = lxml_make_value(vtype, assigned_value); 426 427 xmlFree(assigned_value); 428 429 internal_attach_value(prop, v); 430 } 431 432 return (0); 433 } 434 435 static int 436 lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval) 437 { 438 property_t *p; 439 element_t r; 440 value_t *v; 441 xmlChar *type, *val, *override; 442 443 p = internal_property_new(); 444 445 p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr); 446 if (p->sc_property_name == NULL) 447 uu_die(gettext("property name missing in group '%s'\n"), 448 pgrp->sc_pgroup_name); 449 450 type = xmlGetProp(propval, (xmlChar *)type_attr); 451 if (type == NULL) 452 uu_die(gettext("property type missing for property '%s/%s'\n"), 453 pgrp->sc_pgroup_name, p->sc_property_name); 454 455 for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); ++r) { 456 if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0) 457 break; 458 } 459 if (r >= sizeof (lxml_prop_types) / sizeof (char *)) 460 uu_die(gettext("property type invalid for property '%s/%s'\n"), 461 pgrp->sc_pgroup_name, p->sc_property_name); 462 463 p->sc_value_type = lxml_element_to_type(r); 464 465 val = xmlGetProp(propval, (xmlChar *)value_attr); 466 if (val == NULL) 467 uu_die(gettext("property value missing for property '%s/%s'\n"), 468 pgrp->sc_pgroup_name, p->sc_property_name); 469 470 v = lxml_make_value(r, val); 471 internal_attach_value(p, v); 472 473 override = xmlGetProp(propval, (xmlChar *)override_attr); 474 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0); 475 xmlFree(override); 476 477 return (internal_attach_property(pgrp, p)); 478 } 479 480 static int 481 lxml_get_property(pgroup_t *pgrp, xmlNodePtr property) 482 { 483 property_t *p; 484 xmlNodePtr cursor; 485 element_t r; 486 xmlChar *type, *override; 487 488 p = internal_property_new(); 489 490 if ((p->sc_property_name = (char *)xmlGetProp(property, 491 (xmlChar *)name_attr)) == NULL) 492 uu_die(gettext("property name missing in group \'%s\'\n"), 493 pgrp->sc_pgroup_name); 494 495 if ((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL) 496 uu_die(gettext("property type missing for " 497 "property \'%s/%s\'\n"), pgrp->sc_pgroup_name, 498 p->sc_property_name); 499 500 for (cursor = property->xmlChildrenNode; cursor != NULL; 501 cursor = cursor->next) { 502 if (lxml_ignorable_block(cursor)) 503 continue; 504 505 switch (r = lxml_xlate_element(cursor->name)) { 506 case SC_ASTRING: 507 case SC_BOOLEAN: 508 case SC_COUNT: 509 case SC_FMRI: 510 case SC_HOST: 511 case SC_HOSTNAME: 512 case SC_INTEGER: 513 case SC_NET_ADDR_V4: 514 case SC_NET_ADDR_V6: 515 case SC_OPAQUE: 516 case SC_TIME: 517 case SC_URI: 518 case SC_USTRING: 519 if (strcmp(lxml_prop_types[r], (const char *)type) != 0) 520 uu_die(gettext("property \'%s\' " 521 "type-to-list mismatch\n"), 522 p->sc_property_name); 523 524 p->sc_value_type = lxml_element_to_type(r); 525 (void) lxml_get_value(p, r, cursor); 526 break; 527 default: 528 uu_die(gettext("unknown value list type: %s\n"), 529 cursor->name); 530 break; 531 } 532 } 533 534 xmlFree(type); 535 536 override = xmlGetProp(property, (xmlChar *)override_attr); 537 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0); 538 xmlFree(override); 539 540 return (internal_attach_property(pgrp, p)); 541 } 542 543 static int 544 lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab) 545 { 546 return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY, 547 SCF_TYPE_ASTRING, stab, value_attr)); 548 } 549 550 /* 551 * Property groups can go on any of a service, an instance, or a template. 552 */ 553 static int 554 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup) 555 { 556 pgroup_t *pg; 557 xmlNodePtr cursor; 558 xmlChar *name, *type, *delete; 559 560 /* 561 * property group attributes: 562 * name: string 563 * type: string | framework | application 564 */ 565 name = xmlGetProp(pgroup, (xmlChar *)name_attr); 566 type = xmlGetProp(pgroup, (xmlChar *)type_attr); 567 pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type); 568 xmlFree(name); 569 xmlFree(type); 570 571 /* 572 * Walk the children of this lxml_elements, which are a stability 573 * element, property elements, or propval elements. 574 */ 575 for (cursor = pgroup->xmlChildrenNode; cursor != NULL; 576 cursor = cursor->next) { 577 if (lxml_ignorable_block(cursor)) 578 continue; 579 580 switch (lxml_xlate_element(cursor->name)) { 581 case SC_STABILITY: 582 (void) lxml_get_pgroup_stability(pg, cursor); 583 break; 584 case SC_PROPERTY: 585 (void) lxml_get_property(pg, cursor); 586 break; 587 case SC_PROPVAL: 588 (void) lxml_get_propval(pg, cursor); 589 break; 590 default: 591 abort(); 592 break; 593 } 594 } 595 596 delete = xmlGetProp(pgroup, (xmlChar *)delete_attr); 597 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 598 xmlFree(delete); 599 600 return (0); 601 } 602 603 604 /* 605 * Dependency groups, execution methods can go on either a service or an 606 * instance. 607 */ 608 609 static int 610 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile) 611 { 612 property_t *p; 613 614 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN, 615 1, (uint64_t)1); 616 if (internal_attach_property(pg, p) != 0) 617 return (-1); 618 619 return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE, 620 SCF_TYPE_ASTRING, profile, name_attr)); 621 } 622 623 static int 624 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred) 625 { 626 property_t *p; 627 628 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN, 629 1, (uint64_t)0); 630 if (internal_attach_property(pg, p) != 0) 631 return (-1); 632 633 if (new_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING, 634 cred, "user") != 0) 635 return (-1); 636 637 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING, 638 cred, "group") != 0) 639 return (-1); 640 641 if (new_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS, 642 SCF_TYPE_ASTRING, cred, "supp_groups") != 0) 643 return (-1); 644 645 if (new_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES, 646 SCF_TYPE_ASTRING, cred, "privileges") != 0) 647 return (-1); 648 649 if (new_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES, 650 SCF_TYPE_ASTRING, cred, "limit_privileges") != 0) 651 return (-1); 652 653 return (0); 654 } 655 656 static char * 657 lxml_get_envvar(xmlNodePtr envvar) 658 { 659 char *name; 660 char *value; 661 char *ret; 662 663 name = (char *)xmlGetProp(envvar, (xmlChar *)"name"); 664 value = (char *)xmlGetProp(envvar, (xmlChar *)"value"); 665 666 if (strlen(name) == 0 || strchr(name, '=') != NULL) 667 uu_die(gettext("Invalid environment variable " 668 "\"%s\".\n"), name); 669 if (strstr(name, "SMF_") == name) 670 uu_die(gettext("Invalid environment variable " 671 "\"%s\"; \"SMF_\" prefix is reserved.\n"), name); 672 673 ret = uu_msprintf("%s=%s", name, value); 674 xmlFree(name); 675 xmlFree(value); 676 return (ret); 677 } 678 679 static int 680 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment) 681 { 682 property_t *p; 683 xmlNodePtr cursor; 684 value_t *val; 685 686 p = internal_property_create(SCF_PROPERTY_ENVIRONMENT, 687 SCF_TYPE_ASTRING, 0); 688 689 for (cursor = environment->xmlChildrenNode; cursor != NULL; 690 cursor = cursor->next) { 691 char *tmp; 692 693 if (lxml_ignorable_block(cursor)) 694 continue; 695 696 if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR) 697 uu_die(gettext("illegal element \"%s\" on " 698 "method environment for \"%s\"\n"), 699 cursor->name, pg->sc_pgroup_name); 700 701 if ((tmp = lxml_get_envvar(cursor)) == NULL) 702 uu_die(gettext("Out of memory\n")); 703 704 val = internal_value_new(); 705 val->sc_u.sc_string = tmp; 706 val->sc_type = SCF_TYPE_ASTRING; 707 val->sc_free = lxml_free_str; 708 internal_attach_value(p, val); 709 } 710 711 if (internal_attach_property(pg, p) != 0) { 712 internal_property_free(p); 713 return (-1); 714 } 715 716 return (0); 717 } 718 719 static int 720 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx) 721 { 722 xmlNodePtr cursor; 723 724 if (new_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY, 725 SCF_TYPE_ASTRING, ctx, "working_directory") != 0) 726 return (-1); 727 728 if (new_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT, SCF_TYPE_ASTRING, 729 ctx, "project") != 0) 730 return (-1); 731 732 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL, 733 SCF_TYPE_ASTRING, ctx, "resource_pool") != 0) 734 return (-1); 735 736 for (cursor = ctx->xmlChildrenNode; cursor != NULL; 737 cursor = cursor->next) { 738 if (lxml_ignorable_block(cursor)) 739 continue; 740 741 switch (lxml_xlate_element(cursor->name)) { 742 case SC_METHOD_CREDENTIAL: 743 (void) lxml_get_method_credential(pg, cursor); 744 break; 745 case SC_METHOD_PROFILE: 746 (void) lxml_get_method_profile(pg, cursor); 747 break; 748 case SC_METHOD_ENVIRONMENT: 749 (void) lxml_get_method_environment(pg, cursor); 750 break; 751 default: 752 semerr(gettext("illegal element \'%s\' in method " 753 "context\n"), (char *)cursor); 754 break; 755 } 756 } 757 758 return (0); 759 } 760 761 static int 762 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx) 763 { 764 pgroup_t *pg; 765 766 pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT, 767 (char *)scf_group_framework); 768 769 return (lxml_get_method_context(pg, ctx)); 770 } 771 772 static int 773 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth) 774 { 775 pgroup_t *pg; 776 property_t *p; 777 xmlChar *name, *timeout, *delete; 778 xmlNodePtr cursor; 779 int r = 0; 780 781 name = xmlGetProp(emeth, (xmlChar *)name_attr); 782 pg = internal_pgroup_find_or_create(entity, (char *)name, 783 (char *)SCF_GROUP_METHOD); 784 xmlFree(name); 785 786 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 787 emeth, type_attr) != 0 || 788 new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING, 789 emeth, "exec") != 0) 790 return (-1); 791 792 timeout = xmlGetProp(emeth, (xmlChar *)"timeout_seconds"); 793 if (timeout != NULL) { 794 uint64_t u_timeout; 795 char *endptr; 796 /* 797 * Although an SC_COUNT represents a uint64_t the use 798 * of a negative value is acceptable due to the usage 799 * established by inetd(1M). 800 */ 801 errno = 0; 802 u_timeout = strtoull((char *)timeout, &endptr, 10); 803 if (errno != 0 || endptr == (char *)timeout || *endptr) 804 uu_die(gettext("illegal value \"%s\" for " 805 "timeout_seconds (%s)\n"), 806 (char *)timeout, (errno) ? strerror(errno): 807 gettext("Illegal character")); 808 p = internal_property_create(SCF_PROPERTY_TIMEOUT, 809 SCF_TYPE_COUNT, 1, u_timeout); 810 r = internal_attach_property(pg, p); 811 xmlFree(timeout); 812 } 813 if (r != 0) 814 return (-1); 815 816 /* 817 * There is a possibility that a method context also exists, in which 818 * case the following attributes are defined: project, resource_pool, 819 * working_directory, profile, user, group, privileges, limit_privileges 820 */ 821 for (cursor = emeth->xmlChildrenNode; cursor != NULL; 822 cursor = cursor->next) { 823 if (lxml_ignorable_block(cursor)) 824 continue; 825 826 switch (lxml_xlate_element(cursor->name)) { 827 case SC_STABILITY: 828 if (lxml_get_pgroup_stability(pg, cursor) != 0) 829 return (-1); 830 break; 831 832 case SC_METHOD_CONTEXT: 833 (void) lxml_get_method_context(pg, cursor); 834 break; 835 836 case SC_PROPVAL: 837 (void) lxml_get_propval(pg, cursor); 838 break; 839 840 case SC_PROPERTY: 841 (void) lxml_get_property(pg, cursor); 842 break; 843 844 default: 845 uu_die(gettext("illegal element \"%s\" on " 846 "execution method \"%s\"\n"), cursor->name, 847 pg->sc_pgroup_name); 848 break; 849 } 850 } 851 852 delete = xmlGetProp(emeth, (xmlChar *)delete_attr); 853 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 854 xmlFree(delete); 855 856 return (0); 857 } 858 859 static int 860 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency) 861 { 862 pgroup_t *pg; 863 property_t *p; 864 xmlNodePtr cursor; 865 xmlChar *name; 866 xmlChar *delete; 867 868 /* 869 * dependency attributes: 870 * name: string 871 * grouping: require_all | require_any | exclude_all | optional_all 872 * reset_on: string (error | restart | refresh | none) 873 * type: service / path /host 874 */ 875 876 name = xmlGetProp(dependency, (xmlChar *)name_attr); 877 pg = internal_pgroup_find_or_create(entity, (char *)name, 878 (char *)SCF_GROUP_DEPENDENCY); 879 xmlFree(name); 880 881 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 882 dependency, type_attr) != 0) 883 return (-1); 884 885 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON, 886 SCF_TYPE_ASTRING, dependency, "restart_on") != 0) 887 return (-1); 888 889 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 890 dependency, "grouping") != 0) 891 return (-1); 892 893 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0); 894 if (internal_attach_property(pg, p) != 0) 895 return (-1); 896 897 for (cursor = dependency->xmlChildrenNode; cursor != NULL; 898 cursor = cursor->next) { 899 xmlChar *value; 900 value_t *v; 901 902 if (lxml_ignorable_block(cursor)) 903 continue; 904 905 switch (lxml_xlate_element(cursor->name)) { 906 case SC_STABILITY: 907 if (lxml_get_pgroup_stability(pg, cursor) != 0) 908 return (-1); 909 break; 910 911 case SC_SERVICE_FMRI: 912 value = xmlGetProp(cursor, (xmlChar *)value_attr); 913 if (value != NULL) { 914 if (lxml_validate_string_value(SCF_TYPE_FMRI, 915 (char *)value) != 0) 916 uu_die(gettext("illegal value \"%s\" " 917 "for %s (%s)\n"), (char *)value, 918 lxml_prop_types[SC_FMRI], 919 (scf_error()) ? 920 scf_strerror(scf_error()) : 921 gettext("Illegal format")); 922 v = internal_value_new(); 923 v->sc_type = SCF_TYPE_FMRI; 924 v->sc_u.sc_string = (char *)value; 925 internal_attach_value(p, v); 926 } 927 928 break; 929 930 case SC_PROPVAL: 931 (void) lxml_get_propval(pg, cursor); 932 break; 933 934 case SC_PROPERTY: 935 (void) lxml_get_property(pg, cursor); 936 break; 937 938 default: 939 uu_die(gettext("illegal element \"%s\" on " 940 "dependency group \"%s\"\n"), cursor->name, name); 941 break; 942 } 943 } 944 945 delete = xmlGetProp(dependency, (xmlChar *)delete_attr); 946 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 947 xmlFree(delete); 948 949 return (0); 950 } 951 952 /* 953 * Dependents are hairy. They should cause a dependency pg to be created in 954 * another service, but we can't do that here; we'll have to wait until the 955 * import routines. So for now we'll add the dependency group that should go 956 * in the other service to the entity's dependent list. 957 */ 958 static int 959 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent) 960 { 961 xmlChar *name, *or; 962 xmlNodePtr sf; 963 xmlChar *fmri, *delete; 964 pgroup_t *pg; 965 property_t *p; 966 xmlNodePtr n; 967 char *myfmri; 968 969 name = xmlGetProp(dependent, (xmlChar *)name_attr); 970 971 if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) { 972 semerr(gettext("Property group and dependent of entity %s " 973 "have same name \"%s\".\n"), entity->sc_name, name); 974 xmlFree(name); 975 return (-1); 976 } 977 978 or = xmlGetProp(dependent, (xmlChar *)override_attr); 979 980 pg = internal_pgroup_new(); 981 pg->sc_pgroup_name = (char *)name; 982 pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY; 983 pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0); 984 xmlFree(or); 985 if (internal_attach_dependent(entity, pg) != 0) { 986 xmlFree(name); 987 internal_pgroup_free(pg); 988 return (-1); 989 } 990 991 for (sf = dependent->children; sf != NULL; sf = sf->next) 992 if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0) 993 break; 994 assert(sf != NULL); 995 fmri = xmlGetProp(sf, (xmlChar *)value_attr); 996 pg->sc_pgroup_fmri = (char *)fmri; 997 998 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON, 999 SCF_TYPE_ASTRING, dependent, "restart_on") != 0) 1000 return (-1); 1001 1002 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 1003 dependent, "grouping") != 0) 1004 return (-1); 1005 1006 myfmri = safe_malloc(max_scf_fmri_len + 1); 1007 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) { 1008 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s", 1009 entity->sc_name) < 0) 1010 bad_error("snprintf", errno); 1011 } else { 1012 assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT); 1013 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s", 1014 entity->sc_parent->sc_name, entity->sc_name) < 0) 1015 bad_error("snprintf", errno); 1016 } 1017 1018 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1, 1019 myfmri); 1020 if (internal_attach_property(pg, p) != 0) 1021 return (-1); 1022 1023 /* Create a property to serve as a do-not-export flag. */ 1024 p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1, 1025 (uint64_t)1); 1026 if (internal_attach_property(pg, p) != 0) 1027 return (-1); 1028 1029 for (n = sf->next; n != NULL; n = n->next) { 1030 if (lxml_ignorable_block(n)) 1031 continue; 1032 1033 switch (lxml_xlate_element(n->name)) { 1034 case SC_STABILITY: 1035 if (new_str_prop_from_attr(pg, 1036 SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n, 1037 value_attr) != 0) 1038 return (-1); 1039 break; 1040 1041 case SC_PROPVAL: 1042 (void) lxml_get_propval(pg, n); 1043 break; 1044 1045 case SC_PROPERTY: 1046 (void) lxml_get_property(pg, n); 1047 break; 1048 1049 default: 1050 uu_die(gettext("unexpected element %s.\n"), n->name); 1051 } 1052 } 1053 1054 /* Go back and fill in defaults. */ 1055 if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) { 1056 p = internal_property_create(SCF_PROPERTY_TYPE, 1057 SCF_TYPE_ASTRING, 1, "service"); 1058 if (internal_attach_property(pg, p) != 0) 1059 return (-1); 1060 } 1061 1062 delete = xmlGetProp(dependent, (xmlChar *)delete_attr); 1063 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 1064 xmlFree(delete); 1065 1066 pg = internal_pgroup_find_or_create(entity, "dependents", 1067 (char *)scf_group_framework); 1068 p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri); 1069 if (internal_attach_property(pg, p) != 0) 1070 return (-1); 1071 1072 return (0); 1073 } 1074 1075 static int 1076 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr) 1077 { 1078 pgroup_t *pg; 1079 property_t *p; 1080 xmlChar *stabval; 1081 1082 if ((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) { 1083 uu_warn(gettext("no stability value found\n")); 1084 stabval = (xmlChar *)strdup("External"); 1085 } 1086 1087 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1088 (char *)scf_group_framework); 1089 1090 p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY, 1091 SCF_TYPE_ASTRING, 1, stabval); 1092 1093 return (internal_attach_property(pg, p)); 1094 } 1095 1096 static int 1097 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr) 1098 { 1099 pgroup_t *pg; 1100 property_t *p; 1101 xmlChar *restarter; 1102 xmlNode *cursor; 1103 int r; 1104 1105 /* 1106 * Go find child. Child is a service_fmri element. value attribute 1107 * contains restarter FMRI. 1108 */ 1109 1110 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1111 (char *)scf_group_framework); 1112 1113 /* 1114 * Walk its child elements, as appropriate. 1115 */ 1116 for (cursor = rstr->xmlChildrenNode; cursor != NULL; 1117 cursor = cursor->next) { 1118 if (lxml_ignorable_block(cursor)) 1119 continue; 1120 1121 switch (lxml_xlate_element(cursor->name)) { 1122 case SC_SERVICE_FMRI: 1123 restarter = xmlGetProp(cursor, (xmlChar *)value_attr); 1124 break; 1125 default: 1126 uu_die(gettext("illegal element \"%s\" on restarter " 1127 "element for \"%s\"\n"), cursor->name, 1128 entity->sc_name); 1129 break; 1130 } 1131 } 1132 1133 p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1, 1134 restarter); 1135 1136 r = internal_attach_property(pg, p); 1137 if (r != 0) { 1138 internal_property_free(p); 1139 return (-1); 1140 } 1141 1142 return (0); 1143 } 1144 1145 static void 1146 sanitize_locale(uchar_t *locale) 1147 { 1148 for (; *locale != '\0'; locale++) 1149 if (!isalnum(*locale) && *locale != '_') 1150 *locale = '_'; 1151 } 1152 1153 static int 1154 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext) 1155 { 1156 xmlNodePtr cursor; 1157 xmlChar *val; 1158 char *stripped, *cp; 1159 property_t *p; 1160 int r; 1161 1162 if ((val = xmlGetProp(loctext, (xmlChar *)"xml:lang")) == NULL) 1163 if ((val = xmlGetProp(loctext, (xmlChar *)"lang")) == NULL) 1164 val = (xmlChar *)"unknown"; 1165 1166 sanitize_locale(val); 1167 1168 for (cursor = loctext->xmlChildrenNode; cursor != NULL; 1169 cursor = cursor->next) { 1170 if (strcmp("text", (const char *)cursor->name) == 0) { 1171 break; 1172 } else if (strcmp("comment", (const char *)cursor->name) != 0) { 1173 uu_die(gettext("illegal element \"%s\" on loctext " 1174 "element for \"%s\"\n"), cursor->name, 1175 service->sc_name); 1176 } 1177 } 1178 1179 if (cursor == NULL) { 1180 uu_die(gettext("loctext element has no content for \"%s\"\n"), 1181 service->sc_name); 1182 } 1183 1184 /* 1185 * Remove leading and trailing whitespace. 1186 */ 1187 if ((stripped = strdup((const char *)cursor->content)) == NULL) 1188 uu_die(gettext("Out of memory\n")); 1189 1190 for (; isspace(*stripped); stripped++); 1191 for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--); 1192 *(cp + 1) = '\0'; 1193 1194 p = internal_property_create((const char *)val, SCF_TYPE_USTRING, 1, 1195 stripped); 1196 1197 r = internal_attach_property(pg, p); 1198 if (r != 0) 1199 internal_property_free(p); 1200 1201 return (r); 1202 } 1203 1204 static int 1205 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name) 1206 { 1207 xmlNodePtr cursor; 1208 pgroup_t *pg; 1209 1210 /* 1211 * Create the property group, if absent. 1212 */ 1213 pg = internal_pgroup_find_or_create(service, 1214 (char *)SCF_PG_TM_COMMON_NAME, (char *)SCF_GROUP_TEMPLATE); 1215 1216 /* 1217 * Iterate through one or more loctext elements. The locale is the 1218 * property name; the contents are the ustring value for the property. 1219 */ 1220 for (cursor = common_name->xmlChildrenNode; cursor != NULL; 1221 cursor = cursor->next) { 1222 if (lxml_ignorable_block(cursor)) 1223 continue; 1224 1225 switch (lxml_xlate_element(cursor->name)) { 1226 case SC_LOCTEXT: 1227 if (lxml_get_loctext(service, pg, cursor)) 1228 return (-1); 1229 break; 1230 default: 1231 uu_die(gettext("illegal element \"%s\" on common_name " 1232 "element for \"%s\"\n"), cursor->name, 1233 service->sc_name); 1234 break; 1235 } 1236 } 1237 1238 return (0); 1239 } 1240 1241 static int 1242 lxml_get_tm_description(entity_t *service, xmlNodePtr description) 1243 { 1244 xmlNodePtr cursor; 1245 pgroup_t *pg; 1246 1247 /* 1248 * Create the property group, if absent. 1249 */ 1250 pg = internal_pgroup_find_or_create(service, 1251 (char *)SCF_PG_TM_DESCRIPTION, (char *)SCF_GROUP_TEMPLATE); 1252 1253 /* 1254 * Iterate through one or more loctext elements. The locale is the 1255 * property name; the contents are the ustring value for the property. 1256 */ 1257 for (cursor = description->xmlChildrenNode; cursor != NULL; 1258 cursor = cursor->next) { 1259 if (lxml_ignorable_block(cursor)) 1260 continue; 1261 1262 switch (lxml_xlate_element(cursor->name)) { 1263 case SC_LOCTEXT: 1264 if (lxml_get_loctext(service, pg, cursor)) 1265 return (-1); 1266 break; 1267 default: 1268 uu_die(gettext("illegal element \"%s\" on description " 1269 "element for \"%s\"\n"), cursor->name, 1270 service->sc_name); 1271 break; 1272 } 1273 } 1274 1275 return (0); 1276 } 1277 1278 static char * 1279 lxml_label_to_groupname(const char *prefix, const char *in) 1280 { 1281 char *out, *cp; 1282 size_t len, piece_len; 1283 1284 out = uu_zalloc(2 * scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1); 1285 if (out == NULL) 1286 return (NULL); 1287 1288 (void) strcpy(out, prefix); 1289 (void) strcat(out, in); 1290 1291 len = strlen(out); 1292 if (len > max_scf_name_len) { 1293 /* Use the first half and the second half. */ 1294 piece_len = (max_scf_name_len - 2) / 2; 1295 1296 (void) strncpy(out + piece_len, "..", 2); 1297 1298 (void) strcpy(out + piece_len + 2, out + (len - piece_len)); 1299 1300 len = strlen(out); 1301 } 1302 1303 /* 1304 * Translate non-property characters to '_'. 1305 */ 1306 for (cp = out; *cp != '\0'; ++cp) { 1307 if (!(isalnum(*cp) || *cp == '_' || *cp == '-')) 1308 *cp = '_'; 1309 } 1310 1311 *cp = '\0'; 1312 1313 return (out); 1314 } 1315 1316 static int 1317 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage) 1318 { 1319 pgroup_t *pg; 1320 char *pgname; 1321 xmlChar *title; 1322 1323 /* 1324 * Fetch title attribute, convert to something sanitized, and create 1325 * property group. 1326 */ 1327 title = xmlGetProp(manpage, (xmlChar *)"title"); 1328 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, 1329 (const char *)title); 1330 1331 pg = internal_pgroup_find_or_create(service, pgname, 1332 (char *)SCF_GROUP_TEMPLATE); 1333 1334 /* 1335 * Each attribute is an astring property within the group. 1336 */ 1337 if (new_str_prop_from_attr(pg, "title", SCF_TYPE_ASTRING, manpage, 1338 "title") != 0 || 1339 new_str_prop_from_attr(pg, "section", SCF_TYPE_ASTRING, manpage, 1340 "section") != 0 || 1341 new_str_prop_from_attr(pg, "manpath", SCF_TYPE_ASTRING, manpage, 1342 "manpath") != 0) 1343 return (-1); 1344 1345 return (0); 1346 } 1347 1348 static int 1349 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link) 1350 { 1351 pgroup_t *pg; 1352 char *pgname; 1353 xmlChar *name; 1354 1355 /* 1356 * Fetch name attribute, convert name to something sanitized, and create 1357 * property group. 1358 */ 1359 name = xmlGetProp(doc_link, (xmlChar *)"name"); 1360 1361 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX, 1362 (const char *)name); 1363 1364 pg = internal_pgroup_find_or_create(service, pgname, 1365 (char *)SCF_GROUP_TEMPLATE); 1366 1367 /* 1368 * Each attribute is an astring property within the group. 1369 */ 1370 if (new_str_prop_from_attr(pg, "name", SCF_TYPE_ASTRING, doc_link, 1371 "name") != 0 || 1372 new_str_prop_from_attr(pg, "uri", SCF_TYPE_ASTRING, doc_link, 1373 "uri") != 0) 1374 return (-1); 1375 1376 return (0); 1377 } 1378 1379 static int 1380 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation) 1381 { 1382 xmlNodePtr cursor; 1383 1384 for (cursor = documentation->xmlChildrenNode; cursor != NULL; 1385 cursor = cursor->next) { 1386 if (lxml_ignorable_block(cursor)) 1387 continue; 1388 1389 switch (lxml_xlate_element(cursor->name)) { 1390 case SC_MANPAGE: 1391 (void) lxml_get_tm_manpage(service, cursor); 1392 break; 1393 case SC_DOC_LINK: 1394 (void) lxml_get_tm_doclink(service, cursor); 1395 break; 1396 default: 1397 uu_die(gettext("illegal element \"%s\" on template " 1398 "for service \"%s\"\n"), 1399 cursor->name, service->sc_name); 1400 } 1401 } 1402 1403 return (0); 1404 } 1405 1406 static int 1407 lxml_get_template(entity_t *service, xmlNodePtr templ) 1408 { 1409 xmlNodePtr cursor; 1410 1411 for (cursor = templ->xmlChildrenNode; cursor != NULL; 1412 cursor = cursor->next) { 1413 if (lxml_ignorable_block(cursor)) 1414 continue; 1415 1416 switch (lxml_xlate_element(cursor->name)) { 1417 case SC_COMMON_NAME: 1418 (void) lxml_get_tm_common_name(service, cursor); 1419 break; 1420 case SC_DESCRIPTION: 1421 (void) lxml_get_tm_description(service, cursor); 1422 break; 1423 case SC_DOCUMENTATION: 1424 (void) lxml_get_tm_documentation(service, cursor); 1425 break; 1426 default: 1427 uu_die(gettext("illegal element \"%s\" on template " 1428 "for service \"%s\"\n"), 1429 cursor->name, service->sc_name); 1430 } 1431 } 1432 1433 return (0); 1434 } 1435 1436 static int 1437 lxml_get_default_instance(entity_t *service, xmlNodePtr definst) 1438 { 1439 entity_t *i; 1440 xmlChar *enabled; 1441 pgroup_t *pg; 1442 property_t *p; 1443 char *package; 1444 uint64_t enabled_val = 0; 1445 1446 i = internal_instance_new("default"); 1447 1448 if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) { 1449 enabled_val = (strcmp(true, (const char *)enabled) == 0) ? 1450 1 : 0; 1451 xmlFree(enabled); 1452 } 1453 1454 /* 1455 * New general property group with enabled boolean property set. 1456 */ 1457 1458 pg = internal_pgroup_new(); 1459 (void) internal_attach_pgroup(i, pg); 1460 1461 pg->sc_pgroup_name = (char *)scf_pg_general; 1462 pg->sc_pgroup_type = (char *)scf_group_framework; 1463 pg->sc_pgroup_flags = 0; 1464 1465 p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1, 1466 enabled_val); 1467 1468 (void) internal_attach_property(pg, p); 1469 1470 /* 1471 * Add general/package property if PKGINST is set. 1472 */ 1473 if ((package = getenv("PKGINST")) != NULL) { 1474 p = internal_property_create(SCF_PROPERTY_PACKAGE, 1475 SCF_TYPE_ASTRING, 1, package); 1476 1477 (void) internal_attach_property(pg, p); 1478 } 1479 1480 return (internal_attach_entity(service, i)); 1481 } 1482 1483 /* 1484 * Translate an instance element into an internal property tree, added to 1485 * service. If apply is true, forbid subelements and set the enabled property 1486 * to override. 1487 */ 1488 static int 1489 lxml_get_instance(entity_t *service, xmlNodePtr inst, int apply) 1490 { 1491 entity_t *i; 1492 pgroup_t *pg; 1493 property_t *p; 1494 xmlNodePtr cursor; 1495 xmlChar *enabled; 1496 int r; 1497 1498 /* 1499 * Fetch its attributes, as appropriate. 1500 */ 1501 i = internal_instance_new((char *)xmlGetProp(inst, 1502 (xmlChar *)name_attr)); 1503 1504 /* 1505 * Note that this must be done before walking the children so that 1506 * sc_fmri is set in case we enter lxml_get_dependent(). 1507 */ 1508 r = internal_attach_entity(service, i); 1509 if (r != 0) 1510 return (r); 1511 1512 enabled = xmlGetProp(inst, (xmlChar *)enabled_attr); 1513 1514 /* 1515 * New general property group with enabled boolean property set. 1516 */ 1517 pg = internal_pgroup_new(); 1518 (void) internal_attach_pgroup(i, pg); 1519 1520 pg->sc_pgroup_name = (char *)scf_pg_general; 1521 pg->sc_pgroup_type = (char *)scf_group_framework; 1522 pg->sc_pgroup_flags = 0; 1523 1524 p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1, 1525 (uint64_t)(strcmp(true, (const char *)enabled) == 0 ? 1 : 0)); 1526 1527 p->sc_property_override = apply; 1528 1529 (void) internal_attach_property(pg, p); 1530 1531 xmlFree(enabled); 1532 1533 /* 1534 * Walk its child elements, as appropriate. 1535 */ 1536 for (cursor = inst->xmlChildrenNode; cursor != NULL; 1537 cursor = cursor->next) { 1538 if (lxml_ignorable_block(cursor)) 1539 continue; 1540 1541 if (apply) { 1542 semerr(gettext("Instance \"%s\" may not contain " 1543 "elements in profiles.\n"), i->sc_name, 1544 cursor->name); 1545 return (-1); 1546 } 1547 1548 switch (lxml_xlate_element(cursor->name)) { 1549 case SC_RESTARTER: 1550 (void) lxml_get_restarter(i, cursor); 1551 break; 1552 case SC_DEPENDENCY: 1553 (void) lxml_get_dependency(i, cursor); 1554 break; 1555 case SC_DEPENDENT: 1556 (void) lxml_get_dependent(i, cursor); 1557 break; 1558 case SC_METHOD_CONTEXT: 1559 (void) lxml_get_entity_method_context(i, cursor); 1560 break; 1561 case SC_EXEC_METHOD: 1562 (void) lxml_get_exec_method(i, cursor); 1563 break; 1564 case SC_PROPERTY_GROUP: 1565 (void) lxml_get_pgroup(i, cursor); 1566 break; 1567 case SC_TEMPLATE: 1568 (void) lxml_get_template(i, cursor); 1569 break; 1570 default: 1571 uu_die(gettext( 1572 "illegal element \"%s\" on instance \"%s\"\n"), 1573 cursor->name, i->sc_name); 1574 break; 1575 } 1576 } 1577 1578 return (0); 1579 } 1580 1581 /* ARGSUSED1 */ 1582 static int 1583 lxml_get_single_instance(entity_t *entity, xmlNodePtr si) 1584 { 1585 pgroup_t *pg; 1586 property_t *p; 1587 int r; 1588 1589 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1590 (char *)scf_group_framework); 1591 1592 p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE, 1593 SCF_TYPE_BOOLEAN, 1, (uint64_t)1); 1594 1595 r = internal_attach_property(pg, p); 1596 if (r != 0) { 1597 internal_property_free(p); 1598 return (-1); 1599 } 1600 1601 return (0); 1602 } 1603 1604 /* 1605 * Translate a service element into an internal instance/property tree, added 1606 * to bundle. If apply is true, allow only instance subelements. 1607 */ 1608 static int 1609 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, int apply) 1610 { 1611 entity_t *s; 1612 xmlNodePtr cursor; 1613 xmlChar *type; 1614 xmlChar *version; 1615 int e; 1616 1617 /* 1618 * Fetch attributes, as appropriate. 1619 */ 1620 s = internal_service_new((char *)xmlGetProp(svc, 1621 (xmlChar *)name_attr)); 1622 1623 version = xmlGetProp(svc, (xmlChar *)"version"); 1624 s->sc_u.sc_service.sc_service_version = atol((const char *)version); 1625 xmlFree(version); 1626 1627 type = xmlGetProp(svc, (xmlChar *)type_attr); 1628 s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type); 1629 xmlFree(type); 1630 1631 /* 1632 * Walk its child elements, as appropriate. 1633 */ 1634 for (cursor = svc->xmlChildrenNode; cursor != NULL; 1635 cursor = cursor->next) { 1636 if (lxml_ignorable_block(cursor)) 1637 continue; 1638 1639 e = lxml_xlate_element(cursor->name); 1640 1641 if (apply && e != SC_INSTANCE) { 1642 semerr(gettext("Service \"%s\" may not contain the " 1643 "non-instance element \"%s\" in a profile.\n"), 1644 s->sc_name, cursor->name); 1645 1646 return (-1); 1647 } 1648 1649 switch (e) { 1650 case SC_INSTANCE: 1651 (void) lxml_get_instance(s, cursor, apply); 1652 break; 1653 case SC_TEMPLATE: 1654 (void) lxml_get_template(s, cursor); 1655 break; 1656 case SC_STABILITY: 1657 (void) lxml_get_entity_stability(s, cursor); 1658 break; 1659 case SC_DEPENDENCY: 1660 (void) lxml_get_dependency(s, cursor); 1661 break; 1662 case SC_DEPENDENT: 1663 (void) lxml_get_dependent(s, cursor); 1664 break; 1665 case SC_RESTARTER: 1666 (void) lxml_get_restarter(s, cursor); 1667 break; 1668 case SC_EXEC_METHOD: 1669 (void) lxml_get_exec_method(s, cursor); 1670 break; 1671 case SC_METHOD_CONTEXT: 1672 (void) lxml_get_entity_method_context(s, cursor); 1673 break; 1674 case SC_PROPERTY_GROUP: 1675 (void) lxml_get_pgroup(s, cursor); 1676 break; 1677 case SC_INSTANCE_CREATE_DEFAULT: 1678 (void) lxml_get_default_instance(s, cursor); 1679 break; 1680 case SC_INSTANCE_SINGLE: 1681 (void) lxml_get_single_instance(s, cursor); 1682 break; 1683 default: 1684 uu_die(gettext( 1685 "illegal element \"%s\" on service \"%s\"\n"), 1686 cursor->name, s->sc_name); 1687 break; 1688 } 1689 } 1690 1691 return (internal_attach_service(bundle, s)); 1692 } 1693 1694 #ifdef DEBUG 1695 void 1696 lxml_dump(int g, xmlNodePtr p) 1697 { 1698 if (p && p->name) { 1699 printf("%d %s\n", g, p->name); 1700 1701 for (p = p->xmlChildrenNode; p != NULL; p = p->next) 1702 lxml_dump(g + 1, p); 1703 } 1704 } 1705 #endif /* DEBUG */ 1706 1707 static int 1708 lxml_is_known_dtd(const xmlChar *dtdname) 1709 { 1710 if (dtdname == NULL || 1711 strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0) 1712 return (0); 1713 1714 return (1); 1715 } 1716 1717 static int 1718 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type, 1719 xmlNodePtr subbundle, int apply) 1720 { 1721 xmlNodePtr cursor; 1722 xmlChar *type; 1723 int e; 1724 1725 /* 1726 * 1. Get bundle attributes. 1727 */ 1728 type = xmlGetProp(subbundle, (xmlChar *)"type"); 1729 bundle->sc_bundle_type = lxml_xlate_bundle_type(type); 1730 if (bundle->sc_bundle_type != bundle_type && 1731 bundle_type != SVCCFG_UNKNOWN_BUNDLE) { 1732 semerr(gettext("included bundle of different type.\n")); 1733 return (-1); 1734 } 1735 1736 xmlFree(type); 1737 1738 if (!apply) { 1739 if (bundle->sc_bundle_type != SVCCFG_MANIFEST) { 1740 semerr(gettext("document is not a manifest.\n")); 1741 return (-1); 1742 } 1743 } else { 1744 if (bundle->sc_bundle_type != SVCCFG_PROFILE) { 1745 semerr(gettext("document is not a profile.\n")); 1746 return (-1); 1747 } 1748 } 1749 1750 if ((bundle->sc_bundle_name = xmlGetProp(subbundle, 1751 (xmlChar *)"name")) == NULL) { 1752 semerr(gettext("service bundle lacks name attribute\n")); 1753 return (-1); 1754 } 1755 1756 /* 1757 * 2. Get services, descend into each one and build state. 1758 */ 1759 for (cursor = subbundle->xmlChildrenNode; cursor != NULL; 1760 cursor = cursor->next) { 1761 if (lxml_ignorable_block(cursor)) 1762 continue; 1763 1764 e = lxml_xlate_element(cursor->name); 1765 1766 switch (e) { 1767 case SC_XI_INCLUDE: 1768 continue; 1769 1770 case SC_SERVICE_BUNDLE: 1771 if (lxml_get_bundle(bundle, bundle_type, cursor, apply)) 1772 return (-1); 1773 break; 1774 case SC_SERVICE: 1775 (void) lxml_get_service(bundle, cursor, apply); 1776 break; 1777 } 1778 } 1779 1780 return (0); 1781 } 1782 1783 /* 1784 * Load an XML tree from filename and translate it into an internal service 1785 * tree bundle. If apply is false, require that the the bundle be of type 1786 * manifest, or type profile otherwise. 1787 */ 1788 int 1789 lxml_get_bundle_file(bundle_t *bundle, const char *filename, int apply) 1790 { 1791 xmlDocPtr document; 1792 xmlNodePtr cursor; 1793 xmlDtdPtr dtd = NULL; 1794 xmlValidCtxtPtr vcp; 1795 boolean_t do_validate; 1796 char *dtdpath = NULL; 1797 int r; 1798 1799 /* 1800 * Until libxml2 addresses DTD-based validation with XInclude, we don't 1801 * validate service profiles (i.e. the apply path). 1802 */ 1803 do_validate = (apply == 0) && (getenv("SVCCFG_NOVALIDATE") == NULL); 1804 if (do_validate) 1805 dtdpath = getenv("SVCCFG_DTD"); 1806 1807 if (dtdpath != NULL) 1808 xmlLoadExtDtdDefaultValue = 0; 1809 1810 if ((document = xmlReadFile(filename, NULL, 1811 XML_PARSE_NOERROR | XML_PARSE_NOWARNING)) == NULL) { 1812 semerr(gettext("couldn't parse document\n")); 1813 return (-1); 1814 } 1815 1816 /* 1817 * Verify that this is a document type we understand. 1818 */ 1819 if ((dtd = xmlGetIntSubset(document)) == NULL) { 1820 semerr(gettext("document has no DTD\n")); 1821 return (-1); 1822 } 1823 1824 if (!lxml_is_known_dtd(dtd->SystemID)) { 1825 semerr(gettext("document DTD unknown; not service bundle?\n")); 1826 return (-1); 1827 } 1828 1829 if ((cursor = xmlDocGetRootElement(document)) == NULL) { 1830 semerr(gettext("document is empty\n")); 1831 xmlFreeDoc(document); 1832 return (-1); 1833 } 1834 1835 if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) { 1836 semerr(gettext("document is not a service bundle\n")); 1837 xmlFreeDoc(document); 1838 return (-1); 1839 } 1840 1841 1842 if (dtdpath != NULL) { 1843 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath); 1844 if (dtd == NULL) { 1845 semerr(gettext("Could not parse DTD \"%s\".\n"), 1846 dtdpath); 1847 return (-1); 1848 } 1849 1850 if (document->extSubset != NULL) 1851 xmlFreeDtd(document->extSubset); 1852 1853 document->extSubset = dtd; 1854 } 1855 1856 if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {; 1857 semerr(gettext("couldn't handle XInclude statements " 1858 "in document\n")); 1859 return (-1); 1860 } 1861 1862 if (do_validate) { 1863 vcp = xmlNewValidCtxt(); 1864 if (vcp == NULL) 1865 uu_die(gettext("could not allocate memory")); 1866 vcp->warning = xmlParserValidityWarning; 1867 vcp->error = xmlParserValidityError; 1868 1869 r = xmlValidateDocument(vcp, document); 1870 1871 xmlFreeValidCtxt(vcp); 1872 1873 if (r == 0) { 1874 semerr(gettext("Document is not valid.\n")); 1875 xmlFreeDoc(document); 1876 return (-1); 1877 } 1878 } 1879 1880 1881 #ifdef DEBUG 1882 lxml_dump(0, cursor); 1883 #endif /* DEBUG */ 1884 1885 r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, apply); 1886 1887 xmlFreeDoc(document); 1888 1889 return (r); 1890 } 1891 1892 int 1893 lxml_inventory(const char *filename) 1894 { 1895 bundle_t *b; 1896 uu_list_walk_t *svcs, *insts; 1897 entity_t *svc, *inst; 1898 1899 b = internal_bundle_new(); 1900 1901 if (lxml_get_bundle_file(b, filename, 0) != 0) { 1902 internal_bundle_free(b); 1903 return (-1); 1904 } 1905 1906 svcs = uu_list_walk_start(b->sc_bundle_services, 0); 1907 if (svcs == NULL) 1908 uu_die(gettext("Couldn't walk services")); 1909 1910 while ((svc = uu_list_walk_next(svcs)) != NULL) { 1911 uu_list_t *inst_list; 1912 1913 inst_list = svc->sc_u.sc_service.sc_service_instances; 1914 insts = uu_list_walk_start(inst_list, 0); 1915 if (insts == NULL) 1916 uu_die(gettext("Couldn't walk instances")); 1917 1918 while ((inst = uu_list_walk_next(insts)) != NULL) 1919 (void) printf("svc:/%s:%s\n", svc->sc_name, 1920 inst->sc_name); 1921 1922 uu_list_walk_end(insts); 1923 } 1924 1925 uu_list_walk_end(svcs); 1926 1927 svcs = uu_list_walk_start(b->sc_bundle_services, 0); 1928 while ((svc = uu_list_walk_next(svcs)) != NULL) { 1929 (void) fputs("svc:/", stdout); 1930 (void) puts(svc->sc_name); 1931 } 1932 uu_list_walk_end(svcs); 1933 1934 internal_bundle_free(b); 1935 1936 return (0); 1937 } 1938