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