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