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