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