xref: /titanic_51/usr/src/cmd/svc/svccfg/svccfg_xml.c (revision 821da340f9590922ba8761a64fadda18843a0e88)
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 (cursor = property->xmlChildrenNode; cursor != NULL;
504 	    cursor = cursor->next) {
505 		if (lxml_ignorable_block(cursor))
506 			continue;
507 
508 		switch (r = lxml_xlate_element(cursor->name)) {
509 		case SC_ASTRING:
510 		case SC_BOOLEAN:
511 		case SC_COUNT:
512 		case SC_FMRI:
513 		case SC_HOST:
514 		case SC_HOSTNAME:
515 		case SC_INTEGER:
516 		case SC_NET_ADDR_V4:
517 		case SC_NET_ADDR_V6:
518 		case SC_OPAQUE:
519 		case SC_TIME:
520 		case SC_URI:
521 		case SC_USTRING:
522 			if (strcmp(lxml_prop_types[r], (const char *)type) != 0)
523 				uu_die(gettext("property \'%s\' "
524 				    "type-to-list mismatch\n"),
525 				    p->sc_property_name);
526 
527 			p->sc_value_type = lxml_element_to_type(r);
528 			(void) lxml_get_value(p, r, cursor);
529 			break;
530 		default:
531 			uu_die(gettext("unknown value list type: %s\n"),
532 			    cursor->name);
533 			break;
534 		}
535 	}
536 
537 	xmlFree(type);
538 
539 	override = xmlGetProp(property, (xmlChar *)override_attr);
540 	p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
541 	xmlFree(override);
542 
543 	return (internal_attach_property(pgrp, p));
544 }
545 
546 static int
547 lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab)
548 {
549 	return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY,
550 	    SCF_TYPE_ASTRING, stab, value_attr));
551 }
552 
553 /*
554  * Property groups can go on any of a service, an instance, or a template.
555  */
556 static int
557 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup)
558 {
559 	pgroup_t *pg;
560 	xmlNodePtr cursor;
561 	xmlChar *name, *type, *delete;
562 
563 	/*
564 	 * property group attributes:
565 	 * name: string
566 	 * type: string | framework | application
567 	 */
568 	name = xmlGetProp(pgroup, (xmlChar *)name_attr);
569 	type = xmlGetProp(pgroup, (xmlChar *)type_attr);
570 	pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type);
571 	xmlFree(name);
572 	xmlFree(type);
573 
574 	/*
575 	 * Walk the children of this lxml_elements, which are a stability
576 	 * element, property elements, or propval elements.
577 	 */
578 	for (cursor = pgroup->xmlChildrenNode; cursor != NULL;
579 	    cursor = cursor->next) {
580 		if (lxml_ignorable_block(cursor))
581 			continue;
582 
583 		switch (lxml_xlate_element(cursor->name)) {
584 		case SC_STABILITY:
585 			(void) lxml_get_pgroup_stability(pg, cursor);
586 			break;
587 		case SC_PROPERTY:
588 			(void) lxml_get_property(pg, cursor);
589 			break;
590 		case SC_PROPVAL:
591 			(void) lxml_get_propval(pg, cursor);
592 			break;
593 		default:
594 			abort();
595 			break;
596 		}
597 	}
598 
599 	delete = xmlGetProp(pgroup, (xmlChar *)delete_attr);
600 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
601 	xmlFree(delete);
602 
603 	return (0);
604 }
605 
606 
607 /*
608  * Dependency groups, execution methods can go on either a service or an
609  * instance.
610  */
611 
612 static int
613 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile)
614 {
615 	property_t *p;
616 
617 	p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
618 	    1, (uint64_t)1);
619 	if (internal_attach_property(pg, p) != 0)
620 		return (-1);
621 
622 	return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE,
623 	    SCF_TYPE_ASTRING, profile, name_attr));
624 }
625 
626 static int
627 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred)
628 {
629 	property_t *p;
630 
631 	p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
632 	    1, (uint64_t)0);
633 	if (internal_attach_property(pg, p) != 0)
634 		return (-1);
635 
636 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING,
637 	    cred, "user") != 0)
638 		return (-1);
639 
640 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING,
641 	    cred, "group") != 0)
642 		return (-1);
643 
644 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS,
645 	    SCF_TYPE_ASTRING, cred, "supp_groups") != 0)
646 		return (-1);
647 
648 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES,
649 	    SCF_TYPE_ASTRING, cred, "privileges") != 0)
650 		return (-1);
651 
652 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES,
653 	    SCF_TYPE_ASTRING, cred, "limit_privileges") != 0)
654 		return (-1);
655 
656 	return (0);
657 }
658 
659 static char *
660 lxml_get_envvar(xmlNodePtr envvar)
661 {
662 	char *name;
663 	char *value;
664 	char *ret;
665 
666 	name = (char *)xmlGetProp(envvar, (xmlChar *)"name");
667 	value = (char *)xmlGetProp(envvar, (xmlChar *)"value");
668 
669 	if (strlen(name) == 0 || strchr(name, '=') != NULL)
670 		uu_die(gettext("Invalid environment variable "
671 		    "\"%s\".\n"), name);
672 	if (strstr(name, "SMF_") == name)
673 		uu_die(gettext("Invalid environment variable "
674 		    "\"%s\"; \"SMF_\" prefix is reserved.\n"), name);
675 
676 	ret = uu_msprintf("%s=%s", name, value);
677 	xmlFree(name);
678 	xmlFree(value);
679 	return (ret);
680 }
681 
682 static int
683 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment)
684 {
685 	property_t *p;
686 	xmlNodePtr cursor;
687 	value_t *val;
688 
689 	p = internal_property_create(SCF_PROPERTY_ENVIRONMENT,
690 	    SCF_TYPE_ASTRING, 0);
691 
692 	for (cursor = environment->xmlChildrenNode; cursor != NULL;
693 	    cursor = cursor->next) {
694 		char *tmp;
695 
696 		if (lxml_ignorable_block(cursor))
697 			continue;
698 
699 		if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR)
700 			uu_die(gettext("illegal element \"%s\" on "
701 			    "method environment for \"%s\"\n"),
702 			    cursor->name, pg->sc_pgroup_name);
703 
704 		if ((tmp = lxml_get_envvar(cursor)) == NULL)
705 			uu_die(gettext("Out of memory\n"));
706 
707 		val = internal_value_new();
708 		val->sc_u.sc_string = tmp;
709 		val->sc_type = SCF_TYPE_ASTRING;
710 		val->sc_free = lxml_free_str;
711 		internal_attach_value(p, val);
712 	}
713 
714 	if (internal_attach_property(pg, p) != 0) {
715 		internal_property_free(p);
716 		return (-1);
717 	}
718 
719 	return (0);
720 }
721 
722 static int
723 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx)
724 {
725 	xmlNodePtr cursor;
726 
727 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY,
728 	    SCF_TYPE_ASTRING, ctx, "working_directory") != 0)
729 		return (-1);
730 
731 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT, SCF_TYPE_ASTRING,
732 	    ctx, "project") != 0)
733 		return (-1);
734 
735 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL,
736 	    SCF_TYPE_ASTRING, ctx, "resource_pool") != 0)
737 		return (-1);
738 
739 	for (cursor = ctx->xmlChildrenNode; cursor != NULL;
740 	    cursor = cursor->next) {
741 		if (lxml_ignorable_block(cursor))
742 			continue;
743 
744 		switch (lxml_xlate_element(cursor->name)) {
745 		case SC_METHOD_CREDENTIAL:
746 			(void) lxml_get_method_credential(pg, cursor);
747 			break;
748 		case SC_METHOD_PROFILE:
749 			(void) lxml_get_method_profile(pg, cursor);
750 			break;
751 		case SC_METHOD_ENVIRONMENT:
752 			(void) lxml_get_method_environment(pg, cursor);
753 			break;
754 		default:
755 			semerr(gettext("illegal element \'%s\' in method "
756 			    "context\n"), (char *)cursor);
757 			break;
758 		}
759 	}
760 
761 	return (0);
762 }
763 
764 static int
765 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx)
766 {
767 	pgroup_t *pg;
768 
769 	pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT,
770 	    (char *)scf_group_framework);
771 
772 	return (lxml_get_method_context(pg, ctx));
773 }
774 
775 static int
776 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
777 {
778 	pgroup_t *pg;
779 	property_t *p;
780 	xmlChar *name, *timeout, *delete;
781 	xmlNodePtr cursor;
782 	int r = 0;
783 
784 	name = xmlGetProp(emeth, (xmlChar *)name_attr);
785 	pg = internal_pgroup_find_or_create(entity, (char *)name,
786 	    (char *)SCF_GROUP_METHOD);
787 	xmlFree(name);
788 
789 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
790 	    emeth, type_attr) != 0 ||
791 	    new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING,
792 	    emeth, "exec") != 0)
793 		return (-1);
794 
795 	timeout = xmlGetProp(emeth, (xmlChar *)"timeout_seconds");
796 	if (timeout != NULL) {
797 		uint64_t u_timeout;
798 		char *endptr;
799 		/*
800 		 * Although an SC_COUNT represents a uint64_t the use
801 		 * of a negative value is acceptable due to the usage
802 		 * established by inetd(1M).
803 		 */
804 		errno = 0;
805 		u_timeout = strtoull((char *)timeout, &endptr, 10);
806 		if (errno != 0 || endptr == (char *)timeout || *endptr)
807 			uu_die(gettext("illegal value \"%s\" for "
808 			    "timeout_seconds (%s)\n"),
809 			    (char *)timeout, (errno) ? strerror(errno):
810 			    gettext("Illegal character"));
811 		p = internal_property_create(SCF_PROPERTY_TIMEOUT,
812 		    SCF_TYPE_COUNT, 1, u_timeout);
813 		r = internal_attach_property(pg, p);
814 		xmlFree(timeout);
815 	}
816 	if (r != 0)
817 		return (-1);
818 
819 	/*
820 	 * There is a possibility that a method context also exists, in which
821 	 * case the following attributes are defined: project, resource_pool,
822 	 * working_directory, profile, user, group, privileges, limit_privileges
823 	 */
824 	for (cursor = emeth->xmlChildrenNode; cursor != NULL;
825 	    cursor = cursor->next) {
826 		if (lxml_ignorable_block(cursor))
827 			continue;
828 
829 		switch (lxml_xlate_element(cursor->name)) {
830 		case SC_STABILITY:
831 			if (lxml_get_pgroup_stability(pg, cursor) != 0)
832 				return (-1);
833 			break;
834 
835 		case SC_METHOD_CONTEXT:
836 			(void) lxml_get_method_context(pg, cursor);
837 			break;
838 
839 		case SC_PROPVAL:
840 			(void) lxml_get_propval(pg, cursor);
841 			break;
842 
843 		case SC_PROPERTY:
844 			(void) lxml_get_property(pg, cursor);
845 			break;
846 
847 		default:
848 			uu_die(gettext("illegal element \"%s\" on "
849 			    "execution method \"%s\"\n"), cursor->name,
850 			    pg->sc_pgroup_name);
851 			break;
852 		}
853 	}
854 
855 	delete = xmlGetProp(emeth, (xmlChar *)delete_attr);
856 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
857 	xmlFree(delete);
858 
859 	return (0);
860 }
861 
862 static int
863 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency)
864 {
865 	pgroup_t *pg;
866 	property_t *p;
867 	xmlNodePtr cursor;
868 	xmlChar *name;
869 	xmlChar *delete;
870 
871 	/*
872 	 * dependency attributes:
873 	 * name: string
874 	 * grouping: require_all | require_any | exclude_all | optional_all
875 	 * reset_on: string (error | restart | refresh | none)
876 	 * type:  service / path /host
877 	 */
878 
879 	name = xmlGetProp(dependency, (xmlChar *)name_attr);
880 	pg = internal_pgroup_find_or_create(entity, (char *)name,
881 	    (char *)SCF_GROUP_DEPENDENCY);
882 	xmlFree(name);
883 
884 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
885 	    dependency, type_attr) != 0)
886 		return (-1);
887 
888 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
889 	    SCF_TYPE_ASTRING, dependency, "restart_on") != 0)
890 		return (-1);
891 
892 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
893 	    dependency, "grouping") != 0)
894 		return (-1);
895 
896 	p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0);
897 	if (internal_attach_property(pg, p) != 0)
898 		return (-1);
899 
900 	for (cursor = dependency->xmlChildrenNode; cursor != NULL;
901 	    cursor = cursor->next) {
902 		xmlChar *value;
903 		value_t *v;
904 
905 		if (lxml_ignorable_block(cursor))
906 			continue;
907 
908 		switch (lxml_xlate_element(cursor->name)) {
909 		case SC_STABILITY:
910 			if (lxml_get_pgroup_stability(pg, cursor) != 0)
911 				return (-1);
912 			break;
913 
914 		case SC_SERVICE_FMRI:
915 			value = xmlGetProp(cursor, (xmlChar *)value_attr);
916 			if (value != NULL) {
917 				if (lxml_validate_string_value(SCF_TYPE_FMRI,
918 				    (char *)value) != 0)
919 					uu_die(gettext("illegal value \"%s\" "
920 					    "for %s (%s)\n"), (char *)value,
921 					    lxml_prop_types[SC_FMRI],
922 					    (scf_error()) ?
923 					    scf_strerror(scf_error()) :
924 					    gettext("Illegal format"));
925 				v = internal_value_new();
926 				v->sc_type = SCF_TYPE_FMRI;
927 				v->sc_u.sc_string = (char *)value;
928 				internal_attach_value(p, v);
929 			}
930 
931 			break;
932 
933 		case SC_PROPVAL:
934 			(void) lxml_get_propval(pg, cursor);
935 			break;
936 
937 		case SC_PROPERTY:
938 			(void) lxml_get_property(pg, cursor);
939 			break;
940 
941 		default:
942 			uu_die(gettext("illegal element \"%s\" on "
943 			    "dependency group \"%s\"\n"), cursor->name, name);
944 			break;
945 		}
946 	}
947 
948 	delete = xmlGetProp(dependency, (xmlChar *)delete_attr);
949 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
950 	xmlFree(delete);
951 
952 	return (0);
953 }
954 
955 /*
956  * Dependents are hairy.  They should cause a dependency pg to be created in
957  * another service, but we can't do that here; we'll have to wait until the
958  * import routines.  So for now we'll add the dependency group that should go
959  * in the other service to the entity's dependent list.
960  */
961 static int
962 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent)
963 {
964 	xmlChar *name, *or;
965 	xmlNodePtr sf;
966 	xmlChar *fmri, *delete;
967 	pgroup_t *pg;
968 	property_t *p;
969 	xmlNodePtr n;
970 	char *myfmri;
971 
972 	name = xmlGetProp(dependent, (xmlChar *)name_attr);
973 
974 	if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) {
975 		semerr(gettext("Property group and dependent of entity %s "
976 		    "have same name \"%s\".\n"), entity->sc_name, name);
977 		xmlFree(name);
978 		return (-1);
979 	}
980 
981 	or = xmlGetProp(dependent, (xmlChar *)override_attr);
982 
983 	pg = internal_pgroup_new();
984 	pg->sc_pgroup_name = (char *)name;
985 	pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY;
986 	pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0);
987 	xmlFree(or);
988 	if (internal_attach_dependent(entity, pg) != 0) {
989 		xmlFree(name);
990 		internal_pgroup_free(pg);
991 		return (-1);
992 	}
993 
994 	for (sf = dependent->children; sf != NULL; sf = sf->next)
995 		if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0)
996 			break;
997 	assert(sf != NULL);
998 	fmri = xmlGetProp(sf, (xmlChar *)value_attr);
999 	pg->sc_pgroup_fmri = (char *)fmri;
1000 
1001 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1002 	    SCF_TYPE_ASTRING, dependent, "restart_on") != 0)
1003 		return (-1);
1004 
1005 	if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1006 	    dependent, "grouping") != 0)
1007 		return (-1);
1008 
1009 	myfmri = safe_malloc(max_scf_fmri_len + 1);
1010 	if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) {
1011 		if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s",
1012 		    entity->sc_name) < 0)
1013 			bad_error("snprintf", errno);
1014 	} else {
1015 		assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT);
1016 		if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s",
1017 		    entity->sc_parent->sc_name, entity->sc_name) < 0)
1018 			bad_error("snprintf", errno);
1019 	}
1020 
1021 	p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1,
1022 	    myfmri);
1023 	if (internal_attach_property(pg, p) != 0)
1024 		return (-1);
1025 
1026 	/* Create a property to serve as a do-not-export flag. */
1027 	p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1,
1028 	    (uint64_t)1);
1029 	if (internal_attach_property(pg, p) != 0)
1030 		return (-1);
1031 
1032 	for (n = sf->next; n != NULL; n = n->next) {
1033 		if (lxml_ignorable_block(n))
1034 			continue;
1035 
1036 		switch (lxml_xlate_element(n->name)) {
1037 		case SC_STABILITY:
1038 			if (new_str_prop_from_attr(pg,
1039 			    SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n,
1040 			    value_attr) != 0)
1041 				return (-1);
1042 			break;
1043 
1044 		case SC_PROPVAL:
1045 			(void) lxml_get_propval(pg, n);
1046 			break;
1047 
1048 		case SC_PROPERTY:
1049 			(void) lxml_get_property(pg, n);
1050 			break;
1051 
1052 		default:
1053 			uu_die(gettext("unexpected element %s.\n"), n->name);
1054 		}
1055 	}
1056 
1057 	/* Go back and fill in defaults. */
1058 	if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) {
1059 		p = internal_property_create(SCF_PROPERTY_TYPE,
1060 		    SCF_TYPE_ASTRING, 1, "service");
1061 		if (internal_attach_property(pg, p) != 0)
1062 			return (-1);
1063 	}
1064 
1065 	delete = xmlGetProp(dependent, (xmlChar *)delete_attr);
1066 	pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1067 	xmlFree(delete);
1068 
1069 	pg = internal_pgroup_find_or_create(entity, "dependents",
1070 	    (char *)scf_group_framework);
1071 	p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri);
1072 	if (internal_attach_property(pg, p) != 0)
1073 		return (-1);
1074 
1075 	return (0);
1076 }
1077 
1078 static int
1079 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
1080 {
1081 	pgroup_t *pg;
1082 	property_t *p;
1083 	xmlChar *stabval;
1084 
1085 	if ((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) {
1086 		uu_warn(gettext("no stability value found\n"));
1087 		stabval = (xmlChar *)strdup("External");
1088 	}
1089 
1090 	pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1091 	    (char *)scf_group_framework);
1092 
1093 	p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY,
1094 	    SCF_TYPE_ASTRING, 1, stabval);
1095 
1096 	return (internal_attach_property(pg, p));
1097 }
1098 
1099 static int
1100 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
1101 {
1102 	pgroup_t *pg;
1103 	property_t *p;
1104 	xmlChar *restarter;
1105 	xmlNode *cursor;
1106 	int r;
1107 
1108 	/*
1109 	 * Go find child.  Child is a service_fmri element.  value attribute
1110 	 * contains restarter FMRI.
1111 	 */
1112 
1113 	pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1114 	    (char *)scf_group_framework);
1115 
1116 	/*
1117 	 * Walk its child elements, as appropriate.
1118 	 */
1119 	for (cursor = rstr->xmlChildrenNode; cursor != NULL;
1120 	    cursor = cursor->next) {
1121 		if (lxml_ignorable_block(cursor))
1122 			continue;
1123 
1124 		switch (lxml_xlate_element(cursor->name)) {
1125 		case SC_SERVICE_FMRI:
1126 			restarter = xmlGetProp(cursor, (xmlChar *)value_attr);
1127 			break;
1128 		default:
1129 			uu_die(gettext("illegal element \"%s\" on restarter "
1130 			    "element for \"%s\"\n"), cursor->name,
1131 			    entity->sc_name);
1132 			break;
1133 		}
1134 	}
1135 
1136 	p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1,
1137 	    restarter);
1138 
1139 	r = internal_attach_property(pg, p);
1140 	if (r != 0) {
1141 		internal_property_free(p);
1142 		return (-1);
1143 	}
1144 
1145 	return (0);
1146 }
1147 
1148 static void
1149 sanitize_locale(uchar_t *locale)
1150 {
1151 	for (; *locale != '\0'; locale++)
1152 		if (!isalnum(*locale) && *locale != '_')
1153 			*locale = '_';
1154 }
1155 
1156 static int
1157 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext)
1158 {
1159 	xmlNodePtr cursor;
1160 	xmlChar *val;
1161 	char *stripped, *cp;
1162 	property_t *p;
1163 	int r;
1164 
1165 	if ((val = xmlGetProp(loctext, (xmlChar *)"xml:lang")) == NULL)
1166 		if ((val = xmlGetProp(loctext, (xmlChar *)"lang")) == NULL)
1167 			val = (xmlChar *)"unknown";
1168 
1169 	sanitize_locale(val);
1170 
1171 	for (cursor = loctext->xmlChildrenNode; cursor != NULL;
1172 	    cursor = cursor->next) {
1173 		if (strcmp("text", (const char *)cursor->name) == 0) {
1174 			break;
1175 		} else if (strcmp("comment", (const char *)cursor->name) != 0) {
1176 			uu_die(gettext("illegal element \"%s\" on loctext "
1177 			    "element for \"%s\"\n"), cursor->name,
1178 			    service->sc_name);
1179 		}
1180 	}
1181 
1182 	if (cursor == NULL) {
1183 		uu_die(gettext("loctext element has no content for \"%s\"\n"),
1184 		    service->sc_name);
1185 	}
1186 
1187 	/*
1188 	 * Remove leading and trailing whitespace.
1189 	 */
1190 	if ((stripped = strdup((const char *)cursor->content)) == NULL)
1191 		uu_die(gettext("Out of memory\n"));
1192 
1193 	for (; isspace(*stripped); stripped++)
1194 		;
1195 	for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--)
1196 		;
1197 	*(cp + 1) = '\0';
1198 
1199 	p = internal_property_create((const char *)val, SCF_TYPE_USTRING, 1,
1200 	    stripped);
1201 
1202 	r = internal_attach_property(pg, p);
1203 	if (r != 0)
1204 		internal_property_free(p);
1205 
1206 	return (r);
1207 }
1208 
1209 static int
1210 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
1211 {
1212 	xmlNodePtr cursor;
1213 	pgroup_t *pg;
1214 
1215 	/*
1216 	 * Create the property group, if absent.
1217 	 */
1218 	pg = internal_pgroup_find_or_create(service,
1219 	    (char *)SCF_PG_TM_COMMON_NAME, (char *)SCF_GROUP_TEMPLATE);
1220 
1221 	/*
1222 	 * Iterate through one or more loctext elements.  The locale is the
1223 	 * property name; the contents are the ustring value for the property.
1224 	 */
1225 	for (cursor = common_name->xmlChildrenNode; cursor != NULL;
1226 	    cursor = cursor->next) {
1227 		if (lxml_ignorable_block(cursor))
1228 			continue;
1229 
1230 		switch (lxml_xlate_element(cursor->name)) {
1231 		case SC_LOCTEXT:
1232 			if (lxml_get_loctext(service, pg, cursor))
1233 				return (-1);
1234 			break;
1235 		default:
1236 			uu_die(gettext("illegal element \"%s\" on common_name "
1237 			    "element for \"%s\"\n"), cursor->name,
1238 			    service->sc_name);
1239 			break;
1240 		}
1241 	}
1242 
1243 	return (0);
1244 }
1245 
1246 static int
1247 lxml_get_tm_description(entity_t *service, xmlNodePtr description)
1248 {
1249 	xmlNodePtr cursor;
1250 	pgroup_t *pg;
1251 
1252 	/*
1253 	 * Create the property group, if absent.
1254 	 */
1255 	pg = internal_pgroup_find_or_create(service,
1256 	    (char *)SCF_PG_TM_DESCRIPTION, (char *)SCF_GROUP_TEMPLATE);
1257 
1258 	/*
1259 	 * Iterate through one or more loctext elements.  The locale is the
1260 	 * property name; the contents are the ustring value for the property.
1261 	 */
1262 	for (cursor = description->xmlChildrenNode; cursor != NULL;
1263 	    cursor = cursor->next) {
1264 		if (lxml_ignorable_block(cursor))
1265 			continue;
1266 
1267 		switch (lxml_xlate_element(cursor->name)) {
1268 		case SC_LOCTEXT:
1269 			if (lxml_get_loctext(service, pg, cursor))
1270 				return (-1);
1271 			break;
1272 		default:
1273 			uu_die(gettext("illegal element \"%s\" on description "
1274 			    "element for \"%s\"\n"), cursor->name,
1275 			    service->sc_name);
1276 			break;
1277 		}
1278 	}
1279 
1280 	return (0);
1281 }
1282 
1283 static char *
1284 lxml_label_to_groupname(const char *prefix, const char *in)
1285 {
1286 	char *out, *cp;
1287 	size_t len, piece_len;
1288 
1289 	out = uu_zalloc(2 * scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1);
1290 	if (out == NULL)
1291 		return (NULL);
1292 
1293 	(void) strcpy(out, prefix);
1294 	(void) strcat(out, in);
1295 
1296 	len = strlen(out);
1297 	if (len > max_scf_name_len) {
1298 		/* Use the first half and the second half. */
1299 		piece_len = (max_scf_name_len - 2) / 2;
1300 
1301 		(void) strncpy(out + piece_len, "..", 2);
1302 
1303 		(void) strcpy(out + piece_len + 2, out + (len - piece_len));
1304 
1305 		len = strlen(out);
1306 	}
1307 
1308 	/*
1309 	 * Translate non-property characters to '_'.
1310 	 */
1311 	for (cp = out; *cp != '\0'; ++cp) {
1312 		if (!(isalnum(*cp) || *cp == '_' || *cp == '-'))
1313 			*cp = '_';
1314 	}
1315 
1316 	*cp = '\0';
1317 
1318 	return (out);
1319 }
1320 
1321 static int
1322 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
1323 {
1324 	pgroup_t *pg;
1325 	char *pgname;
1326 	xmlChar *title;
1327 
1328 	/*
1329 	 * Fetch title attribute, convert to something sanitized, and create
1330 	 * property group.
1331 	 */
1332 	title = xmlGetProp(manpage, (xmlChar *)"title");
1333 	pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX,
1334 	    (const char *)title);
1335 
1336 	pg = internal_pgroup_find_or_create(service, pgname,
1337 	    (char *)SCF_GROUP_TEMPLATE);
1338 
1339 	/*
1340 	 * Each attribute is an astring property within the group.
1341 	 */
1342 	if (new_str_prop_from_attr(pg, "title", SCF_TYPE_ASTRING, manpage,
1343 	    "title") != 0 ||
1344 	    new_str_prop_from_attr(pg, "section", SCF_TYPE_ASTRING, manpage,
1345 	    "section") != 0 ||
1346 	    new_str_prop_from_attr(pg, "manpath", SCF_TYPE_ASTRING, manpage,
1347 	    "manpath") != 0)
1348 		return (-1);
1349 
1350 	return (0);
1351 }
1352 
1353 static int
1354 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
1355 {
1356 	pgroup_t *pg;
1357 	char *pgname;
1358 	xmlChar *name;
1359 
1360 	/*
1361 	 * Fetch name attribute, convert name to something sanitized, and create
1362 	 * property group.
1363 	 */
1364 	name = xmlGetProp(doc_link, (xmlChar *)"name");
1365 
1366 	pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX,
1367 	    (const char *)name);
1368 
1369 	pg = internal_pgroup_find_or_create(service, pgname,
1370 	    (char *)SCF_GROUP_TEMPLATE);
1371 
1372 	/*
1373 	 * Each attribute is an astring property within the group.
1374 	 */
1375 	if (new_str_prop_from_attr(pg, "name", SCF_TYPE_ASTRING, doc_link,
1376 	    "name") != 0 ||
1377 	    new_str_prop_from_attr(pg, "uri", SCF_TYPE_ASTRING, doc_link,
1378 	    "uri") != 0)
1379 		return (-1);
1380 
1381 	return (0);
1382 }
1383 
1384 static int
1385 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
1386 {
1387 	xmlNodePtr cursor;
1388 
1389 	for (cursor = documentation->xmlChildrenNode; cursor != NULL;
1390 	    cursor = cursor->next) {
1391 		if (lxml_ignorable_block(cursor))
1392 			continue;
1393 
1394 		switch (lxml_xlate_element(cursor->name)) {
1395 		case SC_MANPAGE:
1396 			(void) lxml_get_tm_manpage(service, cursor);
1397 			break;
1398 		case SC_DOC_LINK:
1399 			(void) lxml_get_tm_doclink(service, cursor);
1400 			break;
1401 		default:
1402 			uu_die(gettext("illegal element \"%s\" on template "
1403 			    "for service \"%s\"\n"),
1404 			    cursor->name, service->sc_name);
1405 		}
1406 	}
1407 
1408 	return (0);
1409 }
1410 
1411 static int
1412 lxml_get_template(entity_t *service, xmlNodePtr templ)
1413 {
1414 	xmlNodePtr cursor;
1415 
1416 	for (cursor = templ->xmlChildrenNode; cursor != NULL;
1417 	    cursor = cursor->next) {
1418 		if (lxml_ignorable_block(cursor))
1419 			continue;
1420 
1421 		switch (lxml_xlate_element(cursor->name)) {
1422 		case SC_COMMON_NAME:
1423 			(void) lxml_get_tm_common_name(service, cursor);
1424 			break;
1425 		case SC_DESCRIPTION:
1426 			(void) lxml_get_tm_description(service, cursor);
1427 			break;
1428 		case SC_DOCUMENTATION:
1429 			(void) lxml_get_tm_documentation(service, cursor);
1430 			break;
1431 		default:
1432 			uu_die(gettext("illegal element \"%s\" on template "
1433 			    "for service \"%s\"\n"),
1434 			    cursor->name, service->sc_name);
1435 		}
1436 	}
1437 
1438 	return (0);
1439 }
1440 
1441 static int
1442 lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
1443 {
1444 	entity_t *i;
1445 	xmlChar *enabled;
1446 	pgroup_t *pg;
1447 	property_t *p;
1448 	char *package;
1449 	uint64_t enabled_val = 0;
1450 
1451 	i = internal_instance_new("default");
1452 
1453 	if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) {
1454 		enabled_val = (strcmp(true, (const char *)enabled) == 0) ?
1455 		    1 : 0;
1456 		xmlFree(enabled);
1457 	}
1458 
1459 	/*
1460 	 * New general property group with enabled boolean property set.
1461 	 */
1462 
1463 	pg = internal_pgroup_new();
1464 	(void) internal_attach_pgroup(i, pg);
1465 
1466 	pg->sc_pgroup_name = (char *)scf_pg_general;
1467 	pg->sc_pgroup_type = (char *)scf_group_framework;
1468 	pg->sc_pgroup_flags = 0;
1469 
1470 	p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
1471 	    enabled_val);
1472 
1473 	(void) internal_attach_property(pg, p);
1474 
1475 	/*
1476 	 * Add general/package property if PKGINST is set.
1477 	 */
1478 	if ((package = getenv("PKGINST")) != NULL) {
1479 		p = internal_property_create(SCF_PROPERTY_PACKAGE,
1480 		    SCF_TYPE_ASTRING, 1, package);
1481 
1482 		(void) internal_attach_property(pg, p);
1483 	}
1484 
1485 	return (internal_attach_entity(service, i));
1486 }
1487 
1488 /*
1489  * Translate an instance element into an internal property tree, added to
1490  * service.  If apply is true, forbid subelements and set the enabled property
1491  * to override.
1492  */
1493 static int
1494 lxml_get_instance(entity_t *service, xmlNodePtr inst, int apply)
1495 {
1496 	entity_t *i;
1497 	pgroup_t *pg;
1498 	property_t *p;
1499 	xmlNodePtr cursor;
1500 	xmlChar *enabled;
1501 	int r;
1502 
1503 	/*
1504 	 * Fetch its attributes, as appropriate.
1505 	 */
1506 	i = internal_instance_new((char *)xmlGetProp(inst,
1507 	    (xmlChar *)name_attr));
1508 
1509 	/*
1510 	 * Note that this must be done before walking the children so that
1511 	 * sc_fmri is set in case we enter lxml_get_dependent().
1512 	 */
1513 	r = internal_attach_entity(service, i);
1514 	if (r != 0)
1515 		return (r);
1516 
1517 	enabled = xmlGetProp(inst, (xmlChar *)enabled_attr);
1518 
1519 	/*
1520 	 * New general property group with enabled boolean property set.
1521 	 */
1522 	pg = internal_pgroup_new();
1523 	(void) internal_attach_pgroup(i, pg);
1524 
1525 	pg->sc_pgroup_name = (char *)scf_pg_general;
1526 	pg->sc_pgroup_type = (char *)scf_group_framework;
1527 	pg->sc_pgroup_flags = 0;
1528 
1529 	p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
1530 	    (uint64_t)(strcmp(true, (const char *)enabled) == 0 ? 1 : 0));
1531 
1532 	p->sc_property_override = apply;
1533 
1534 	(void) internal_attach_property(pg, p);
1535 
1536 	xmlFree(enabled);
1537 
1538 	/*
1539 	 * Walk its child elements, as appropriate.
1540 	 */
1541 	for (cursor = inst->xmlChildrenNode; cursor != NULL;
1542 	    cursor = cursor->next) {
1543 		if (lxml_ignorable_block(cursor))
1544 			continue;
1545 
1546 		if (apply) {
1547 			semerr(gettext("Instance \"%s\" may not contain "
1548 			    "elements in profiles.\n"), i->sc_name,
1549 			    cursor->name);
1550 			return (-1);
1551 		}
1552 
1553 		switch (lxml_xlate_element(cursor->name)) {
1554 		case SC_RESTARTER:
1555 			(void) lxml_get_restarter(i, cursor);
1556 			break;
1557 		case SC_DEPENDENCY:
1558 			(void) lxml_get_dependency(i, cursor);
1559 			break;
1560 		case SC_DEPENDENT:
1561 			(void) lxml_get_dependent(i, cursor);
1562 			break;
1563 		case SC_METHOD_CONTEXT:
1564 			(void) lxml_get_entity_method_context(i, cursor);
1565 			break;
1566 		case SC_EXEC_METHOD:
1567 			(void) lxml_get_exec_method(i, cursor);
1568 			break;
1569 		case SC_PROPERTY_GROUP:
1570 			(void) lxml_get_pgroup(i, cursor);
1571 			break;
1572 		case SC_TEMPLATE:
1573 			(void) lxml_get_template(i, cursor);
1574 			break;
1575 		default:
1576 			uu_die(gettext(
1577 			    "illegal element \"%s\" on instance \"%s\"\n"),
1578 			    cursor->name, i->sc_name);
1579 			break;
1580 		}
1581 	}
1582 
1583 	return (0);
1584 }
1585 
1586 /* ARGSUSED1 */
1587 static int
1588 lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
1589 {
1590 	pgroup_t *pg;
1591 	property_t *p;
1592 	int r;
1593 
1594 	pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
1595 	    (char *)scf_group_framework);
1596 
1597 	p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE,
1598 	    SCF_TYPE_BOOLEAN, 1, (uint64_t)1);
1599 
1600 	r = internal_attach_property(pg, p);
1601 	if (r != 0) {
1602 		internal_property_free(p);
1603 		return (-1);
1604 	}
1605 
1606 	return (0);
1607 }
1608 
1609 /*
1610  * Translate a service element into an internal instance/property tree, added
1611  * to bundle.  If apply is true, allow only instance subelements.
1612  */
1613 static int
1614 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, int apply)
1615 {
1616 	entity_t *s;
1617 	xmlNodePtr cursor;
1618 	xmlChar *type;
1619 	xmlChar *version;
1620 	int e;
1621 
1622 	/*
1623 	 * Fetch attributes, as appropriate.
1624 	 */
1625 	s = internal_service_new((char *)xmlGetProp(svc,
1626 	    (xmlChar *)name_attr));
1627 
1628 	version = xmlGetProp(svc, (xmlChar *)"version");
1629 	s->sc_u.sc_service.sc_service_version = atol((const char *)version);
1630 	xmlFree(version);
1631 
1632 	type = xmlGetProp(svc, (xmlChar *)type_attr);
1633 	s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type);
1634 	xmlFree(type);
1635 
1636 	/*
1637 	 * Walk its child elements, as appropriate.
1638 	 */
1639 	for (cursor = svc->xmlChildrenNode; cursor != NULL;
1640 	    cursor = cursor->next) {
1641 		if (lxml_ignorable_block(cursor))
1642 			continue;
1643 
1644 		e = lxml_xlate_element(cursor->name);
1645 
1646 		if (apply && e != SC_INSTANCE) {
1647 			semerr(gettext("Service \"%s\" may not contain the "
1648 			    "non-instance element \"%s\" in a profile.\n"),
1649 			    s->sc_name, cursor->name);
1650 
1651 			return (-1);
1652 		}
1653 
1654 		switch (e) {
1655 		case SC_INSTANCE:
1656 			(void) lxml_get_instance(s, cursor, apply);
1657 			break;
1658 		case SC_TEMPLATE:
1659 			(void) lxml_get_template(s, cursor);
1660 			break;
1661 		case SC_STABILITY:
1662 			(void) lxml_get_entity_stability(s, cursor);
1663 			break;
1664 		case SC_DEPENDENCY:
1665 			(void) lxml_get_dependency(s, cursor);
1666 			break;
1667 		case SC_DEPENDENT:
1668 			(void) lxml_get_dependent(s, cursor);
1669 			break;
1670 		case SC_RESTARTER:
1671 			(void) lxml_get_restarter(s, cursor);
1672 			break;
1673 		case SC_EXEC_METHOD:
1674 			(void) lxml_get_exec_method(s, cursor);
1675 			break;
1676 		case SC_METHOD_CONTEXT:
1677 			(void) lxml_get_entity_method_context(s, cursor);
1678 			break;
1679 		case SC_PROPERTY_GROUP:
1680 			(void) lxml_get_pgroup(s, cursor);
1681 			break;
1682 		case SC_INSTANCE_CREATE_DEFAULT:
1683 			(void) lxml_get_default_instance(s, cursor);
1684 			break;
1685 		case SC_INSTANCE_SINGLE:
1686 			(void) lxml_get_single_instance(s, cursor);
1687 			break;
1688 		default:
1689 			uu_die(gettext(
1690 			    "illegal element \"%s\" on service \"%s\"\n"),
1691 			    cursor->name, s->sc_name);
1692 			break;
1693 		}
1694 	}
1695 
1696 	return (internal_attach_service(bundle, s));
1697 }
1698 
1699 #ifdef DEBUG
1700 void
1701 lxml_dump(int g, xmlNodePtr p)
1702 {
1703 	if (p && p->name) {
1704 		printf("%d %s\n", g, p->name);
1705 
1706 		for (p = p->xmlChildrenNode; p != NULL; p = p->next)
1707 			lxml_dump(g + 1, p);
1708 	}
1709 }
1710 #endif /* DEBUG */
1711 
1712 static int
1713 lxml_is_known_dtd(const xmlChar *dtdname)
1714 {
1715 	if (dtdname == NULL ||
1716 	    strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0)
1717 		return (0);
1718 
1719 	return (1);
1720 }
1721 
1722 static int
1723 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
1724     xmlNodePtr subbundle, int apply)
1725 {
1726 	xmlNodePtr cursor;
1727 	xmlChar *type;
1728 	int e;
1729 
1730 	/*
1731 	 * 1.  Get bundle attributes.
1732 	 */
1733 	type = xmlGetProp(subbundle, (xmlChar *)"type");
1734 	bundle->sc_bundle_type = lxml_xlate_bundle_type(type);
1735 	if (bundle->sc_bundle_type != bundle_type &&
1736 	    bundle_type != SVCCFG_UNKNOWN_BUNDLE) {
1737 		semerr(gettext("included bundle of different type.\n"));
1738 		return (-1);
1739 	}
1740 
1741 	xmlFree(type);
1742 
1743 	if (!apply) {
1744 		if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
1745 			semerr(gettext("document is not a manifest.\n"));
1746 			return (-1);
1747 		}
1748 	} else {
1749 		if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
1750 			semerr(gettext("document is not a profile.\n"));
1751 			return (-1);
1752 		}
1753 	}
1754 
1755 	if ((bundle->sc_bundle_name = xmlGetProp(subbundle,
1756 	    (xmlChar *)"name")) == NULL) {
1757 		semerr(gettext("service bundle lacks name attribute\n"));
1758 		return (-1);
1759 	}
1760 
1761 	/*
1762 	 * 2.  Get services, descend into each one and build state.
1763 	 */
1764 	for (cursor = subbundle->xmlChildrenNode; cursor != NULL;
1765 	    cursor = cursor->next) {
1766 		if (lxml_ignorable_block(cursor))
1767 			continue;
1768 
1769 		e = lxml_xlate_element(cursor->name);
1770 
1771 		switch (e) {
1772 		case SC_XI_INCLUDE:
1773 			continue;
1774 
1775 		case SC_SERVICE_BUNDLE:
1776 			if (lxml_get_bundle(bundle, bundle_type, cursor, apply))
1777 				return (-1);
1778 			break;
1779 		case SC_SERVICE:
1780 			(void) lxml_get_service(bundle, cursor, apply);
1781 			break;
1782 		}
1783 	}
1784 
1785 	return (0);
1786 }
1787 
1788 /*
1789  * Load an XML tree from filename and translate it into an internal service
1790  * tree bundle.  If apply is false, require that the the bundle be of type
1791  * manifest, or type profile otherwise.
1792  */
1793 int
1794 lxml_get_bundle_file(bundle_t *bundle, const char *filename, int apply)
1795 {
1796 	xmlDocPtr document;
1797 	xmlNodePtr cursor;
1798 	xmlDtdPtr dtd = NULL;
1799 	xmlValidCtxtPtr vcp;
1800 	boolean_t do_validate;
1801 	char *dtdpath = NULL;
1802 	int r;
1803 
1804 	/*
1805 	 * Verify we can read the file before we try to parse it.
1806 	 */
1807 	if (access(filename, R_OK | F_OK) == -1) {
1808 		semerr(gettext("unable to open file: %s\n"), strerror(errno));
1809 		return (-1);
1810 	}
1811 
1812 	/*
1813 	 * Until libxml2 addresses DTD-based validation with XInclude, we don't
1814 	 * validate service profiles (i.e. the apply path).
1815 	 */
1816 	do_validate = (apply == 0) && (getenv("SVCCFG_NOVALIDATE") == NULL);
1817 	if (do_validate)
1818 		dtdpath = getenv("SVCCFG_DTD");
1819 
1820 	if (dtdpath != NULL)
1821 		xmlLoadExtDtdDefaultValue = 0;
1822 
1823 	if ((document = xmlReadFile(filename, NULL, 0)) == NULL) {
1824 		semerr(gettext("couldn't parse document\n"));
1825 		return (-1);
1826 	}
1827 
1828 	/*
1829 	 * Verify that this is a document type we understand.
1830 	 */
1831 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
1832 		semerr(gettext("document has no DTD\n"));
1833 		return (-1);
1834 	}
1835 
1836 	if (!lxml_is_known_dtd(dtd->SystemID)) {
1837 		semerr(gettext("document DTD unknown; not service bundle?\n"));
1838 		return (-1);
1839 	}
1840 
1841 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
1842 		semerr(gettext("document is empty\n"));
1843 		xmlFreeDoc(document);
1844 		return (-1);
1845 	}
1846 
1847 	if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) {
1848 		semerr(gettext("document is not a service bundle\n"));
1849 		xmlFreeDoc(document);
1850 		return (-1);
1851 	}
1852 
1853 
1854 	if (dtdpath != NULL) {
1855 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1856 		if (dtd == NULL) {
1857 			semerr(gettext("Could not parse DTD \"%s\".\n"),
1858 			    dtdpath);
1859 			return (-1);
1860 		}
1861 
1862 		if (document->extSubset != NULL)
1863 			xmlFreeDtd(document->extSubset);
1864 
1865 		document->extSubset = dtd;
1866 	}
1867 
1868 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
1869 		semerr(gettext("couldn't handle XInclude statements "
1870 		    "in document\n"));
1871 		return (-1);
1872 	}
1873 
1874 	if (do_validate) {
1875 		vcp = xmlNewValidCtxt();
1876 		if (vcp == NULL)
1877 			uu_die(gettext("could not allocate memory"));
1878 		vcp->warning = xmlParserValidityWarning;
1879 		vcp->error = xmlParserValidityError;
1880 
1881 		r = xmlValidateDocument(vcp, document);
1882 
1883 		xmlFreeValidCtxt(vcp);
1884 
1885 		if (r == 0) {
1886 			semerr(gettext("Document is not valid.\n"));
1887 			xmlFreeDoc(document);
1888 			return (-1);
1889 		}
1890 	}
1891 
1892 
1893 #ifdef DEBUG
1894 	lxml_dump(0, cursor);
1895 #endif /* DEBUG */
1896 
1897 	r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, apply);
1898 
1899 	xmlFreeDoc(document);
1900 
1901 	return (r);
1902 }
1903 
1904 int
1905 lxml_inventory(const char *filename)
1906 {
1907 	bundle_t *b;
1908 	uu_list_walk_t *svcs, *insts;
1909 	entity_t *svc, *inst;
1910 
1911 	b = internal_bundle_new();
1912 
1913 	if (lxml_get_bundle_file(b, filename, 0) != 0) {
1914 		internal_bundle_free(b);
1915 		return (-1);
1916 	}
1917 
1918 	svcs = uu_list_walk_start(b->sc_bundle_services, 0);
1919 	if (svcs == NULL)
1920 		uu_die(gettext("Couldn't walk services"));
1921 
1922 	while ((svc = uu_list_walk_next(svcs)) != NULL) {
1923 		uu_list_t *inst_list;
1924 
1925 		inst_list = svc->sc_u.sc_service.sc_service_instances;
1926 		insts = uu_list_walk_start(inst_list, 0);
1927 		if (insts == NULL)
1928 			uu_die(gettext("Couldn't walk instances"));
1929 
1930 		while ((inst = uu_list_walk_next(insts)) != NULL)
1931 			(void) printf("svc:/%s:%s\n", svc->sc_name,
1932 			    inst->sc_name);
1933 
1934 		uu_list_walk_end(insts);
1935 	}
1936 
1937 	uu_list_walk_end(svcs);
1938 
1939 	svcs = uu_list_walk_start(b->sc_bundle_services, 0);
1940 	while ((svc = uu_list_walk_next(svcs)) != NULL) {
1941 		(void) fputs("svc:/", stdout);
1942 		(void) puts(svc->sc_name);
1943 	}
1944 	uu_list_walk_end(svcs);
1945 
1946 	internal_bundle_free(b);
1947 
1948 	return (0);
1949 }
1950