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