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.
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.
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]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
30 * XML document manipulation routines
32 * These routines provide translation to and from the internal representation to
33 * XML. Directionally-oriented verbs are with respect to the external source,
34 * so lxml_get_service() fetches a service from the XML file into the
35 * internal representation.
38 #include <libxml/parser.h>
39 #include <libxml/xinclude.h>
46 #include <libscf_priv.h>
48 #include <sasl/saslutil.h>
53 #include <sys/types.h>
57 #include <sys/param.h>
58 #include "manifest_hash.h"
61 #include "notify_params.h"
64 * snprintf(3C) format strings for constructing property names that include
65 * the locale designation. Use %s to indicate where the locale should go.
67 * The VALUE_* symbols are an exception. The firs %s will be replaced with
68 * "value_". The second %s will be replaced by the name of the value and
69 * %%s will be replaced by the locale designation. These formats are
70 * processed twice by snprintf(3C). The first time captures the value name
71 * and the second time captures the locale.
73 #define LOCALE_ONLY_FMT ("%s")
74 #define COMMON_NAME_FMT ("common_name_%s")
75 #define DESCRIPTION_FMT ("description_%s")
76 #define UNITS_FMT ("units_%s")
77 #define VALUE_COMMON_NAME_FMT ("%s%s_common_name_%%s")
78 #define VALUE_DESCRIPTION_FMT ("%s%s_description_%%s")
81 const char * const delete_attr
= "delete";
82 const char * const enabled_attr
= "enabled";
83 const char * const lang_attr
= "lang";
84 const char * const manpath_attr
= "manpath";
85 const char * const max_attr
= "max";
86 const char * const min_attr
= "min";
87 const char * const name_attr
= "name";
88 const char * const override_attr
= "override";
89 const char * const required_attr
= "required";
90 const char * const section_attr
= "section";
91 const char * const set_attr
= "set";
92 const char * const target_attr
= "target";
93 const char * const timeout_seconds_attr
= "timeout_seconds";
94 const char * const title_attr
= "title";
95 const char * const type_attr
= "type";
96 const char * const uri_attr
= "uri";
97 const char * const value_attr
= "value";
98 const char * const version_attr
= "version";
99 const char * const xml_lang_attr
= "xml:lang";
100 const char * const active_attr
= "active";
102 /* Attribute values */
103 const char * const all_value
= "all";
105 const char * const true = "true";
106 const char * const false = "false";
109 * The following list must be kept in the same order as that of
112 static const char *lxml_elements
[] = {
113 "astring_list", /* SC_ASTRING */
114 "boolean_list", /* SC_BOOLEAN */
115 "cardinality", /* SC_CARDINALITY */
116 "choices", /* SC_CHOICES */
117 "common_name", /* SC_COMMON_NAME */
118 "constraints", /* SC_CONSTRAINTS */
119 "count_list", /* SC_COUNT */
120 "create_default_instance", /* SC_INSTANCE_CREATE_DEFAULT */
121 "dependency", /* SC_DEPENDENCY */
122 "dependent", /* SC_DEPENDENT */
123 "description", /* SC_DESCRIPTION */
124 "doc_link", /* SC_DOC_LINK */
125 "documentation", /* SC_DOCUMENTATION */
126 "enabled", /* SC_ENABLED */
127 "event", /* SC_EVENT */
128 "exec_method", /* SC_EXEC_METHOD */
129 "fmri_list", /* SC_FMRI */
130 "host_list", /* SC_HOST */
131 "hostname_list", /* SC_HOSTNAME */
132 "include_values", /* SC_INCLUDE_VALUES */
133 "instance", /* SC_INSTANCE */
134 "integer_list", /* SC_INTEGER */
135 "internal_separators", /* SC_INTERNAL_SEPARATORS */
136 "loctext", /* SC_LOCTEXT */
137 "manpage", /* SC_MANPAGE */
138 "method_context", /* SC_METHOD_CONTEXT */
139 "method_credential", /* SC_METHOD_CREDENTIAL */
140 "method_profile", /* SC_METHOD_PROFILE */
141 "method_environment", /* SC_METHOD_ENVIRONMENT */
142 "envvar", /* SC_METHOD_ENVVAR */
143 "net_address_list", /* SC_NET_ADDR */
144 "net_address_v4_list", /* SC_NET_ADDR_V4 */
145 "net_address_v6_list", /* SC_NET_ADDR_V6 */
146 "notification_parameters", /* SC_NOTIFICATION_PARAMETERS */
147 "opaque_list", /* SC_OPAQUE */
148 "parameter", /* SC_PARAMETER */
149 "paramval", /* SC_PARAMVAL */
150 "pg_pattern", /* SC_PG_PATTERN */
151 "prop_pattern", /* SC_PROP_PATTERN */
152 "property", /* SC_PROPERTY */
153 "property_group", /* SC_PROPERTY_GROUP */
154 "propval", /* SC_PROPVAL */
155 "range", /* SC_RANGE */
156 "restarter", /* SC_RESTARTER */
157 "service", /* SC_SERVICE */
158 "service_bundle", /* SC_SERVICE_BUNDLE */
159 "service_fmri", /* SC_SERVICE_FMRI */
160 "single_instance", /* SC_INSTANCE_SINGLE */
161 "stability", /* SC_STABILITY */
162 "template", /* SC_TEMPLATE */
163 "time_list", /* SC_TIME */
164 "type", /* SC_TYPE */
165 "units", /* SC_UNITS */
166 "uri_list", /* SC_URI */
167 "ustring_list", /* SC_USTRING */
168 "value", /* SC_VALUE */
169 "value_node", /* SC_VALUE_NODE */
170 "values", /* SC_VALUES */
171 "visibility", /* SC_VISIBILITY */
172 "xi:fallback", /* SC_XI_FALLBACK */
173 "xi:include" /* SC_XI_INCLUDE */
177 * The following list must be kept in the same order as that of
180 static const char *lxml_prop_types
[] = {
181 "astring", /* SC_ASTRING */
182 "boolean", /* SC_BOOLEAN */
183 "", /* SC_CARDINALITY */
185 "", /* SC_COMMON_NAME */
186 "", /* SC_CONSTRAINTS */
187 "count", /* SC_COUNT */
188 "", /* SC_INSTANCE_CREATE_DEFAULT */
189 "", /* SC_DEPENDENCY */
190 "", /* SC_DEPENDENT */
191 "", /* SC_DESCRIPTION */
192 "", /* SC_DOC_LINK */
193 "", /* SC_DOCUMENTATION */
196 "", /* SC_EXEC_METHOD */
197 "fmri", /* SC_FMRI */
198 "host", /* SC_HOST */
199 "hostname", /* SC_HOSTNAME */
200 "", /* SC_INCLUDE_VALUES */
201 "", /* SC_INSTANCE */
202 "integer", /* SC_INTEGER */
203 "", /* SC_INTERNAL_SEPARATORS */
206 "", /* SC_METHOD_CONTEXT */
207 "", /* SC_METHOD_CREDENTIAL */
208 "", /* SC_METHOD_PROFILE */
209 "", /* SC_METHOD_ENVIRONMENT */
210 "", /* SC_METHOD_ENVVAR */
211 "net_address", /* SC_NET_ADDR */
212 "net_address_v4", /* SC_NET_ADDR_V4 */
213 "net_address_v6", /* SC_NET_ADDR_V6 */
214 "", /* SC_NOTIFICATION_PARAMETERS */
215 "opaque", /* SC_OPAQUE */
216 "", /* SC_PARAMETER */
217 "", /* SC_PARAMVAL */
218 "", /* SC_PG_PATTERN */
219 "", /* SC_PROP_PATTERN */
220 "", /* SC_PROPERTY */
221 "", /* SC_PROPERTY_GROUP */
224 "", /* SC_RESTARTER */
226 "", /* SC_SERVICE_BUNDLE */
227 "", /* SC_SERVICE_FMRI */
228 "", /* SC_INSTANCE_SINGLE */
229 "", /* SC_STABILITY */
230 "", /* SC_TEMPLATE */
231 "time", /* SC_TIME */
235 "ustring", /* SC_USTRING */
237 "", /* SC_VALUE_NODE */
239 "", /* SC_VISIBILITY */
240 "", /* SC_XI_FALLBACK */
241 "" /* SC_XI_INCLUDE */
247 if (getenv("SVCCFG_NOVALIDATE") == NULL
) {
249 * DTD validation, with line numbers.
251 (void) xmlLineNumbersDefault(1);
252 xmlLoadExtDtdDefaultValue
|= XML_DETECT_IDS
;
253 xmlLoadExtDtdDefaultValue
|= XML_COMPLETE_ATTRS
;
260 lxml_xlate_bundle_type(xmlChar
*type
)
262 if (xmlStrcmp(type
, (const xmlChar
*)"manifest") == 0)
263 return (SVCCFG_MANIFEST
);
265 if (xmlStrcmp(type
, (const xmlChar
*)"profile") == 0)
266 return (SVCCFG_PROFILE
);
268 if (xmlStrcmp(type
, (const xmlChar
*)"archive") == 0)
269 return (SVCCFG_ARCHIVE
);
271 return (SVCCFG_UNKNOWN_BUNDLE
);
274 static service_type_t
275 lxml_xlate_service_type(xmlChar
*type
)
277 if (xmlStrcmp(type
, (const xmlChar
*)"service") == 0)
278 return (SVCCFG_SERVICE
);
280 if (xmlStrcmp(type
, (const xmlChar
*)"restarter") == 0)
281 return (SVCCFG_RESTARTER
);
283 if (xmlStrcmp(type
, (const xmlChar
*)"milestone") == 0)
284 return (SVCCFG_MILESTONE
);
286 return (SVCCFG_UNKNOWN_SERVICE
);
290 lxml_xlate_element(const xmlChar
*tag
)
294 for (i
= 0; i
< sizeof (lxml_elements
) / sizeof (char *); i
++)
295 if (xmlStrcmp(tag
, (const xmlChar
*)lxml_elements
[i
]) == 0)
296 return ((element_t
)i
);
298 return ((element_t
)-1);
302 lxml_xlate_boolean(const xmlChar
*value
)
304 if (xmlStrcmp(value
, (const xmlChar
*)true) == 0)
307 if (xmlStrcmp(value
, (const xmlChar
*)false) == 0)
310 uu_die(gettext("illegal boolean value \"%s\"\n"), value
);
316 lxml_element_to_type(element_t type
)
319 case SC_ASTRING
: return (SCF_TYPE_ASTRING
);
320 case SC_BOOLEAN
: return (SCF_TYPE_BOOLEAN
);
321 case SC_COUNT
: return (SCF_TYPE_COUNT
);
322 case SC_FMRI
: return (SCF_TYPE_FMRI
);
323 case SC_HOST
: return (SCF_TYPE_HOST
);
324 case SC_HOSTNAME
: return (SCF_TYPE_HOSTNAME
);
325 case SC_INTEGER
: return (SCF_TYPE_INTEGER
);
326 case SC_NET_ADDR
: return (SCF_TYPE_NET_ADDR
);
327 case SC_NET_ADDR_V4
: return (SCF_TYPE_NET_ADDR_V4
);
328 case SC_NET_ADDR_V6
: return (SCF_TYPE_NET_ADDR_V6
);
329 case SC_OPAQUE
: return (SCF_TYPE_OPAQUE
);
330 case SC_TIME
: return (SCF_TYPE_TIME
);
331 case SC_URI
: return (SCF_TYPE_URI
);
332 case SC_USTRING
: return (SCF_TYPE_USTRING
);
335 uu_die(gettext("unknown value type (%d)\n"), type
);
342 lxml_type_to_element(scf_type_t type
)
345 case SCF_TYPE_ASTRING
: return (SC_ASTRING
);
346 case SCF_TYPE_BOOLEAN
: return (SC_BOOLEAN
);
347 case SCF_TYPE_COUNT
: return (SC_COUNT
);
348 case SCF_TYPE_FMRI
: return (SC_FMRI
);
349 case SCF_TYPE_HOST
: return (SC_HOST
);
350 case SCF_TYPE_HOSTNAME
: return (SC_HOSTNAME
);
351 case SCF_TYPE_INTEGER
: return (SC_INTEGER
);
352 case SCF_TYPE_NET_ADDR
: return (SC_NET_ADDR
);
353 case SCF_TYPE_NET_ADDR_V4
: return (SC_NET_ADDR_V4
);
354 case SCF_TYPE_NET_ADDR_V6
: return (SC_NET_ADDR_V6
);
355 case SCF_TYPE_OPAQUE
: return (SC_OPAQUE
);
356 case SCF_TYPE_TIME
: return (SC_TIME
);
357 case SCF_TYPE_URI
: return (SC_URI
);
358 case SCF_TYPE_USTRING
: return (SC_USTRING
);
361 uu_die(gettext("unknown value type (%d)\n"), type
);
368 * Create a SCF_TYPE_BOOLEAN property name pname and attach it to the
369 * property group at pgrp. The value of the property will be set from the
370 * attribute named attr. attr must have a value of 0, 1, true or false.
372 * Zero is returned on success. An error is indicated by -1. It indicates
373 * that either the attribute had an invalid value or that we could not
374 * attach the property to pgrp. The attribute should not have an invalid
375 * value if the DTD is correctly written.
378 new_bool_prop_from_attr(pgroup_t
*pgrp
, const char *pname
, xmlNodePtr n
,
386 val
= xmlGetProp(n
, (xmlChar
*)attr
);
390 if ((xmlStrcmp(val
, (xmlChar
*)"0") == 0) ||
391 (xmlStrcmp(val
, (xmlChar
*)"false") == 0)) {
393 } else if ((xmlStrcmp(val
, (xmlChar
*)"1") == 0) ||
394 (xmlStrcmp(val
, (xmlChar
*)"true") == 0)) {
401 p
= internal_property_create(pname
, SCF_TYPE_BOOLEAN
, 1, bool);
402 r
= internal_attach_property(pgrp
, p
);
405 internal_property_free(p
);
411 new_str_prop_from_attr(pgroup_t
*pgrp
, const char *pname
, scf_type_t ty
,
412 xmlNodePtr n
, const char *attr
)
418 val
= xmlGetProp(n
, (xmlChar
*)attr
);
420 p
= internal_property_create(pname
, ty
, 1, val
);
421 r
= internal_attach_property(pgrp
, p
);
424 internal_property_free(p
);
430 new_opt_str_prop_from_attr(pgroup_t
*pgrp
, const char *pname
, scf_type_t ty
,
431 xmlNodePtr n
, const char *attr
, const char *dflt
)
437 val
= xmlGetProp(n
, (xmlChar
*)attr
);
441 * A missing attribute is considered to be a
442 * success in this function, because many of the
443 * attributes are optional. Missing non-optional
444 * attributes will be detected later when template
445 * validation is done.
449 val
= (xmlChar
*)dflt
;
453 p
= internal_property_create(pname
, ty
, 1, val
);
454 r
= internal_attach_property(pgrp
, p
);
457 internal_property_free(p
);
463 lxml_ignorable_block(xmlNodePtr n
)
465 return ((xmlStrcmp(n
->name
, (xmlChar
*)"text") == 0 ||
466 xmlStrcmp(n
->name
, (xmlChar
*)"comment") == 0) ? 1 : 0);
470 lxml_validate_element(xmlNodePtr n
)
475 uu_die(gettext("Could not validate element\n"));
477 if (n
->doc
->extSubset
== NULL
) {
479 dtd
= xmlParseDTD(NULL
, n
->doc
->intSubset
->SystemID
);
482 uu_die(gettext("Could not parse DTD \"%s\".\n"),
483 n
->doc
->intSubset
->SystemID
);
486 n
->doc
->extSubset
= dtd
;
489 vcp
= xmlNewValidCtxt();
491 uu_die(gettext("could not allocate memory"));
493 vcp
->warning
= xmlParserValidityWarning
;
494 vcp
->error
= xmlParserValidityError
;
496 if (xmlValidateElement(vcp
, n
->doc
, n
) == 0)
497 uu_die(gettext("Document is not valid.\n"));
499 xmlFreeValidCtxt(vcp
);
503 lxml_validate_string_value(scf_type_t type
, const char *v
)
505 static scf_value_t
*scf_value
= NULL
;
506 static scf_handle_t
*scf_hndl
= NULL
;
508 if (scf_hndl
== NULL
&& (scf_hndl
= scf_handle_create(SCF_VERSION
)) ==
512 if (scf_value
== NULL
&& (scf_value
= scf_value_create(scf_hndl
)) ==
516 return (scf_value_set_from_string(scf_value
, type
, v
));
520 lxml_free_str(value_t
*val
)
522 free(val
->sc_u
.sc_string
);
526 * Take a value_t structure and a type and value. Based on the type
527 * ensure that the value is of that type. If so store the value in
528 * the correct location of the value_t structure.
530 * If the value is NULL, the value_t structure will have been created
531 * and the value would have ultimately been stored as a string value
532 * but at the time the type was unknown. Now the type should be known
533 * so take the type and value from value_t and validate and store
534 * the value correctly if the value is of the stated type.
537 lxml_store_value(value_t
*v
, element_t type
, const xmlChar
*value
)
541 scf_type_t scf_type
= SCF_TYPE_INVALID
;
544 type
= lxml_type_to_element(v
->sc_type
);
545 value
= (const xmlChar
*)v
->sc_u
.sc_string
;
552 * Although an SC_COUNT represents a uint64_t the use
553 * of a negative value is acceptable due to the usage
554 * established by inetd(8).
557 v
->sc_u
.sc_count
= strtoull((char *)value
, &endptr
, 10);
558 if (errno
!= 0 || endptr
== (char *)value
|| *endptr
)
559 uu_die(gettext("illegal value \"%s\" for "
560 "%s (%s)\n"), (char *)value
,
561 lxml_prop_types
[type
],
562 (errno
) ? strerror(errno
) :
563 gettext("Illegal character"));
567 v
->sc_u
.sc_integer
= strtoll((char *)value
, &endptr
, 10);
568 if (errno
!= 0 || *endptr
)
569 uu_die(gettext("illegal value \"%s\" for "
570 "%s (%s)\n"), (char *)value
,
571 lxml_prop_types
[type
],
572 (errno
) ? strerror(errno
) : "Illegal character");
585 scf_type
= lxml_element_to_type(type
);
587 v
->sc_u
.sc_string
= safe_strdup((const char *)value
);
588 if (lxml_validate_string_value(scf_type
,
589 v
->sc_u
.sc_string
) != 0)
590 uu_die(gettext("illegal value \"%s\" for "
591 "%s (%s)\n"), (char *)value
,
592 lxml_prop_types
[type
],
593 (scf_error()) ? scf_strerror(scf_error()) :
594 gettext("Illegal format"));
595 v
->sc_free
= lxml_free_str
;
598 v
->sc_u
.sc_count
= lxml_xlate_boolean(value
);
601 uu_die(gettext("unknown value type (%d)\n"), type
);
605 /* Free the old value */
606 if (fov
&& v
->sc_free
!= NULL
)
611 lxml_make_value(element_t type
, const xmlChar
*value
)
615 v
= internal_value_new();
617 v
->sc_type
= lxml_element_to_type(type
);
619 lxml_store_value(v
, type
, value
);
625 lxml_get_value(property_t
*prop
, element_t vtype
, xmlNodePtr value
)
629 for (cursor
= value
->xmlChildrenNode
; cursor
!= NULL
;
630 cursor
= cursor
->next
) {
631 xmlChar
*assigned_value
;
634 if (lxml_ignorable_block(cursor
))
637 switch (lxml_xlate_element(cursor
->name
)) {
639 if ((assigned_value
= xmlGetProp(cursor
,
640 (xmlChar
*)value_attr
)) == NULL
)
641 uu_die(gettext("no value on value node?\n"));
644 uu_die(gettext("value list contains illegal element "
645 "\'%s\'\n"), cursor
->name
);
649 v
= lxml_make_value(vtype
, assigned_value
);
651 xmlFree(assigned_value
);
653 internal_attach_value(prop
, v
);
660 lxml_get_propval(pgroup_t
*pgrp
, xmlNodePtr propval
)
665 xmlChar
*type
, *val
, *override
;
666 int op
= pgrp
->sc_parent
->sc_op
;
668 p
= internal_property_new();
670 p
->sc_property_name
= (char *)xmlGetProp(propval
, (xmlChar
*)name_attr
);
671 if ((p
->sc_property_name
== NULL
) || (*p
->sc_property_name
== 0))
672 uu_die(gettext("property name missing in group '%s'\n"),
673 pgrp
->sc_pgroup_name
);
675 type
= xmlGetProp(propval
, (xmlChar
*)type_attr
);
676 if ((type
!= NULL
) && (*type
!= 0)) {
678 r
< sizeof (lxml_prop_types
) / sizeof (char *); ++r
) {
680 (const xmlChar
*)lxml_prop_types
[r
]) == 0)
684 if (r
>= sizeof (lxml_prop_types
) / sizeof (char *))
685 uu_die(gettext("property type invalid for "
686 "property '%s/%s'\n"), pgrp
->sc_pgroup_name
,
687 p
->sc_property_name
);
689 p
->sc_value_type
= lxml_element_to_type(r
);
690 } else if (op
== SVCCFG_OP_APPLY
) {
692 * Store the property type as invalid, and the value
693 * as an ASTRING and let the bundle apply code validate
694 * the type/value once the type is found.
696 est
->sc_miss_type
= B_TRUE
;
697 p
->sc_value_type
= SCF_TYPE_INVALID
;
700 uu_die(gettext("property type missing for property '%s/%s'\n"),
701 pgrp
->sc_pgroup_name
, p
->sc_property_name
);
704 val
= xmlGetProp(propval
, (xmlChar
*)value_attr
);
706 uu_die(gettext("property value missing for property '%s/%s'\n"),
707 pgrp
->sc_pgroup_name
, p
->sc_property_name
);
709 v
= lxml_make_value(r
, val
);
711 internal_attach_value(p
, v
);
715 override
= xmlGetProp(propval
, (xmlChar
*)override_attr
);
716 p
->sc_property_override
= (xmlStrcmp(override
, (xmlChar
*)true) == 0);
719 return (internal_attach_property(pgrp
, p
));
723 lxml_get_property(pgroup_t
*pgrp
, xmlNodePtr property
)
728 xmlChar
*type
, *override
;
729 int op
= pgrp
->sc_parent
->sc_op
;
731 p
= internal_property_new();
733 if (((p
->sc_property_name
= (char *)xmlGetProp(property
,
734 (xmlChar
*)name_attr
)) == NULL
) || (*p
->sc_property_name
== 0))
735 uu_die(gettext("property name missing in group \'%s\'\n"),
736 pgrp
->sc_pgroup_name
);
738 type
= xmlGetProp(property
, (xmlChar
*)type_attr
);
739 if ((type
!= NULL
) && (*type
!= 0)) {
741 r
< sizeof (lxml_prop_types
) / sizeof (char *); r
++) {
743 (const xmlChar
*)lxml_prop_types
[r
]) == 0)
747 if (r
>= sizeof (lxml_prop_types
) / sizeof (char *))
748 uu_die(gettext("property type invalid for "
749 "property '%s/%s'\n"), pgrp
->sc_pgroup_name
,
750 p
->sc_property_name
);
752 p
->sc_value_type
= lxml_element_to_type(r
);
753 } else if (op
== SVCCFG_OP_APPLY
) {
755 * Store the property type as invalid, and let the bundle apply
756 * code validate the type/value once the type is found.
758 p
->sc_value_type
= SCF_TYPE_INVALID
;
759 est
->sc_miss_type
= B_TRUE
;
761 uu_die(gettext("property type missing for "
762 "property \'%s/%s\'\n"), pgrp
->sc_pgroup_name
,
763 p
->sc_property_name
);
766 for (cursor
= property
->xmlChildrenNode
; cursor
!= NULL
;
767 cursor
= cursor
->next
) {
768 if (lxml_ignorable_block(cursor
))
771 switch (r
= lxml_xlate_element(cursor
->name
)) {
787 * If the type is invalid then this is an apply
788 * operation and the type can be taken from the
791 if (p
->sc_value_type
== SCF_TYPE_INVALID
) {
792 p
->sc_value_type
= lxml_element_to_type(r
);
793 type
= xmlStrdup((const
794 xmlChar
*)lxml_prop_types
[r
]);
796 } else if (strcmp(lxml_prop_types
[r
],
797 (const char *)type
) != 0) {
798 uu_die(gettext("property \'%s\' "
799 "type-to-list mismatch\n"),
800 p
->sc_property_name
);
803 (void) lxml_get_value(p
, r
, cursor
);
806 uu_die(gettext("unknown value list type: %s\n"),
814 override
= xmlGetProp(property
, (xmlChar
*)override_attr
);
815 p
->sc_property_override
= (xmlStrcmp(override
, (xmlChar
*)true) == 0);
818 return (internal_attach_property(pgrp
, p
));
822 lxml_get_pgroup_stability(pgroup_t
*pgrp
, xmlNodePtr stab
)
824 if (pgrp
->sc_parent
->sc_op
== SVCCFG_OP_APPLY
)
825 lxml_validate_element(stab
);
827 return (new_str_prop_from_attr(pgrp
, SCF_PROPERTY_STABILITY
,
828 SCF_TYPE_ASTRING
, stab
, value_attr
));
832 * Property groups can go on any of a service, an instance, or a template.
835 lxml_get_pgroup(entity_t
*entity
, xmlNodePtr pgroup
)
839 xmlChar
*name
, *type
, *delete;
842 * property group attributes:
844 * type: string | framework | application
846 name
= xmlGetProp(pgroup
, (xmlChar
*)name_attr
);
847 type
= xmlGetProp(pgroup
, (xmlChar
*)type_attr
);
848 pg
= internal_pgroup_find_or_create(entity
, (char *)name
, (char *)type
);
853 * Walk the children of this lxml_elements, which are a stability
854 * element, property elements, or propval elements.
856 for (cursor
= pgroup
->xmlChildrenNode
; cursor
!= NULL
;
857 cursor
= cursor
->next
) {
858 if (lxml_ignorable_block(cursor
))
861 switch (lxml_xlate_element(cursor
->name
)) {
863 (void) lxml_get_pgroup_stability(pg
, cursor
);
866 (void) lxml_get_property(pg
, cursor
);
869 (void) lxml_get_propval(pg
, cursor
);
877 delete = xmlGetProp(pgroup
, (xmlChar
*)delete_attr
);
878 pg
->sc_pgroup_delete
= (xmlStrcmp(delete, (xmlChar
*)true) == 0);
886 * Dependency groups, execution methods can go on either a service or an
891 lxml_get_method_profile(pgroup_t
*pg
, xmlNodePtr profile
)
895 p
= internal_property_create(SCF_PROPERTY_USE_PROFILE
, SCF_TYPE_BOOLEAN
,
897 if (internal_attach_property(pg
, p
) != 0)
900 return (new_str_prop_from_attr(pg
, SCF_PROPERTY_PROFILE
,
901 SCF_TYPE_ASTRING
, profile
, name_attr
));
905 lxml_get_method_credential(pgroup_t
*pg
, xmlNodePtr cred
)
909 p
= internal_property_create(SCF_PROPERTY_USE_PROFILE
, SCF_TYPE_BOOLEAN
,
911 if (internal_attach_property(pg
, p
) != 0)
914 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_USER
, SCF_TYPE_ASTRING
,
915 cred
, "user", NULL
) != 0)
918 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_GROUP
, SCF_TYPE_ASTRING
,
919 cred
, "group", NULL
) != 0)
922 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_SUPP_GROUPS
,
923 SCF_TYPE_ASTRING
, cred
, "supp_groups", NULL
) != 0)
926 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_PRIVILEGES
,
927 SCF_TYPE_ASTRING
, cred
, "privileges", NULL
) != 0)
930 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_LIMIT_PRIVILEGES
,
931 SCF_TYPE_ASTRING
, cred
, "limit_privileges", NULL
) != 0)
938 lxml_get_envvar(xmlNodePtr envvar
)
944 name
= (char *)xmlGetProp(envvar
, (xmlChar
*)name_attr
);
945 value
= (char *)xmlGetProp(envvar
, (xmlChar
*)value_attr
);
947 if (strlen(name
) == 0 || strchr(name
, '=') != NULL
)
948 uu_die(gettext("Invalid environment variable "
950 if (strstr(name
, "SMF_") == name
)
951 uu_die(gettext("Invalid environment variable "
952 "\"%s\"; \"SMF_\" prefix is reserved.\n"), name
);
954 ret
= uu_msprintf("%s=%s", name
, value
);
961 lxml_get_method_environment(pgroup_t
*pg
, xmlNodePtr environment
)
967 p
= internal_property_create(SCF_PROPERTY_ENVIRONMENT
,
968 SCF_TYPE_ASTRING
, 0);
970 for (cursor
= environment
->xmlChildrenNode
; cursor
!= NULL
;
971 cursor
= cursor
->next
) {
974 if (lxml_ignorable_block(cursor
))
977 if (lxml_xlate_element(cursor
->name
) != SC_METHOD_ENVVAR
)
978 uu_die(gettext("illegal element \"%s\" on "
979 "method environment for \"%s\"\n"),
980 cursor
->name
, pg
->sc_pgroup_name
);
982 if ((tmp
= lxml_get_envvar(cursor
)) == NULL
)
983 uu_die(gettext("Out of memory\n"));
985 val
= internal_value_new();
986 val
->sc_u
.sc_string
= tmp
;
987 val
->sc_type
= SCF_TYPE_ASTRING
;
988 val
->sc_free
= lxml_free_str
;
989 internal_attach_value(p
, val
);
992 if (internal_attach_property(pg
, p
) != 0) {
993 internal_property_free(p
);
1001 lxml_get_method_context(pgroup_t
*pg
, xmlNodePtr ctx
)
1005 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_WORKING_DIRECTORY
,
1006 SCF_TYPE_ASTRING
, ctx
, "working_directory", NULL
) != 0)
1009 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_PROJECT
,
1010 SCF_TYPE_ASTRING
, ctx
, "project", NULL
) != 0)
1013 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_RESOURCE_POOL
,
1014 SCF_TYPE_ASTRING
, ctx
, "resource_pool", NULL
) != 0)
1017 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_SECFLAGS
,
1018 SCF_TYPE_ASTRING
, ctx
, "security_flags", NULL
) != 0)
1021 for (cursor
= ctx
->xmlChildrenNode
; cursor
!= NULL
;
1022 cursor
= cursor
->next
) {
1023 if (lxml_ignorable_block(cursor
))
1026 switch (lxml_xlate_element(cursor
->name
)) {
1027 case SC_METHOD_CREDENTIAL
:
1028 (void) lxml_get_method_credential(pg
, cursor
);
1030 case SC_METHOD_PROFILE
:
1031 (void) lxml_get_method_profile(pg
, cursor
);
1033 case SC_METHOD_ENVIRONMENT
:
1034 (void) lxml_get_method_environment(pg
, cursor
);
1037 semerr(gettext("illegal element \'%s\' in method "
1038 "context\n"), (char *)cursor
);
1047 lxml_get_entity_method_context(entity_t
*entity
, xmlNodePtr ctx
)
1051 pg
= internal_pgroup_find_or_create(entity
, SCF_PG_METHOD_CONTEXT
,
1052 (char *)scf_group_framework
);
1054 return (lxml_get_method_context(pg
, ctx
));
1058 lxml_get_exec_method(entity_t
*entity
, xmlNodePtr emeth
)
1062 xmlChar
*name
, *timeout
, *delete;
1066 if (entity
->sc_op
== SVCCFG_OP_APPLY
)
1067 lxml_validate_element(emeth
);
1069 name
= xmlGetProp(emeth
, (xmlChar
*)name_attr
);
1070 pg
= internal_pgroup_find_or_create(entity
, (char *)name
,
1071 (char *)SCF_GROUP_METHOD
);
1074 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_TYPE
, SCF_TYPE_ASTRING
,
1075 emeth
, type_attr
) != 0 ||
1076 new_str_prop_from_attr(pg
, SCF_PROPERTY_EXEC
, SCF_TYPE_ASTRING
,
1077 emeth
, "exec") != 0)
1080 timeout
= xmlGetProp(emeth
, (xmlChar
*)timeout_seconds_attr
);
1081 if (timeout
!= NULL
) {
1085 * Although an SC_COUNT represents a uint64_t the use
1086 * of a negative value is acceptable due to the usage
1087 * established by inetd(8).
1090 u_timeout
= strtoull((char *)timeout
, &endptr
, 10);
1091 if (errno
!= 0 || endptr
== (char *)timeout
|| *endptr
)
1092 uu_die(gettext("illegal value \"%s\" for "
1093 "timeout_seconds (%s)\n"),
1094 (char *)timeout
, (errno
) ? strerror(errno
):
1095 gettext("Illegal character"));
1096 p
= internal_property_create(SCF_PROPERTY_TIMEOUT
,
1097 SCF_TYPE_COUNT
, 1, u_timeout
);
1098 r
= internal_attach_property(pg
, p
);
1105 * There is a possibility that a method context also exists, in which
1106 * case the following attributes are defined: project, resource_pool,
1107 * working_directory, profile, user, group, privileges,
1108 * limit_privileges, security_flags
1110 for (cursor
= emeth
->xmlChildrenNode
; cursor
!= NULL
;
1111 cursor
= cursor
->next
) {
1112 if (lxml_ignorable_block(cursor
))
1115 switch (lxml_xlate_element(cursor
->name
)) {
1117 if (lxml_get_pgroup_stability(pg
, cursor
) != 0)
1121 case SC_METHOD_CONTEXT
:
1122 (void) lxml_get_method_context(pg
, cursor
);
1126 (void) lxml_get_propval(pg
, cursor
);
1130 (void) lxml_get_property(pg
, cursor
);
1134 uu_die(gettext("illegal element \"%s\" on "
1135 "execution method \"%s\"\n"), cursor
->name
,
1136 pg
->sc_pgroup_name
);
1141 delete = xmlGetProp(emeth
, (xmlChar
*)delete_attr
);
1142 pg
->sc_pgroup_delete
= (xmlStrcmp(delete, (xmlChar
*)true) == 0);
1149 lxml_get_dependency(entity_t
*entity
, xmlNodePtr dependency
)
1158 * dependency attributes:
1160 * grouping: require_all | require_any | exclude_all | optional_all
1161 * reset_on: string (error | restart | refresh | none)
1162 * type: service / path /host
1165 if (entity
->sc_op
== SVCCFG_OP_APPLY
)
1166 lxml_validate_element(dependency
);
1168 name
= xmlGetProp(dependency
, (xmlChar
*)name_attr
);
1169 pg
= internal_pgroup_find_or_create(entity
, (char *)name
,
1170 (char *)SCF_GROUP_DEPENDENCY
);
1173 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_TYPE
, SCF_TYPE_ASTRING
,
1174 dependency
, type_attr
) != 0)
1177 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_RESTART_ON
,
1178 SCF_TYPE_ASTRING
, dependency
, "restart_on") != 0)
1181 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_GROUPING
, SCF_TYPE_ASTRING
,
1182 dependency
, "grouping") != 0)
1185 p
= internal_property_create(SCF_PROPERTY_ENTITIES
, SCF_TYPE_FMRI
, 0);
1186 if (internal_attach_property(pg
, p
) != 0)
1189 for (cursor
= dependency
->xmlChildrenNode
; cursor
!= NULL
;
1190 cursor
= cursor
->next
) {
1194 if (lxml_ignorable_block(cursor
))
1197 switch (lxml_xlate_element(cursor
->name
)) {
1199 if (lxml_get_pgroup_stability(pg
, cursor
) != 0)
1203 case SC_SERVICE_FMRI
:
1204 value
= xmlGetProp(cursor
, (xmlChar
*)value_attr
);
1205 if (value
!= NULL
) {
1206 if (lxml_validate_string_value(SCF_TYPE_FMRI
,
1207 (char *)value
) != 0)
1208 uu_die(gettext("illegal value \"%s\" "
1209 "for %s (%s)\n"), (char *)value
,
1210 lxml_prop_types
[SC_FMRI
],
1212 scf_strerror(scf_error()) :
1213 gettext("Illegal format"));
1214 v
= internal_value_new();
1215 v
->sc_type
= SCF_TYPE_FMRI
;
1216 v
->sc_u
.sc_string
= (char *)value
;
1217 internal_attach_value(p
, v
);
1223 (void) lxml_get_propval(pg
, cursor
);
1227 (void) lxml_get_property(pg
, cursor
);
1231 uu_die(gettext("illegal element \"%s\" on "
1232 "dependency group \"%s\"\n"), cursor
->name
, name
);
1237 delete = xmlGetProp(dependency
, (xmlChar
*)delete_attr
);
1238 pg
->sc_pgroup_delete
= (xmlStrcmp(delete, (xmlChar
*)true) == 0);
1245 * Dependents are hairy. They should cause a dependency pg to be created in
1246 * another service, but we can't do that here; we'll have to wait until the
1247 * import routines. So for now we'll add the dependency group that should go
1248 * in the other service to the entity's dependent list.
1251 lxml_get_dependent(entity_t
*entity
, xmlNodePtr dependent
)
1255 xmlChar
*fmri
, *delete;
1261 if (entity
->sc_op
== SVCCFG_OP_APPLY
)
1262 lxml_validate_element(dependent
);
1264 name
= xmlGetProp(dependent
, (xmlChar
*)name_attr
);
1266 if (internal_pgroup_find(entity
, (char *)name
, NULL
) != NULL
) {
1267 semerr(gettext("Property group and dependent of entity %s "
1268 "have same name \"%s\".\n"), entity
->sc_name
, name
);
1273 or = xmlGetProp(dependent
, (xmlChar
*)override_attr
);
1275 pg
= internal_pgroup_new();
1276 pg
->sc_pgroup_name
= (char *)name
;
1277 pg
->sc_pgroup_type
= (char *)SCF_GROUP_DEPENDENCY
;
1278 pg
->sc_pgroup_override
= (xmlStrcmp(or, (xmlChar
*)true) == 0);
1280 if (internal_attach_dependent(entity
, pg
) != 0) {
1282 internal_pgroup_free(pg
);
1286 for (sf
= dependent
->children
; sf
!= NULL
; sf
= sf
->next
)
1287 if (xmlStrcmp(sf
->name
, (xmlChar
*)"service_fmri") == 0)
1290 fmri
= xmlGetProp(sf
, (xmlChar
*)value_attr
);
1291 pg
->sc_pgroup_fmri
= (char *)fmri
;
1293 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_RESTART_ON
,
1294 SCF_TYPE_ASTRING
, dependent
, "restart_on") != 0)
1297 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_GROUPING
, SCF_TYPE_ASTRING
,
1298 dependent
, "grouping") != 0)
1301 myfmri
= safe_malloc(max_scf_fmri_len
+ 1);
1302 if (entity
->sc_etype
== SVCCFG_SERVICE_OBJECT
) {
1303 if (snprintf(myfmri
, max_scf_fmri_len
+ 1, "svc:/%s",
1304 entity
->sc_name
) < 0)
1305 bad_error("snprintf", errno
);
1307 assert(entity
->sc_etype
== SVCCFG_INSTANCE_OBJECT
);
1308 if (snprintf(myfmri
, max_scf_fmri_len
+ 1, "svc:/%s:%s",
1309 entity
->sc_parent
->sc_name
, entity
->sc_name
) < 0)
1310 bad_error("snprintf", errno
);
1313 p
= internal_property_create(SCF_PROPERTY_ENTITIES
, SCF_TYPE_FMRI
, 1,
1315 if (internal_attach_property(pg
, p
) != 0)
1318 /* Create a property to serve as a do-not-export flag. */
1319 p
= internal_property_create("external", SCF_TYPE_BOOLEAN
, 1,
1321 if (internal_attach_property(pg
, p
) != 0)
1324 for (n
= sf
->next
; n
!= NULL
; n
= n
->next
) {
1325 if (lxml_ignorable_block(n
))
1328 switch (lxml_xlate_element(n
->name
)) {
1330 if (new_str_prop_from_attr(pg
,
1331 SCF_PROPERTY_ENTITY_STABILITY
, SCF_TYPE_ASTRING
, n
,
1337 (void) lxml_get_propval(pg
, n
);
1341 (void) lxml_get_property(pg
, n
);
1345 uu_die(gettext("unexpected element %s.\n"), n
->name
);
1349 /* Go back and fill in defaults. */
1350 if (internal_property_find(pg
, SCF_PROPERTY_TYPE
) == NULL
) {
1351 p
= internal_property_create(SCF_PROPERTY_TYPE
,
1352 SCF_TYPE_ASTRING
, 1, "service");
1353 if (internal_attach_property(pg
, p
) != 0)
1357 delete = xmlGetProp(dependent
, (xmlChar
*)delete_attr
);
1358 pg
->sc_pgroup_delete
= (xmlStrcmp(delete, (xmlChar
*)true) == 0);
1361 pg
= internal_pgroup_find_or_create(entity
, "dependents",
1362 (char *)scf_group_framework
);
1363 p
= internal_property_create((char *)name
, SCF_TYPE_FMRI
, 1, fmri
);
1364 if (internal_attach_property(pg
, p
) != 0)
1371 lxml_get_entity_stability(entity_t
*entity
, xmlNodePtr rstr
)
1377 if (((stabval
= xmlGetProp(rstr
, (xmlChar
*)value_attr
)) == NULL
) ||
1379 uu_warn(gettext("no stability value found\n"));
1380 stabval
= (xmlChar
*)safe_strdup("External");
1383 pg
= internal_pgroup_find_or_create(entity
, (char *)scf_pg_general
,
1384 (char *)scf_group_framework
);
1386 p
= internal_property_create(SCF_PROPERTY_ENTITY_STABILITY
,
1387 SCF_TYPE_ASTRING
, 1, stabval
);
1389 return (internal_attach_property(pg
, p
));
1393 lxml_get_restarter(entity_t
*entity
, xmlNodePtr rstr
)
1397 xmlChar
*restarter
= NULL
;
1402 * Go find child. Child is a service_fmri element. value attribute
1403 * contains restarter FMRI.
1406 pg
= internal_pgroup_find_or_create(entity
, (char *)scf_pg_general
,
1407 (char *)scf_group_framework
);
1410 * Walk its child elements, as appropriate.
1412 for (cursor
= rstr
->xmlChildrenNode
; cursor
!= NULL
;
1413 cursor
= cursor
->next
) {
1414 if (lxml_ignorable_block(cursor
))
1417 switch (lxml_xlate_element(cursor
->name
)) {
1418 case SC_SERVICE_FMRI
:
1419 restarter
= xmlGetProp(cursor
, (xmlChar
*)value_attr
);
1422 uu_die(gettext("illegal element \"%s\" on restarter "
1423 "element for \"%s\"\n"), cursor
->name
,
1429 p
= internal_property_create(SCF_PROPERTY_RESTARTER
, SCF_TYPE_FMRI
, 1,
1432 r
= internal_attach_property(pg
, p
);
1434 internal_property_free(p
);
1442 lxml_get_paramval(pgroup_t
*pgrp
, const char *propname
, xmlNodePtr pval
)
1448 prop
= safe_strdup(propname
);
1450 value
= (char *)xmlGetProp(pval
, (xmlChar
*)value_attr
);
1451 if (value
== NULL
|| *value
== '\0')
1452 uu_die(gettext("property value missing for property '%s/%s'\n"),
1453 pgrp
->sc_pgroup_name
, propname
);
1454 p
= internal_property_create(prop
, SCF_TYPE_ASTRING
, 1, value
);
1456 (void) internal_attach_property(pgrp
, p
);
1460 lxml_get_parameter(pgroup_t
*pgrp
, const char *propname
, xmlNodePtr param
)
1462 property_t
*p
= internal_property_new();
1464 p
->sc_property_name
= safe_strdup(propname
);
1465 p
->sc_value_type
= SCF_TYPE_ASTRING
;
1467 (void) lxml_get_value(p
, SC_ASTRING
, param
);
1469 (void) internal_attach_property(pgrp
, p
);
1473 lxml_get_type(pgroup_t
*pgrp
, xmlNodePtr type
)
1479 uint64_t active_val
;
1480 size_t sz
= max_scf_name_len
+ 1;
1481 char *propname
= safe_malloc(sz
);
1483 if (pgrp
->sc_parent
->sc_op
== SVCCFG_OP_APPLY
)
1484 lxml_validate_element(type
);
1486 name
= xmlGetProp(type
, (xmlChar
*)name_attr
);
1487 if (name
== NULL
|| *name
== '\0')
1488 uu_die(gettext("attribute name missing in element 'type'\n"));
1490 for (cursor
= type
->xmlChildrenNode
; cursor
!= NULL
;
1491 cursor
= cursor
->next
) {
1494 if (lxml_ignorable_block(cursor
))
1497 pname
= xmlGetProp(cursor
, (xmlChar
*)name_attr
);
1498 if (pname
== NULL
|| *pname
== '\0')
1500 "attribute name missing in sub-element of type\n"));
1502 if (snprintf(propname
, sz
, "%s,%s", (char *)name
,
1503 (char *)pname
) >= sz
)
1504 uu_die(gettext("name '%s,%s' is too long\n"),
1505 (char *)name
, (char *)pname
);
1508 switch (lxml_xlate_element(cursor
->name
)) {
1510 lxml_get_parameter(pgrp
, propname
, cursor
);
1514 lxml_get_paramval(pgrp
, propname
, cursor
);
1518 uu_die(gettext("unknown element %s\n"), cursor
->name
);
1522 active
= xmlGetProp(type
, (xmlChar
*)active_attr
);
1523 if (active
== NULL
|| strcmp(true, (const char *)active
) == 0)
1529 if (snprintf(propname
, sz
, "%s,%s", (char *)name
,
1530 SCF_PROPERTY_ACTIVE_POSTFIX
) >= sz
)
1531 uu_die(gettext("name '%s,%s' is too long\n"),
1532 (char *)name
, SCF_PROPERTY_ACTIVE_POSTFIX
);
1534 p
= internal_property_create(propname
, SCF_TYPE_BOOLEAN
, 1, active_val
);
1536 (void) internal_attach_property(pgrp
, p
);
1542 lxml_get_event(entity_t
*entity
, const char *pgname
, xmlNodePtr np
)
1547 pgrp
= internal_pgroup_find_or_create(entity
, pgname
,
1548 SCF_NOTIFY_PARAMS_PG_TYPE
);
1549 for (cursor
= np
->xmlChildrenNode
; cursor
!= NULL
;
1550 cursor
= cursor
->next
) {
1551 if (lxml_ignorable_block(cursor
))
1554 switch (lxml_xlate_element(cursor
->name
)) {
1559 lxml_get_type(pgrp
, cursor
);
1563 uu_warn(gettext("illegal element '%s' on "
1564 "notification parameters\n"), cursor
->name
);
1570 lxml_get_notification_parameters(entity_t
*entity
, xmlNodePtr np
)
1575 char *pgname
= NULL
;
1578 size_t sz
= max_scf_name_len
+ 1;
1582 for (count
= 0, cursor
= np
->xmlChildrenNode
; cursor
!= NULL
;
1583 cursor
= cursor
->next
) {
1584 if (lxml_ignorable_block(cursor
))
1587 if (lxml_xlate_element(cursor
->name
) == SC_EVENT
) {
1592 uu_die(gettext("Can't have more than 1 element "
1593 "event in a notification parameter\n"));
1594 s
= xmlGetProp(cursor
, (xmlChar
*)value_attr
);
1595 if (s
== NULL
|| (event
= strdup((char *)s
)) == NULL
)
1596 uu_die(gettext("couldn't allocate memory"));
1601 pgs
= tokenize(event
, ",");
1603 switch (tset
= check_tokens(pgs
)) {
1604 case INVALID_TOKENS
:
1605 uu_die(gettext("Invalid input.\n"));
1608 semerr(gettext("Can't mix SMF and FMA event definitions\n"));
1611 /* make sure this is SCF_NOTIFY_PARAMS_INST */
1612 if (entity
->sc_etype
!= SVCCFG_INSTANCE_OBJECT
||
1613 strcmp(entity
->sc_fmri
, SCF_NOTIFY_PARAMS_INST
) != 0) {
1615 "Non-SMF transition events must go to %s\n"),
1616 SCF_NOTIFY_PARAMS_INST
);
1619 pgname
= safe_malloc(sz
);
1620 for (p
= pgs
; *p
; ++p
) {
1621 if (snprintf(pgname
, sz
, "%s,%s", de_tag(*p
),
1622 SCF_NOTIFY_PG_POSTFIX
) >= sz
)
1623 uu_die(gettext("event name too long: %s\n"),
1626 lxml_get_event(entity
, pgname
, np
);
1630 default: /* smf state transition tokens */
1631 if (entity
->sc_etype
== SVCCFG_SERVICE_OBJECT
&&
1632 strcmp(entity
->sc_fmri
, SCF_SERVICE_GLOBAL
) == 0) {
1634 "Can't set events for global service\n"));
1637 for (t
= 0x1; t
< SCF_STATE_ALL
; t
<<= 1) {
1639 lxml_get_event(entity
, tset_to_string(t
), np
);
1641 if ((t
<< 16) & tset
) {
1642 lxml_get_event(entity
, tset_to_string(t
<< 16),
1658 * Add a property containing the localized text from the manifest. The
1659 * property is added to the property group at pg. The name of the created
1660 * property is based on the format at pn_format. This is an snprintf(3C)
1661 * format containing a single %s conversion specification. At conversion
1662 * time, the %s is replaced by the locale designation.
1664 * source is the source element and it is only used for error messages.
1667 lxml_get_loctext(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr loctext
,
1668 const char *pn_format
, const char *source
)
1673 char *stripped
, *cp
;
1678 if (((val
= xmlGetProp(loctext
, (xmlChar
*)xml_lang_attr
)) == NULL
) ||
1680 if (((val
= xmlGetProp(loctext
,
1681 (xmlChar
*)lang_attr
)) == NULL
) || (*val
== 0)) {
1682 val
= (xmlChar
*)"unknown";
1686 _scf_sanitize_locale((char *)val
);
1687 prop_name
= safe_malloc(max_scf_name_len
+ 1);
1688 if ((extra
= snprintf(prop_name
, max_scf_name_len
+ 1, pn_format
,
1689 val
)) >= max_scf_name_len
+ 1) {
1690 extra
-= max_scf_name_len
;
1691 uu_die(gettext("%s attribute is %d characters too long for "
1693 xml_lang_attr
, extra
, source
, service
->sc_name
);
1697 for (cursor
= loctext
->xmlChildrenNode
; cursor
!= NULL
;
1698 cursor
= cursor
->next
) {
1699 if (strcmp("text", (const char *)cursor
->name
) == 0) {
1701 } else if (strcmp("comment", (const char *)cursor
->name
) != 0) {
1702 uu_die(gettext("illegal element \"%s\" on loctext "
1703 "element for \"%s\"\n"), cursor
->name
,
1708 if (cursor
== NULL
) {
1709 uu_die(gettext("loctext element has no content for \"%s\"\n"),
1714 * Remove leading and trailing whitespace.
1716 stripped
= safe_strdup((const char *)cursor
->content
);
1718 for (; isspace(*stripped
); stripped
++)
1720 for (cp
= stripped
+ strlen(stripped
) - 1; isspace(*cp
); cp
--)
1724 p
= internal_property_create(prop_name
, SCF_TYPE_USTRING
, 1,
1727 r
= internal_attach_property(pg
, p
);
1729 internal_property_free(p
);
1737 * This function processes all loctext elements in the current XML element
1738 * designated by container. A property is created for each loctext element
1739 * and added to the property group at pg. The name of the property is
1740 * derived from the loctext language designation using the format at
1741 * pn_format. pn_format should be an snprintf format string containing one
1742 * %s which is replaced by the language designation.
1744 * The function returns 0 on success and -1 if it is unable to attach the
1745 * newly created property to pg.
1748 lxml_get_all_loctext(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr container
,
1749 const char *pn_format
, const char *source
)
1754 * Iterate through one or more loctext elements. The locale is
1755 * used to generate the property name; the contents are the ustring
1756 * value for the property.
1758 for (cursor
= container
->xmlChildrenNode
; cursor
!= NULL
;
1759 cursor
= cursor
->next
) {
1760 if (lxml_ignorable_block(cursor
))
1763 switch (lxml_xlate_element(cursor
->name
)) {
1765 if (lxml_get_loctext(service
, pg
, cursor
, pn_format
,
1770 uu_die(gettext("illegal element \"%s\" on %s element "
1771 "for \"%s\"\n"), cursor
->name
, container
->name
,
1781 * Obtain the specified cardinality attribute and place it in a property
1782 * named prop_name. The converted attribute is placed at *value, and the
1783 * newly created property is returned to propp. NULL is returned to propp
1784 * if the attribute is not provided in the manifest.
1786 * 0 is returned upon success, and -1 indicates that the manifest contained
1787 * an invalid cardinality value.
1790 lxml_get_cardinality_attribute(entity_t
*service
, xmlNodePtr cursor
,
1791 const char *attr_name
, const char *prop_name
, uint64_t *value
,
1801 val
= xmlGetProp(cursor
, (xmlChar
*)attr_name
);
1810 * Make sure that the string at val doesn't have a leading minus
1811 * sign. The strtoull() call below does not catch this problem.
1813 for (c
= (char *)val
; *c
!= 0; c
++) {
1818 semerr(gettext("\"%c\" is not a legal character in the %s "
1819 "attribute of the %s element in %s.\n"), *c
,
1820 attr_name
, prop_name
, service
->sc_name
);
1825 count
= strtoull((char *)val
, &endptr
, 10);
1826 if (errno
!= 0 || endptr
== (char *)val
|| *endptr
) {
1827 semerr(gettext("\"%s\" is not a legal number for the %s "
1828 "attribute of the %s element in %s.\n"), (char *)val
,
1829 attr_name
, prop_name
, service
->sc_name
);
1836 /* Value is valid. Create the property. */
1837 p
= internal_property_create(prop_name
, SCF_TYPE_COUNT
, 1, count
);
1844 * The cardinality is specified by two attributes max and min at cursor.
1845 * Both are optional, but if present they must be unsigned integers.
1848 lxml_get_tm_cardinality(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr cursor
)
1850 int min_attached
= 0;
1852 property_t
*min_prop
;
1853 property_t
*max_prop
;
1858 r
= lxml_get_cardinality_attribute(service
, cursor
, min_attr
,
1859 SCF_PROPERTY_TM_CARDINALITY_MIN
, &min
, &min_prop
);
1862 if (min_prop
== NULL
)
1864 r
= lxml_get_cardinality_attribute(service
, cursor
, max_attr
,
1865 SCF_PROPERTY_TM_CARDINALITY_MAX
, &max
, &max_prop
);
1868 if ((max_prop
!= NULL
) && (compare
== 1)) {
1870 semerr(gettext("Cardinality max is less than min for "
1871 "the %s element in %s.\n"), pg
->sc_pgroup_name
,
1877 /* Attach the properties to the property group. */
1879 if (internal_attach_property(pg
, min_prop
) == 0) {
1886 if (internal_attach_property(pg
, max_prop
) != 0) {
1888 internal_detach_property(pg
, min_prop
);
1896 internal_property_free(min_prop
);
1898 internal_property_free(max_prop
);
1903 * Get the common_name which is present as localized text at common_name in
1904 * the manifest. The common_name is stored as the value of a property in
1905 * the property group whose name is SCF_PG_TM_COMMON_NAME and type is
1906 * SCF_GROUP_TEMPLATE. This property group will be created in service if
1907 * it is not already there.
1910 lxml_get_tm_common_name(entity_t
*service
, xmlNodePtr common_name
)
1915 * Create the property group, if absent.
1917 pg
= internal_pgroup_find_or_create(service
, SCF_PG_TM_COMMON_NAME
,
1918 SCF_GROUP_TEMPLATE
);
1920 return (lxml_get_all_loctext(service
, pg
, common_name
, LOCALE_ONLY_FMT
,
1925 * Get the description which is present as localized text at description in
1926 * the manifest. The description is stored as the value of a property in
1927 * the property group whose name is SCF_PG_TM_DESCRIPTION and type is
1928 * SCF_GROUP_TEMPLATE. This property group will be created in service if
1929 * it is not already there.
1932 lxml_get_tm_description(entity_t
*service
, xmlNodePtr description
)
1937 * Create the property group, if absent.
1939 pg
= internal_pgroup_find_or_create(service
, SCF_PG_TM_DESCRIPTION
,
1940 SCF_GROUP_TEMPLATE
);
1942 return (lxml_get_all_loctext(service
, pg
, description
,
1943 LOCALE_ONLY_FMT
, "description"));
1947 lxml_label_to_groupname(const char *prefix
, const char *in
)
1950 size_t len
, piece_len
;
1952 out
= uu_zalloc(2 * max_scf_name_len
+ 1);
1956 (void) strlcpy(out
, prefix
, 2 * max_scf_name_len
+ 1);
1958 len
= strlcat(out
, in
, 2 * max_scf_name_len
+ 1);
1959 if (len
> max_scf_name_len
) {
1960 /* Use the first half and the second half. */
1961 piece_len
= (max_scf_name_len
- 2) / 2;
1963 (void) strncpy(out
+ piece_len
, "..", 2);
1965 (void) strcpy(out
+ piece_len
+ 2, out
+ (len
- piece_len
));
1969 * Translate non-property characters to '_'.
1971 for (cp
= out
; *cp
!= '\0'; ++cp
) {
1972 if (!(isalnum(*cp
) || *cp
== '_' || *cp
== '-'))
1980 * If *p is NULL, astring_prop_value() first creates a property with the
1981 * name specified in prop_name. The address of the newly created property
1984 * In either case, newly created property or existing property, a new
1985 * SCF_TYPE_ASTRING value will created and attached to the property at *p.
1986 * The value of the newly created property is prop_value.
1988 * free_flag is used to indicate whether or not the memory at prop_value
1989 * should be freed when the property is freed by a call to
1990 * internal_property_free().
1993 astring_prop_value(property_t
**p
, const char *prop_name
, char *prop_value
,
1994 boolean_t free_flag
)
1999 /* Create the property */
2000 *p
= internal_property_new();
2001 (*p
)->sc_property_name
= (char *)prop_name
;
2002 (*p
)->sc_value_type
= SCF_TYPE_ASTRING
;
2005 /* Add the property value to the property's list of values. */
2006 v
= internal_value_new();
2007 v
->sc_type
= SCF_TYPE_ASTRING
;
2008 if (free_flag
== B_TRUE
)
2009 v
->sc_free
= lxml_free_str
;
2010 v
->sc_u
.sc_string
= prop_value
;
2011 internal_attach_value(*p
, v
);
2015 * If p points to a null pointer, create an internal_separators property
2016 * saving the address at p. For each character at seps create a property
2017 * value and attach it to the property at p.
2020 seps_to_prop_values(property_t
**p
, xmlChar
*seps
)
2026 *p
= internal_property_new();
2027 (*p
)->sc_property_name
=
2028 (char *)SCF_PROPERTY_INTERNAL_SEPARATORS
;
2029 (*p
)->sc_value_type
= SCF_TYPE_ASTRING
;
2032 /* Add the values to the property's list. */
2033 val_str
[1] = 0; /* Terminate the string. */
2034 for (; *seps
!= 0; seps
++) {
2035 v
= internal_value_new();
2036 v
->sc_type
= (*p
)->sc_value_type
;
2037 v
->sc_free
= lxml_free_str
;
2039 v
->sc_u
.sc_string
= safe_strdup(val_str
);
2040 internal_attach_value(*p
, v
);
2045 * Create an internal_separators property and attach it to the property
2046 * group at pg. The separator characters are provided in the text nodes
2047 * that are the children of seps. Each separator character is stored as a
2048 * property value in the internal_separators property.
2051 lxml_get_tm_internal_seps(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr seps
)
2054 property_t
*prop
= NULL
;
2057 for (cursor
= seps
->xmlChildrenNode
; cursor
!= NULL
;
2058 cursor
= cursor
->next
) {
2059 if (strcmp("text", (const char *)cursor
->name
) == 0) {
2060 seps_to_prop_values(&prop
, cursor
->content
);
2061 } else if (strcmp("comment", (const char *)cursor
->name
) != 0) {
2062 uu_die(gettext("illegal element \"%s\" on %s element "
2063 "for \"%s\"\n"), cursor
->name
, seps
->name
,
2068 semerr(gettext("The %s element in %s had an empty list of "
2069 "separators.\n"), (const char *)seps
->name
,
2073 r
= internal_attach_property(pg
, prop
);
2075 internal_property_free(prop
);
2080 lxml_get_tm_manpage(entity_t
*service
, xmlNodePtr manpage
)
2089 * Fetch title and section attributes, convert to something sanitized,
2090 * and create property group.
2092 title
= xmlGetProp(manpage
, (xmlChar
*)title_attr
);
2095 section
= xmlGetProp(manpage
, (xmlChar
*)section_attr
);
2096 if (section
== NULL
) {
2101 name
= safe_malloc(max_scf_name_len
+ 1);
2103 /* Find existing property group with underscore separators */
2104 (void) snprintf(name
, max_scf_name_len
+ 1, "%s_%s", title
, section
);
2105 pgname
= lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX
, name
);
2106 pg
= internal_pgroup_find(service
, pgname
, SCF_GROUP_TEMPLATE
);
2109 (void) snprintf(name
, max_scf_name_len
+ 1, "%s%s", title
, section
);
2110 pgname
= lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX
, name
);
2113 pg
= internal_pgroup_find_or_create(service
, pgname
,
2114 SCF_GROUP_TEMPLATE
);
2116 /* Rename property group */
2117 free((char *)pg
->sc_pgroup_name
);
2118 pg
->sc_pgroup_name
= safe_strdup(pgname
);
2128 * Each attribute is an astring property within the group.
2130 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_TM_TITLE
,
2131 SCF_TYPE_ASTRING
, manpage
, title_attr
) != 0 ||
2132 new_str_prop_from_attr(pg
, SCF_PROPERTY_TM_SECTION
,
2133 SCF_TYPE_ASTRING
, manpage
, section_attr
) != 0 ||
2134 new_str_prop_from_attr(pg
, SCF_PROPERTY_TM_MANPATH
,
2135 SCF_TYPE_ASTRING
, manpage
, manpath_attr
) != 0)
2142 lxml_get_tm_doclink(entity_t
*service
, xmlNodePtr doc_link
)
2149 * Fetch name attribute, convert name to something sanitized, and create
2152 name
= xmlGetProp(doc_link
, (xmlChar
*)name_attr
);
2156 pgname
= (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX
,
2157 (const char *)name
);
2159 pg
= internal_pgroup_find_or_create(service
, pgname
,
2160 (char *)SCF_GROUP_TEMPLATE
);
2166 * Each attribute is an astring property within the group.
2168 if (new_str_prop_from_attr(pg
, SCF_PROPERTY_TM_NAME
, SCF_TYPE_ASTRING
,
2169 doc_link
, name_attr
) != 0 ||
2170 new_str_prop_from_attr(pg
, SCF_PROPERTY_TM_URI
, SCF_TYPE_ASTRING
,
2171 doc_link
, uri_attr
) != 0)
2178 lxml_get_tm_documentation(entity_t
*service
, xmlNodePtr documentation
)
2182 for (cursor
= documentation
->xmlChildrenNode
; cursor
!= NULL
;
2183 cursor
= cursor
->next
) {
2184 if (lxml_ignorable_block(cursor
))
2187 switch (lxml_xlate_element(cursor
->name
)) {
2189 (void) lxml_get_tm_manpage(service
, cursor
);
2192 (void) lxml_get_tm_doclink(service
, cursor
);
2195 uu_die(gettext("illegal element \"%s\" on template "
2196 "for service \"%s\"\n"),
2197 cursor
->name
, service
->sc_name
);
2205 lxml_get_prop_pattern_attributes(pgroup_t
*pg
, xmlNodePtr cursor
)
2207 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_TM_NAME
,
2208 SCF_TYPE_ASTRING
, cursor
, name_attr
, NULL
) != 0) {
2211 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_TM_TYPE
,
2212 SCF_TYPE_ASTRING
, cursor
, type_attr
, "") != 0) {
2215 if (new_bool_prop_from_attr(pg
, SCF_PROPERTY_TM_REQUIRED
, cursor
,
2216 required_attr
) != 0)
2222 lxml_get_tm_include_values(entity_t
*service
, pgroup_t
*pg
,
2223 xmlNodePtr include_values
, const char *prop_name
)
2225 boolean_t attach_to_pg
= B_FALSE
;
2230 /* Get the type attribute of the include_values element. */
2231 type
= (char *)xmlGetProp(include_values
, (const xmlChar
*)type_attr
);
2232 if ((type
== NULL
) || (*type
== 0)) {
2233 uu_die(gettext("%s element requires a %s attribute in the %s "
2234 "service.\n"), include_values
->name
, type_attr
,
2238 /* Add the type to the values of the prop_name property. */
2239 p
= internal_property_find(pg
, prop_name
);
2241 attach_to_pg
= B_TRUE
;
2242 astring_prop_value(&p
, prop_name
, type
, B_FALSE
);
2243 if (attach_to_pg
== B_TRUE
) {
2244 r
= internal_attach_property(pg
, p
);
2246 internal_property_free(p
);
2256 * Verify that the strings at min and max are valid numeric strings. Also
2257 * verify that max is numerically >= min.
2259 * 0 is returned if the range is valid, and -1 is returned if it is not.
2262 verify_range(entity_t
*service
, xmlNodePtr range
, char *min
, char *max
)
2268 const char *limit
[RC_COUNT
];
2269 char *strings
[RC_COUNT
];
2270 uint64_t urange
[RC_COUNT
]; /* unsigned range. */
2271 int64_t srange
[RC_COUNT
]; /* signed range. */
2273 strings
[RC_MIN
] = min
;
2274 strings
[RC_MAX
] = max
;
2275 limit
[RC_MIN
] = min_attr
;
2276 limit
[RC_MAX
] = max_attr
;
2278 /* See if the range is signed. */
2279 for (i
= 0; (i
< RC_COUNT
) && (is_signed
== 0); i
++) {
2281 while (isspace(*c
)) {
2288 /* Attempt to convert the strings. */
2289 for (i
= 0; i
< RC_COUNT
; i
++) {
2292 srange
[i
] = strtoll(strings
[i
], &c
, 0);
2294 urange
[i
] = strtoull(strings
[i
], &c
, 0);
2296 if ((errno
!= 0) || (c
== strings
[i
]) || (*c
!= 0)) {
2297 /* Conversion failed. */
2298 uu_die(gettext("Unable to convert %s for the %s "
2299 "element in service %s.\n"), limit
[i
],
2300 (char *)range
->name
, service
->sc_name
);
2304 /* Make sure that min is <= max */
2306 if (srange
[RC_MAX
] < srange
[RC_MIN
])
2309 if (urange
[RC_MAX
] < urange
[RC_MIN
])
2312 if (inverted
!= 0) {
2313 semerr(gettext("Maximum less than minimum for the %s element "
2314 "in service %s.\n"), (char *)range
->name
,
2323 * This, function creates a property named prop_name. The range element
2324 * should have two attributes -- min and max. The property value then
2325 * becomes the concatenation of their value separated by a comma. The
2326 * property is then attached to the property group at pg.
2328 * If pg already contains a property with a name of prop_name, it is only
2329 * necessary to create a new value and attach it to the existing property.
2332 lxml_get_tm_range(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr range
,
2333 const char *prop_name
)
2335 boolean_t attach_to_pg
= B_FALSE
;
2342 /* Get max and min from the XML description. */
2343 max
= (char *)xmlGetProp(range
, (xmlChar
*)max_attr
);
2344 if ((max
== NULL
) || (*max
== 0)) {
2345 uu_die(gettext("%s element is missing the %s attribute in "
2346 "service %s.\n"), (char *)range
->name
, max_attr
,
2349 min
= (char *)xmlGetProp(range
, (xmlChar
*)min_attr
);
2350 if ((min
== NULL
) || (*min
== 0)) {
2351 uu_die(gettext("%s element is missing the %s attribute in "
2352 "service %s.\n"), (char *)range
->name
, min_attr
,
2355 if (verify_range(service
, range
, min
, max
) != 0) {
2361 /* Property value is concatenation of min and max. */
2362 prop_value
= safe_malloc(max_scf_value_len
+ 1);
2363 if (snprintf(prop_value
, max_scf_value_len
+ 1, "%s,%s", min
, max
) >=
2364 max_scf_value_len
+ 1) {
2365 uu_die(gettext("min and max are too long for the %s element "
2366 "of %s.\n"), (char *)range
->name
, service
->sc_name
);
2372 * If necessary create the property and attach it to the property
2375 p
= internal_property_find(pg
, prop_name
);
2377 attach_to_pg
= B_TRUE
;
2378 astring_prop_value(&p
, prop_name
, prop_value
, B_TRUE
);
2379 if (attach_to_pg
== B_TRUE
) {
2380 r
= internal_attach_property(pg
, p
);
2382 internal_property_free(p
);
2389 * Determine how many plain characters are represented by count Base32
2390 * encoded characters. 5 plain text characters are converted to 8 Base32
2394 encoded_count_to_plain(size_t count
)
2396 return (5 * ((count
+ 7) / 8));
2400 * The value element contains 0 or 1 common_name element followed by 0 or 1
2401 * description element. It also has a required attribute called "name".
2402 * The common_name and description are stored as property values in pg.
2403 * The property names are:
2404 * value_<name>_common_name_<lang>
2405 * value_<name>_description_<lang>
2407 * The <name> portion of the preceeding proper names requires more
2408 * explanation. Ideally it would just the name attribute of this value
2409 * element. Unfortunately, the name attribute can contain characters that
2410 * are not legal in a property name. Thus, we base 32 encode the name
2411 * attribute and use that for <name>.
2413 * There are cases where the caller needs to know the name, so it is
2414 * returned through the name_value pointer if it is not NULL.
2417 * service - Information about the service that is being
2418 * processed. This function only uses this parameter
2419 * for producing error messages.
2421 * pg - The property group to receive the newly created
2424 * value - Pointer to the value element in the XML tree.
2426 * name_value - Address to receive the value of the name attribute.
2427 * The caller must free the memory.
2430 lxml_get_tm_value_element(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr value
,
2433 char *common_name_fmt
;
2435 char *description_fmt
;
2436 char *encoded_value
= NULL
;
2441 common_name_fmt
= safe_malloc(max_scf_name_len
+ 1);
2442 description_fmt
= safe_malloc(max_scf_name_len
+ 1);
2445 * Get the value of our name attribute, so that we can use it to
2446 * construct property names.
2448 value_name
= (char *)xmlGetProp(value
, (xmlChar
*)name_attr
);
2449 /* The value name must be present, but it can be empty. */
2450 if (value_name
== NULL
) {
2451 uu_die(gettext("%s element requires a %s attribute in the %s "
2452 "service.\n"), (char *)value
->name
, name_attr
,
2457 * The value_name may contain characters that are not valid in in a
2458 * property name. So we will encode value_name and then use the
2459 * encoded value in the property name.
2461 encoded_value
= safe_malloc(max_scf_name_len
+ 1);
2462 if (scf_encode32(value_name
, strlen(value_name
), encoded_value
,
2463 max_scf_name_len
+ 1, &extra
, SCF_ENCODE32_PAD
) != 0) {
2464 extra
= encoded_count_to_plain(extra
- max_scf_name_len
);
2465 uu_die(gettext("Constructed property name is %u characters "
2466 "too long for value \"%s\" in the %s service.\n"),
2467 extra
, value_name
, service
->sc_name
);
2469 if ((extra
= snprintf(common_name_fmt
, max_scf_name_len
+ 1,
2470 VALUE_COMMON_NAME_FMT
, SCF_PROPERTY_TM_VALUE_PREFIX
,
2471 encoded_value
)) >= max_scf_name_len
+ 1) {
2472 extra
= encoded_count_to_plain(extra
- max_scf_name_len
);
2473 uu_die(gettext("Name attribute is "
2474 "%u characters too long for %s in service %s\n"),
2475 extra
, (char *)value
->name
, service
->sc_name
);
2477 if ((extra
= snprintf(description_fmt
, max_scf_name_len
+ 1,
2478 VALUE_DESCRIPTION_FMT
, SCF_PROPERTY_TM_VALUE_PREFIX
,
2479 encoded_value
)) >= max_scf_name_len
+ 1) {
2480 extra
= encoded_count_to_plain(extra
- max_scf_name_len
);
2481 uu_die(gettext("Name attribute is "
2482 "%u characters too long for %s in service %s\n"),
2483 extra
, (char *)value
->name
, service
->sc_name
);
2486 for (cursor
= value
->xmlChildrenNode
;
2488 cursor
= cursor
->next
) {
2489 if (lxml_ignorable_block(cursor
))
2491 switch (lxml_xlate_element(cursor
->name
)) {
2492 case SC_COMMON_NAME
:
2493 r
= lxml_get_all_loctext(service
, pg
, cursor
,
2494 common_name_fmt
, (const char *)cursor
->name
);
2496 case SC_DESCRIPTION
:
2497 r
= lxml_get_all_loctext(service
, pg
, cursor
,
2498 description_fmt
, (const char *)cursor
->name
);
2501 uu_die(gettext("\"%s\" is an illegal element in %s "
2502 "of service %s\n"), (char *)cursor
->name
,
2503 (char *)value
->name
, service
->sc_name
);
2509 free(description_fmt
);
2510 free(common_name_fmt
);
2512 *name_value
= safe_strdup(value_name
);
2514 xmlFree(value_name
);
2515 free(encoded_value
);
2520 lxml_get_tm_choices(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr choices
)
2524 property_t
*name_prop
= NULL
;
2527 for (cursor
= choices
->xmlChildrenNode
;
2528 (cursor
!= NULL
) && (r
== 0);
2529 cursor
= cursor
->next
) {
2530 if (lxml_ignorable_block(cursor
))
2532 switch (lxml_xlate_element(cursor
->name
)) {
2533 case SC_INCLUDE_VALUES
:
2534 (void) lxml_get_tm_include_values(service
, pg
, cursor
,
2535 SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES
);
2538 r
= lxml_get_tm_range(service
, pg
, cursor
,
2539 SCF_PROPERTY_TM_CHOICES_RANGE
);
2544 r
= lxml_get_tm_value_element(service
, pg
, cursor
,
2548 * There is no need to free the memory
2549 * associated with name_value, because the
2550 * property value will end up pointing to
2553 astring_prop_value(&name_prop
,
2554 SCF_PROPERTY_TM_CHOICES_NAME
, name_value
,
2561 uu_die(gettext("%s is an invalid element of "
2562 "choices for service %s.\n"), cursor
->name
,
2568 /* Attach the name property if we created one. */
2569 if ((r
== 0) && (name_prop
!= NULL
)) {
2570 r
= internal_attach_property(pg
, name_prop
);
2572 if ((r
!= 0) && (name_prop
!= NULL
)) {
2573 internal_property_free(name_prop
);
2580 lxml_get_tm_constraints(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr constraints
)
2584 property_t
*name_prop
= NULL
;
2587 for (cursor
= constraints
->xmlChildrenNode
;
2588 (cursor
!= NULL
) && (r
== 0);
2589 cursor
= cursor
->next
) {
2590 if (lxml_ignorable_block(cursor
))
2592 switch (lxml_xlate_element(cursor
->name
)) {
2594 r
= lxml_get_tm_range(service
, pg
, cursor
,
2595 SCF_PROPERTY_TM_CONSTRAINT_RANGE
);
2600 r
= lxml_get_tm_value_element(service
, pg
, cursor
,
2604 * There is no need to free the memory
2605 * associated with name_value, because the
2606 * property value will end up pointing to
2609 astring_prop_value(&name_prop
,
2610 SCF_PROPERTY_TM_CONSTRAINT_NAME
, name_value
,
2617 uu_die(gettext("%s is an invalid element of "
2618 "constraints for service %s.\n"), cursor
->name
,
2624 /* Attach the name property if we created one. */
2625 if ((r
== 0) && (name_prop
!= NULL
)) {
2626 r
= internal_attach_property(pg
, name_prop
);
2628 if ((r
!= 0) && (name_prop
!= NULL
)) {
2629 internal_property_free(name_prop
);
2636 * The values element contains one or more value elements.
2639 lxml_get_tm_values(entity_t
*service
, pgroup_t
*pg
, xmlNodePtr values
)
2643 property_t
*name_prop
= NULL
;
2646 for (cursor
= values
->xmlChildrenNode
;
2647 (cursor
!= NULL
) && (r
== 0);
2648 cursor
= cursor
->next
) {
2649 if (lxml_ignorable_block(cursor
))
2651 if (lxml_xlate_element(cursor
->name
) != SC_VALUE
) {
2652 uu_die(gettext("\"%s\" is an illegal element in the "
2653 "%s element of %s\n"), (char *)cursor
->name
,
2654 (char *)values
->name
, service
->sc_name
);
2656 r
= lxml_get_tm_value_element(service
, pg
, cursor
, &name_value
);
2659 * There is no need to free the memory
2660 * associated with name_value, because the
2661 * property value will end up pointing to
2664 astring_prop_value(&name_prop
,
2665 SCF_PROPERTY_TM_VALUES_NAME
, name_value
,
2670 /* Attach the name property if we created one. */
2671 if ((r
== 0) && (name_prop
!= NULL
)) {
2672 r
= internal_attach_property(pg
, name_prop
);
2674 if ((r
!= 0) && (name_prop
!= NULL
)) {
2675 internal_property_free(name_prop
);
2682 * This function processes a prop_pattern element within a pg_pattern XML
2683 * element. First it creates a property group to hold the prop_pattern
2684 * information. The name of this property group is the concatenation of:
2685 * - SCF_PG_TM_PROP_PATTERN_PREFIX
2686 * - The unique part of the property group name of the enclosing
2687 * pg_pattern. The property group name of the enclosing pg_pattern
2688 * is passed to us in pgpat_name. The unique part, is the part
2689 * following SCF_PG_TM_PG_PATTERN_PREFIX.
2690 * - The name of this prop_pattern element.
2692 * After creating the property group, the prop_pattern attributes are saved
2693 * as properties in the PG. Finally, the prop_pattern elements are
2694 * processed and added to the PG.
2697 lxml_get_tm_prop_pattern(entity_t
*service
, xmlNodePtr prop_pattern
,
2698 const char *pgpat_name
)
2706 xmlChar
*prop_pattern_name
;
2711 /* Find the unique part of the pg_pattern property group name. */
2712 prefix_len
= strlen(SCF_PG_TM_PG_PAT_BASE
);
2713 assert(strncmp(pgpat_name
, SCF_PG_TM_PG_PAT_BASE
, prefix_len
) == 0);
2714 unique
= pgpat_name
+ prefix_len
;
2717 * We need to get the value of the name attribute first. The
2718 * prop_pattern name as well as the name of the enclosing
2719 * pg_pattern both constitute part of the name of the property
2720 * group that we will create.
2722 prop_pattern_name
= xmlGetProp(prop_pattern
, (xmlChar
*)name_attr
);
2723 if ((prop_pattern_name
== NULL
) || (*prop_pattern_name
== 0)) {
2724 semerr(gettext("prop_pattern name is missing for %s\n"),
2728 if (uu_check_name((const char *)prop_pattern_name
,
2729 UU_NAME_DOMAIN
) != 0) {
2730 semerr(gettext("prop_pattern name, \"%s\", for %s is not "
2731 "valid.\n"), prop_pattern_name
, service
->sc_name
);
2732 xmlFree(prop_pattern_name
);
2735 pg_name
= safe_malloc(max_scf_name_len
+ 1);
2736 if ((extra
= snprintf(pg_name
, max_scf_name_len
+ 1, "%s%s_%s",
2737 SCF_PG_TM_PROP_PATTERN_PREFIX
, unique
,
2738 (char *)prop_pattern_name
)) >= max_scf_name_len
+ 1) {
2739 uu_die(gettext("prop_pattern name, \"%s\", for %s is %d "
2740 "characters too long\n"), (char *)prop_pattern_name
,
2741 service
->sc_name
, extra
- max_scf_name_len
);
2745 * Create the property group, the property referencing the pg_pattern
2746 * name, and add the prop_pattern attributes to the property group.
2748 pg
= internal_pgroup_create_strict(service
, pg_name
,
2749 SCF_GROUP_TEMPLATE_PROP_PATTERN
);
2751 uu_die(gettext("Property group for prop_pattern, \"%s\", "
2752 "already exists in %s\n"), prop_pattern_name
,
2756 p
= internal_property_create(SCF_PROPERTY_TM_PG_PATTERN
,
2757 SCF_TYPE_ASTRING
, 1, safe_strdup(pgpat_name
));
2759 * Unfortunately, internal_property_create() does not set the free
2760 * function for the value, so we'll set it now.
2762 v
= uu_list_first(p
->sc_property_values
);
2763 v
->sc_free
= lxml_free_str
;
2764 if (internal_attach_property(pg
, p
) != 0)
2765 internal_property_free(p
);
2768 r
= lxml_get_prop_pattern_attributes(pg
, prop_pattern
);
2773 * Now process the elements of prop_pattern
2775 for (cursor
= prop_pattern
->xmlChildrenNode
;
2777 cursor
= cursor
->next
) {
2778 if (lxml_ignorable_block(cursor
))
2781 switch (lxml_xlate_element(cursor
->name
)) {
2782 case SC_CARDINALITY
:
2783 r
= lxml_get_tm_cardinality(service
, pg
, cursor
);
2788 r
= lxml_get_tm_choices(service
, pg
, cursor
);
2792 case SC_COMMON_NAME
:
2793 (void) lxml_get_all_loctext(service
, pg
, cursor
,
2794 COMMON_NAME_FMT
, (const char *)cursor
->name
);
2796 case SC_CONSTRAINTS
:
2797 r
= lxml_get_tm_constraints(service
, pg
, cursor
);
2801 case SC_DESCRIPTION
:
2802 (void) lxml_get_all_loctext(service
, pg
, cursor
,
2803 DESCRIPTION_FMT
, (const char *)cursor
->name
);
2805 case SC_INTERNAL_SEPARATORS
:
2806 r
= lxml_get_tm_internal_seps(service
, pg
, cursor
);
2811 (void) lxml_get_all_loctext(service
, pg
, cursor
,
2812 UNITS_FMT
, "units");
2815 (void) lxml_get_tm_values(service
, pg
, cursor
);
2819 * The visibility element is empty, so we only need
2820 * to proccess the value attribute.
2822 (void) new_str_prop_from_attr(pg
,
2823 SCF_PROPERTY_TM_VISIBILITY
, SCF_TYPE_ASTRING
,
2824 cursor
, value_attr
);
2827 uu_die(gettext("illegal element \"%s\" in prop_pattern "
2828 "for service \"%s\"\n"), cursor
->name
,
2834 xmlFree(prop_pattern_name
);
2840 * Get the pg_pattern attributes and save them as properties in the
2841 * property group at pg. The pg_pattern element accepts four attributes --
2842 * name, type, required and target.
2845 lxml_get_pg_pattern_attributes(pgroup_t
*pg
, xmlNodePtr cursor
)
2847 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_TM_NAME
,
2848 SCF_TYPE_ASTRING
, cursor
, name_attr
, NULL
) != 0) {
2851 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_TM_TYPE
,
2852 SCF_TYPE_ASTRING
, cursor
, type_attr
, NULL
) != 0) {
2855 if (new_opt_str_prop_from_attr(pg
, SCF_PROPERTY_TM_TARGET
,
2856 SCF_TYPE_ASTRING
, cursor
, target_attr
, NULL
) != 0) {
2859 if (new_bool_prop_from_attr(pg
, SCF_PROPERTY_TM_REQUIRED
, cursor
,
2860 required_attr
) != 0)
2866 * There are several restrictions on the pg_pattern attributes that cannot
2867 * be specifed in the service bundle DTD. This function verifies that
2868 * those restrictions have been satisfied. The restrictions are:
2870 * - The target attribute may have a value of "instance" only when the
2871 * template block is in a service declaration.
2873 * - The target attribute may have a value of "delegate" only when the
2874 * template block applies to a restarter.
2876 * - The target attribute may have a value of "all" only when the
2877 * template block applies to the master restarter.
2879 * The function returns 0 on success and -1 on failure.
2882 verify_pg_pattern_attributes(entity_t
*s
, pgroup_t
*pg
)
2888 /* Find the value of the target property. */
2889 target
= internal_property_find(pg
, SCF_PROPERTY_TM_TARGET
);
2890 if (target
== NULL
) {
2891 uu_die(gettext("pg_pattern is missing the %s attribute "
2892 "in %s\n"), target_attr
, s
->sc_name
);
2895 v
= uu_list_first(target
->sc_property_values
);
2897 assert(v
->sc_type
== SCF_TYPE_ASTRING
);
2900 * If target has a value of instance, the template must be in a
2903 if (strcmp(v
->sc_u
.sc_string
, "instance") == 0) {
2904 if (s
->sc_etype
!= SVCCFG_SERVICE_OBJECT
) {
2905 uu_warn(gettext("pg_pattern %s attribute may only "
2906 "have a value of \"instance\" when it is in a "
2907 "service declaration.\n"), target_attr
);
2913 * If target has a value of "delegate", the template must be in a
2916 if (strcmp(v
->sc_u
.sc_string
, "delegate") == 0) {
2918 if ((s
->sc_etype
== SVCCFG_SERVICE_OBJECT
) &&
2919 (s
->sc_u
.sc_service
.sc_service_type
== SVCCFG_RESTARTER
)) {
2922 if ((s
->sc_etype
== SVCCFG_INSTANCE_OBJECT
) &&
2923 (s
->sc_parent
->sc_u
.sc_service
.sc_service_type
==
2924 SVCCFG_RESTARTER
)) {
2927 if (is_restarter
== 0) {
2928 uu_warn(gettext("pg_pattern %s attribute has a "
2929 "value of \"delegate\" but is not in a "
2930 "restarter service\n"), target_attr
);
2936 * If target has a value of "all", the template must be in the
2937 * global (SCF_SERVICE_GLOBAL) service.
2939 if (strcmp(v
->sc_u
.sc_string
, all_value
) == 0) {
2940 if (s
->sc_etype
!= SVCCFG_SERVICE_OBJECT
) {
2941 uu_warn(gettext("pg_pattern %s attribute has a "
2942 "value of \"%s\" but is not in a "
2943 "service entity.\n"), target_attr
, all_value
);
2946 if (strcmp(s
->sc_fmri
, SCF_SERVICE_GLOBAL
) != 0) {
2947 uu_warn(gettext("pg_pattern %s attribute has a "
2948 "value of \"%s\" but is in the \"%s\" service. "
2949 "pg_patterns with target \"%s\" are only allowed "
2950 "in the global service.\n"),
2951 target_attr
, all_value
, s
->sc_fmri
, all_value
);
2960 lxml_get_tm_pg_pattern(entity_t
*service
, xmlNodePtr pg_pattern
)
2965 pgroup_t
*pg
= NULL
;
2970 pg_name
= safe_malloc(max_scf_name_len
+ 1);
2973 * Get the name and type attributes. Their presence or absence
2974 * determines whcih prefix we will use for the property group name.
2975 * There are four cases -- neither attribute is present, both are
2976 * present, only name is present or only type is present.
2978 name
= xmlGetProp(pg_pattern
, (xmlChar
*)name_attr
);
2979 type
= xmlGetProp(pg_pattern
, (xmlChar
*)type_attr
);
2980 if ((name
== NULL
) || (*name
== 0)) {
2981 if ((type
== NULL
) || (*type
== 0)) {
2982 /* PG name contains only the prefix in this case */
2983 if (strlcpy(pg_name
, SCF_PG_TM_PG_PATTERN_PREFIX
,
2984 max_scf_name_len
+ 1) >= max_scf_name_len
+ 1) {
2985 uu_die(gettext("Unable to create pg_pattern "
2986 "property for %s\n"), service
->sc_name
);
2990 * If we have a type and no name, the type becomes
2991 * part of the pg_pattern property group name.
2993 if ((out_len
= snprintf(pg_name
, max_scf_name_len
+ 1,
2994 "%s%s", SCF_PG_TM_PG_PATTERN_T_PREFIX
, type
)) >=
2995 max_scf_name_len
+ 1) {
2996 uu_die(gettext("pg_pattern type is for %s is "
2997 "%d bytes too long\n"), service
->sc_name
,
2998 out_len
- max_scf_name_len
);
3004 /* Make sure that the name is valid. */
3005 if (uu_check_name((const char *)name
, UU_NAME_DOMAIN
) != 0) {
3006 semerr(gettext("pg_pattern name attribute, \"%s\", "
3007 "for %s is invalid\n"), name
, service
->sc_name
);
3012 * As long as the pg_pattern has a name, it becomes part of
3013 * the name of the pg_pattern property group name. We
3014 * merely need to pick the appropriate prefix.
3016 if ((type
== NULL
) || (*type
== 0)) {
3017 prefix
= SCF_PG_TM_PG_PATTERN_N_PREFIX
;
3019 prefix
= SCF_PG_TM_PG_PATTERN_NT_PREFIX
;
3021 if ((out_len
= snprintf(pg_name
, max_scf_name_len
+ 1, "%s%s",
3022 prefix
, name
)) >= max_scf_name_len
+ 1) {
3023 uu_die(gettext("pg_pattern property group name "
3024 "for %s is %d bytes too long\n"), service
->sc_name
,
3025 out_len
- max_scf_name_len
);
3030 * Create the property group for holding this pg_pattern
3031 * information, and capture the pg_pattern attributes.
3033 pg
= internal_pgroup_create_strict(service
, pg_name
,
3034 SCF_GROUP_TEMPLATE_PG_PATTERN
);
3036 if ((name
== NULL
) || (*name
== 0)) {
3037 if ((type
== NULL
) ||(*type
== 0)) {
3038 semerr(gettext("pg_pattern with empty name and "
3039 "type is not unique in %s\n"),
3042 semerr(gettext("pg_pattern with empty name and "
3043 "type \"%s\" is not unique in %s\n"),
3044 type
, service
->sc_name
);
3047 if ((type
== NULL
) || (*type
== 0)) {
3048 semerr(gettext("pg_pattern with name \"%s\" "
3049 "and empty type is not unique in %s\n"),
3050 name
, service
->sc_name
);
3052 semerr(gettext("pg_pattern with name \"%s\" "
3053 "and type \"%s\" is not unique in %s\n"),
3054 name
, type
, service
->sc_name
);
3061 * Get the pg_pattern attributes from the manifest and verify
3062 * that they satisfy our restrictions.
3064 r
= lxml_get_pg_pattern_attributes(pg
, pg_pattern
);
3067 if (verify_pg_pattern_attributes(service
, pg
) != 0) {
3068 semerr(gettext("Invalid pg_pattern attributes in %s\n"),
3075 * Now process all of the elements of pg_pattern.
3077 for (cursor
= pg_pattern
->xmlChildrenNode
;
3079 cursor
= cursor
->next
) {
3080 if (lxml_ignorable_block(cursor
))
3083 switch (lxml_xlate_element(cursor
->name
)) {
3084 case SC_COMMON_NAME
:
3085 (void) lxml_get_all_loctext(service
, pg
, cursor
,
3086 COMMON_NAME_FMT
, (const char *)cursor
->name
);
3088 case SC_DESCRIPTION
:
3089 (void) lxml_get_all_loctext(service
, pg
, cursor
,
3090 DESCRIPTION_FMT
, (const char *)cursor
->name
);
3092 case SC_PROP_PATTERN
:
3093 r
= lxml_get_tm_prop_pattern(service
, cursor
,
3099 uu_die(gettext("illegal element \"%s\" in pg_pattern "
3100 "for service \"%s\"\n"), cursor
->name
,
3106 if ((r
!= 0) && (pg
!= NULL
)) {
3107 internal_detach_pgroup(service
, pg
);
3108 internal_pgroup_free(pg
);
3118 lxml_get_template(entity_t
*service
, xmlNodePtr templ
)
3122 for (cursor
= templ
->xmlChildrenNode
; cursor
!= NULL
;
3123 cursor
= cursor
->next
) {
3124 if (lxml_ignorable_block(cursor
))
3127 switch (lxml_xlate_element(cursor
->name
)) {
3128 case SC_COMMON_NAME
:
3129 (void) lxml_get_tm_common_name(service
, cursor
);
3131 case SC_DESCRIPTION
:
3132 (void) lxml_get_tm_description(service
, cursor
);
3134 case SC_DOCUMENTATION
:
3135 (void) lxml_get_tm_documentation(service
, cursor
);
3138 if (lxml_get_tm_pg_pattern(service
, cursor
) != 0)
3142 uu_die(gettext("illegal element \"%s\" on template "
3143 "for service \"%s\"\n"),
3144 cursor
->name
, service
->sc_name
);
3152 lxml_get_default_instance(entity_t
*service
, xmlNodePtr definst
)
3159 uint64_t enabled_val
= 0;
3161 i
= internal_instance_new("default");
3163 if ((enabled
= xmlGetProp(definst
, (xmlChar
*)enabled_attr
)) != NULL
) {
3164 enabled_val
= (strcmp(true, (const char *)enabled
) == 0) ?
3170 * New general property group with enabled boolean property set.
3173 i
->sc_op
= service
->sc_op
;
3174 pg
= internal_pgroup_new();
3175 (void) internal_attach_pgroup(i
, pg
);
3177 pg
->sc_pgroup_name
= (char *)scf_pg_general
;
3178 pg
->sc_pgroup_type
= (char *)scf_group_framework
;
3179 pg
->sc_pgroup_flags
= 0;
3181 p
= internal_property_create(SCF_PROPERTY_ENABLED
, SCF_TYPE_BOOLEAN
, 1,
3184 (void) internal_attach_property(pg
, p
);
3187 * Add general/package property if PKGINST is set.
3189 if ((package
= getenv("PKGINST")) != NULL
) {
3190 p
= internal_property_create(SCF_PROPERTY_PACKAGE
,
3191 SCF_TYPE_ASTRING
, 1, package
);
3193 (void) internal_attach_property(pg
, p
);
3196 return (internal_attach_entity(service
, i
));
3200 * Translate an instance element into an internal property tree, added to
3201 * service. If op is SVCCFG_OP_APPLY (i.e., apply a profile), set the
3202 * enabled property to override.
3204 * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3205 * modification of template data.
3208 lxml_get_instance(entity_t
*service
, xmlNodePtr inst
, bundle_type_t bt
,
3219 * Fetch its attributes, as appropriate.
3221 i
= internal_instance_new((char *)xmlGetProp(inst
,
3222 (xmlChar
*)name_attr
));
3225 * Note that this must be done before walking the children so that
3226 * sc_fmri is set in case we enter lxml_get_dependent().
3228 r
= internal_attach_entity(service
, i
);
3233 enabled
= xmlGetProp(inst
, (xmlChar
*)enabled_attr
);
3235 if (enabled
== NULL
) {
3236 if (bt
== SVCCFG_MANIFEST
) {
3237 semerr(gettext("Instance \"%s\" missing attribute "
3238 "\"%s\".\n"), i
->sc_name
, enabled_attr
);
3241 } else { /* enabled != NULL */
3242 if (strcmp(true, (const char *)enabled
) != 0 &&
3243 strcmp(false, (const char *)enabled
) != 0) {
3245 semerr(gettext("Invalid enabled value\n"));
3248 pg
= internal_pgroup_new();
3249 (void) internal_attach_pgroup(i
, pg
);
3251 pg
->sc_pgroup_name
= (char *)scf_pg_general
;
3252 pg
->sc_pgroup_type
= (char *)scf_group_framework
;
3253 pg
->sc_pgroup_flags
= 0;
3255 e_val
= (strcmp(true, (const char *)enabled
) == 0);
3256 p
= internal_property_create(SCF_PROPERTY_ENABLED
,
3257 SCF_TYPE_BOOLEAN
, 1, (uint64_t)e_val
);
3259 p
->sc_property_override
= (op
== SVCCFG_OP_APPLY
);
3261 (void) internal_attach_property(pg
, p
);
3267 * Walk its child elements, as appropriate.
3269 for (cursor
= inst
->xmlChildrenNode
; cursor
!= NULL
;
3270 cursor
= cursor
->next
) {
3271 if (lxml_ignorable_block(cursor
))
3274 switch (lxml_xlate_element(cursor
->name
)) {
3276 (void) lxml_get_restarter(i
, cursor
);
3279 (void) lxml_get_dependency(i
, cursor
);
3282 (void) lxml_get_dependent(i
, cursor
);
3284 case SC_METHOD_CONTEXT
:
3285 (void) lxml_get_entity_method_context(i
, cursor
);
3287 case SC_EXEC_METHOD
:
3288 (void) lxml_get_exec_method(i
, cursor
);
3290 case SC_PROPERTY_GROUP
:
3291 (void) lxml_get_pgroup(i
, cursor
);
3294 if (op
== SVCCFG_OP_APPLY
) {
3295 semerr(gettext("Template data for \"%s\" may "
3296 "not be modified in a profile.\n"),
3302 if (lxml_get_template(i
, cursor
) != 0)
3305 case SC_NOTIFICATION_PARAMETERS
:
3306 if (lxml_get_notification_parameters(i
, cursor
) != 0)
3311 "illegal element \"%s\" on instance \"%s\"\n"),
3312 cursor
->name
, i
->sc_name
);
3322 lxml_get_single_instance(entity_t
*entity
, xmlNodePtr si
)
3328 pg
= internal_pgroup_find_or_create(entity
, (char *)scf_pg_general
,
3329 (char *)scf_group_framework
);
3331 p
= internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE
,
3332 SCF_TYPE_BOOLEAN
, 1, (uint64_t)1);
3334 r
= internal_attach_property(pg
, p
);
3336 internal_property_free(p
);
3344 * Check to see if the service should allow the upgrade
3345 * process to handle adding of the manifestfiles linkage.
3347 * If the service exists and does not have a manifestfiles
3348 * property group then the upgrade process should handle
3351 * If the service doesn't exist or the service exists
3352 * and has a manifestfiles property group then the import
3353 * process can handle the manifestfiles property group
3356 * This prevents potential cleanup of unaccounted for instances
3357 * in early manifest import due to upgrade process needing
3358 * information that has not yet been supplied by manifests
3359 * that are still located in the /var/svc manifests directory.
3362 lxml_check_upgrade(const char *service
)
3364 scf_handle_t
*h
= NULL
;
3365 scf_scope_t
*sc
= NULL
;
3366 scf_service_t
*svc
= NULL
;
3367 scf_propertygroup_t
*pg
= NULL
;
3368 int rc
= SCF_FAILED
;
3370 if ((h
= scf_handle_create(SCF_VERSION
)) == NULL
||
3371 (sc
= scf_scope_create(h
)) == NULL
||
3372 (svc
= scf_service_create(h
)) == NULL
||
3373 (pg
= scf_pg_create(h
)) == NULL
)
3376 if (scf_handle_bind(h
) != 0)
3379 if (scf_handle_get_scope(h
, SCF_FMRI_LOCAL_SCOPE
, sc
) == -1)
3382 if (scf_scope_get_service(sc
, service
, svc
) != SCF_SUCCESS
) {
3383 if (scf_error() == SCF_ERROR_NOT_FOUND
)
3389 if (scf_service_get_pg(svc
, SCF_PG_MANIFESTFILES
, pg
) != SCF_SUCCESS
)
3395 scf_service_destroy(svc
);
3396 scf_scope_destroy(sc
);
3397 scf_handle_destroy(h
);
3403 * Translate a service element into an internal instance/property tree, added
3406 * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3407 * modification of template data.
3410 lxml_get_service(bundle_t
*bundle
, xmlNodePtr svc
, svccfg_op_t op
)
3421 * Fetch attributes, as appropriate.
3423 s
= internal_service_new((char *)xmlGetProp(svc
,
3424 (xmlChar
*)name_attr
));
3426 version
= xmlGetProp(svc
, (xmlChar
*)version_attr
);
3427 s
->sc_u
.sc_service
.sc_service_version
= atol((const char *)version
);
3430 type
= xmlGetProp(svc
, (xmlChar
*)type_attr
);
3431 s
->sc_u
.sc_service
.sc_service_type
= lxml_xlate_service_type(type
);
3435 * Set the global missing type to false before processing the service
3437 est
->sc_miss_type
= B_FALSE
;
3441 * Now that the service is created create the manifest
3442 * property group and add the property value of the service.
3444 if (lxml_check_upgrade(s
->sc_name
) == SCF_SUCCESS
&&
3445 svc
->doc
->name
!= NULL
&&
3446 bundle
->sc_bundle_type
== SVCCFG_MANIFEST
) {
3447 char *buf
, *base
, *fname
, *bname
;
3451 * Must remove the PKG_INSTALL_ROOT, point to the correct
3452 * directory after install
3454 bname
= uu_zalloc(PATH_MAX
+ 1);
3455 if (realpath(svc
->doc
->name
, bname
) == NULL
) {
3456 uu_die(gettext("Unable to create the real path of the "
3457 "manifest file \"%s\" : %d\n"), svc
->doc
->name
,
3461 base
= getenv("PKG_INSTALL_ROOT");
3462 if (base
!= NULL
&& strncmp(bname
, base
, strlen(base
)) == 0) {
3463 base_sz
= strlen(base
);
3465 fname
= safe_strdup(bname
+ base_sz
);
3468 buf
= mhash_filename_to_propname(svc
->doc
->name
, B_FALSE
);
3470 pg
= internal_pgroup_create_strict(s
, SCF_PG_MANIFESTFILES
,
3471 SCF_GROUP_FRAMEWORK
);
3474 uu_die(gettext("Property group for prop_pattern, "
3475 "\"%s\", already exists in %s\n"),
3476 SCF_PG_MANIFESTFILES
, s
->sc_name
);
3479 p
= internal_property_create(buf
, SCF_TYPE_ASTRING
, 1, fname
);
3481 (void) internal_attach_property(pg
, p
);
3485 * Walk its child elements, as appropriate.
3487 for (cursor
= svc
->xmlChildrenNode
; cursor
!= NULL
;
3488 cursor
= cursor
->next
) {
3489 if (lxml_ignorable_block(cursor
))
3492 e
= lxml_xlate_element(cursor
->name
);
3496 if (lxml_get_instance(s
, cursor
,
3497 bundle
->sc_bundle_type
, op
) != 0)
3501 if (op
== SVCCFG_OP_APPLY
) {
3502 semerr(gettext("Template data for \"%s\" may "
3503 "not be modified in a profile.\n"),
3509 if (lxml_get_template(s
, cursor
) != 0)
3512 case SC_NOTIFICATION_PARAMETERS
:
3513 if (lxml_get_notification_parameters(s
, cursor
) != 0)
3517 (void) lxml_get_entity_stability(s
, cursor
);
3520 (void) lxml_get_dependency(s
, cursor
);
3523 (void) lxml_get_dependent(s
, cursor
);
3526 (void) lxml_get_restarter(s
, cursor
);
3528 case SC_EXEC_METHOD
:
3529 (void) lxml_get_exec_method(s
, cursor
);
3531 case SC_METHOD_CONTEXT
:
3532 (void) lxml_get_entity_method_context(s
, cursor
);
3534 case SC_PROPERTY_GROUP
:
3535 (void) lxml_get_pgroup(s
, cursor
);
3537 case SC_INSTANCE_CREATE_DEFAULT
:
3538 (void) lxml_get_default_instance(s
, cursor
);
3540 case SC_INSTANCE_SINGLE
:
3541 (void) lxml_get_single_instance(s
, cursor
);
3545 "illegal element \"%s\" on service \"%s\"\n"),
3546 cursor
->name
, s
->sc_name
);
3552 * Now that the service has been processed set the missing type
3553 * for the service. So that only the services with missing
3554 * types are processed.
3556 s
->sc_miss_type
= est
->sc_miss_type
;
3557 if (est
->sc_miss_type
)
3558 est
->sc_miss_type
= B_FALSE
;
3560 return (internal_attach_service(bundle
, s
));
3565 lxml_dump(int g
, xmlNodePtr p
)
3568 (void) printf("%d %s\n", g
, p
->name
);
3570 for (p
= p
->xmlChildrenNode
; p
!= NULL
; p
= p
->next
)
3571 lxml_dump(g
+ 1, p
);
3577 lxml_is_known_dtd(const xmlChar
*dtdname
)
3579 if (dtdname
== NULL
||
3580 strcmp(MANIFEST_DTD_PATH
, (const char *)dtdname
) != 0)
3587 lxml_get_bundle(bundle_t
*bundle
, bundle_type_t bundle_type
,
3588 xmlNodePtr subbundle
, svccfg_op_t op
)
3595 * 1. Get bundle attributes.
3597 type
= xmlGetProp(subbundle
, (xmlChar
*)type_attr
);
3598 bundle
->sc_bundle_type
= lxml_xlate_bundle_type(type
);
3599 if (bundle
->sc_bundle_type
!= bundle_type
&&
3600 bundle_type
!= SVCCFG_UNKNOWN_BUNDLE
) {
3601 semerr(gettext("included bundle of different type.\n"));
3608 case SVCCFG_OP_IMPORT
:
3609 if (bundle
->sc_bundle_type
!= SVCCFG_MANIFEST
) {
3610 semerr(gettext("document is not a manifest.\n"));
3614 case SVCCFG_OP_APPLY
:
3615 if (bundle
->sc_bundle_type
!= SVCCFG_PROFILE
) {
3616 semerr(gettext("document is not a profile.\n"));
3620 case SVCCFG_OP_RESTORE
:
3621 if (bundle
->sc_bundle_type
!= SVCCFG_ARCHIVE
) {
3622 semerr(gettext("document is not an archive.\n"));
3628 if (((bundle
->sc_bundle_name
= xmlGetProp(subbundle
,
3629 (xmlChar
*)name_attr
)) == NULL
) || (*bundle
->sc_bundle_name
== 0)) {
3630 semerr(gettext("service bundle lacks name attribute\n"));
3635 * 2. Get services, descend into each one and build state.
3637 for (cursor
= subbundle
->xmlChildrenNode
; cursor
!= NULL
;
3638 cursor
= cursor
->next
) {
3639 if (lxml_ignorable_block(cursor
))
3642 e
= lxml_xlate_element(cursor
->name
);
3648 case SC_SERVICE_BUNDLE
:
3649 if (lxml_get_bundle(bundle
, bundle_type
, cursor
, op
))
3653 if (lxml_get_service(bundle
, cursor
, op
) != 0)
3663 * Load an XML tree from filename and translate it into an internal service
3664 * tree bundle. Require that the bundle be of appropriate type for the
3665 * operation: archive for RESTORE, manifest for IMPORT, profile for APPLY.
3668 lxml_get_bundle_file(bundle_t
*bundle
, const char *filename
, svccfg_op_t op
)
3672 xmlDtdPtr dtd
= NULL
;
3673 xmlValidCtxtPtr vcp
;
3674 boolean_t do_validate
;
3675 char *dtdpath
= NULL
;
3679 * Verify we can read the file before we try to parse it.
3681 if (access(filename
, R_OK
| F_OK
) == -1) {
3682 semerr(gettext("unable to open file: %s\n"), strerror(errno
));
3687 * Until libxml2 addresses DTD-based validation with XInclude, we don't
3688 * validate service profiles (i.e. the apply path).
3690 do_validate
= (op
!= SVCCFG_OP_APPLY
) &&
3691 (getenv("SVCCFG_NOVALIDATE") == NULL
);
3693 dtdpath
= getenv("SVCCFG_DTD");
3695 if (dtdpath
!= NULL
)
3696 xmlLoadExtDtdDefaultValue
= 0;
3698 if ((document
= xmlReadFile(filename
, NULL
, 0)) == NULL
) {
3699 semerr(gettext("couldn't parse document\n"));
3703 document
->name
= safe_strdup(filename
);
3706 * Verify that this is a document type we understand.
3708 if ((dtd
= xmlGetIntSubset(document
)) == NULL
) {
3709 semerr(gettext("document has no DTD\n"));
3711 } else if (dtdpath
== NULL
&& !do_validate
) {
3713 * If apply then setup so that some validation
3714 * for specific elements can be done.
3716 dtdpath
= (char *)document
->intSubset
->SystemID
;
3719 if (!lxml_is_known_dtd(dtd
->SystemID
)) {
3720 semerr(gettext("document DTD unknown; not service bundle?\n"));
3724 if ((cursor
= xmlDocGetRootElement(document
)) == NULL
) {
3725 semerr(gettext("document is empty\n"));
3726 xmlFreeDoc(document
);
3730 if (xmlStrcmp(cursor
->name
, (const xmlChar
*)"service_bundle") != 0) {
3731 semerr(gettext("document is not a service bundle\n"));
3732 xmlFreeDoc(document
);
3737 if (dtdpath
!= NULL
) {
3738 dtd
= xmlParseDTD(NULL
, (xmlChar
*)dtdpath
);
3740 semerr(gettext("Could not parse DTD \"%s\".\n"),
3745 if (document
->extSubset
!= NULL
)
3746 xmlFreeDtd(document
->extSubset
);
3748 document
->extSubset
= dtd
;
3751 if (xmlXIncludeProcessFlags(document
, XML_PARSE_XINCLUDE
) == -1) {
3752 semerr(gettext("couldn't handle XInclude statements "
3758 vcp
= xmlNewValidCtxt();
3760 uu_die(gettext("could not allocate memory"));
3761 vcp
->warning
= xmlParserValidityWarning
;
3762 vcp
->error
= xmlParserValidityError
;
3764 r
= xmlValidateDocument(vcp
, document
);
3766 xmlFreeValidCtxt(vcp
);
3769 semerr(gettext("Document is not valid.\n"));
3770 xmlFreeDoc(document
);
3776 lxml_dump(0, cursor
);
3779 r
= lxml_get_bundle(bundle
, SVCCFG_UNKNOWN_BUNDLE
, cursor
, op
);
3781 xmlFreeDoc(document
);
3787 lxml_inventory(const char *filename
)
3790 uu_list_walk_t
*svcs
, *insts
;
3791 entity_t
*svc
, *inst
;
3793 b
= internal_bundle_new();
3795 if (lxml_get_bundle_file(b
, filename
, SVCCFG_OP_IMPORT
) != 0) {
3796 internal_bundle_free(b
);
3800 svcs
= uu_list_walk_start(b
->sc_bundle_services
, 0);
3802 uu_die(gettext("Couldn't walk services"));
3804 while ((svc
= uu_list_walk_next(svcs
)) != NULL
) {
3805 uu_list_t
*inst_list
;
3807 inst_list
= svc
->sc_u
.sc_service
.sc_service_instances
;
3808 insts
= uu_list_walk_start(inst_list
, 0);
3810 uu_die(gettext("Couldn't walk instances"));
3812 while ((inst
= uu_list_walk_next(insts
)) != NULL
)
3813 (void) printf("svc:/%s:%s\n", svc
->sc_name
,
3816 uu_list_walk_end(insts
);
3819 uu_list_walk_end(svcs
);
3821 svcs
= uu_list_walk_start(b
->sc_bundle_services
, 0);
3822 while ((svc
= uu_list_walk_next(svcs
)) != NULL
) {
3823 (void) fputs("svc:/", stdout
);
3824 (void) puts(svc
->sc_name
);
3826 uu_list_walk_end(svcs
);
3828 internal_bundle_free(b
);