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