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