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