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