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 (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); r++) { 504 if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0) 505 break; 506 } 507 508 if (r >= sizeof (lxml_prop_types) / sizeof (char *)) { 509 uu_die(gettext("property type invalid for property '%s/%s'\n"), 510 pgrp->sc_pgroup_name, p->sc_property_name); 511 } 512 513 p->sc_value_type = lxml_element_to_type(r); 514 515 for (cursor = property->xmlChildrenNode; cursor != NULL; 516 cursor = cursor->next) { 517 if (lxml_ignorable_block(cursor)) 518 continue; 519 520 switch (r = lxml_xlate_element(cursor->name)) { 521 case SC_ASTRING: 522 case SC_BOOLEAN: 523 case SC_COUNT: 524 case SC_FMRI: 525 case SC_HOST: 526 case SC_HOSTNAME: 527 case SC_INTEGER: 528 case SC_NET_ADDR_V4: 529 case SC_NET_ADDR_V6: 530 case SC_OPAQUE: 531 case SC_TIME: 532 case SC_URI: 533 case SC_USTRING: 534 if (strcmp(lxml_prop_types[r], (const char *)type) != 0) 535 uu_die(gettext("property \'%s\' " 536 "type-to-list mismatch\n"), 537 p->sc_property_name); 538 539 (void) lxml_get_value(p, r, cursor); 540 break; 541 default: 542 uu_die(gettext("unknown value list type: %s\n"), 543 cursor->name); 544 break; 545 } 546 } 547 548 xmlFree(type); 549 550 override = xmlGetProp(property, (xmlChar *)override_attr); 551 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0); 552 xmlFree(override); 553 554 return (internal_attach_property(pgrp, p)); 555 } 556 557 static int 558 lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab) 559 { 560 return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY, 561 SCF_TYPE_ASTRING, stab, value_attr)); 562 } 563 564 /* 565 * Property groups can go on any of a service, an instance, or a template. 566 */ 567 static int 568 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup) 569 { 570 pgroup_t *pg; 571 xmlNodePtr cursor; 572 xmlChar *name, *type, *delete; 573 574 /* 575 * property group attributes: 576 * name: string 577 * type: string | framework | application 578 */ 579 name = xmlGetProp(pgroup, (xmlChar *)name_attr); 580 type = xmlGetProp(pgroup, (xmlChar *)type_attr); 581 pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type); 582 xmlFree(name); 583 xmlFree(type); 584 585 /* 586 * Walk the children of this lxml_elements, which are a stability 587 * element, property elements, or propval elements. 588 */ 589 for (cursor = pgroup->xmlChildrenNode; cursor != NULL; 590 cursor = cursor->next) { 591 if (lxml_ignorable_block(cursor)) 592 continue; 593 594 switch (lxml_xlate_element(cursor->name)) { 595 case SC_STABILITY: 596 (void) lxml_get_pgroup_stability(pg, cursor); 597 break; 598 case SC_PROPERTY: 599 (void) lxml_get_property(pg, cursor); 600 break; 601 case SC_PROPVAL: 602 (void) lxml_get_propval(pg, cursor); 603 break; 604 default: 605 abort(); 606 break; 607 } 608 } 609 610 delete = xmlGetProp(pgroup, (xmlChar *)delete_attr); 611 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 612 xmlFree(delete); 613 614 return (0); 615 } 616 617 618 /* 619 * Dependency groups, execution methods can go on either a service or an 620 * instance. 621 */ 622 623 static int 624 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile) 625 { 626 property_t *p; 627 628 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN, 629 1, (uint64_t)1); 630 if (internal_attach_property(pg, p) != 0) 631 return (-1); 632 633 return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE, 634 SCF_TYPE_ASTRING, profile, name_attr)); 635 } 636 637 static int 638 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred) 639 { 640 property_t *p; 641 642 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN, 643 1, (uint64_t)0); 644 if (internal_attach_property(pg, p) != 0) 645 return (-1); 646 647 if (new_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING, 648 cred, "user") != 0) 649 return (-1); 650 651 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING, 652 cred, "group") != 0) 653 return (-1); 654 655 if (new_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS, 656 SCF_TYPE_ASTRING, cred, "supp_groups") != 0) 657 return (-1); 658 659 if (new_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES, 660 SCF_TYPE_ASTRING, cred, "privileges") != 0) 661 return (-1); 662 663 if (new_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES, 664 SCF_TYPE_ASTRING, cred, "limit_privileges") != 0) 665 return (-1); 666 667 return (0); 668 } 669 670 static char * 671 lxml_get_envvar(xmlNodePtr envvar) 672 { 673 char *name; 674 char *value; 675 char *ret; 676 677 name = (char *)xmlGetProp(envvar, (xmlChar *)"name"); 678 value = (char *)xmlGetProp(envvar, (xmlChar *)"value"); 679 680 if (strlen(name) == 0 || strchr(name, '=') != NULL) 681 uu_die(gettext("Invalid environment variable " 682 "\"%s\".\n"), name); 683 if (strstr(name, "SMF_") == name) 684 uu_die(gettext("Invalid environment variable " 685 "\"%s\"; \"SMF_\" prefix is reserved.\n"), name); 686 687 ret = uu_msprintf("%s=%s", name, value); 688 xmlFree(name); 689 xmlFree(value); 690 return (ret); 691 } 692 693 static int 694 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment) 695 { 696 property_t *p; 697 xmlNodePtr cursor; 698 value_t *val; 699 700 p = internal_property_create(SCF_PROPERTY_ENVIRONMENT, 701 SCF_TYPE_ASTRING, 0); 702 703 for (cursor = environment->xmlChildrenNode; cursor != NULL; 704 cursor = cursor->next) { 705 char *tmp; 706 707 if (lxml_ignorable_block(cursor)) 708 continue; 709 710 if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR) 711 uu_die(gettext("illegal element \"%s\" on " 712 "method environment for \"%s\"\n"), 713 cursor->name, pg->sc_pgroup_name); 714 715 if ((tmp = lxml_get_envvar(cursor)) == NULL) 716 uu_die(gettext("Out of memory\n")); 717 718 val = internal_value_new(); 719 val->sc_u.sc_string = tmp; 720 val->sc_type = SCF_TYPE_ASTRING; 721 val->sc_free = lxml_free_str; 722 internal_attach_value(p, val); 723 } 724 725 if (internal_attach_property(pg, p) != 0) { 726 internal_property_free(p); 727 return (-1); 728 } 729 730 return (0); 731 } 732 733 static int 734 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx) 735 { 736 xmlNodePtr cursor; 737 738 if (new_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY, 739 SCF_TYPE_ASTRING, ctx, "working_directory") != 0) 740 return (-1); 741 742 if (new_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT, SCF_TYPE_ASTRING, 743 ctx, "project") != 0) 744 return (-1); 745 746 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL, 747 SCF_TYPE_ASTRING, ctx, "resource_pool") != 0) 748 return (-1); 749 750 for (cursor = ctx->xmlChildrenNode; cursor != NULL; 751 cursor = cursor->next) { 752 if (lxml_ignorable_block(cursor)) 753 continue; 754 755 switch (lxml_xlate_element(cursor->name)) { 756 case SC_METHOD_CREDENTIAL: 757 (void) lxml_get_method_credential(pg, cursor); 758 break; 759 case SC_METHOD_PROFILE: 760 (void) lxml_get_method_profile(pg, cursor); 761 break; 762 case SC_METHOD_ENVIRONMENT: 763 (void) lxml_get_method_environment(pg, cursor); 764 break; 765 default: 766 semerr(gettext("illegal element \'%s\' in method " 767 "context\n"), (char *)cursor); 768 break; 769 } 770 } 771 772 return (0); 773 } 774 775 static int 776 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx) 777 { 778 pgroup_t *pg; 779 780 pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT, 781 (char *)scf_group_framework); 782 783 return (lxml_get_method_context(pg, ctx)); 784 } 785 786 static int 787 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth) 788 { 789 pgroup_t *pg; 790 property_t *p; 791 xmlChar *name, *timeout, *delete; 792 xmlNodePtr cursor; 793 int r = 0; 794 795 name = xmlGetProp(emeth, (xmlChar *)name_attr); 796 pg = internal_pgroup_find_or_create(entity, (char *)name, 797 (char *)SCF_GROUP_METHOD); 798 xmlFree(name); 799 800 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 801 emeth, type_attr) != 0 || 802 new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING, 803 emeth, "exec") != 0) 804 return (-1); 805 806 timeout = xmlGetProp(emeth, (xmlChar *)"timeout_seconds"); 807 if (timeout != NULL) { 808 uint64_t u_timeout; 809 char *endptr; 810 /* 811 * Although an SC_COUNT represents a uint64_t the use 812 * of a negative value is acceptable due to the usage 813 * established by inetd(1M). 814 */ 815 errno = 0; 816 u_timeout = strtoull((char *)timeout, &endptr, 10); 817 if (errno != 0 || endptr == (char *)timeout || *endptr) 818 uu_die(gettext("illegal value \"%s\" for " 819 "timeout_seconds (%s)\n"), 820 (char *)timeout, (errno) ? strerror(errno): 821 gettext("Illegal character")); 822 p = internal_property_create(SCF_PROPERTY_TIMEOUT, 823 SCF_TYPE_COUNT, 1, u_timeout); 824 r = internal_attach_property(pg, p); 825 xmlFree(timeout); 826 } 827 if (r != 0) 828 return (-1); 829 830 /* 831 * There is a possibility that a method context also exists, in which 832 * case the following attributes are defined: project, resource_pool, 833 * working_directory, profile, user, group, privileges, limit_privileges 834 */ 835 for (cursor = emeth->xmlChildrenNode; cursor != NULL; 836 cursor = cursor->next) { 837 if (lxml_ignorable_block(cursor)) 838 continue; 839 840 switch (lxml_xlate_element(cursor->name)) { 841 case SC_STABILITY: 842 if (lxml_get_pgroup_stability(pg, cursor) != 0) 843 return (-1); 844 break; 845 846 case SC_METHOD_CONTEXT: 847 (void) lxml_get_method_context(pg, cursor); 848 break; 849 850 case SC_PROPVAL: 851 (void) lxml_get_propval(pg, cursor); 852 break; 853 854 case SC_PROPERTY: 855 (void) lxml_get_property(pg, cursor); 856 break; 857 858 default: 859 uu_die(gettext("illegal element \"%s\" on " 860 "execution method \"%s\"\n"), cursor->name, 861 pg->sc_pgroup_name); 862 break; 863 } 864 } 865 866 delete = xmlGetProp(emeth, (xmlChar *)delete_attr); 867 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 868 xmlFree(delete); 869 870 return (0); 871 } 872 873 static int 874 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency) 875 { 876 pgroup_t *pg; 877 property_t *p; 878 xmlNodePtr cursor; 879 xmlChar *name; 880 xmlChar *delete; 881 882 /* 883 * dependency attributes: 884 * name: string 885 * grouping: require_all | require_any | exclude_all | optional_all 886 * reset_on: string (error | restart | refresh | none) 887 * type: service / path /host 888 */ 889 890 name = xmlGetProp(dependency, (xmlChar *)name_attr); 891 pg = internal_pgroup_find_or_create(entity, (char *)name, 892 (char *)SCF_GROUP_DEPENDENCY); 893 xmlFree(name); 894 895 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING, 896 dependency, type_attr) != 0) 897 return (-1); 898 899 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON, 900 SCF_TYPE_ASTRING, dependency, "restart_on") != 0) 901 return (-1); 902 903 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 904 dependency, "grouping") != 0) 905 return (-1); 906 907 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0); 908 if (internal_attach_property(pg, p) != 0) 909 return (-1); 910 911 for (cursor = dependency->xmlChildrenNode; cursor != NULL; 912 cursor = cursor->next) { 913 xmlChar *value; 914 value_t *v; 915 916 if (lxml_ignorable_block(cursor)) 917 continue; 918 919 switch (lxml_xlate_element(cursor->name)) { 920 case SC_STABILITY: 921 if (lxml_get_pgroup_stability(pg, cursor) != 0) 922 return (-1); 923 break; 924 925 case SC_SERVICE_FMRI: 926 value = xmlGetProp(cursor, (xmlChar *)value_attr); 927 if (value != NULL) { 928 if (lxml_validate_string_value(SCF_TYPE_FMRI, 929 (char *)value) != 0) 930 uu_die(gettext("illegal value \"%s\" " 931 "for %s (%s)\n"), (char *)value, 932 lxml_prop_types[SC_FMRI], 933 (scf_error()) ? 934 scf_strerror(scf_error()) : 935 gettext("Illegal format")); 936 v = internal_value_new(); 937 v->sc_type = SCF_TYPE_FMRI; 938 v->sc_u.sc_string = (char *)value; 939 internal_attach_value(p, v); 940 } 941 942 break; 943 944 case SC_PROPVAL: 945 (void) lxml_get_propval(pg, cursor); 946 break; 947 948 case SC_PROPERTY: 949 (void) lxml_get_property(pg, cursor); 950 break; 951 952 default: 953 uu_die(gettext("illegal element \"%s\" on " 954 "dependency group \"%s\"\n"), cursor->name, name); 955 break; 956 } 957 } 958 959 delete = xmlGetProp(dependency, (xmlChar *)delete_attr); 960 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 961 xmlFree(delete); 962 963 return (0); 964 } 965 966 /* 967 * Dependents are hairy. They should cause a dependency pg to be created in 968 * another service, but we can't do that here; we'll have to wait until the 969 * import routines. So for now we'll add the dependency group that should go 970 * in the other service to the entity's dependent list. 971 */ 972 static int 973 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent) 974 { 975 xmlChar *name, *or; 976 xmlNodePtr sf; 977 xmlChar *fmri, *delete; 978 pgroup_t *pg; 979 property_t *p; 980 xmlNodePtr n; 981 char *myfmri; 982 983 name = xmlGetProp(dependent, (xmlChar *)name_attr); 984 985 if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) { 986 semerr(gettext("Property group and dependent of entity %s " 987 "have same name \"%s\".\n"), entity->sc_name, name); 988 xmlFree(name); 989 return (-1); 990 } 991 992 or = xmlGetProp(dependent, (xmlChar *)override_attr); 993 994 pg = internal_pgroup_new(); 995 pg->sc_pgroup_name = (char *)name; 996 pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY; 997 pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0); 998 xmlFree(or); 999 if (internal_attach_dependent(entity, pg) != 0) { 1000 xmlFree(name); 1001 internal_pgroup_free(pg); 1002 return (-1); 1003 } 1004 1005 for (sf = dependent->children; sf != NULL; sf = sf->next) 1006 if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0) 1007 break; 1008 assert(sf != NULL); 1009 fmri = xmlGetProp(sf, (xmlChar *)value_attr); 1010 pg->sc_pgroup_fmri = (char *)fmri; 1011 1012 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON, 1013 SCF_TYPE_ASTRING, dependent, "restart_on") != 0) 1014 return (-1); 1015 1016 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING, 1017 dependent, "grouping") != 0) 1018 return (-1); 1019 1020 myfmri = safe_malloc(max_scf_fmri_len + 1); 1021 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) { 1022 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s", 1023 entity->sc_name) < 0) 1024 bad_error("snprintf", errno); 1025 } else { 1026 assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT); 1027 if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s", 1028 entity->sc_parent->sc_name, entity->sc_name) < 0) 1029 bad_error("snprintf", errno); 1030 } 1031 1032 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1, 1033 myfmri); 1034 if (internal_attach_property(pg, p) != 0) 1035 return (-1); 1036 1037 /* Create a property to serve as a do-not-export flag. */ 1038 p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1, 1039 (uint64_t)1); 1040 if (internal_attach_property(pg, p) != 0) 1041 return (-1); 1042 1043 for (n = sf->next; n != NULL; n = n->next) { 1044 if (lxml_ignorable_block(n)) 1045 continue; 1046 1047 switch (lxml_xlate_element(n->name)) { 1048 case SC_STABILITY: 1049 if (new_str_prop_from_attr(pg, 1050 SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n, 1051 value_attr) != 0) 1052 return (-1); 1053 break; 1054 1055 case SC_PROPVAL: 1056 (void) lxml_get_propval(pg, n); 1057 break; 1058 1059 case SC_PROPERTY: 1060 (void) lxml_get_property(pg, n); 1061 break; 1062 1063 default: 1064 uu_die(gettext("unexpected element %s.\n"), n->name); 1065 } 1066 } 1067 1068 /* Go back and fill in defaults. */ 1069 if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) { 1070 p = internal_property_create(SCF_PROPERTY_TYPE, 1071 SCF_TYPE_ASTRING, 1, "service"); 1072 if (internal_attach_property(pg, p) != 0) 1073 return (-1); 1074 } 1075 1076 delete = xmlGetProp(dependent, (xmlChar *)delete_attr); 1077 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0); 1078 xmlFree(delete); 1079 1080 pg = internal_pgroup_find_or_create(entity, "dependents", 1081 (char *)scf_group_framework); 1082 p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri); 1083 if (internal_attach_property(pg, p) != 0) 1084 return (-1); 1085 1086 return (0); 1087 } 1088 1089 static int 1090 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr) 1091 { 1092 pgroup_t *pg; 1093 property_t *p; 1094 xmlChar *stabval; 1095 1096 if ((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) { 1097 uu_warn(gettext("no stability value found\n")); 1098 stabval = (xmlChar *)strdup("External"); 1099 } 1100 1101 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1102 (char *)scf_group_framework); 1103 1104 p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY, 1105 SCF_TYPE_ASTRING, 1, stabval); 1106 1107 return (internal_attach_property(pg, p)); 1108 } 1109 1110 static int 1111 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr) 1112 { 1113 pgroup_t *pg; 1114 property_t *p; 1115 xmlChar *restarter; 1116 xmlNode *cursor; 1117 int r; 1118 1119 /* 1120 * Go find child. Child is a service_fmri element. value attribute 1121 * contains restarter FMRI. 1122 */ 1123 1124 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1125 (char *)scf_group_framework); 1126 1127 /* 1128 * Walk its child elements, as appropriate. 1129 */ 1130 for (cursor = rstr->xmlChildrenNode; cursor != NULL; 1131 cursor = cursor->next) { 1132 if (lxml_ignorable_block(cursor)) 1133 continue; 1134 1135 switch (lxml_xlate_element(cursor->name)) { 1136 case SC_SERVICE_FMRI: 1137 restarter = xmlGetProp(cursor, (xmlChar *)value_attr); 1138 break; 1139 default: 1140 uu_die(gettext("illegal element \"%s\" on restarter " 1141 "element for \"%s\"\n"), cursor->name, 1142 entity->sc_name); 1143 break; 1144 } 1145 } 1146 1147 p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1, 1148 restarter); 1149 1150 r = internal_attach_property(pg, p); 1151 if (r != 0) { 1152 internal_property_free(p); 1153 return (-1); 1154 } 1155 1156 return (0); 1157 } 1158 1159 static void 1160 sanitize_locale(uchar_t *locale) 1161 { 1162 for (; *locale != '\0'; locale++) 1163 if (!isalnum(*locale) && *locale != '_') 1164 *locale = '_'; 1165 } 1166 1167 static int 1168 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext) 1169 { 1170 xmlNodePtr cursor; 1171 xmlChar *val; 1172 char *stripped, *cp; 1173 property_t *p; 1174 int r; 1175 1176 if ((val = xmlGetProp(loctext, (xmlChar *)"xml:lang")) == NULL) 1177 if ((val = xmlGetProp(loctext, (xmlChar *)"lang")) == NULL) 1178 val = (xmlChar *)"unknown"; 1179 1180 sanitize_locale(val); 1181 1182 for (cursor = loctext->xmlChildrenNode; cursor != NULL; 1183 cursor = cursor->next) { 1184 if (strcmp("text", (const char *)cursor->name) == 0) { 1185 break; 1186 } else if (strcmp("comment", (const char *)cursor->name) != 0) { 1187 uu_die(gettext("illegal element \"%s\" on loctext " 1188 "element for \"%s\"\n"), cursor->name, 1189 service->sc_name); 1190 } 1191 } 1192 1193 if (cursor == NULL) { 1194 uu_die(gettext("loctext element has no content for \"%s\"\n"), 1195 service->sc_name); 1196 } 1197 1198 /* 1199 * Remove leading and trailing whitespace. 1200 */ 1201 if ((stripped = strdup((const char *)cursor->content)) == NULL) 1202 uu_die(gettext("Out of memory\n")); 1203 1204 for (; isspace(*stripped); stripped++) 1205 ; 1206 for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--) 1207 ; 1208 *(cp + 1) = '\0'; 1209 1210 p = internal_property_create((const char *)val, SCF_TYPE_USTRING, 1, 1211 stripped); 1212 1213 r = internal_attach_property(pg, p); 1214 if (r != 0) 1215 internal_property_free(p); 1216 1217 return (r); 1218 } 1219 1220 static int 1221 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name) 1222 { 1223 xmlNodePtr cursor; 1224 pgroup_t *pg; 1225 1226 /* 1227 * Create the property group, if absent. 1228 */ 1229 pg = internal_pgroup_find_or_create(service, 1230 (char *)SCF_PG_TM_COMMON_NAME, (char *)SCF_GROUP_TEMPLATE); 1231 1232 /* 1233 * Iterate through one or more loctext elements. The locale is the 1234 * property name; the contents are the ustring value for the property. 1235 */ 1236 for (cursor = common_name->xmlChildrenNode; cursor != NULL; 1237 cursor = cursor->next) { 1238 if (lxml_ignorable_block(cursor)) 1239 continue; 1240 1241 switch (lxml_xlate_element(cursor->name)) { 1242 case SC_LOCTEXT: 1243 if (lxml_get_loctext(service, pg, cursor)) 1244 return (-1); 1245 break; 1246 default: 1247 uu_die(gettext("illegal element \"%s\" on common_name " 1248 "element for \"%s\"\n"), cursor->name, 1249 service->sc_name); 1250 break; 1251 } 1252 } 1253 1254 return (0); 1255 } 1256 1257 static int 1258 lxml_get_tm_description(entity_t *service, xmlNodePtr description) 1259 { 1260 xmlNodePtr cursor; 1261 pgroup_t *pg; 1262 1263 /* 1264 * Create the property group, if absent. 1265 */ 1266 pg = internal_pgroup_find_or_create(service, 1267 (char *)SCF_PG_TM_DESCRIPTION, (char *)SCF_GROUP_TEMPLATE); 1268 1269 /* 1270 * Iterate through one or more loctext elements. The locale is the 1271 * property name; the contents are the ustring value for the property. 1272 */ 1273 for (cursor = description->xmlChildrenNode; cursor != NULL; 1274 cursor = cursor->next) { 1275 if (lxml_ignorable_block(cursor)) 1276 continue; 1277 1278 switch (lxml_xlate_element(cursor->name)) { 1279 case SC_LOCTEXT: 1280 if (lxml_get_loctext(service, pg, cursor)) 1281 return (-1); 1282 break; 1283 default: 1284 uu_die(gettext("illegal element \"%s\" on description " 1285 "element for \"%s\"\n"), cursor->name, 1286 service->sc_name); 1287 break; 1288 } 1289 } 1290 1291 return (0); 1292 } 1293 1294 static char * 1295 lxml_label_to_groupname(const char *prefix, const char *in) 1296 { 1297 char *out, *cp; 1298 size_t len, piece_len; 1299 1300 out = uu_zalloc(2 * scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1); 1301 if (out == NULL) 1302 return (NULL); 1303 1304 (void) strcpy(out, prefix); 1305 (void) strcat(out, in); 1306 1307 len = strlen(out); 1308 if (len > max_scf_name_len) { 1309 /* Use the first half and the second half. */ 1310 piece_len = (max_scf_name_len - 2) / 2; 1311 1312 (void) strncpy(out + piece_len, "..", 2); 1313 1314 (void) strcpy(out + piece_len + 2, out + (len - piece_len)); 1315 1316 len = strlen(out); 1317 } 1318 1319 /* 1320 * Translate non-property characters to '_'. 1321 */ 1322 for (cp = out; *cp != '\0'; ++cp) { 1323 if (!(isalnum(*cp) || *cp == '_' || *cp == '-')) 1324 *cp = '_'; 1325 } 1326 1327 *cp = '\0'; 1328 1329 return (out); 1330 } 1331 1332 static int 1333 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage) 1334 { 1335 pgroup_t *pg; 1336 char *pgname; 1337 xmlChar *title; 1338 1339 /* 1340 * Fetch title attribute, convert to something sanitized, and create 1341 * property group. 1342 */ 1343 title = xmlGetProp(manpage, (xmlChar *)"title"); 1344 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX, 1345 (const char *)title); 1346 1347 pg = internal_pgroup_find_or_create(service, pgname, 1348 (char *)SCF_GROUP_TEMPLATE); 1349 1350 /* 1351 * Each attribute is an astring property within the group. 1352 */ 1353 if (new_str_prop_from_attr(pg, "title", SCF_TYPE_ASTRING, manpage, 1354 "title") != 0 || 1355 new_str_prop_from_attr(pg, "section", SCF_TYPE_ASTRING, manpage, 1356 "section") != 0 || 1357 new_str_prop_from_attr(pg, "manpath", SCF_TYPE_ASTRING, manpage, 1358 "manpath") != 0) 1359 return (-1); 1360 1361 return (0); 1362 } 1363 1364 static int 1365 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link) 1366 { 1367 pgroup_t *pg; 1368 char *pgname; 1369 xmlChar *name; 1370 1371 /* 1372 * Fetch name attribute, convert name to something sanitized, and create 1373 * property group. 1374 */ 1375 name = xmlGetProp(doc_link, (xmlChar *)"name"); 1376 1377 pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX, 1378 (const char *)name); 1379 1380 pg = internal_pgroup_find_or_create(service, pgname, 1381 (char *)SCF_GROUP_TEMPLATE); 1382 1383 /* 1384 * Each attribute is an astring property within the group. 1385 */ 1386 if (new_str_prop_from_attr(pg, "name", SCF_TYPE_ASTRING, doc_link, 1387 "name") != 0 || 1388 new_str_prop_from_attr(pg, "uri", SCF_TYPE_ASTRING, doc_link, 1389 "uri") != 0) 1390 return (-1); 1391 1392 return (0); 1393 } 1394 1395 static int 1396 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation) 1397 { 1398 xmlNodePtr cursor; 1399 1400 for (cursor = documentation->xmlChildrenNode; cursor != NULL; 1401 cursor = cursor->next) { 1402 if (lxml_ignorable_block(cursor)) 1403 continue; 1404 1405 switch (lxml_xlate_element(cursor->name)) { 1406 case SC_MANPAGE: 1407 (void) lxml_get_tm_manpage(service, cursor); 1408 break; 1409 case SC_DOC_LINK: 1410 (void) lxml_get_tm_doclink(service, cursor); 1411 break; 1412 default: 1413 uu_die(gettext("illegal element \"%s\" on template " 1414 "for service \"%s\"\n"), 1415 cursor->name, service->sc_name); 1416 } 1417 } 1418 1419 return (0); 1420 } 1421 1422 static int 1423 lxml_get_template(entity_t *service, xmlNodePtr templ) 1424 { 1425 xmlNodePtr cursor; 1426 1427 for (cursor = templ->xmlChildrenNode; cursor != NULL; 1428 cursor = cursor->next) { 1429 if (lxml_ignorable_block(cursor)) 1430 continue; 1431 1432 switch (lxml_xlate_element(cursor->name)) { 1433 case SC_COMMON_NAME: 1434 (void) lxml_get_tm_common_name(service, cursor); 1435 break; 1436 case SC_DESCRIPTION: 1437 (void) lxml_get_tm_description(service, cursor); 1438 break; 1439 case SC_DOCUMENTATION: 1440 (void) lxml_get_tm_documentation(service, cursor); 1441 break; 1442 default: 1443 uu_die(gettext("illegal element \"%s\" on template " 1444 "for service \"%s\"\n"), 1445 cursor->name, service->sc_name); 1446 } 1447 } 1448 1449 return (0); 1450 } 1451 1452 static int 1453 lxml_get_default_instance(entity_t *service, xmlNodePtr definst) 1454 { 1455 entity_t *i; 1456 xmlChar *enabled; 1457 pgroup_t *pg; 1458 property_t *p; 1459 char *package; 1460 uint64_t enabled_val = 0; 1461 1462 i = internal_instance_new("default"); 1463 1464 if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) { 1465 enabled_val = (strcmp(true, (const char *)enabled) == 0) ? 1466 1 : 0; 1467 xmlFree(enabled); 1468 } 1469 1470 /* 1471 * New general property group with enabled boolean property set. 1472 */ 1473 1474 pg = internal_pgroup_new(); 1475 (void) internal_attach_pgroup(i, pg); 1476 1477 pg->sc_pgroup_name = (char *)scf_pg_general; 1478 pg->sc_pgroup_type = (char *)scf_group_framework; 1479 pg->sc_pgroup_flags = 0; 1480 1481 p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1, 1482 enabled_val); 1483 1484 (void) internal_attach_property(pg, p); 1485 1486 /* 1487 * Add general/package property if PKGINST is set. 1488 */ 1489 if ((package = getenv("PKGINST")) != NULL) { 1490 p = internal_property_create(SCF_PROPERTY_PACKAGE, 1491 SCF_TYPE_ASTRING, 1, package); 1492 1493 (void) internal_attach_property(pg, p); 1494 } 1495 1496 return (internal_attach_entity(service, i)); 1497 } 1498 1499 /* 1500 * Translate an instance element into an internal property tree, added to 1501 * service. If op is SVCCFG_OP_APPLY (i.e., apply a profile), forbid 1502 * subelements and set the enabled property to override. 1503 */ 1504 static int 1505 lxml_get_instance(entity_t *service, xmlNodePtr inst, svccfg_op_t op) 1506 { 1507 entity_t *i; 1508 pgroup_t *pg; 1509 property_t *p; 1510 xmlNodePtr cursor; 1511 xmlChar *enabled; 1512 int r; 1513 1514 /* 1515 * Fetch its attributes, as appropriate. 1516 */ 1517 i = internal_instance_new((char *)xmlGetProp(inst, 1518 (xmlChar *)name_attr)); 1519 1520 /* 1521 * Note that this must be done before walking the children so that 1522 * sc_fmri is set in case we enter lxml_get_dependent(). 1523 */ 1524 r = internal_attach_entity(service, i); 1525 if (r != 0) 1526 return (r); 1527 1528 enabled = xmlGetProp(inst, (xmlChar *)enabled_attr); 1529 1530 /* 1531 * New general property group with enabled boolean property set. 1532 */ 1533 pg = internal_pgroup_new(); 1534 (void) internal_attach_pgroup(i, pg); 1535 1536 pg->sc_pgroup_name = (char *)scf_pg_general; 1537 pg->sc_pgroup_type = (char *)scf_group_framework; 1538 pg->sc_pgroup_flags = 0; 1539 1540 p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1, 1541 (uint64_t)(strcmp(true, (const char *)enabled) == 0 ? 1 : 0)); 1542 1543 p->sc_property_override = (op == SVCCFG_OP_APPLY); 1544 1545 (void) internal_attach_property(pg, p); 1546 1547 xmlFree(enabled); 1548 1549 /* 1550 * Walk its child elements, as appropriate. 1551 */ 1552 for (cursor = inst->xmlChildrenNode; cursor != NULL; 1553 cursor = cursor->next) { 1554 if (lxml_ignorable_block(cursor)) 1555 continue; 1556 1557 if (op == SVCCFG_OP_APPLY) { 1558 semerr(gettext("Instance \"%s\" may not contain " 1559 "elements in profiles.\n"), i->sc_name, 1560 cursor->name); 1561 return (-1); 1562 } 1563 1564 switch (lxml_xlate_element(cursor->name)) { 1565 case SC_RESTARTER: 1566 (void) lxml_get_restarter(i, cursor); 1567 break; 1568 case SC_DEPENDENCY: 1569 (void) lxml_get_dependency(i, cursor); 1570 break; 1571 case SC_DEPENDENT: 1572 (void) lxml_get_dependent(i, cursor); 1573 break; 1574 case SC_METHOD_CONTEXT: 1575 (void) lxml_get_entity_method_context(i, cursor); 1576 break; 1577 case SC_EXEC_METHOD: 1578 (void) lxml_get_exec_method(i, cursor); 1579 break; 1580 case SC_PROPERTY_GROUP: 1581 (void) lxml_get_pgroup(i, cursor); 1582 break; 1583 case SC_TEMPLATE: 1584 (void) lxml_get_template(i, cursor); 1585 break; 1586 default: 1587 uu_die(gettext( 1588 "illegal element \"%s\" on instance \"%s\"\n"), 1589 cursor->name, i->sc_name); 1590 break; 1591 } 1592 } 1593 1594 return (0); 1595 } 1596 1597 /* ARGSUSED1 */ 1598 static int 1599 lxml_get_single_instance(entity_t *entity, xmlNodePtr si) 1600 { 1601 pgroup_t *pg; 1602 property_t *p; 1603 int r; 1604 1605 pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general, 1606 (char *)scf_group_framework); 1607 1608 p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE, 1609 SCF_TYPE_BOOLEAN, 1, (uint64_t)1); 1610 1611 r = internal_attach_property(pg, p); 1612 if (r != 0) { 1613 internal_property_free(p); 1614 return (-1); 1615 } 1616 1617 return (0); 1618 } 1619 1620 /* 1621 * Translate a service element into an internal instance/property tree, added 1622 * to bundle. If op is SVCCFG_OP_APPLY, allow only instance subelements. 1623 */ 1624 static int 1625 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op) 1626 { 1627 entity_t *s; 1628 xmlNodePtr cursor; 1629 xmlChar *type; 1630 xmlChar *version; 1631 int e; 1632 1633 /* 1634 * Fetch attributes, as appropriate. 1635 */ 1636 s = internal_service_new((char *)xmlGetProp(svc, 1637 (xmlChar *)name_attr)); 1638 1639 version = xmlGetProp(svc, (xmlChar *)"version"); 1640 s->sc_u.sc_service.sc_service_version = atol((const char *)version); 1641 xmlFree(version); 1642 1643 type = xmlGetProp(svc, (xmlChar *)type_attr); 1644 s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type); 1645 xmlFree(type); 1646 1647 /* 1648 * Walk its child elements, as appropriate. 1649 */ 1650 for (cursor = svc->xmlChildrenNode; cursor != NULL; 1651 cursor = cursor->next) { 1652 if (lxml_ignorable_block(cursor)) 1653 continue; 1654 1655 e = lxml_xlate_element(cursor->name); 1656 1657 if (op == SVCCFG_OP_APPLY && e != SC_INSTANCE) { 1658 semerr(gettext("Service \"%s\" may not contain the " 1659 "non-instance element \"%s\" in a profile.\n"), 1660 s->sc_name, cursor->name); 1661 1662 return (-1); 1663 } 1664 1665 switch (e) { 1666 case SC_INSTANCE: 1667 (void) lxml_get_instance(s, cursor, op); 1668 break; 1669 case SC_TEMPLATE: 1670 (void) lxml_get_template(s, cursor); 1671 break; 1672 case SC_STABILITY: 1673 (void) lxml_get_entity_stability(s, cursor); 1674 break; 1675 case SC_DEPENDENCY: 1676 (void) lxml_get_dependency(s, cursor); 1677 break; 1678 case SC_DEPENDENT: 1679 (void) lxml_get_dependent(s, cursor); 1680 break; 1681 case SC_RESTARTER: 1682 (void) lxml_get_restarter(s, cursor); 1683 break; 1684 case SC_EXEC_METHOD: 1685 (void) lxml_get_exec_method(s, cursor); 1686 break; 1687 case SC_METHOD_CONTEXT: 1688 (void) lxml_get_entity_method_context(s, cursor); 1689 break; 1690 case SC_PROPERTY_GROUP: 1691 (void) lxml_get_pgroup(s, cursor); 1692 break; 1693 case SC_INSTANCE_CREATE_DEFAULT: 1694 (void) lxml_get_default_instance(s, cursor); 1695 break; 1696 case SC_INSTANCE_SINGLE: 1697 (void) lxml_get_single_instance(s, cursor); 1698 break; 1699 default: 1700 uu_die(gettext( 1701 "illegal element \"%s\" on service \"%s\"\n"), 1702 cursor->name, s->sc_name); 1703 break; 1704 } 1705 } 1706 1707 return (internal_attach_service(bundle, s)); 1708 } 1709 1710 #ifdef DEBUG 1711 void 1712 lxml_dump(int g, xmlNodePtr p) 1713 { 1714 if (p && p->name) { 1715 printf("%d %s\n", g, p->name); 1716 1717 for (p = p->xmlChildrenNode; p != NULL; p = p->next) 1718 lxml_dump(g + 1, p); 1719 } 1720 } 1721 #endif /* DEBUG */ 1722 1723 static int 1724 lxml_is_known_dtd(const xmlChar *dtdname) 1725 { 1726 if (dtdname == NULL || 1727 strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0) 1728 return (0); 1729 1730 return (1); 1731 } 1732 1733 static int 1734 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type, 1735 xmlNodePtr subbundle, svccfg_op_t op) 1736 { 1737 xmlNodePtr cursor; 1738 xmlChar *type; 1739 int e; 1740 1741 /* 1742 * 1. Get bundle attributes. 1743 */ 1744 type = xmlGetProp(subbundle, (xmlChar *)"type"); 1745 bundle->sc_bundle_type = lxml_xlate_bundle_type(type); 1746 if (bundle->sc_bundle_type != bundle_type && 1747 bundle_type != SVCCFG_UNKNOWN_BUNDLE) { 1748 semerr(gettext("included bundle of different type.\n")); 1749 return (-1); 1750 } 1751 1752 xmlFree(type); 1753 1754 switch (op) { 1755 case SVCCFG_OP_IMPORT: 1756 if (bundle->sc_bundle_type != SVCCFG_MANIFEST) { 1757 semerr(gettext("document is not a manifest.\n")); 1758 return (-1); 1759 } 1760 break; 1761 case SVCCFG_OP_APPLY: 1762 if (bundle->sc_bundle_type != SVCCFG_PROFILE) { 1763 semerr(gettext("document is not a profile.\n")); 1764 return (-1); 1765 } 1766 break; 1767 case SVCCFG_OP_RESTORE: 1768 if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) { 1769 semerr(gettext("document is not an archive.\n")); 1770 return (-1); 1771 } 1772 break; 1773 } 1774 1775 if ((bundle->sc_bundle_name = xmlGetProp(subbundle, 1776 (xmlChar *)"name")) == NULL) { 1777 semerr(gettext("service bundle lacks name attribute\n")); 1778 return (-1); 1779 } 1780 1781 /* 1782 * 2. Get services, descend into each one and build state. 1783 */ 1784 for (cursor = subbundle->xmlChildrenNode; cursor != NULL; 1785 cursor = cursor->next) { 1786 if (lxml_ignorable_block(cursor)) 1787 continue; 1788 1789 e = lxml_xlate_element(cursor->name); 1790 1791 switch (e) { 1792 case SC_XI_INCLUDE: 1793 continue; 1794 1795 case SC_SERVICE_BUNDLE: 1796 if (lxml_get_bundle(bundle, bundle_type, cursor, op)) 1797 return (-1); 1798 break; 1799 case SC_SERVICE: 1800 (void) lxml_get_service(bundle, cursor, op); 1801 break; 1802 } 1803 } 1804 1805 return (0); 1806 } 1807 1808 /* 1809 * Load an XML tree from filename and translate it into an internal service 1810 * tree bundle. Require that the bundle be of appropriate type for the 1811 * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY. 1812 */ 1813 int 1814 lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op) 1815 { 1816 xmlDocPtr document; 1817 xmlNodePtr cursor; 1818 xmlDtdPtr dtd = NULL; 1819 xmlValidCtxtPtr vcp; 1820 boolean_t do_validate; 1821 char *dtdpath = NULL; 1822 int r; 1823 1824 /* 1825 * Verify we can read the file before we try to parse it. 1826 */ 1827 if (access(filename, R_OK | F_OK) == -1) { 1828 semerr(gettext("unable to open file: %s\n"), strerror(errno)); 1829 return (-1); 1830 } 1831 1832 /* 1833 * Until libxml2 addresses DTD-based validation with XInclude, we don't 1834 * validate service profiles (i.e. the apply path). 1835 */ 1836 do_validate = (op != SVCCFG_OP_APPLY) && 1837 (getenv("SVCCFG_NOVALIDATE") == NULL); 1838 if (do_validate) 1839 dtdpath = getenv("SVCCFG_DTD"); 1840 1841 if (dtdpath != NULL) 1842 xmlLoadExtDtdDefaultValue = 0; 1843 1844 if ((document = xmlReadFile(filename, NULL, 0)) == NULL) { 1845 semerr(gettext("couldn't parse document\n")); 1846 return (-1); 1847 } 1848 1849 /* 1850 * Verify that this is a document type we understand. 1851 */ 1852 if ((dtd = xmlGetIntSubset(document)) == NULL) { 1853 semerr(gettext("document has no DTD\n")); 1854 return (-1); 1855 } 1856 1857 if (!lxml_is_known_dtd(dtd->SystemID)) { 1858 semerr(gettext("document DTD unknown; not service bundle?\n")); 1859 return (-1); 1860 } 1861 1862 if ((cursor = xmlDocGetRootElement(document)) == NULL) { 1863 semerr(gettext("document is empty\n")); 1864 xmlFreeDoc(document); 1865 return (-1); 1866 } 1867 1868 if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) { 1869 semerr(gettext("document is not a service bundle\n")); 1870 xmlFreeDoc(document); 1871 return (-1); 1872 } 1873 1874 1875 if (dtdpath != NULL) { 1876 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath); 1877 if (dtd == NULL) { 1878 semerr(gettext("Could not parse DTD \"%s\".\n"), 1879 dtdpath); 1880 return (-1); 1881 } 1882 1883 if (document->extSubset != NULL) 1884 xmlFreeDtd(document->extSubset); 1885 1886 document->extSubset = dtd; 1887 } 1888 1889 if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) { 1890 semerr(gettext("couldn't handle XInclude statements " 1891 "in document\n")); 1892 return (-1); 1893 } 1894 1895 if (do_validate) { 1896 vcp = xmlNewValidCtxt(); 1897 if (vcp == NULL) 1898 uu_die(gettext("could not allocate memory")); 1899 vcp->warning = xmlParserValidityWarning; 1900 vcp->error = xmlParserValidityError; 1901 1902 r = xmlValidateDocument(vcp, document); 1903 1904 xmlFreeValidCtxt(vcp); 1905 1906 if (r == 0) { 1907 semerr(gettext("Document is not valid.\n")); 1908 xmlFreeDoc(document); 1909 return (-1); 1910 } 1911 } 1912 1913 1914 #ifdef DEBUG 1915 lxml_dump(0, cursor); 1916 #endif /* DEBUG */ 1917 1918 r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op); 1919 1920 xmlFreeDoc(document); 1921 1922 return (r); 1923 } 1924 1925 int 1926 lxml_inventory(const char *filename) 1927 { 1928 bundle_t *b; 1929 uu_list_walk_t *svcs, *insts; 1930 entity_t *svc, *inst; 1931 1932 b = internal_bundle_new(); 1933 1934 if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) { 1935 internal_bundle_free(b); 1936 return (-1); 1937 } 1938 1939 svcs = uu_list_walk_start(b->sc_bundle_services, 0); 1940 if (svcs == NULL) 1941 uu_die(gettext("Couldn't walk services")); 1942 1943 while ((svc = uu_list_walk_next(svcs)) != NULL) { 1944 uu_list_t *inst_list; 1945 1946 inst_list = svc->sc_u.sc_service.sc_service_instances; 1947 insts = uu_list_walk_start(inst_list, 0); 1948 if (insts == NULL) 1949 uu_die(gettext("Couldn't walk instances")); 1950 1951 while ((inst = uu_list_walk_next(insts)) != NULL) 1952 (void) printf("svc:/%s:%s\n", svc->sc_name, 1953 inst->sc_name); 1954 1955 uu_list_walk_end(insts); 1956 } 1957 1958 uu_list_walk_end(svcs); 1959 1960 svcs = uu_list_walk_start(b->sc_bundle_services, 0); 1961 while ((svc = uu_list_walk_next(svcs)) != NULL) { 1962 (void) fputs("svc:/", stdout); 1963 (void) puts(svc->sc_name); 1964 } 1965 uu_list_walk_end(svcs); 1966 1967 internal_bundle_free(b); 1968 1969 return (0); 1970 } 1971