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