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