xref: /illumos-gate/usr/src/cmd/svc/svccfg/svccfg_xml.c (revision 499fd601)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <libxml/parser.h>
29 #include <libxml/xinclude.h>
30 
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <libintl.h>
35 #include <libuutil.h>
36 #include <stdlib.h>
37 #include <string.h>
38 
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 
43 #include "svccfg.h"
44 
45 /*
46  * XML document manipulation routines
47  *
48  * These routines provide translation to and from the internal representation to
49  * XML.  Directionally-oriented verbs are with respect to the external source,
50  * so lxml_get_service() fetches a service from the XML file into the
51  * internal representation.
52  */
53 
54 const char * const delete_attr = "delete";
55 const char * const enabled_attr = "enabled";
56 const char * const name_attr = "name";
57 const char * const override_attr = "override";
58 const char * const type_attr = "type";
59 const char * const value_attr = "value";
60 const char * const true = "true";
61 const char * const false = "false";
62 
63 /*
64  * The following list must be kept in the same order as that of
65  * element_t array
66  */
67 static const char *lxml_elements[] = {
68 	"astring_list",			/* SC_ASTRING */
69 	"boolean_list",			/* SC_BOOLEAN */
70 	"common_name",			/* SC_COMMON_NAME */
71 	"count_list",			/* SC_COUNT */
72 	"create_default_instance",	/* SC_INSTANCE_CREATE_DEFAULT */
73 	"dependency",			/* SC_DEPENDENCY */
74 	"dependent",			/* SC_DEPENDENT */
75 	"description",			/* SC_DESCRIPTION */
76 	"doc_link",			/* SC_DOC_LINK */
77 	"documentation",		/* SC_DOCUMENTATION */
78 	"enabled",			/* SC_ENABLED */
79 	"exec_method",			/* SC_EXEC_METHOD */
80 	"fmri_list",			/* SC_FMRI */
81 	"host_list",			/* SC_HOST */
82 	"hostname_list",		/* SC_HOSTNAME */
83 	"instance",			/* SC_INSTANCE */
84 	"integer_list",			/* SC_INTEGER */
85 	"loctext",			/* SC_LOCTEXT */
86 	"manpage",			/* SC_MANPAGE */
87 	"method_context",		/* SC_METHOD_CONTEXT */
88 	"method_credential",		/* SC_METHOD_CREDENTIAL */
89 	"method_profile",		/* SC_METHOD_PROFILE */
90 	"method_environment",		/* SC_METHOD_ENVIRONMENT */
91 	"envvar",			/* SC_METHOD_ENVVAR */
92 	"net_address_v4_list",		/* SC_NET_ADDR_V4 */
93 	"net_address_v6_list",		/* SC_NET_ADDR_V6 */
94 	"opaque_list",			/* SC_OPAQUE */
95 	"property",			/* SC_PROPERTY */
96 	"property_group",		/* SC_PROPERTY_GROUP */
97 	"propval",			/* SC_PROPVAL */
98 	"restarter",			/* SC_RESTARTER */
99 	"service",			/* SC_SERVICE */
100 	"service_bundle",		/* SC_SERVICE_BUNDLE */
101 	"service_fmri",			/* SC_SERVICE_FMRI */
102 	"single_instance",		/* SC_INSTANCE_SINGLE */
103 	"stability",			/* SC_STABILITY */
104 	"template",			/* SC_TEMPLATE */
105 	"time_list",			/* SC_TIME */
106 	"uri_list",			/* SC_URI */
107 	"ustring_list",			/* SC_USTRING */
108 	"value_node",			/* SC_VALUE_NODE */
109 	"xi:fallback",			/* SC_XI_FALLBACK */
110 	"xi:include"			/* SC_XI_INCLUDE */
111 };
112 
113 /*
114  * The following list must be kept in the same order as that of
115  * element_t array
116  */
117 static const char *lxml_prop_types[] = {
118 	"astring",			/* SC_ASTRING */
119 	"boolean",			/* SC_BOOLEAN */
120 	"",				/* SC_COMMON_NAME */
121 	"count",			/* SC_COUNT */
122 	"",				/* SC_INSTANCE_CREATE_DEFAULT */
123 	"",				/* SC_DEPENDENCY */
124 	"",				/* SC_DEPENDENT */
125 	"",				/* SC_DESCRIPTION */
126 	"",				/* SC_DOC_LINK */
127 	"",				/* SC_DOCUMENTATION */
128 	"",				/* SC_ENABLED */
129 	"",				/* SC_EXEC_METHOD */
130 	"fmri",				/* SC_FMRI */
131 	"host",				/* SC_HOST */
132 	"hostname",			/* SC_HOSTNAME */
133 	"",				/* SC_INSTANCE */
134 	"integer",			/* SC_INTEGER */
135 	"",				/* SC_LOCTEXT */
136 	"",				/* SC_MANPAGE */
137 	"",				/* SC_METHOD_CONTEXT */
138 	"",				/* SC_METHOD_CREDENTIAL */
139 	"",				/* SC_METHOD_PROFILE */
140 	"",				/* SC_METHOD_ENVIRONMENT */
141 	"",				/* SC_METHOD_ENVVAR */
142 	"net_address_v4",		/* SC_NET_ADDR_V4 */
143 	"net_address_v6",		/* SC_NET_ADDR_V6 */
144 	"opaque",			/* SC_OPAQUE */
145 	"",				/* SC_PROPERTY */
146 	"",				/* SC_PROPERTY_GROUP */
147 	"",				/* SC_PROPVAL */
148 	"",				/* SC_RESTARTER */
149 	"",				/* SC_SERVICE */
150 	"",				/* SC_SERVICE_BUNDLE */
151 	"",				/* SC_SERVICE_FMRI */
152 	"",				/* SC_INSTANCE_SINGLE */
153 	"",				/* SC_STABILITY */
154 	"",				/* SC_TEMPLATE */
155 	"time",				/* SC_TIME */
156 	"uri",				/* SC_URI */
157 	"ustring",			/* SC_USTRING */
158 	""				/* SC_VALUE_NODE */
159 	""				/* SC_XI_FALLBACK */
160 	""				/* SC_XI_INCLUDE */
161 };
162 
163 int
164 lxml_init()
165 {
166 	if (getenv("SVCCFG_NOVALIDATE") == NULL) {
167 		/*
168 		 * DTD validation, with line numbers.
169 		 */
170 		xmlLineNumbersDefault(1);
171 		xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
172 		xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS;
173 	}
174 
175 	return (0);
176 }
177 
178 static bundle_type_t
179 lxml_xlate_bundle_type(xmlChar *type)
180 {
181 	if (xmlStrcmp(type, (const xmlChar *)"manifest") == 0)
182 		return (SVCCFG_MANIFEST);
183 
184 	if (xmlStrcmp(type, (const xmlChar *)"profile") == 0)
185 		return (SVCCFG_PROFILE);
186 
187 	if (xmlStrcmp(type, (const xmlChar *)"archive") == 0)
188 		return (SVCCFG_ARCHIVE);
189 
190 	return (SVCCFG_UNKNOWN_BUNDLE);
191 }
192 
193 static service_type_t
194 lxml_xlate_service_type(xmlChar *type)
195 {
196 	if (xmlStrcmp(type, (const xmlChar *)"service") == 0)
197 		return (SVCCFG_SERVICE);
198 
199 	if (xmlStrcmp(type, (const xmlChar *)"restarter") == 0)
200 		return (SVCCFG_RESTARTER);
201 
202 	if (xmlStrcmp(type, (const xmlChar *)"milestone") == 0)
203 		return (SVCCFG_MILESTONE);
204 
205 	return (SVCCFG_UNKNOWN_SERVICE);
206 }
207 
208 static element_t
209 lxml_xlate_element(const xmlChar *tag)
210 {
211 	int i;
212 
213 	for (i = 0; i < sizeof (lxml_elements) / sizeof (char *); i++)
214 		if (xmlStrcmp(tag, (const xmlChar *)lxml_elements[i]) == 0)
215 			return ((element_t)i);
216 
217 	return ((element_t)-1);
218 }
219 
220 static uint_t
221 lxml_xlate_boolean(const xmlChar *value)
222 {
223 	if (xmlStrcmp(value, (const xmlChar *)true) == 0)
224 		return (1);
225 
226 	if (xmlStrcmp(value, (const xmlChar *)false) == 0)
227 		return (0);
228 
229 	uu_die(gettext("illegal boolean value \"%s\"\n"), value);
230 
231 	/*NOTREACHED*/
232 }
233 
234 static scf_type_t
235 lxml_element_to_type(element_t type)
236 {
237 	switch (type) {
238 	case SC_ASTRING:	return (SCF_TYPE_ASTRING);
239 	case SC_BOOLEAN:	return (SCF_TYPE_BOOLEAN);
240 	case SC_COUNT:		return (SCF_TYPE_COUNT);
241 	case SC_FMRI:		return (SCF_TYPE_FMRI);
242 	case SC_HOST:		return (SCF_TYPE_HOST);
243 	case SC_HOSTNAME:	return (SCF_TYPE_HOSTNAME);
244 	case SC_INTEGER:	return (SCF_TYPE_INTEGER);
245 	case SC_NET_ADDR_V4:	return (SCF_TYPE_NET_ADDR_V4);
246 	case SC_NET_ADDR_V6:	return (SCF_TYPE_NET_ADDR_V6);
247 	case SC_OPAQUE:		return (SCF_TYPE_OPAQUE);
248 	case SC_TIME:		return (SCF_TYPE_TIME);
249 	case SC_URI:		return (SCF_TYPE_URI);
250 	case SC_USTRING:	return (SCF_TYPE_USTRING);
251 
252 	default:
253 		uu_die(gettext("unknown value type (%d)\n"), type);
254 	}
255 
256 	/* NOTREACHED */
257 }
258 
259 static scf_type_t
260 lxml_element_to_scf_type(element_t type)
261 {
262 	switch (type) {
263 	case SC_ASTRING:	return (SCF_TYPE_ASTRING);
264 	case SC_BOOLEAN:	return (SCF_TYPE_BOOLEAN);
265 	case SC_COUNT:		return (SCF_TYPE_COUNT);
266 	case SC_FMRI:		return (SCF_TYPE_FMRI);
267 	case SC_HOST:		return (SCF_TYPE_HOST);
268 	case SC_HOSTNAME:	return (SCF_TYPE_HOSTNAME);
269 	case SC_INTEGER:	return (SCF_TYPE_INTEGER);
270 	case SC_NET_ADDR_V4:	return (SCF_TYPE_NET_ADDR_V4);
271 	case SC_NET_ADDR_V6:	return (SCF_TYPE_NET_ADDR_V6);
272 	case SC_OPAQUE:		return (SCF_TYPE_OPAQUE);
273 	case SC_TIME:		return (SCF_TYPE_TIME);
274 	case SC_URI:		return (SCF_TYPE_URI);
275 	case SC_USTRING:	return (SCF_TYPE_USTRING);
276 	default:
277 		uu_die(gettext("unknown value type (%d)\n"), type);
278 	}
279 
280 	/* NOTREACHED */
281 }
282 
283 static int
284 new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
285     xmlNodePtr n, const char *attr)
286 {
287 	xmlChar *val;
288 	property_t *p;
289 	int r;
290 
291 	val = xmlGetProp(n, (xmlChar *)attr);
292 
293 	p = internal_property_create(pname, ty, 1, val);
294 	r = internal_attach_property(pgrp, p);
295 
296 	if (r != 0)
297 		internal_property_free(p);
298 
299 	return (r);
300 }
301 
302 static int
303 lxml_ignorable_block(xmlNodePtr n)
304 {
305 	return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 ||
306 	    xmlStrcmp(n->name, (xmlChar *)"comment") == 0) ? 1 : 0);
307 }
308 
309 static int
310 lxml_validate_string_value(scf_type_t type, const char *v)
311 {
312 	static scf_value_t *scf_value = NULL;
313 	static scf_handle_t *scf_hndl = NULL;
314 
315 	if (scf_hndl == NULL && (scf_hndl = scf_handle_create(SCF_VERSION)) ==
316 	    NULL)
317 		return (-1);
318 
319 	if (scf_value == NULL && (scf_value = scf_value_create(scf_hndl)) ==
320 	    NULL)
321 		return (-1);
322 
323 	return (scf_value_set_from_string(scf_value, type, v));
324 }
325 
326 static void
327 lxml_free_str(value_t *val)
328 {
329 	free(val->sc_u.sc_string);
330 }
331 
332 static value_t *
333 lxml_make_value(element_t type, const xmlChar *value)
334 {
335 	value_t *v;
336 	char *endptr;
337 	scf_type_t scf_type = SCF_TYPE_INVALID;
338 
339 	v = internal_value_new();
340 
341 	v->sc_type = lxml_element_to_type(type);
342 
343 	switch (type) {
344 	case SC_COUNT:
345 		/*
346 		 * Although an SC_COUNT represents a uint64_t the use
347 		 * of a negative value is acceptable due to the usage
348 		 * established by inetd(1M).
349 		 */
350 		errno = 0;
351 		v->sc_u.sc_count = strtoull((char *)value, &endptr, 10);
352 		if (errno != 0 || endptr == (char *)value || *endptr)
353 			uu_die(gettext("illegal value \"%s\" for "
354 			    "%s (%s)\n"), (char *)value,
355 			    lxml_prop_types[type],
356 			    (errno) ? strerror(errno) :
357 			    gettext("Illegal character"));
358 		break;
359 	case SC_INTEGER:
360 		errno = 0;
361 		v->sc_u.sc_integer = strtoll((char *)value, &endptr, 10);
362 		if (errno != 0 || *endptr)
363 			uu_die(gettext("illegal value \"%s\" for "
364 			    "%s (%s)\n"), (char *)value,
365 			    lxml_prop_types[type],
366 			    (errno) ? strerror(errno) : "Illegal character");
367 		break;
368 	case SC_OPAQUE:
369 	case SC_HOST:
370 	case SC_HOSTNAME:
371 	case SC_NET_ADDR_V4:
372 	case SC_NET_ADDR_V6:
373 	case SC_FMRI:
374 	case SC_URI:
375 	case SC_TIME:
376 	case SC_ASTRING:
377 	case SC_USTRING:
378 		scf_type = lxml_element_to_scf_type(type);
379 
380 		if ((v->sc_u.sc_string = strdup((char *)value)) == NULL)
381 			uu_die(gettext("string duplication failed (%s)\n"),
382 			    strerror(errno));
383 		if (lxml_validate_string_value(scf_type,
384 		    v->sc_u.sc_string) != 0)
385 			uu_die(gettext("illegal value \"%s\" for "
386 			    "%s (%s)\n"), (char *)value,
387 			    lxml_prop_types[type],
388 			    (scf_error()) ? scf_strerror(scf_error()) :
389 			    gettext("Illegal format"));
390 		v->sc_free = lxml_free_str;
391 		break;
392 	case SC_BOOLEAN:
393 		v->sc_u.sc_count = lxml_xlate_boolean(value);
394 		break;
395 	default:
396 		uu_die(gettext("unknown value type (%d)\n"), type);
397 		break;
398 	}
399 
400 	return (v);
401 }
402 
403 static int
404 lxml_get_value(property_t *prop, element_t vtype, xmlNodePtr value)
405 {
406 	xmlNodePtr cursor;
407 
408 	for (cursor = value->xmlChildrenNode; cursor != NULL;
409 	    cursor = cursor->next) {
410 		xmlChar *assigned_value;
411 		value_t *v;
412 
413 		if (lxml_ignorable_block(cursor))
414 			continue;
415 
416 		switch (lxml_xlate_element(cursor->name)) {
417 		case SC_VALUE_NODE:
418 			if ((assigned_value = xmlGetProp(cursor,
419 			    (xmlChar *)value_attr)) == NULL)
420 				uu_die(gettext("no value on value node?\n"));
421 			break;
422 		default:
423 			uu_die(gettext("value list contains illegal element "
424 			    "\'%s\'\n"), cursor->name);
425 			break;
426 		}
427 
428 		v = lxml_make_value(vtype, assigned_value);
429 
430 		xmlFree(assigned_value);
431 
432 		internal_attach_value(prop, v);
433 	}
434 
435 	return (0);
436 }
437 
438 static int
439 lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
440 {
441 	property_t *p;
442 	element_t r;
443 	value_t *v;
444 	xmlChar *type, *val, *override;
445 
446 	p = internal_property_new();
447 
448 	p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr);
449 	if (p->sc_property_name == NULL)
450 		uu_die(gettext("property name missing in group '%s'\n"),
451 		    pgrp->sc_pgroup_name);
452 
453 	type = xmlGetProp(propval, (xmlChar *)type_attr);
454 	if (type == NULL)
455 		uu_die(gettext("property type missing for property '%s/%s'\n"),
456 		    pgrp->sc_pgroup_name, p->sc_property_name);
457 
458 	for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); ++r) {
459 		if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
460 			break;
461 	}
462 	if (r >= sizeof (lxml_prop_types) / sizeof (char *))
463 		uu_die(gettext("property type invalid for property '%s/%s'\n"),
464 		    pgrp->sc_pgroup_name, p->sc_property_name);
465 
466 	p->sc_value_type = lxml_element_to_type(r);
467 
468 	val = xmlGetProp(propval, (xmlChar *)value_attr);
469 	if (val == NULL)
470 		uu_die(gettext("property value missing for property '%s/%s'\n"),
471 		    pgrp->sc_pgroup_name, p->sc_property_name);
472 
473 	v = lxml_make_value(r, val);
474 	internal_attach_value(p, v);
475 
476 	override = xmlGetProp(propval, (xmlChar *)override_attr);
477 	p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
478 	xmlFree(override);
479 
480 	return (internal_attach_property(pgrp, p));
481 }
482 
483 static int
484 lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
485 {
486 	property_t *p;
487 	xmlNodePtr cursor;
488 	element_t r;
489 	xmlChar *type, *override;
490 
491 	p = internal_property_new();
492 
493 	if ((p->sc_property_name = (char *)xmlGetProp(property,
494 	    (xmlChar *)name_attr)) == NULL)
495 		uu_die(gettext("property name missing in group \'%s\'\n"),
496 		    pgrp->sc_pgroup_name);
497 
498 	if ((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL)
499 		uu_die(gettext("property type missing for "
500 		    "property \'%s/%s\'\n"), pgrp->sc_pgroup_name,
501 		    p->sc_property_name);
502 
503 	for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); r++) {
504 		if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
505 			break;
506 	}
507 
508 	if (r >= sizeof (lxml_prop_types) / sizeof (char *)) {
509 		uu_die(gettext("property type invalid for property '%s/%s'\n"),
510 		    pgrp->sc_pgroup_name, p->sc_property_name);
511 	}
512 
513 	p->sc_value_type = lxml_element_to_type(r);
514 
515 	for (cursor = property->xmlChildrenNode; cursor != NULL;
516 	    cursor = cursor->next) {
517 		if (lxml_ignorable_block(cursor))
518 			continue;
519 
520 		switch (r = lxml_xlate_element(cursor->name)) {
521 		case SC_ASTRING:
522 		case SC_BOOLEAN:
523 		case SC_COUNT:
524 		case SC_FMRI:
525 		case SC_HOST:
526 		case SC_HOSTNAME:
527 		case SC_INTEGER:
528 		case SC_NET_ADDR_V4:
529 		case SC_NET_ADDR_V6:
530 		case SC_OPAQUE:
531 		case SC_TIME:
532 		case SC_URI:
533 		case SC_USTRING:
534 			if (strcmp(lxml_prop_types[r], (const char *)type) != 0)
535 				uu_die(gettext("property \'%s\' "
536 				    "type-to-list mismatch\n"),
537 				    p->sc_property_name);
538 
539 			(void) lxml_get_value(p, r, cursor);
540 			break;
541 		default:
542 			uu_die(gettext("unknown value list type: %s\n"),
543 			    cursor->name);
544 			break;
545 		}
546 	}
547 
548 	xmlFree(type);
549 
550 	override = xmlGetProp(property, (xmlChar *)override_attr);
551 	p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
552 	xmlFree(override);
553 
554 	return (internal_attach_property(pgrp, p));
555 }
556 
557 static int
558 lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab)
559 {
560 	return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY,
561 	    SCF_TYPE_ASTRING, stab, value_attr));
562 }
563 
564 /*
565  * Property groups can go on any of a service, an instance, or a template.
566  */
567 static int
568 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup)
569 {
570 	pgroup_t *pg;
571 	xmlNodePtr cursor;
572 	xmlChar *name, *type, *delete;
573 
574 	/*
575 	 * property group attributes:
576 	 * name: string
577 	 * type: string | framework | application
578 	 */
579 	name = xmlGetProp(pgroup, (xmlChar *)name_attr);
580 	type = xmlGetProp(pgroup, (xmlChar *)type_attr);
581 	pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type);
582 	xmlFree(name);
583 	xmlFree(type);
584 
585 	/*
586 	 * Walk the children of this lxml_elements, which are a stability
587 	 * element, property elements, or propval elements.
588 	 */
589 	for (cursor = pgroup->xmlChildrenNode; cursor != NULL;
590 	    cursor = cursor->next) {
591 		if (lxml_ignorable_block(cursor))
592 			continue;
593 
594 		switch (lxml_xlate_element(cursor->name)) {
595 		case SC_STABILITY:
596 			(void) lxml_get_pgroup_stability(pg, cursor);
597 			break;
598 		case SC_PROPERTY:
599 			(void) lxml_get_property(pg, cursor);
600 			break;
601 		case SC_PROPVAL:
602 			(void) lxml_get_propval(pg, cursor);
603 			break;
604 		default:
605 			abort();
606 			break;
607 		}
608 	}
609 
610 	delete = xmlGetProp(pgroup, (xmlChar *)delete_attr);
611 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
612 	xmlFree(delete);
613 
614 	return (0);
615 }
616 
617 
618 /*
619  * Dependency groups, execution methods can go on either a service or an
620  * instance.
621  */
622 
623 static int
624 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile)
625 {
626 	property_t *p;
627 
628 	p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
629 	    1, (uint64_t)1);
630 	if (internal_attach_property(pg, p) != 0)
631 		return (-1);
632 
633 	return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE,
634 	    SCF_TYPE_ASTRING, profile, name_attr));
635 }
636 
637 static int
638 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred)
639 {
640 	property_t *p;
641 
642 	p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
643 	    1, (uint64_t)0);
644 	if (internal_attach_property(pg, p) != 0)
645 		return (-1);
646 
647 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING,
648 	    cred, "user") != 0)
649 		return (-1);
650 
651 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING,
652 	    cred, "group") != 0)
653 		return (-1);
654 
655 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS,
656 	    SCF_TYPE_ASTRING, cred, "supp_groups") != 0)
657 		return (-1);
658 
659 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES,
660 	    SCF_TYPE_ASTRING, cred, "privileges") != 0)
661 		return (-1);
662 
663 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES,
664 	    SCF_TYPE_ASTRING, cred, "limit_privileges") != 0)
665 		return (-1);
666 
667 	return (0);
668 }
669 
670 static char *
671 lxml_get_envvar(xmlNodePtr envvar)
672 {
673 	char *name;
674 	char *value;
675 	char *ret;
676 
677 	name = (char *)xmlGetProp(envvar, (xmlChar *)"name");
678 	value = (char *)xmlGetProp(envvar, (xmlChar *)"value");
679 
680 	if (strlen(name) == 0 || strchr(name, '=') != NULL)
681 		uu_die(gettext("Invalid environment variable "
682 		    "\"%s\".\n"), name);
683 	if (strstr(name, "SMF_") == name)
684 		uu_die(gettext("Invalid environment variable "
685 		    "\"%s\"; \"SMF_\" prefix is reserved.\n"), name);
686 
687 	ret = uu_msprintf("%s=%s", name, value);
688 	xmlFree(name);
689 	xmlFree(value);
690 	return (ret);
691 }
692 
693 static int
694 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment)
695 {
696 	property_t *p;
697 	xmlNodePtr cursor;
698 	value_t *val;
699 
700 	p = internal_property_create(SCF_PROPERTY_ENVIRONMENT,
701 	    SCF_TYPE_ASTRING, 0);
702 
703 	for (cursor = environment->xmlChildrenNode; cursor != NULL;
704 	    cursor = cursor->next) {
705 		char *tmp;
706 
707 		if (lxml_ignorable_block(cursor))
708 			continue;
709 
710 		if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR)
711 			uu_die(gettext("illegal element \"%s\" on "
712 			    "method environment for \"%s\"\n"),
713 			    cursor->name, pg->sc_pgroup_name);
714 
715 		if ((tmp = lxml_get_envvar(cursor)) == NULL)
716 			uu_die(gettext("Out of memory\n"));
717 
718 		val = internal_value_new();
719 		val->sc_u.sc_string = tmp;
720 		val->sc_type = SCF_TYPE_ASTRING;
721 		val->sc_free = lxml_free_str;
722 		internal_attach_value(p, val);
723 	}
724 
725 	if (internal_attach_property(pg, p) != 0) {
726 		internal_property_free(p);
727 		return (-1);
728 	}
729 
730 	return (0);
731 }
732 
733 static int
734 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx)
735 {
736 	xmlNodePtr cursor;
737 
738 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY,
739 	    SCF_TYPE_ASTRING, ctx, "working_directory") != 0)
740 		return (-1);
741 
742 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT, SCF_TYPE_ASTRING,
743 	    ctx, "project") != 0)
744 		return (-1);
745 
746 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL,
747 	    SCF_TYPE_ASTRING, ctx, "resource_pool") != 0)
748 		return (-1);
749 
750 	for (cursor = ctx->xmlChildrenNode; cursor != NULL;
751 	    cursor = cursor->next) {
752 		if (lxml_ignorable_block(cursor))
753 			continue;
754 
755 		switch (lxml_xlate_element(cursor->name)) {
756 		case SC_METHOD_CREDENTIAL:
757 			(void) lxml_get_method_credential(pg, cursor);
758 			break;
759 		case SC_METHOD_PROFILE:
760 			(void) lxml_get_method_profile(pg, cursor);
761 			break;
762 		case SC_METHOD_ENVIRONMENT:
763 			(void) lxml_get_method_environment(pg, cursor);
764 			break;
765 		default:
766 			semerr(gettext("illegal element \'%s\' in method "
767 			    "context\n"), (char *)cursor);
768 			break;
769 		}
770 	}
771 
772 	return (0);
773 }
774 
775 static int
776 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx)
777 {
778 	pgroup_t *pg;
779 
780 	pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT,
781 	    (char *)scf_group_framework);
782 
783 	return (lxml_get_method_context(pg, ctx));
784 }
785 
786 static int
787 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
788 {
789 	pgroup_t *pg;
790 	property_t *p;
791 	xmlChar *name, *timeout, *delete;
792 	xmlNodePtr cursor;
793 	int r = 0;
794 
795 	name = xmlGetProp(emeth, (xmlChar *)name_attr);
796 	pg = internal_pgroup_find_or_create(entity, (char *)name,
797 	    (char *)SCF_GROUP_METHOD);
798 	xmlFree(name);
799 
800 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
801 	    emeth, type_attr) != 0 ||
802 	    new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING,
803 	    emeth, "exec") != 0)
804 		return (-1);
805 
806 	timeout = xmlGetProp(emeth, (xmlChar *)"timeout_seconds");
807 	if (timeout != NULL) {
808 		uint64_t u_timeout;
809 		char *endptr;
810 		/*
811 		 * Although an SC_COUNT represents a uint64_t the use
812 		 * of a negative value is acceptable due to the usage
813 		 * established by inetd(1M).
814 		 */
815 		errno = 0;
816 		u_timeout = strtoull((char *)timeout, &endptr, 10);
817 		if (errno != 0 || endptr == (char *)timeout || *endptr)
818 			uu_die(gettext("illegal value \"%s\" for "
819 			    "timeout_seconds (%s)\n"),
820 			    (char *)timeout, (errno) ? strerror(errno):
821 			    gettext("Illegal character"));
822 		p = internal_property_create(SCF_PROPERTY_TIMEOUT,
823 		    SCF_TYPE_COUNT, 1, u_timeout);
824 		r = internal_attach_property(pg, p);
825 		xmlFree(timeout);
826 	}
827 	if (r != 0)
828 		return (-1);
829 
830 	/*
831 	 * There is a possibility that a method context also exists, in which
832 	 * case the following attributes are defined: project, resource_pool,
833 	 * working_directory, profile, user, group, privileges, limit_privileges
834 	 */
835 	for (cursor = emeth->xmlChildrenNode; cursor != NULL;
836 	    cursor = cursor->next) {
837 		if (lxml_ignorable_block(cursor))
838 			continue;
839 
840 		switch (lxml_xlate_element(cursor->name)) {
841 		case SC_STABILITY:
842 			if (lxml_get_pgroup_stability(pg, cursor) != 0)
843 				return (-1);
844 			break;
845 
846 		case SC_METHOD_CONTEXT:
847 			(void) lxml_get_method_context(pg, cursor);
848 			break;
849 
850 		case SC_PROPVAL:
851 			(void) lxml_get_propval(pg, cursor);
852 			break;
853 
854 		case SC_PROPERTY:
855 			(void) lxml_get_property(pg, cursor);
856 			break;
857 
858 		default:
859 			uu_die(gettext("illegal element \"%s\" on "
860 			    "execution method \"%s\"\n"), cursor->name,
861 			    pg->sc_pgroup_name);
862 			break;
863 		}
864 	}
865 
866 	delete = xmlGetProp(emeth, (xmlChar *)delete_attr);
867 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
868 	xmlFree(delete);
869 
870 	return (0);
871 }
872 
873 static int
874 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency)
875 {
876 	pgroup_t *pg;
877 	property_t *p;
878 	xmlNodePtr cursor;
879 	xmlChar *name;
880 	xmlChar *delete;
881 
882 	/*
883 	 * dependency attributes:
884 	 * name: string
885 	 * grouping: require_all | require_any | exclude_all | optional_all
886 	 * reset_on: string (error | restart | refresh | none)
887 	 * type:  service / path /host
888 	 */
889 
890 	name = xmlGetProp(dependency, (xmlChar *)name_attr);
891 	pg = internal_pgroup_find_or_create(entity, (char *)name,
892 	    (char *)SCF_GROUP_DEPENDENCY);
893 	xmlFree(name);
894 
895 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
896 	    dependency, type_attr) != 0)
897 		return (-1);
898 
899 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
900 	    SCF_TYPE_ASTRING, dependency, "restart_on") != 0)
901 		return (-1);
902 
903 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
904 	    dependency, "grouping") != 0)
905 		return (-1);
906 
907 	p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0);
908 	if (internal_attach_property(pg, p) != 0)
909 		return (-1);
910 
911 	for (cursor = dependency->xmlChildrenNode; cursor != NULL;
912 	    cursor = cursor->next) {
913 		xmlChar *value;
914 		value_t *v;
915 
916 		if (lxml_ignorable_block(cursor))
917 			continue;
918 
919 		switch (lxml_xlate_element(cursor->name)) {
920 		case SC_STABILITY:
921 			if (lxml_get_pgroup_stability(pg, cursor) != 0)
922 				return (-1);
923 			break;
924 
925 		case SC_SERVICE_FMRI:
926 			value = xmlGetProp(cursor, (xmlChar *)value_attr);
927 			if (value != NULL) {
928 				if (lxml_validate_string_value(SCF_TYPE_FMRI,
929 				    (char *)value) != 0)
930 					uu_die(gettext("illegal value \"%s\" "
931 					    "for %s (%s)\n"), (char *)value,
932 					    lxml_prop_types[SC_FMRI],
933 					    (scf_error()) ?
934 					    scf_strerror(scf_error()) :
935 					    gettext("Illegal format"));
936 				v = internal_value_new();
937 				v->sc_type = SCF_TYPE_FMRI;
938 				v->sc_u.sc_string = (char *)value;
939 				internal_attach_value(p, v);
940 			}
941 
942 			break;
943 
944 		case SC_PROPVAL:
945 			(void) lxml_get_propval(pg, cursor);
946 			break;
947 
948 		case SC_PROPERTY:
949 			(void) lxml_get_property(pg, cursor);
950 			break;
951 
952 		default:
953 			uu_die(gettext("illegal element \"%s\" on "
954 			    "dependency group \"%s\"\n"), cursor->name, name);
955 			break;
956 		}
957 	}
958 
959 	delete = xmlGetProp(dependency, (xmlChar *)delete_attr);
960 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
961 	xmlFree(delete);
962 
963 	return (0);
964 }
965 
966 /*
967  * Dependents are hairy.  They should cause a dependency pg to be created in
968  * another service, but we can't do that here; we'll have to wait until the
969  * import routines.  So for now we'll add the dependency group that should go
970  * in the other service to the entity's dependent list.
971  */
972 static int
973 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent)
974 {
975 	xmlChar *name, *or;
976 	xmlNodePtr sf;
977 	xmlChar *fmri, *delete;
978 	pgroup_t *pg;
979 	property_t *p;
980 	xmlNodePtr n;
981 	char *myfmri;
982 
983 	name = xmlGetProp(dependent, (xmlChar *)name_attr);
984 
985 	if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) {
986 		semerr(gettext("Property group and dependent of entity %s "
987 		    "have same name \"%s\".\n"), entity->sc_name, name);
988 		xmlFree(name);
989 		return (-1);
990 	}
991 
992 	or = xmlGetProp(dependent, (xmlChar *)override_attr);
993 
994 	pg = internal_pgroup_new();
995 	pg->sc_pgroup_name = (char *)name;
996 	pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY;
997 	pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0);
998 	xmlFree(or);
999 	if (internal_attach_dependent(entity, pg) != 0) {
1000 		xmlFree(name);
1001 		internal_pgroup_free(pg);
1002 		return (-1);
1003 	}
1004 
1005 	for (sf = dependent->children; sf != NULL; sf = sf->next)
1006 		if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0)
1007 			break;
1008 	assert(sf != NULL);
1009 	fmri = xmlGetProp(sf, (xmlChar *)value_attr);
1010 	pg->sc_pgroup_fmri = (char *)fmri;
1011 
1012 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1013 	    SCF_TYPE_ASTRING, dependent, "restart_on") != 0)
1014 		return (-1);
1015 
1016 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1017 	    dependent, "grouping") != 0)
1018 		return (-1);
1019 
1020 	myfmri = safe_malloc(max_scf_fmri_len + 1);
1021 	if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) {
1022 		if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s",
1023 		    entity->sc_name) < 0)
1024 			bad_error("snprintf", errno);
1025 	} else {
1026 		assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT);
1027 		if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s",
1028 		    entity->sc_parent->sc_name, entity->sc_name) < 0)
1029 			bad_error("snprintf", errno);
1030 	}
1031 
1032 	p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1,
1033 	    myfmri);
1034 	if (internal_attach_property(pg, p) != 0)
1035 		return (-1);
1036 
1037 	/* Create a property to serve as a do-not-export flag. */
1038 	p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1,
1039 	    (uint64_t)1);
1040 	if (internal_attach_property(pg, p) != 0)
1041 		return (-1);
1042 
1043 	for (n = sf->next; n != NULL; n = n->next) {
1044 		if (lxml_ignorable_block(n))
1045 			continue;
1046 
1047 		switch (lxml_xlate_element(n->name)) {
1048 		case SC_STABILITY:
1049 			if (new_str_prop_from_attr(pg,
1050 			    SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n,
1051 			    value_attr) != 0)
1052 				return (-1);
1053 			break;
1054 
1055 		case SC_PROPVAL:
1056 			(void) lxml_get_propval(pg, n);
1057 			break;
1058 
1059 		case SC_PROPERTY:
1060 			(void) lxml_get_property(pg, n);
1061 			break;
1062 
1063 		default:
1064 			uu_die(gettext("unexpected element %s.\n"), n->name);
1065 		}
1066 	}
1067 
1068 	/* Go back and fill in defaults. */
1069 	if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) {
1070 		p = internal_property_create(SCF_PROPERTY_TYPE,
1071 		    SCF_TYPE_ASTRING, 1, "service");
1072 		if (internal_attach_property(pg, p) != 0)
1073 			return (-1);
1074 	}
1075 
1076 	delete = xmlGetProp(dependent, (xmlChar *)delete_attr);
1077 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1078 	xmlFree(delete);
1079 
1080 	pg = internal_pgroup_find_or_create(entity, "dependents",
1081 	    (char *)scf_group_framework);
1082 	p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri);
1083 	if (internal_attach_property(pg, p) != 0)
1084 		return (-1);
1085 
1086 	return (0);
1087 }
1088 
1089 static int
1090 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
1091 {
1092 	pgroup_t *pg;
1093 	property_t *p;
1094 	xmlChar *stabval;
1095 
1096 	if ((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) {
1097 		uu_warn(gettext("no stability value found\n"));
1098 		stabval = (xmlChar *)strdup("External");
1099 	}
1100 
1101 	pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1102 	    (char *)scf_group_framework);
1103 
1104 	p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY,
1105 	    SCF_TYPE_ASTRING, 1, stabval);
1106 
1107 	return (internal_attach_property(pg, p));
1108 }
1109 
1110 static int
1111 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
1112 {
1113 	pgroup_t *pg;
1114 	property_t *p;
1115 	xmlChar *restarter;
1116 	xmlNode *cursor;
1117 	int r;
1118 
1119 	/*
1120 	 * Go find child.  Child is a service_fmri element.  value attribute
1121 	 * contains restarter FMRI.
1122 	 */
1123 
1124 	pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1125 	    (char *)scf_group_framework);
1126 
1127 	/*
1128 	 * Walk its child elements, as appropriate.
1129 	 */
1130 	for (cursor = rstr->xmlChildrenNode; cursor != NULL;
1131 	    cursor = cursor->next) {
1132 		if (lxml_ignorable_block(cursor))
1133 			continue;
1134 
1135 		switch (lxml_xlate_element(cursor->name)) {
1136 		case SC_SERVICE_FMRI:
1137 			restarter = xmlGetProp(cursor, (xmlChar *)value_attr);
1138 			break;
1139 		default:
1140 			uu_die(gettext("illegal element \"%s\" on restarter "
1141 			    "element for \"%s\"\n"), cursor->name,
1142 			    entity->sc_name);
1143 			break;
1144 		}
1145 	}
1146 
1147 	p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1,
1148 	    restarter);
1149 
1150 	r = internal_attach_property(pg, p);
1151 	if (r != 0) {
1152 		internal_property_free(p);
1153 		return (-1);
1154 	}
1155 
1156 	return (0);
1157 }
1158 
1159 static void
1160 sanitize_locale(uchar_t *locale)
1161 {
1162 	for (; *locale != '\0'; locale++)
1163 		if (!isalnum(*locale) && *locale != '_')
1164 			*locale = '_';
1165 }
1166 
1167 static int
1168 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext)
1169 {
1170 	xmlNodePtr cursor;
1171 	xmlChar *val;
1172 	char *stripped, *cp;
1173 	property_t *p;
1174 	int r;
1175 
1176 	if ((val = xmlGetProp(loctext, (xmlChar *)"xml:lang")) == NULL)
1177 		if ((val = xmlGetProp(loctext, (xmlChar *)"lang")) == NULL)
1178 			val = (xmlChar *)"unknown";
1179 
1180 	sanitize_locale(val);
1181 
1182 	for (cursor = loctext->xmlChildrenNode; cursor != NULL;
1183 	    cursor = cursor->next) {
1184 		if (strcmp("text", (const char *)cursor->name) == 0) {
1185 			break;
1186 		} else if (strcmp("comment", (const char *)cursor->name) != 0) {
1187 			uu_die(gettext("illegal element \"%s\" on loctext "
1188 			    "element for \"%s\"\n"), cursor->name,
1189 			    service->sc_name);
1190 		}
1191 	}
1192 
1193 	if (cursor == NULL) {
1194 		uu_die(gettext("loctext element has no content for \"%s\"\n"),
1195 		    service->sc_name);
1196 	}
1197 
1198 	/*
1199 	 * Remove leading and trailing whitespace.
1200 	 */
1201 	if ((stripped = strdup((const char *)cursor->content)) == NULL)
1202 		uu_die(gettext("Out of memory\n"));
1203 
1204 	for (; isspace(*stripped); stripped++)
1205 		;
1206 	for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--)
1207 		;
1208 	*(cp + 1) = '\0';
1209 
1210 	p = internal_property_create((const char *)val, SCF_TYPE_USTRING, 1,
1211 	    stripped);
1212 
1213 	r = internal_attach_property(pg, p);
1214 	if (r != 0)
1215 		internal_property_free(p);
1216 
1217 	return (r);
1218 }
1219 
1220 static int
1221 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
1222 {
1223 	xmlNodePtr cursor;
1224 	pgroup_t *pg;
1225 
1226 	/*
1227 	 * Create the property group, if absent.
1228 	 */
1229 	pg = internal_pgroup_find_or_create(service,
1230 	    (char *)SCF_PG_TM_COMMON_NAME, (char *)SCF_GROUP_TEMPLATE);
1231 
1232 	/*
1233 	 * Iterate through one or more loctext elements.  The locale is the
1234 	 * property name; the contents are the ustring value for the property.
1235 	 */
1236 	for (cursor = common_name->xmlChildrenNode; cursor != NULL;
1237 	    cursor = cursor->next) {
1238 		if (lxml_ignorable_block(cursor))
1239 			continue;
1240 
1241 		switch (lxml_xlate_element(cursor->name)) {
1242 		case SC_LOCTEXT:
1243 			if (lxml_get_loctext(service, pg, cursor))
1244 				return (-1);
1245 			break;
1246 		default:
1247 			uu_die(gettext("illegal element \"%s\" on common_name "
1248 			    "element for \"%s\"\n"), cursor->name,
1249 			    service->sc_name);
1250 			break;
1251 		}
1252 	}
1253 
1254 	return (0);
1255 }
1256 
1257 static int
1258 lxml_get_tm_description(entity_t *service, xmlNodePtr description)
1259 {
1260 	xmlNodePtr cursor;
1261 	pgroup_t *pg;
1262 
1263 	/*
1264 	 * Create the property group, if absent.
1265 	 */
1266 	pg = internal_pgroup_find_or_create(service,
1267 	    (char *)SCF_PG_TM_DESCRIPTION, (char *)SCF_GROUP_TEMPLATE);
1268 
1269 	/*
1270 	 * Iterate through one or more loctext elements.  The locale is the
1271 	 * property name; the contents are the ustring value for the property.
1272 	 */
1273 	for (cursor = description->xmlChildrenNode; cursor != NULL;
1274 	    cursor = cursor->next) {
1275 		if (lxml_ignorable_block(cursor))
1276 			continue;
1277 
1278 		switch (lxml_xlate_element(cursor->name)) {
1279 		case SC_LOCTEXT:
1280 			if (lxml_get_loctext(service, pg, cursor))
1281 				return (-1);
1282 			break;
1283 		default:
1284 			uu_die(gettext("illegal element \"%s\" on description "
1285 			    "element for \"%s\"\n"), cursor->name,
1286 			    service->sc_name);
1287 			break;
1288 		}
1289 	}
1290 
1291 	return (0);
1292 }
1293 
1294 static char *
1295 lxml_label_to_groupname(const char *prefix, const char *in)
1296 {
1297 	char *out, *cp;
1298 	size_t len, piece_len;
1299 
1300 	out = uu_zalloc(2 * scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1);
1301 	if (out == NULL)
1302 		return (NULL);
1303 
1304 	(void) strcpy(out, prefix);
1305 	(void) strcat(out, in);
1306 
1307 	len = strlen(out);
1308 	if (len > max_scf_name_len) {
1309 		/* Use the first half and the second half. */
1310 		piece_len = (max_scf_name_len - 2) / 2;
1311 
1312 		(void) strncpy(out + piece_len, "..", 2);
1313 
1314 		(void) strcpy(out + piece_len + 2, out + (len - piece_len));
1315 
1316 		len = strlen(out);
1317 	}
1318 
1319 	/*
1320 	 * Translate non-property characters to '_'.
1321 	 */
1322 	for (cp = out; *cp != '\0'; ++cp) {
1323 		if (!(isalnum(*cp) || *cp == '_' || *cp == '-'))
1324 			*cp = '_';
1325 	}
1326 
1327 	*cp = '\0';
1328 
1329 	return (out);
1330 }
1331 
1332 static int
1333 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
1334 {
1335 	pgroup_t *pg;
1336 	char *pgname;
1337 	xmlChar *title;
1338 
1339 	/*
1340 	 * Fetch title attribute, convert to something sanitized, and create
1341 	 * property group.
1342 	 */
1343 	title = xmlGetProp(manpage, (xmlChar *)"title");
1344 	pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX,
1345 	    (const char *)title);
1346 
1347 	pg = internal_pgroup_find_or_create(service, pgname,
1348 	    (char *)SCF_GROUP_TEMPLATE);
1349 
1350 	/*
1351 	 * Each attribute is an astring property within the group.
1352 	 */
1353 	if (new_str_prop_from_attr(pg, "title", SCF_TYPE_ASTRING, manpage,
1354 	    "title") != 0 ||
1355 	    new_str_prop_from_attr(pg, "section", SCF_TYPE_ASTRING, manpage,
1356 	    "section") != 0 ||
1357 	    new_str_prop_from_attr(pg, "manpath", SCF_TYPE_ASTRING, manpage,
1358 	    "manpath") != 0)
1359 		return (-1);
1360 
1361 	return (0);
1362 }
1363 
1364 static int
1365 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
1366 {
1367 	pgroup_t *pg;
1368 	char *pgname;
1369 	xmlChar *name;
1370 
1371 	/*
1372 	 * Fetch name attribute, convert name to something sanitized, and create
1373 	 * property group.
1374 	 */
1375 	name = xmlGetProp(doc_link, (xmlChar *)"name");
1376 
1377 	pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX,
1378 	    (const char *)name);
1379 
1380 	pg = internal_pgroup_find_or_create(service, pgname,
1381 	    (char *)SCF_GROUP_TEMPLATE);
1382 
1383 	/*
1384 	 * Each attribute is an astring property within the group.
1385 	 */
1386 	if (new_str_prop_from_attr(pg, "name", SCF_TYPE_ASTRING, doc_link,
1387 	    "name") != 0 ||
1388 	    new_str_prop_from_attr(pg, "uri", SCF_TYPE_ASTRING, doc_link,
1389 	    "uri") != 0)
1390 		return (-1);
1391 
1392 	return (0);
1393 }
1394 
1395 static int
1396 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
1397 {
1398 	xmlNodePtr cursor;
1399 
1400 	for (cursor = documentation->xmlChildrenNode; cursor != NULL;
1401 	    cursor = cursor->next) {
1402 		if (lxml_ignorable_block(cursor))
1403 			continue;
1404 
1405 		switch (lxml_xlate_element(cursor->name)) {
1406 		case SC_MANPAGE:
1407 			(void) lxml_get_tm_manpage(service, cursor);
1408 			break;
1409 		case SC_DOC_LINK:
1410 			(void) lxml_get_tm_doclink(service, cursor);
1411 			break;
1412 		default:
1413 			uu_die(gettext("illegal element \"%s\" on template "
1414 			    "for service \"%s\"\n"),
1415 			    cursor->name, service->sc_name);
1416 		}
1417 	}
1418 
1419 	return (0);
1420 }
1421 
1422 static int
1423 lxml_get_template(entity_t *service, xmlNodePtr templ)
1424 {
1425 	xmlNodePtr cursor;
1426 
1427 	for (cursor = templ->xmlChildrenNode; cursor != NULL;
1428 	    cursor = cursor->next) {
1429 		if (lxml_ignorable_block(cursor))
1430 			continue;
1431 
1432 		switch (lxml_xlate_element(cursor->name)) {
1433 		case SC_COMMON_NAME:
1434 			(void) lxml_get_tm_common_name(service, cursor);
1435 			break;
1436 		case SC_DESCRIPTION:
1437 			(void) lxml_get_tm_description(service, cursor);
1438 			break;
1439 		case SC_DOCUMENTATION:
1440 			(void) lxml_get_tm_documentation(service, cursor);
1441 			break;
1442 		default:
1443 			uu_die(gettext("illegal element \"%s\" on template "
1444 			    "for service \"%s\"\n"),
1445 			    cursor->name, service->sc_name);
1446 		}
1447 	}
1448 
1449 	return (0);
1450 }
1451 
1452 static int
1453 lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
1454 {
1455 	entity_t *i;
1456 	xmlChar *enabled;
1457 	pgroup_t *pg;
1458 	property_t *p;
1459 	char *package;
1460 	uint64_t enabled_val = 0;
1461 
1462 	i = internal_instance_new("default");
1463 
1464 	if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) {
1465 		enabled_val = (strcmp(true, (const char *)enabled) == 0) ?
1466 		    1 : 0;
1467 		xmlFree(enabled);
1468 	}
1469 
1470 	/*
1471 	 * New general property group with enabled boolean property set.
1472 	 */
1473 
1474 	pg = internal_pgroup_new();
1475 	(void) internal_attach_pgroup(i, pg);
1476 
1477 	pg->sc_pgroup_name = (char *)scf_pg_general;
1478 	pg->sc_pgroup_type = (char *)scf_group_framework;
1479 	pg->sc_pgroup_flags = 0;
1480 
1481 	p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
1482 	    enabled_val);
1483 
1484 	(void) internal_attach_property(pg, p);
1485 
1486 	/*
1487 	 * Add general/package property if PKGINST is set.
1488 	 */
1489 	if ((package = getenv("PKGINST")) != NULL) {
1490 		p = internal_property_create(SCF_PROPERTY_PACKAGE,
1491 		    SCF_TYPE_ASTRING, 1, package);
1492 
1493 		(void) internal_attach_property(pg, p);
1494 	}
1495 
1496 	return (internal_attach_entity(service, i));
1497 }
1498 
1499 /*
1500  * Translate an instance element into an internal property tree, added to
1501  * service.  If op is SVCCFG_OP_APPLY (i.e., apply a profile), forbid
1502  * subelements and set the enabled property to override.
1503  */
1504 static int
1505 lxml_get_instance(entity_t *service, xmlNodePtr inst, svccfg_op_t op)
1506 {
1507 	entity_t *i;
1508 	pgroup_t *pg;
1509 	property_t *p;
1510 	xmlNodePtr cursor;
1511 	xmlChar *enabled;
1512 	int r;
1513 
1514 	/*
1515 	 * Fetch its attributes, as appropriate.
1516 	 */
1517 	i = internal_instance_new((char *)xmlGetProp(inst,
1518 	    (xmlChar *)name_attr));
1519 
1520 	/*
1521 	 * Note that this must be done before walking the children so that
1522 	 * sc_fmri is set in case we enter lxml_get_dependent().
1523 	 */
1524 	r = internal_attach_entity(service, i);
1525 	if (r != 0)
1526 		return (r);
1527 
1528 	enabled = xmlGetProp(inst, (xmlChar *)enabled_attr);
1529 
1530 	/*
1531 	 * New general property group with enabled boolean property set.
1532 	 */
1533 	pg = internal_pgroup_new();
1534 	(void) internal_attach_pgroup(i, pg);
1535 
1536 	pg->sc_pgroup_name = (char *)scf_pg_general;
1537 	pg->sc_pgroup_type = (char *)scf_group_framework;
1538 	pg->sc_pgroup_flags = 0;
1539 
1540 	p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
1541 	    (uint64_t)(strcmp(true, (const char *)enabled) == 0 ? 1 : 0));
1542 
1543 	p->sc_property_override = (op == SVCCFG_OP_APPLY);
1544 
1545 	(void) internal_attach_property(pg, p);
1546 
1547 	xmlFree(enabled);
1548 
1549 	/*
1550 	 * Walk its child elements, as appropriate.
1551 	 */
1552 	for (cursor = inst->xmlChildrenNode; cursor != NULL;
1553 	    cursor = cursor->next) {
1554 		if (lxml_ignorable_block(cursor))
1555 			continue;
1556 
1557 		if (op == SVCCFG_OP_APPLY) {
1558 			semerr(gettext("Instance \"%s\" may not contain "
1559 			    "elements in profiles.\n"), i->sc_name,
1560 			    cursor->name);
1561 			return (-1);
1562 		}
1563 
1564 		switch (lxml_xlate_element(cursor->name)) {
1565 		case SC_RESTARTER:
1566 			(void) lxml_get_restarter(i, cursor);
1567 			break;
1568 		case SC_DEPENDENCY:
1569 			(void) lxml_get_dependency(i, cursor);
1570 			break;
1571 		case SC_DEPENDENT:
1572 			(void) lxml_get_dependent(i, cursor);
1573 			break;
1574 		case SC_METHOD_CONTEXT:
1575 			(void) lxml_get_entity_method_context(i, cursor);
1576 			break;
1577 		case SC_EXEC_METHOD:
1578 			(void) lxml_get_exec_method(i, cursor);
1579 			break;
1580 		case SC_PROPERTY_GROUP:
1581 			(void) lxml_get_pgroup(i, cursor);
1582 			break;
1583 		case SC_TEMPLATE:
1584 			(void) lxml_get_template(i, cursor);
1585 			break;
1586 		default:
1587 			uu_die(gettext(
1588 			    "illegal element \"%s\" on instance \"%s\"\n"),
1589 			    cursor->name, i->sc_name);
1590 			break;
1591 		}
1592 	}
1593 
1594 	return (0);
1595 }
1596 
1597 /* ARGSUSED1 */
1598 static int
1599 lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
1600 {
1601 	pgroup_t *pg;
1602 	property_t *p;
1603 	int r;
1604 
1605 	pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1606 	    (char *)scf_group_framework);
1607 
1608 	p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE,
1609 	    SCF_TYPE_BOOLEAN, 1, (uint64_t)1);
1610 
1611 	r = internal_attach_property(pg, p);
1612 	if (r != 0) {
1613 		internal_property_free(p);
1614 		return (-1);
1615 	}
1616 
1617 	return (0);
1618 }
1619 
1620 /*
1621  * Translate a service element into an internal instance/property tree, added
1622  * to bundle.  If op is SVCCFG_OP_APPLY, allow only instance subelements.
1623  */
1624 static int
1625 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
1626 {
1627 	entity_t *s;
1628 	xmlNodePtr cursor;
1629 	xmlChar *type;
1630 	xmlChar *version;
1631 	int e;
1632 
1633 	/*
1634 	 * Fetch attributes, as appropriate.
1635 	 */
1636 	s = internal_service_new((char *)xmlGetProp(svc,
1637 	    (xmlChar *)name_attr));
1638 
1639 	version = xmlGetProp(svc, (xmlChar *)"version");
1640 	s->sc_u.sc_service.sc_service_version = atol((const char *)version);
1641 	xmlFree(version);
1642 
1643 	type = xmlGetProp(svc, (xmlChar *)type_attr);
1644 	s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type);
1645 	xmlFree(type);
1646 
1647 	/*
1648 	 * Walk its child elements, as appropriate.
1649 	 */
1650 	for (cursor = svc->xmlChildrenNode; cursor != NULL;
1651 	    cursor = cursor->next) {
1652 		if (lxml_ignorable_block(cursor))
1653 			continue;
1654 
1655 		e = lxml_xlate_element(cursor->name);
1656 
1657 		if (op == SVCCFG_OP_APPLY && e != SC_INSTANCE) {
1658 			semerr(gettext("Service \"%s\" may not contain the "
1659 			    "non-instance element \"%s\" in a profile.\n"),
1660 			    s->sc_name, cursor->name);
1661 
1662 			return (-1);
1663 		}
1664 
1665 		switch (e) {
1666 		case SC_INSTANCE:
1667 			(void) lxml_get_instance(s, cursor, op);
1668 			break;
1669 		case SC_TEMPLATE:
1670 			(void) lxml_get_template(s, cursor);
1671 			break;
1672 		case SC_STABILITY:
1673 			(void) lxml_get_entity_stability(s, cursor);
1674 			break;
1675 		case SC_DEPENDENCY:
1676 			(void) lxml_get_dependency(s, cursor);
1677 			break;
1678 		case SC_DEPENDENT:
1679 			(void) lxml_get_dependent(s, cursor);
1680 			break;
1681 		case SC_RESTARTER:
1682 			(void) lxml_get_restarter(s, cursor);
1683 			break;
1684 		case SC_EXEC_METHOD:
1685 			(void) lxml_get_exec_method(s, cursor);
1686 			break;
1687 		case SC_METHOD_CONTEXT:
1688 			(void) lxml_get_entity_method_context(s, cursor);
1689 			break;
1690 		case SC_PROPERTY_GROUP:
1691 			(void) lxml_get_pgroup(s, cursor);
1692 			break;
1693 		case SC_INSTANCE_CREATE_DEFAULT:
1694 			(void) lxml_get_default_instance(s, cursor);
1695 			break;
1696 		case SC_INSTANCE_SINGLE:
1697 			(void) lxml_get_single_instance(s, cursor);
1698 			break;
1699 		default:
1700 			uu_die(gettext(
1701 			    "illegal element \"%s\" on service \"%s\"\n"),
1702 			    cursor->name, s->sc_name);
1703 			break;
1704 		}
1705 	}
1706 
1707 	return (internal_attach_service(bundle, s));
1708 }
1709 
1710 #ifdef DEBUG
1711 void
1712 lxml_dump(int g, xmlNodePtr p)
1713 {
1714 	if (p && p->name) {
1715 		printf("%d %s\n", g, p->name);
1716 
1717 		for (p = p->xmlChildrenNode; p != NULL; p = p->next)
1718 			lxml_dump(g + 1, p);
1719 	}
1720 }
1721 #endif /* DEBUG */
1722 
1723 static int
1724 lxml_is_known_dtd(const xmlChar *dtdname)
1725 {
1726 	if (dtdname == NULL ||
1727 	    strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0)
1728 		return (0);
1729 
1730 	return (1);
1731 }
1732 
1733 static int
1734 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
1735     xmlNodePtr subbundle, svccfg_op_t op)
1736 {
1737 	xmlNodePtr cursor;
1738 	xmlChar *type;
1739 	int e;
1740 
1741 	/*
1742 	 * 1.  Get bundle attributes.
1743 	 */
1744 	type = xmlGetProp(subbundle, (xmlChar *)"type");
1745 	bundle->sc_bundle_type = lxml_xlate_bundle_type(type);
1746 	if (bundle->sc_bundle_type != bundle_type &&
1747 	    bundle_type != SVCCFG_UNKNOWN_BUNDLE) {
1748 		semerr(gettext("included bundle of different type.\n"));
1749 		return (-1);
1750 	}
1751 
1752 	xmlFree(type);
1753 
1754 	switch (op) {
1755 	case SVCCFG_OP_IMPORT:
1756 		if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
1757 			semerr(gettext("document is not a manifest.\n"));
1758 			return (-1);
1759 		}
1760 		break;
1761 	case SVCCFG_OP_APPLY:
1762 		if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
1763 			semerr(gettext("document is not a profile.\n"));
1764 			return (-1);
1765 		}
1766 		break;
1767 	case SVCCFG_OP_RESTORE:
1768 		if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) {
1769 			semerr(gettext("document is not an archive.\n"));
1770 			return (-1);
1771 		}
1772 		break;
1773 	}
1774 
1775 	if ((bundle->sc_bundle_name = xmlGetProp(subbundle,
1776 	    (xmlChar *)"name")) == NULL) {
1777 		semerr(gettext("service bundle lacks name attribute\n"));
1778 		return (-1);
1779 	}
1780 
1781 	/*
1782 	 * 2.  Get services, descend into each one and build state.
1783 	 */
1784 	for (cursor = subbundle->xmlChildrenNode; cursor != NULL;
1785 	    cursor = cursor->next) {
1786 		if (lxml_ignorable_block(cursor))
1787 			continue;
1788 
1789 		e = lxml_xlate_element(cursor->name);
1790 
1791 		switch (e) {
1792 		case SC_XI_INCLUDE:
1793 			continue;
1794 
1795 		case SC_SERVICE_BUNDLE:
1796 			if (lxml_get_bundle(bundle, bundle_type, cursor, op))
1797 				return (-1);
1798 			break;
1799 		case SC_SERVICE:
1800 			(void) lxml_get_service(bundle, cursor, op);
1801 			break;
1802 		}
1803 	}
1804 
1805 	return (0);
1806 }
1807 
1808 /*
1809  * Load an XML tree from filename and translate it into an internal service
1810  * tree bundle.  Require that the bundle be of appropriate type for the
1811  * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY.
1812  */
1813 int
1814 lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op)
1815 {
1816 	xmlDocPtr document;
1817 	xmlNodePtr cursor;
1818 	xmlDtdPtr dtd = NULL;
1819 	xmlValidCtxtPtr vcp;
1820 	boolean_t do_validate;
1821 	char *dtdpath = NULL;
1822 	int r;
1823 
1824 	/*
1825 	 * Verify we can read the file before we try to parse it.
1826 	 */
1827 	if (access(filename, R_OK | F_OK) == -1) {
1828 		semerr(gettext("unable to open file: %s\n"), strerror(errno));
1829 		return (-1);
1830 	}
1831 
1832 	/*
1833 	 * Until libxml2 addresses DTD-based validation with XInclude, we don't
1834 	 * validate service profiles (i.e. the apply path).
1835 	 */
1836 	do_validate = (op != SVCCFG_OP_APPLY) &&
1837 	    (getenv("SVCCFG_NOVALIDATE") == NULL);
1838 	if (do_validate)
1839 		dtdpath = getenv("SVCCFG_DTD");
1840 
1841 	if (dtdpath != NULL)
1842 		xmlLoadExtDtdDefaultValue = 0;
1843 
1844 	if ((document = xmlReadFile(filename, NULL, 0)) == NULL) {
1845 		semerr(gettext("couldn't parse document\n"));
1846 		return (-1);
1847 	}
1848 
1849 	/*
1850 	 * Verify that this is a document type we understand.
1851 	 */
1852 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
1853 		semerr(gettext("document has no DTD\n"));
1854 		return (-1);
1855 	}
1856 
1857 	if (!lxml_is_known_dtd(dtd->SystemID)) {
1858 		semerr(gettext("document DTD unknown; not service bundle?\n"));
1859 		return (-1);
1860 	}
1861 
1862 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
1863 		semerr(gettext("document is empty\n"));
1864 		xmlFreeDoc(document);
1865 		return (-1);
1866 	}
1867 
1868 	if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) {
1869 		semerr(gettext("document is not a service bundle\n"));
1870 		xmlFreeDoc(document);
1871 		return (-1);
1872 	}
1873 
1874 
1875 	if (dtdpath != NULL) {
1876 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1877 		if (dtd == NULL) {
1878 			semerr(gettext("Could not parse DTD \"%s\".\n"),
1879 			    dtdpath);
1880 			return (-1);
1881 		}
1882 
1883 		if (document->extSubset != NULL)
1884 			xmlFreeDtd(document->extSubset);
1885 
1886 		document->extSubset = dtd;
1887 	}
1888 
1889 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
1890 		semerr(gettext("couldn't handle XInclude statements "
1891 		    "in document\n"));
1892 		return (-1);
1893 	}
1894 
1895 	if (do_validate) {
1896 		vcp = xmlNewValidCtxt();
1897 		if (vcp == NULL)
1898 			uu_die(gettext("could not allocate memory"));
1899 		vcp->warning = xmlParserValidityWarning;
1900 		vcp->error = xmlParserValidityError;
1901 
1902 		r = xmlValidateDocument(vcp, document);
1903 
1904 		xmlFreeValidCtxt(vcp);
1905 
1906 		if (r == 0) {
1907 			semerr(gettext("Document is not valid.\n"));
1908 			xmlFreeDoc(document);
1909 			return (-1);
1910 		}
1911 	}
1912 
1913 
1914 #ifdef DEBUG
1915 	lxml_dump(0, cursor);
1916 #endif /* DEBUG */
1917 
1918 	r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op);
1919 
1920 	xmlFreeDoc(document);
1921 
1922 	return (r);
1923 }
1924 
1925 int
1926 lxml_inventory(const char *filename)
1927 {
1928 	bundle_t *b;
1929 	uu_list_walk_t *svcs, *insts;
1930 	entity_t *svc, *inst;
1931 
1932 	b = internal_bundle_new();
1933 
1934 	if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) {
1935 		internal_bundle_free(b);
1936 		return (-1);
1937 	}
1938 
1939 	svcs = uu_list_walk_start(b->sc_bundle_services, 0);
1940 	if (svcs == NULL)
1941 		uu_die(gettext("Couldn't walk services"));
1942 
1943 	while ((svc = uu_list_walk_next(svcs)) != NULL) {
1944 		uu_list_t *inst_list;
1945 
1946 		inst_list = svc->sc_u.sc_service.sc_service_instances;
1947 		insts = uu_list_walk_start(inst_list, 0);
1948 		if (insts == NULL)
1949 			uu_die(gettext("Couldn't walk instances"));
1950 
1951 		while ((inst = uu_list_walk_next(insts)) != NULL)
1952 			(void) printf("svc:/%s:%s\n", svc->sc_name,
1953 			    inst->sc_name);
1954 
1955 		uu_list_walk_end(insts);
1956 	}
1957 
1958 	uu_list_walk_end(svcs);
1959 
1960 	svcs = uu_list_walk_start(b->sc_bundle_services, 0);
1961 	while ((svc = uu_list_walk_next(svcs)) != NULL) {
1962 		(void) fputs("svc:/", stdout);
1963 		(void) puts(svc->sc_name);
1964 	}
1965 	uu_list_walk_end(svcs);
1966 
1967 	internal_bundle_free(b);
1968 
1969 	return (0);
1970 }
1971