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