svccfg: remove -Wno-unitialized
[unleashed.git] / usr / src / cmd / svc / svccfg / svccfg_xml.c
blob1ad8f185b32f64e56427cb016f238e66bab81c65
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
41 #include <assert.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <libintl.h>
45 #include <libscf.h>
46 #include <libscf_priv.h>
47 #include <libuutil.h>
48 #include <sasl/saslutil.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <limits.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <unistd.h>
57 #include <sys/param.h>
58 #include "manifest_hash.h"
60 #include "svccfg.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")
80 /* Attribute names */
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
110 * element_t array
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
178 * element_t array
180 static const char *lxml_prop_types[] = {
181 "astring", /* SC_ASTRING */
182 "boolean", /* SC_BOOLEAN */
183 "", /* SC_CARDINALITY */
184 "", /* SC_CHOICES */
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 */
194 "", /* SC_ENABLED */
195 "", /* SC_EVENT */
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 */
204 "", /* SC_LOCTEXT */
205 "", /* SC_MANPAGE */
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 */
222 "", /* SC_PROPVAL */
223 "", /* SC_RANGE */
224 "", /* SC_RESTARTER */
225 "", /* SC_SERVICE */
226 "", /* SC_SERVICE_BUNDLE */
227 "", /* SC_SERVICE_FMRI */
228 "", /* SC_INSTANCE_SINGLE */
229 "", /* SC_STABILITY */
230 "", /* SC_TEMPLATE */
231 "time", /* SC_TIME */
232 "", /* SC_TYPE */
233 "", /* SC_UNITS */
234 "uri", /* SC_URI */
235 "ustring", /* SC_USTRING */
236 "", /* SC_VALUE */
237 "", /* SC_VALUE_NODE */
238 "", /* SC_VALUES */
239 "", /* SC_VISIBILITY */
240 "", /* SC_XI_FALLBACK */
241 "" /* SC_XI_INCLUDE */
245 lxml_init()
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;
256 return (0);
259 static bundle_type_t
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);
289 static element_t
290 lxml_xlate_element(const xmlChar *tag)
292 int i;
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);
301 static uint_t
302 lxml_xlate_boolean(const xmlChar *value)
304 if (xmlStrcmp(value, (const xmlChar *)true) == 0)
305 return (1);
307 if (xmlStrcmp(value, (const xmlChar *)false) == 0)
308 return (0);
310 uu_die(gettext("illegal boolean value \"%s\"\n"), value);
312 /*NOTREACHED*/
315 static scf_type_t
316 lxml_element_to_type(element_t type)
318 switch (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);
334 default:
335 uu_die(gettext("unknown value type (%d)\n"), type);
338 /* NOTREACHED */
341 static element_t
342 lxml_type_to_element(scf_type_t type)
344 switch (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);
360 default:
361 uu_die(gettext("unknown value type (%d)\n"), type);
364 /* NOTREACHED */
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.
377 static int
378 new_bool_prop_from_attr(pgroup_t *pgrp, const char *pname, xmlNodePtr n,
379 const char *attr)
381 uint64_t bool;
382 xmlChar *val;
383 property_t *p;
384 int r;
386 val = xmlGetProp(n, (xmlChar *)attr);
387 if (val == NULL)
388 return (0);
390 if ((xmlStrcmp(val, (xmlChar *)"0") == 0) ||
391 (xmlStrcmp(val, (xmlChar *)"false") == 0)) {
392 bool = 0;
393 } else if ((xmlStrcmp(val, (xmlChar *)"1") == 0) ||
394 (xmlStrcmp(val, (xmlChar *)"true") == 0)) {
395 bool = 1;
396 } else {
397 xmlFree(val);
398 return (-1);
400 xmlFree(val);
401 p = internal_property_create(pname, SCF_TYPE_BOOLEAN, 1, bool);
402 r = internal_attach_property(pgrp, p);
404 if (r != 0)
405 internal_property_free(p);
407 return (r);
410 static int
411 new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
412 xmlNodePtr n, const char *attr)
414 xmlChar *val;
415 property_t *p;
416 int r;
418 val = xmlGetProp(n, (xmlChar *)attr);
420 p = internal_property_create(pname, ty, 1, val);
421 r = internal_attach_property(pgrp, p);
423 if (r != 0)
424 internal_property_free(p);
426 return (r);
429 static int
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)
433 xmlChar *val;
434 property_t *p;
435 int r;
437 val = xmlGetProp(n, (xmlChar *)attr);
438 if (val == NULL) {
439 if (dflt == NULL) {
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.
447 return (0);
448 } else {
449 val = (xmlChar *)dflt;
453 p = internal_property_create(pname, ty, 1, val);
454 r = internal_attach_property(pgrp, p);
456 if (r != 0)
457 internal_property_free(p);
459 return (r);
462 static int
463 lxml_ignorable_block(xmlNodePtr n)
465 return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 ||
466 xmlStrcmp(n->name, (xmlChar *)"comment") == 0) ? 1 : 0);
469 static void
470 lxml_validate_element(xmlNodePtr n)
472 xmlValidCtxtPtr vcp;
474 if (n->doc == NULL)
475 uu_die(gettext("Could not validate element\n"));
477 if (n->doc->extSubset == NULL) {
478 xmlDtdPtr dtd;
479 dtd = xmlParseDTD(NULL, n->doc->intSubset->SystemID);
481 if (dtd == NULL) {
482 uu_die(gettext("Could not parse DTD \"%s\".\n"),
483 n->doc->intSubset->SystemID);
486 n->doc->extSubset = dtd;
489 vcp = xmlNewValidCtxt();
490 if (vcp == NULL)
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);
502 static int
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)) ==
509 NULL)
510 return (-1);
512 if (scf_value == NULL && (scf_value = scf_value_create(scf_hndl)) ==
513 NULL)
514 return (-1);
516 return (scf_value_set_from_string(scf_value, type, v));
519 static void
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.
536 void
537 lxml_store_value(value_t *v, element_t type, const xmlChar *value)
539 char *endptr;
540 int fov = 0;
541 scf_type_t scf_type = SCF_TYPE_INVALID;
543 if (value == NULL) {
544 type = lxml_type_to_element(v->sc_type);
545 value = (const xmlChar *)v->sc_u.sc_string;
546 fov = 1;
549 switch (type) {
550 case SC_COUNT:
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).
556 errno = 0;
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"));
564 break;
565 case SC_INTEGER:
566 errno = 0;
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");
573 break;
574 case SC_OPAQUE:
575 case SC_HOST:
576 case SC_HOSTNAME:
577 case SC_NET_ADDR:
578 case SC_NET_ADDR_V4:
579 case SC_NET_ADDR_V6:
580 case SC_FMRI:
581 case SC_URI:
582 case SC_TIME:
583 case SC_ASTRING:
584 case SC_USTRING:
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;
596 break;
597 case SC_BOOLEAN:
598 v->sc_u.sc_count = lxml_xlate_boolean(value);
599 break;
600 default:
601 uu_die(gettext("unknown value type (%d)\n"), type);
602 break;
605 /* Free the old value */
606 if (fov && v->sc_free != NULL)
607 free((char *)value);
610 static value_t *
611 lxml_make_value(element_t type, const xmlChar *value)
613 value_t *v;
615 v = internal_value_new();
617 v->sc_type = lxml_element_to_type(type);
619 lxml_store_value(v, type, value);
621 return (v);
624 static int
625 lxml_get_value(property_t *prop, element_t vtype, xmlNodePtr value)
627 xmlNodePtr cursor;
629 for (cursor = value->xmlChildrenNode; cursor != NULL;
630 cursor = cursor->next) {
631 xmlChar *assigned_value;
632 value_t *v;
634 if (lxml_ignorable_block(cursor))
635 continue;
637 switch (lxml_xlate_element(cursor->name)) {
638 case SC_VALUE_NODE:
639 if ((assigned_value = xmlGetProp(cursor,
640 (xmlChar *)value_attr)) == NULL)
641 uu_die(gettext("no value on value node?\n"));
642 break;
643 default:
644 uu_die(gettext("value list contains illegal element "
645 "\'%s\'\n"), cursor->name);
646 break;
649 v = lxml_make_value(vtype, assigned_value);
651 xmlFree(assigned_value);
653 internal_attach_value(prop, v);
656 return (0);
659 static int
660 lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
662 property_t *p;
663 element_t r;
664 value_t *v;
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)) {
677 for (r = 0;
678 r < sizeof (lxml_prop_types) / sizeof (char *); ++r) {
679 if (xmlStrcmp(type,
680 (const xmlChar *)lxml_prop_types[r]) == 0)
681 break;
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;
698 r = SC_ASTRING;
699 } else {
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);
705 if (val == NULL)
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);
710 xmlFree(val);
711 internal_attach_value(p, v);
713 xmlFree(type);
715 override = xmlGetProp(propval, (xmlChar *)override_attr);
716 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
717 xmlFree(override);
719 return (internal_attach_property(pgrp, p));
722 static int
723 lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
725 property_t *p;
726 xmlNodePtr cursor;
727 element_t r;
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)) {
740 for (r = 0;
741 r < sizeof (lxml_prop_types) / sizeof (char *); r++) {
742 if (xmlStrcmp(type,
743 (const xmlChar *)lxml_prop_types[r]) == 0)
744 break;
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;
760 } else {
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))
769 continue;
771 switch (r = lxml_xlate_element(cursor->name)) {
772 case SC_ASTRING:
773 case SC_BOOLEAN:
774 case SC_COUNT:
775 case SC_FMRI:
776 case SC_HOST:
777 case SC_HOSTNAME:
778 case SC_INTEGER:
779 case SC_NET_ADDR:
780 case SC_NET_ADDR_V4:
781 case SC_NET_ADDR_V6:
782 case SC_OPAQUE:
783 case SC_TIME:
784 case SC_URI:
785 case SC_USTRING:
787 * If the type is invalid then this is an apply
788 * operation and the type can be taken from the
789 * value list.
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);
804 break;
805 default:
806 uu_die(gettext("unknown value list type: %s\n"),
807 cursor->name);
808 break;
812 xmlFree(type);
814 override = xmlGetProp(property, (xmlChar *)override_attr);
815 p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
816 xmlFree(override);
818 return (internal_attach_property(pgrp, p));
821 static int
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.
834 static int
835 lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup)
837 pgroup_t *pg;
838 xmlNodePtr cursor;
839 xmlChar *name, *type, *delete;
842 * property group attributes:
843 * name: string
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);
849 xmlFree(name);
850 xmlFree(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))
859 continue;
861 switch (lxml_xlate_element(cursor->name)) {
862 case SC_STABILITY:
863 (void) lxml_get_pgroup_stability(pg, cursor);
864 break;
865 case SC_PROPERTY:
866 (void) lxml_get_property(pg, cursor);
867 break;
868 case SC_PROPVAL:
869 (void) lxml_get_propval(pg, cursor);
870 break;
871 default:
872 abort();
873 break;
877 delete = xmlGetProp(pgroup, (xmlChar *)delete_attr);
878 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
879 xmlFree(delete);
881 return (0);
886 * Dependency groups, execution methods can go on either a service or an
887 * instance.
890 static int
891 lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile)
893 property_t *p;
895 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
896 1, (uint64_t)1);
897 if (internal_attach_property(pg, p) != 0)
898 return (-1);
900 return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE,
901 SCF_TYPE_ASTRING, profile, name_attr));
904 static int
905 lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred)
907 property_t *p;
909 p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
910 1, (uint64_t)0);
911 if (internal_attach_property(pg, p) != 0)
912 return (-1);
914 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING,
915 cred, "user", NULL) != 0)
916 return (-1);
918 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING,
919 cred, "group", NULL) != 0)
920 return (-1);
922 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS,
923 SCF_TYPE_ASTRING, cred, "supp_groups", NULL) != 0)
924 return (-1);
926 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES,
927 SCF_TYPE_ASTRING, cred, "privileges", NULL) != 0)
928 return (-1);
930 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES,
931 SCF_TYPE_ASTRING, cred, "limit_privileges", NULL) != 0)
932 return (-1);
934 return (0);
937 static char *
938 lxml_get_envvar(xmlNodePtr envvar)
940 char *name;
941 char *value;
942 char *ret;
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 "
949 "\"%s\".\n"), name);
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);
955 xmlFree(name);
956 xmlFree(value);
957 return (ret);
960 static int
961 lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment)
963 property_t *p;
964 xmlNodePtr cursor;
965 value_t *val;
967 p = internal_property_create(SCF_PROPERTY_ENVIRONMENT,
968 SCF_TYPE_ASTRING, 0);
970 for (cursor = environment->xmlChildrenNode; cursor != NULL;
971 cursor = cursor->next) {
972 char *tmp;
974 if (lxml_ignorable_block(cursor))
975 continue;
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);
994 return (-1);
997 return (0);
1000 static int
1001 lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx)
1003 xmlNodePtr cursor;
1005 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY,
1006 SCF_TYPE_ASTRING, ctx, "working_directory", NULL) != 0)
1007 return (-1);
1009 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT,
1010 SCF_TYPE_ASTRING, ctx, "project", NULL) != 0)
1011 return (-1);
1013 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL,
1014 SCF_TYPE_ASTRING, ctx, "resource_pool", NULL) != 0)
1015 return (-1);
1017 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SECFLAGS,
1018 SCF_TYPE_ASTRING, ctx, "security_flags", NULL) != 0)
1019 return (-1);
1021 for (cursor = ctx->xmlChildrenNode; cursor != NULL;
1022 cursor = cursor->next) {
1023 if (lxml_ignorable_block(cursor))
1024 continue;
1026 switch (lxml_xlate_element(cursor->name)) {
1027 case SC_METHOD_CREDENTIAL:
1028 (void) lxml_get_method_credential(pg, cursor);
1029 break;
1030 case SC_METHOD_PROFILE:
1031 (void) lxml_get_method_profile(pg, cursor);
1032 break;
1033 case SC_METHOD_ENVIRONMENT:
1034 (void) lxml_get_method_environment(pg, cursor);
1035 break;
1036 default:
1037 semerr(gettext("illegal element \'%s\' in method "
1038 "context\n"), (char *)cursor);
1039 break;
1043 return (0);
1046 static int
1047 lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx)
1049 pgroup_t *pg;
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));
1057 static int
1058 lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
1060 pgroup_t *pg;
1061 property_t *p;
1062 xmlChar *name, *timeout, *delete;
1063 xmlNodePtr cursor;
1064 int r = 0;
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);
1072 xmlFree(name);
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)
1078 return (-1);
1080 timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr);
1081 if (timeout != NULL) {
1082 uint64_t u_timeout;
1083 char *endptr;
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).
1089 errno = 0;
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);
1099 xmlFree(timeout);
1101 if (r != 0)
1102 return (-1);
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))
1113 continue;
1115 switch (lxml_xlate_element(cursor->name)) {
1116 case SC_STABILITY:
1117 if (lxml_get_pgroup_stability(pg, cursor) != 0)
1118 return (-1);
1119 break;
1121 case SC_METHOD_CONTEXT:
1122 (void) lxml_get_method_context(pg, cursor);
1123 break;
1125 case SC_PROPVAL:
1126 (void) lxml_get_propval(pg, cursor);
1127 break;
1129 case SC_PROPERTY:
1130 (void) lxml_get_property(pg, cursor);
1131 break;
1133 default:
1134 uu_die(gettext("illegal element \"%s\" on "
1135 "execution method \"%s\"\n"), cursor->name,
1136 pg->sc_pgroup_name);
1137 break;
1141 delete = xmlGetProp(emeth, (xmlChar *)delete_attr);
1142 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1143 xmlFree(delete);
1145 return (0);
1148 static int
1149 lxml_get_dependency(entity_t *entity, xmlNodePtr dependency)
1151 pgroup_t *pg;
1152 property_t *p;
1153 xmlNodePtr cursor;
1154 xmlChar *name;
1155 xmlChar *delete;
1158 * dependency attributes:
1159 * name: string
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);
1171 xmlFree(name);
1173 if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
1174 dependency, type_attr) != 0)
1175 return (-1);
1177 if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
1178 SCF_TYPE_ASTRING, dependency, "restart_on") != 0)
1179 return (-1);
1181 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1182 dependency, "grouping") != 0)
1183 return (-1);
1185 p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0);
1186 if (internal_attach_property(pg, p) != 0)
1187 return (-1);
1189 for (cursor = dependency->xmlChildrenNode; cursor != NULL;
1190 cursor = cursor->next) {
1191 xmlChar *value;
1192 value_t *v;
1194 if (lxml_ignorable_block(cursor))
1195 continue;
1197 switch (lxml_xlate_element(cursor->name)) {
1198 case SC_STABILITY:
1199 if (lxml_get_pgroup_stability(pg, cursor) != 0)
1200 return (-1);
1201 break;
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],
1211 (scf_error()) ?
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);
1220 break;
1222 case SC_PROPVAL:
1223 (void) lxml_get_propval(pg, cursor);
1224 break;
1226 case SC_PROPERTY:
1227 (void) lxml_get_property(pg, cursor);
1228 break;
1230 default:
1231 uu_die(gettext("illegal element \"%s\" on "
1232 "dependency group \"%s\"\n"), cursor->name, name);
1233 break;
1237 delete = xmlGetProp(dependency, (xmlChar *)delete_attr);
1238 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1239 xmlFree(delete);
1241 return (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.
1250 static int
1251 lxml_get_dependent(entity_t *entity, xmlNodePtr dependent)
1253 xmlChar *name, *or;
1254 xmlNodePtr sf;
1255 xmlChar *fmri, *delete;
1256 pgroup_t *pg;
1257 property_t *p;
1258 xmlNodePtr n;
1259 char *myfmri;
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);
1269 xmlFree(name);
1270 return (-1);
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);
1279 xmlFree(or);
1280 if (internal_attach_dependent(entity, pg) != 0) {
1281 xmlFree(name);
1282 internal_pgroup_free(pg);
1283 return (-1);
1286 for (sf = dependent->children; sf != NULL; sf = sf->next)
1287 if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0)
1288 break;
1289 assert(sf != NULL);
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)
1295 return (-1);
1297 if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
1298 dependent, "grouping") != 0)
1299 return (-1);
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);
1306 } else {
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,
1314 myfmri);
1315 if (internal_attach_property(pg, p) != 0)
1316 return (-1);
1318 /* Create a property to serve as a do-not-export flag. */
1319 p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1,
1320 (uint64_t)1);
1321 if (internal_attach_property(pg, p) != 0)
1322 return (-1);
1324 for (n = sf->next; n != NULL; n = n->next) {
1325 if (lxml_ignorable_block(n))
1326 continue;
1328 switch (lxml_xlate_element(n->name)) {
1329 case SC_STABILITY:
1330 if (new_str_prop_from_attr(pg,
1331 SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n,
1332 value_attr) != 0)
1333 return (-1);
1334 break;
1336 case SC_PROPVAL:
1337 (void) lxml_get_propval(pg, n);
1338 break;
1340 case SC_PROPERTY:
1341 (void) lxml_get_property(pg, n);
1342 break;
1344 default:
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)
1354 return (-1);
1357 delete = xmlGetProp(dependent, (xmlChar *)delete_attr);
1358 pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
1359 xmlFree(delete);
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)
1365 return (-1);
1367 return (0);
1370 static int
1371 lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
1373 pgroup_t *pg;
1374 property_t *p;
1375 xmlChar *stabval;
1377 if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) ||
1378 (*stabval == 0)) {
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));
1392 static int
1393 lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
1395 pgroup_t *pg;
1396 property_t *p;
1397 xmlChar *restarter = NULL;
1398 xmlNode *cursor;
1399 int r;
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))
1415 continue;
1417 switch (lxml_xlate_element(cursor->name)) {
1418 case SC_SERVICE_FMRI:
1419 restarter = xmlGetProp(cursor, (xmlChar *)value_attr);
1420 break;
1421 default:
1422 uu_die(gettext("illegal element \"%s\" on restarter "
1423 "element for \"%s\"\n"), cursor->name,
1424 entity->sc_name);
1425 break;
1429 p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1,
1430 restarter);
1432 r = internal_attach_property(pg, p);
1433 if (r != 0) {
1434 internal_property_free(p);
1435 return (-1);
1438 return (0);
1441 static void
1442 lxml_get_paramval(pgroup_t *pgrp, const char *propname, xmlNodePtr pval)
1444 property_t *p;
1445 char *value;
1446 char *prop;
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);
1459 static void
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);
1472 static void
1473 lxml_get_type(pgroup_t *pgrp, xmlNodePtr type)
1475 property_t *p;
1476 xmlChar *name;
1477 xmlChar *active;
1478 xmlNodePtr cursor;
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) {
1492 xmlChar *pname;
1494 if (lxml_ignorable_block(cursor))
1495 continue;
1497 pname = xmlGetProp(cursor, (xmlChar *)name_attr);
1498 if (pname == NULL || *pname == '\0')
1499 uu_die(gettext(
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);
1506 xmlFree(pname);
1508 switch (lxml_xlate_element(cursor->name)) {
1509 case SC_PARAMETER:
1510 lxml_get_parameter(pgrp, propname, cursor);
1511 break;
1513 case SC_PARAMVAL:
1514 lxml_get_paramval(pgrp, propname, cursor);
1515 break;
1517 default:
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)
1524 active_val = 1;
1525 else
1526 active_val = 0;
1527 xmlFree(active);
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);
1538 xmlFree(name);
1541 static void
1542 lxml_get_event(entity_t *entity, const char *pgname, xmlNodePtr np)
1544 xmlNodePtr cursor;
1545 pgroup_t *pgrp;
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))
1552 continue;
1554 switch (lxml_xlate_element(cursor->name)) {
1555 case SC_EVENT:
1556 continue;
1558 case SC_TYPE:
1559 lxml_get_type(pgrp, cursor);
1560 break;
1562 default:
1563 uu_warn(gettext("illegal element '%s' on "
1564 "notification parameters\n"), cursor->name);
1569 static int
1570 lxml_get_notification_parameters(entity_t *entity, xmlNodePtr np)
1572 char *event = NULL;
1573 char **pgs = NULL;
1574 char **p;
1575 char *pgname = NULL;
1576 xmlNodePtr cursor;
1577 int32_t tset, t;
1578 size_t sz = max_scf_name_len + 1;
1579 int count;
1580 int r = -1;
1582 for (count = 0, cursor = np->xmlChildrenNode; cursor != NULL;
1583 cursor = cursor->next) {
1584 if (lxml_ignorable_block(cursor))
1585 continue;
1587 if (lxml_xlate_element(cursor->name) == SC_EVENT) {
1588 xmlChar *s;
1590 count++;
1591 if (count > 1)
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"));
1597 xmlFree(s);
1601 pgs = tokenize(event, ",");
1603 switch (tset = check_tokens(pgs)) {
1604 case INVALID_TOKENS:
1605 uu_die(gettext("Invalid input.\n"));
1606 /*NOTREACHED*/
1607 case MIXED_TOKENS:
1608 semerr(gettext("Can't mix SMF and FMA event definitions\n"));
1609 goto out;
1610 case FMA_TOKENS:
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) {
1614 semerr(gettext(
1615 "Non-SMF transition events must go to %s\n"),
1616 SCF_NOTIFY_PARAMS_INST);
1617 goto out;
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"),
1624 *p);
1626 lxml_get_event(entity, pgname, np);
1628 break;
1630 default: /* smf state transition tokens */
1631 if (entity->sc_etype == SVCCFG_SERVICE_OBJECT &&
1632 strcmp(entity->sc_fmri, SCF_SERVICE_GLOBAL) == 0) {
1633 semerr(gettext(
1634 "Can't set events for global service\n"));
1635 goto out;
1637 for (t = 0x1; t < SCF_STATE_ALL; t <<= 1) {
1638 if (t & tset) {
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),
1643 np);
1648 r = 0;
1649 out:
1650 free(pgname);
1651 free(pgs);
1652 free(event);
1654 return (r);
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.
1666 static int
1667 lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext,
1668 const char *pn_format, const char *source)
1670 int extra;
1671 xmlNodePtr cursor;
1672 xmlChar *val;
1673 char *stripped, *cp;
1674 property_t *p;
1675 char *prop_name;
1676 int r;
1678 if (((val = xmlGetProp(loctext, (xmlChar *)xml_lang_attr)) == NULL) ||
1679 (*val == 0)) {
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 "
1692 "%s in %s\n"),
1693 xml_lang_attr, extra, source, service->sc_name);
1695 xmlFree(val);
1697 for (cursor = loctext->xmlChildrenNode; cursor != NULL;
1698 cursor = cursor->next) {
1699 if (strcmp("text", (const char *)cursor->name) == 0) {
1700 break;
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,
1704 service->sc_name);
1708 if (cursor == NULL) {
1709 uu_die(gettext("loctext element has no content for \"%s\"\n"),
1710 service->sc_name);
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--)
1722 *(cp + 1) = '\0';
1724 p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1,
1725 stripped);
1727 r = internal_attach_property(pg, p);
1728 if (r != 0) {
1729 internal_property_free(p);
1730 free(prop_name);
1733 return (r);
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.
1747 static int
1748 lxml_get_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container,
1749 const char *pn_format, const char *source)
1751 xmlNodePtr cursor;
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))
1761 continue;
1763 switch (lxml_xlate_element(cursor->name)) {
1764 case SC_LOCTEXT:
1765 if (lxml_get_loctext(service, pg, cursor, pn_format,
1766 source))
1767 return (-1);
1768 break;
1769 default:
1770 uu_die(gettext("illegal element \"%s\" on %s element "
1771 "for \"%s\"\n"), cursor->name, container->name,
1772 service->sc_name);
1773 break;
1777 return (0);
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.
1789 static int
1790 lxml_get_cardinality_attribute(entity_t *service, xmlNodePtr cursor,
1791 const char *attr_name, const char *prop_name, uint64_t *value,
1792 property_t **propp)
1794 char *c;
1795 property_t *p;
1796 xmlChar *val;
1797 uint64_t count;
1798 char *endptr;
1800 *propp = NULL;
1801 val = xmlGetProp(cursor, (xmlChar *)attr_name);
1802 if (val == NULL)
1803 return (0);
1804 if (*val == 0) {
1805 xmlFree(val);
1806 return (0);
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++) {
1814 if (isspace(*c))
1815 continue;
1816 if (isdigit(*c))
1817 break;
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);
1821 xmlFree(val);
1822 return (-1);
1824 errno = 0;
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);
1830 xmlFree(val);
1831 return (-1);
1834 xmlFree(val);
1836 /* Value is valid. Create the property. */
1837 p = internal_property_create(prop_name, SCF_TYPE_COUNT, 1, count);
1838 *value = count;
1839 *propp = p;
1840 return (0);
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.
1847 static int
1848 lxml_get_tm_cardinality(entity_t *service, pgroup_t *pg, xmlNodePtr cursor)
1850 int min_attached = 0;
1851 int compare = 1;
1852 property_t *min_prop;
1853 property_t *max_prop;
1854 uint64_t max;
1855 uint64_t min;
1856 int r;
1858 r = lxml_get_cardinality_attribute(service, cursor, min_attr,
1859 SCF_PROPERTY_TM_CARDINALITY_MIN, &min, &min_prop);
1860 if (r != 0)
1861 return (r);
1862 if (min_prop == NULL)
1863 compare = 0;
1864 r = lxml_get_cardinality_attribute(service, cursor, max_attr,
1865 SCF_PROPERTY_TM_CARDINALITY_MAX, &max, &max_prop);
1866 if (r != 0)
1867 goto errout;
1868 if ((max_prop != NULL) && (compare == 1)) {
1869 if (max < min) {
1870 semerr(gettext("Cardinality max is less than min for "
1871 "the %s element in %s.\n"), pg->sc_pgroup_name,
1872 service->sc_fmri);
1873 goto errout;
1877 /* Attach the properties to the property group. */
1878 if (min_prop) {
1879 if (internal_attach_property(pg, min_prop) == 0) {
1880 min_attached = 1;
1881 } else {
1882 goto errout;
1885 if (max_prop) {
1886 if (internal_attach_property(pg, max_prop) != 0) {
1887 if (min_attached)
1888 internal_detach_property(pg, min_prop);
1889 goto errout;
1892 return (0);
1894 errout:
1895 if (min_prop)
1896 internal_property_free(min_prop);
1897 if (max_prop)
1898 internal_property_free(max_prop);
1899 return (-1);
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.
1909 static int
1910 lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
1912 pgroup_t *pg;
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,
1921 "common_name"));
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.
1931 static int
1932 lxml_get_tm_description(entity_t *service, xmlNodePtr description)
1934 pgroup_t *pg;
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"));
1946 static char *
1947 lxml_label_to_groupname(const char *prefix, const char *in)
1949 char *out, *cp;
1950 size_t len, piece_len;
1952 out = uu_zalloc(2 * max_scf_name_len + 1);
1953 if (out == NULL)
1954 return (NULL);
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 == '-'))
1973 *cp = '_';
1976 return (out);
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
1982 * is placed in *p.
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().
1992 static void
1993 astring_prop_value(property_t **p, const char *prop_name, char *prop_value,
1994 boolean_t free_flag)
1996 value_t *v;
1998 if (*p == NULL) {
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.
2019 static void
2020 seps_to_prop_values(property_t **p, xmlChar *seps)
2022 value_t *v;
2023 char val_str[2];
2025 if (*p == NULL) {
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;
2038 val_str[0] = *seps;
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.
2050 static int
2051 lxml_get_tm_internal_seps(entity_t *service, pgroup_t *pg, xmlNodePtr seps)
2053 xmlNodePtr cursor;
2054 property_t *prop = NULL;
2055 int r;
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,
2064 service->sc_name);
2067 if (prop == NULL) {
2068 semerr(gettext("The %s element in %s had an empty list of "
2069 "separators.\n"), (const char *)seps->name,
2070 service->sc_name);
2071 return (-1);
2073 r = internal_attach_property(pg, prop);
2074 if (r != 0)
2075 internal_property_free(prop);
2076 return (r);
2079 static int
2080 lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
2082 pgroup_t *pg;
2083 char *pgname;
2084 char *name;
2085 xmlChar *title;
2086 xmlChar *section;
2089 * Fetch title and section attributes, convert to something sanitized,
2090 * and create property group.
2092 title = xmlGetProp(manpage, (xmlChar *)title_attr);
2093 if (title == NULL)
2094 return (-1);
2095 section = xmlGetProp(manpage, (xmlChar *)section_attr);
2096 if (section == NULL) {
2097 xmlFree(title);
2098 return (-1);
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);
2108 uu_free(pgname);
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);
2112 if (pg == NULL) {
2113 pg = internal_pgroup_find_or_create(service, pgname,
2114 SCF_GROUP_TEMPLATE);
2115 } else {
2116 /* Rename property group */
2117 free((char *)pg->sc_pgroup_name);
2118 pg->sc_pgroup_name = safe_strdup(pgname);
2121 uu_free(pgname);
2122 free(name);
2123 xmlFree(section);
2124 xmlFree(title);
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)
2136 return (-1);
2138 return (0);
2141 static int
2142 lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
2144 pgroup_t *pg;
2145 char *pgname;
2146 xmlChar *name;
2149 * Fetch name attribute, convert name to something sanitized, and create
2150 * property group.
2152 name = xmlGetProp(doc_link, (xmlChar *)name_attr);
2153 if (name == NULL)
2154 return (-1);
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);
2162 uu_free(pgname);
2163 xmlFree(name);
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)
2172 return (-1);
2174 return (0);
2177 static int
2178 lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
2180 xmlNodePtr cursor;
2182 for (cursor = documentation->xmlChildrenNode; cursor != NULL;
2183 cursor = cursor->next) {
2184 if (lxml_ignorable_block(cursor))
2185 continue;
2187 switch (lxml_xlate_element(cursor->name)) {
2188 case SC_MANPAGE:
2189 (void) lxml_get_tm_manpage(service, cursor);
2190 break;
2191 case SC_DOC_LINK:
2192 (void) lxml_get_tm_doclink(service, cursor);
2193 break;
2194 default:
2195 uu_die(gettext("illegal element \"%s\" on template "
2196 "for service \"%s\"\n"),
2197 cursor->name, service->sc_name);
2201 return (0);
2204 static int
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) {
2209 return (-1);
2211 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
2212 SCF_TYPE_ASTRING, cursor, type_attr, "") != 0) {
2213 return (-1);
2215 if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
2216 required_attr) != 0)
2217 return (-1);
2218 return (0);
2221 static int
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;
2226 property_t *p;
2227 int r = 0;
2228 char *type;
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,
2235 service->sc_name);
2238 /* Add the type to the values of the prop_name property. */
2239 p = internal_property_find(pg, prop_name);
2240 if (p == NULL)
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);
2245 if (r != 0)
2246 internal_property_free(p);
2248 return (r);
2251 #define RC_MIN 0
2252 #define RC_MAX 1
2253 #define RC_COUNT 2
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.
2261 static int
2262 verify_range(entity_t *service, xmlNodePtr range, char *min, char *max)
2264 char *c;
2265 int i;
2266 int is_signed = 0;
2267 int inverted = 0;
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++) {
2280 c = strings[i];
2281 while (isspace(*c)) {
2282 c++;
2284 if (*c == '-')
2285 is_signed = 1;
2288 /* Attempt to convert the strings. */
2289 for (i = 0; i < RC_COUNT; i++) {
2290 errno = 0;
2291 if (is_signed) {
2292 srange[i] = strtoll(strings[i], &c, 0);
2293 } else {
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 */
2305 if (is_signed) {
2306 if (srange[RC_MAX] < srange[RC_MIN])
2307 inverted = 1;
2308 } else {
2309 if (urange[RC_MAX] < urange[RC_MIN])
2310 inverted = 1;
2312 if (inverted != 0) {
2313 semerr(gettext("Maximum less than minimum for the %s element "
2314 "in service %s.\n"), (char *)range->name,
2315 service->sc_name);
2316 return (-1);
2319 return (0);
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.
2331 static int
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;
2336 char *max;
2337 char *min;
2338 property_t *p;
2339 char *prop_value;
2340 int r = 0;
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,
2347 service->sc_name);
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,
2353 service->sc_name);
2355 if (verify_range(service, range, min, max) != 0) {
2356 xmlFree(min);
2357 xmlFree(max);
2358 return (-1);
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);
2368 xmlFree(min);
2369 xmlFree(max);
2372 * If necessary create the property and attach it to the property
2373 * group.
2375 p = internal_property_find(pg, prop_name);
2376 if (p == NULL)
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);
2381 if (r != 0) {
2382 internal_property_free(p);
2385 return (r);
2389 * Determine how many plain characters are represented by count Base32
2390 * encoded characters. 5 plain text characters are converted to 8 Base32
2391 * characters.
2393 static size_t
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.
2416 * Parameters:
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
2422 * properties.
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.
2429 static int
2430 lxml_get_tm_value_element(entity_t *service, pgroup_t *pg, xmlNodePtr value,
2431 char **name_value)
2433 char *common_name_fmt;
2434 xmlNodePtr cursor;
2435 char *description_fmt;
2436 char *encoded_value = NULL;
2437 size_t extra;
2438 char *value_name;
2439 int r = 0;
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,
2453 service->sc_name);
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;
2487 cursor != NULL;
2488 cursor = cursor->next) {
2489 if (lxml_ignorable_block(cursor))
2490 continue;
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);
2495 break;
2496 case SC_DESCRIPTION:
2497 r = lxml_get_all_loctext(service, pg, cursor,
2498 description_fmt, (const char *)cursor->name);
2499 break;
2500 default:
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);
2505 if (r != 0)
2506 break;
2509 free(description_fmt);
2510 free(common_name_fmt);
2511 if (r == 0) {
2512 *name_value = safe_strdup(value_name);
2514 xmlFree(value_name);
2515 free(encoded_value);
2516 return (r);
2519 static int
2520 lxml_get_tm_choices(entity_t *service, pgroup_t *pg, xmlNodePtr choices)
2522 xmlNodePtr cursor;
2523 char *name_value;
2524 property_t *name_prop = NULL;
2525 int r = 0;
2527 for (cursor = choices->xmlChildrenNode;
2528 (cursor != NULL) && (r == 0);
2529 cursor = cursor->next) {
2530 if (lxml_ignorable_block(cursor))
2531 continue;
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);
2536 break;
2537 case SC_RANGE:
2538 r = lxml_get_tm_range(service, pg, cursor,
2539 SCF_PROPERTY_TM_CHOICES_RANGE);
2540 if (r != 0)
2541 goto out;
2542 break;
2543 case SC_VALUE:
2544 r = lxml_get_tm_value_element(service, pg, cursor,
2545 &name_value);
2546 if (r == 0) {
2548 * There is no need to free the memory
2549 * associated with name_value, because the
2550 * property value will end up pointing to
2551 * the memory.
2553 astring_prop_value(&name_prop,
2554 SCF_PROPERTY_TM_CHOICES_NAME, name_value,
2555 B_TRUE);
2556 } else {
2557 goto out;
2559 break;
2560 default:
2561 uu_die(gettext("%s is an invalid element of "
2562 "choices for service %s.\n"), cursor->name,
2563 service->sc_name);
2567 out:
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);
2576 return (r);
2579 static int
2580 lxml_get_tm_constraints(entity_t *service, pgroup_t *pg, xmlNodePtr constraints)
2582 xmlNodePtr cursor;
2583 char *name_value;
2584 property_t *name_prop = NULL;
2585 int r = 0;
2587 for (cursor = constraints->xmlChildrenNode;
2588 (cursor != NULL) && (r == 0);
2589 cursor = cursor->next) {
2590 if (lxml_ignorable_block(cursor))
2591 continue;
2592 switch (lxml_xlate_element(cursor->name)) {
2593 case SC_RANGE:
2594 r = lxml_get_tm_range(service, pg, cursor,
2595 SCF_PROPERTY_TM_CONSTRAINT_RANGE);
2596 if (r != 0)
2597 goto out;
2598 break;
2599 case SC_VALUE:
2600 r = lxml_get_tm_value_element(service, pg, cursor,
2601 &name_value);
2602 if (r == 0) {
2604 * There is no need to free the memory
2605 * associated with name_value, because the
2606 * property value will end up pointing to
2607 * the memory.
2609 astring_prop_value(&name_prop,
2610 SCF_PROPERTY_TM_CONSTRAINT_NAME, name_value,
2611 B_TRUE);
2612 } else {
2613 goto out;
2615 break;
2616 default:
2617 uu_die(gettext("%s is an invalid element of "
2618 "constraints for service %s.\n"), cursor->name,
2619 service->sc_name);
2623 out:
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);
2632 return (r);
2636 * The values element contains one or more value elements.
2638 static int
2639 lxml_get_tm_values(entity_t *service, pgroup_t *pg, xmlNodePtr values)
2641 xmlNodePtr cursor;
2642 char *name_value;
2643 property_t *name_prop = NULL;
2644 int r = 0;
2646 for (cursor = values->xmlChildrenNode;
2647 (cursor != NULL) && (r == 0);
2648 cursor = cursor->next) {
2649 if (lxml_ignorable_block(cursor))
2650 continue;
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);
2657 if (r == 0) {
2659 * There is no need to free the memory
2660 * associated with name_value, because the
2661 * property value will end up pointing to
2662 * the memory.
2664 astring_prop_value(&name_prop,
2665 SCF_PROPERTY_TM_VALUES_NAME, name_value,
2666 B_TRUE);
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);
2678 return (r);
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.
2696 static int
2697 lxml_get_tm_prop_pattern(entity_t *service, xmlNodePtr prop_pattern,
2698 const char *pgpat_name)
2700 xmlNodePtr cursor;
2701 int extra;
2702 pgroup_t *pg;
2703 property_t *p;
2704 char *pg_name;
2705 size_t prefix_len;
2706 xmlChar *prop_pattern_name;
2707 int r;
2708 const char *unique;
2709 value_t *v;
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"),
2725 service->sc_name);
2726 return (-1);
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);
2733 return (-1);
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);
2750 if (pg == NULL) {
2751 uu_die(gettext("Property group for prop_pattern, \"%s\", "
2752 "already exists in %s\n"), prop_pattern_name,
2753 service->sc_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);
2769 if (r != 0)
2770 goto out;
2773 * Now process the elements of prop_pattern
2775 for (cursor = prop_pattern->xmlChildrenNode;
2776 cursor != NULL;
2777 cursor = cursor->next) {
2778 if (lxml_ignorable_block(cursor))
2779 continue;
2781 switch (lxml_xlate_element(cursor->name)) {
2782 case SC_CARDINALITY:
2783 r = lxml_get_tm_cardinality(service, pg, cursor);
2784 if (r != 0)
2785 goto out;
2786 break;
2787 case SC_CHOICES:
2788 r = lxml_get_tm_choices(service, pg, cursor);
2789 if (r != 0)
2790 goto out;
2791 break;
2792 case SC_COMMON_NAME:
2793 (void) lxml_get_all_loctext(service, pg, cursor,
2794 COMMON_NAME_FMT, (const char *)cursor->name);
2795 break;
2796 case SC_CONSTRAINTS:
2797 r = lxml_get_tm_constraints(service, pg, cursor);
2798 if (r != 0)
2799 goto out;
2800 break;
2801 case SC_DESCRIPTION:
2802 (void) lxml_get_all_loctext(service, pg, cursor,
2803 DESCRIPTION_FMT, (const char *)cursor->name);
2804 break;
2805 case SC_INTERNAL_SEPARATORS:
2806 r = lxml_get_tm_internal_seps(service, pg, cursor);
2807 if (r != 0)
2808 goto out;
2809 break;
2810 case SC_UNITS:
2811 (void) lxml_get_all_loctext(service, pg, cursor,
2812 UNITS_FMT, "units");
2813 break;
2814 case SC_VALUES:
2815 (void) lxml_get_tm_values(service, pg, cursor);
2816 break;
2817 case SC_VISIBILITY:
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);
2825 break;
2826 default:
2827 uu_die(gettext("illegal element \"%s\" in prop_pattern "
2828 "for service \"%s\"\n"), cursor->name,
2829 service->sc_name);
2833 out:
2834 xmlFree(prop_pattern_name);
2835 free(pg_name);
2836 return (r);
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.
2844 static int
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) {
2849 return (-1);
2851 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
2852 SCF_TYPE_ASTRING, cursor, type_attr, NULL) != 0) {
2853 return (-1);
2855 if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TARGET,
2856 SCF_TYPE_ASTRING, cursor, target_attr, NULL) != 0) {
2857 return (-1);
2859 if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
2860 required_attr) != 0)
2861 return (-1);
2862 return (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.
2881 static int
2882 verify_pg_pattern_attributes(entity_t *s, pgroup_t *pg)
2884 int is_restarter;
2885 property_t *target;
2886 value_t *v;
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);
2893 return (-1);
2895 v = uu_list_first(target->sc_property_values);
2896 assert(v != NULL);
2897 assert(v->sc_type == SCF_TYPE_ASTRING);
2900 * If target has a value of instance, the template must be in a
2901 * service object.
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);
2908 return (-1);
2913 * If target has a value of "delegate", the template must be in a
2914 * restarter.
2916 if (strcmp(v->sc_u.sc_string, "delegate") == 0) {
2917 is_restarter = 0;
2918 if ((s->sc_etype == SVCCFG_SERVICE_OBJECT) &&
2919 (s->sc_u.sc_service.sc_service_type == SVCCFG_RESTARTER)) {
2920 is_restarter = 1;
2922 if ((s->sc_etype == SVCCFG_INSTANCE_OBJECT) &&
2923 (s->sc_parent->sc_u.sc_service.sc_service_type ==
2924 SVCCFG_RESTARTER)) {
2925 is_restarter = 1;
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);
2931 return (-1);
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);
2944 return (-1);
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);
2952 return (-1);
2956 return (0);
2959 static int
2960 lxml_get_tm_pg_pattern(entity_t *service, xmlNodePtr pg_pattern)
2962 xmlNodePtr cursor;
2963 int out_len;
2964 xmlChar *name;
2965 pgroup_t *pg = NULL;
2966 char *pg_name;
2967 int r = -1;
2968 xmlChar *type;
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);
2988 } else {
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);
3001 } else {
3002 const char *prefix;
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);
3008 goto out;
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;
3018 } else {
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);
3035 if (pg == NULL) {
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"),
3040 service->sc_name);
3041 } else {
3042 semerr(gettext("pg_pattern with empty name and "
3043 "type \"%s\" is not unique in %s\n"),
3044 type, service->sc_name);
3046 } else {
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);
3051 } else {
3052 semerr(gettext("pg_pattern with name \"%s\" "
3053 "and type \"%s\" is not unique in %s\n"),
3054 name, type, service->sc_name);
3057 goto out;
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);
3065 if (r != 0)
3066 goto out;
3067 if (verify_pg_pattern_attributes(service, pg) != 0) {
3068 semerr(gettext("Invalid pg_pattern attributes in %s\n"),
3069 service->sc_name);
3070 r = -1;
3071 goto out;
3075 * Now process all of the elements of pg_pattern.
3077 for (cursor = pg_pattern->xmlChildrenNode;
3078 cursor != NULL;
3079 cursor = cursor->next) {
3080 if (lxml_ignorable_block(cursor))
3081 continue;
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);
3087 break;
3088 case SC_DESCRIPTION:
3089 (void) lxml_get_all_loctext(service, pg, cursor,
3090 DESCRIPTION_FMT, (const char *)cursor->name);
3091 break;
3092 case SC_PROP_PATTERN:
3093 r = lxml_get_tm_prop_pattern(service, cursor,
3094 pg_name);
3095 if (r != 0)
3096 goto out;
3097 break;
3098 default:
3099 uu_die(gettext("illegal element \"%s\" in pg_pattern "
3100 "for service \"%s\"\n"), cursor->name,
3101 service->sc_name);
3105 out:
3106 if ((r != 0) && (pg != NULL)) {
3107 internal_detach_pgroup(service, pg);
3108 internal_pgroup_free(pg);
3110 free(pg_name);
3111 xmlFree(name);
3112 xmlFree(type);
3114 return (r);
3117 static int
3118 lxml_get_template(entity_t *service, xmlNodePtr templ)
3120 xmlNodePtr cursor;
3122 for (cursor = templ->xmlChildrenNode; cursor != NULL;
3123 cursor = cursor->next) {
3124 if (lxml_ignorable_block(cursor))
3125 continue;
3127 switch (lxml_xlate_element(cursor->name)) {
3128 case SC_COMMON_NAME:
3129 (void) lxml_get_tm_common_name(service, cursor);
3130 break;
3131 case SC_DESCRIPTION:
3132 (void) lxml_get_tm_description(service, cursor);
3133 break;
3134 case SC_DOCUMENTATION:
3135 (void) lxml_get_tm_documentation(service, cursor);
3136 break;
3137 case SC_PG_PATTERN:
3138 if (lxml_get_tm_pg_pattern(service, cursor) != 0)
3139 return (-1);
3140 break;
3141 default:
3142 uu_die(gettext("illegal element \"%s\" on template "
3143 "for service \"%s\"\n"),
3144 cursor->name, service->sc_name);
3148 return (0);
3151 static int
3152 lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
3154 entity_t *i;
3155 xmlChar *enabled;
3156 pgroup_t *pg;
3157 property_t *p;
3158 char *package;
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) ?
3165 1 : 0;
3166 xmlFree(enabled);
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,
3182 enabled_val);
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.
3207 static int
3208 lxml_get_instance(entity_t *service, xmlNodePtr inst, bundle_type_t bt,
3209 svccfg_op_t op)
3211 entity_t *i;
3212 pgroup_t *pg;
3213 property_t *p;
3214 xmlNodePtr cursor;
3215 xmlChar *enabled;
3216 int r, e_val;
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);
3229 if (r != 0)
3230 return (r);
3232 i->sc_op = op;
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);
3239 return (-1);
3241 } else { /* enabled != NULL */
3242 if (strcmp(true, (const char *)enabled) != 0 &&
3243 strcmp(false, (const char *)enabled) != 0) {
3244 xmlFree(enabled);
3245 semerr(gettext("Invalid enabled value\n"));
3246 return (-1);
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);
3263 xmlFree(enabled);
3267 * Walk its child elements, as appropriate.
3269 for (cursor = inst->xmlChildrenNode; cursor != NULL;
3270 cursor = cursor->next) {
3271 if (lxml_ignorable_block(cursor))
3272 continue;
3274 switch (lxml_xlate_element(cursor->name)) {
3275 case SC_RESTARTER:
3276 (void) lxml_get_restarter(i, cursor);
3277 break;
3278 case SC_DEPENDENCY:
3279 (void) lxml_get_dependency(i, cursor);
3280 break;
3281 case SC_DEPENDENT:
3282 (void) lxml_get_dependent(i, cursor);
3283 break;
3284 case SC_METHOD_CONTEXT:
3285 (void) lxml_get_entity_method_context(i, cursor);
3286 break;
3287 case SC_EXEC_METHOD:
3288 (void) lxml_get_exec_method(i, cursor);
3289 break;
3290 case SC_PROPERTY_GROUP:
3291 (void) lxml_get_pgroup(i, cursor);
3292 break;
3293 case SC_TEMPLATE:
3294 if (op == SVCCFG_OP_APPLY) {
3295 semerr(gettext("Template data for \"%s\" may "
3296 "not be modified in a profile.\n"),
3297 i->sc_name);
3299 return (-1);
3302 if (lxml_get_template(i, cursor) != 0)
3303 return (-1);
3304 break;
3305 case SC_NOTIFICATION_PARAMETERS:
3306 if (lxml_get_notification_parameters(i, cursor) != 0)
3307 return (-1);
3308 break;
3309 default:
3310 uu_die(gettext(
3311 "illegal element \"%s\" on instance \"%s\"\n"),
3312 cursor->name, i->sc_name);
3313 break;
3317 return (0);
3320 /* ARGSUSED1 */
3321 static int
3322 lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
3324 pgroup_t *pg;
3325 property_t *p;
3326 int r;
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);
3335 if (r != 0) {
3336 internal_property_free(p);
3337 return (-1);
3340 return (0);
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
3349 * the service.
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
3354 * work.
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.
3361 static int
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)
3374 goto out;
3376 if (scf_handle_bind(h) != 0)
3377 goto out;
3379 if (scf_handle_get_scope(h, SCF_FMRI_LOCAL_SCOPE, sc) == -1)
3380 goto out;
3382 if (scf_scope_get_service(sc, service, svc) != SCF_SUCCESS) {
3383 if (scf_error() == SCF_ERROR_NOT_FOUND)
3384 rc = SCF_SUCCESS;
3386 goto out;
3389 if (scf_service_get_pg(svc, SCF_PG_MANIFESTFILES, pg) != SCF_SUCCESS)
3390 goto out;
3392 rc = SCF_SUCCESS;
3393 out:
3394 scf_pg_destroy(pg);
3395 scf_service_destroy(svc);
3396 scf_scope_destroy(sc);
3397 scf_handle_destroy(h);
3399 return (rc);
3403 * Translate a service element into an internal instance/property tree, added
3404 * to bundle.
3406 * If op is SVCCFG_OP_APPLY (i.e., apply a profile), do not allow for
3407 * modification of template data.
3409 static int
3410 lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
3412 pgroup_t *pg;
3413 property_t *p;
3414 entity_t *s;
3415 xmlNodePtr cursor;
3416 xmlChar *type;
3417 xmlChar *version;
3418 int e;
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);
3428 xmlFree(version);
3430 type = xmlGetProp(svc, (xmlChar *)type_attr);
3431 s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type);
3432 xmlFree(type);
3435 * Set the global missing type to false before processing the service
3437 est->sc_miss_type = B_FALSE;
3438 s->sc_op = op;
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;
3448 size_t base_sz = 0;
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,
3458 errno);
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);
3467 uu_free(bname);
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);
3473 if (pg == NULL) {
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))
3490 continue;
3492 e = lxml_xlate_element(cursor->name);
3494 switch (e) {
3495 case SC_INSTANCE:
3496 if (lxml_get_instance(s, cursor,
3497 bundle->sc_bundle_type, op) != 0)
3498 return (-1);
3499 break;
3500 case SC_TEMPLATE:
3501 if (op == SVCCFG_OP_APPLY) {
3502 semerr(gettext("Template data for \"%s\" may "
3503 "not be modified in a profile.\n"),
3504 s->sc_name);
3506 return (-1);
3509 if (lxml_get_template(s, cursor) != 0)
3510 return (-1);
3511 break;
3512 case SC_NOTIFICATION_PARAMETERS:
3513 if (lxml_get_notification_parameters(s, cursor) != 0)
3514 return (-1);
3515 break;
3516 case SC_STABILITY:
3517 (void) lxml_get_entity_stability(s, cursor);
3518 break;
3519 case SC_DEPENDENCY:
3520 (void) lxml_get_dependency(s, cursor);
3521 break;
3522 case SC_DEPENDENT:
3523 (void) lxml_get_dependent(s, cursor);
3524 break;
3525 case SC_RESTARTER:
3526 (void) lxml_get_restarter(s, cursor);
3527 break;
3528 case SC_EXEC_METHOD:
3529 (void) lxml_get_exec_method(s, cursor);
3530 break;
3531 case SC_METHOD_CONTEXT:
3532 (void) lxml_get_entity_method_context(s, cursor);
3533 break;
3534 case SC_PROPERTY_GROUP:
3535 (void) lxml_get_pgroup(s, cursor);
3536 break;
3537 case SC_INSTANCE_CREATE_DEFAULT:
3538 (void) lxml_get_default_instance(s, cursor);
3539 break;
3540 case SC_INSTANCE_SINGLE:
3541 (void) lxml_get_single_instance(s, cursor);
3542 break;
3543 default:
3544 uu_die(gettext(
3545 "illegal element \"%s\" on service \"%s\"\n"),
3546 cursor->name, s->sc_name);
3547 break;
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));
3563 #ifdef DEBUG
3564 void
3565 lxml_dump(int g, xmlNodePtr p)
3567 if (p && p->name) {
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);
3574 #endif /* DEBUG */
3576 static int
3577 lxml_is_known_dtd(const xmlChar *dtdname)
3579 if (dtdname == NULL ||
3580 strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0)
3581 return (0);
3583 return (1);
3586 static int
3587 lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
3588 xmlNodePtr subbundle, svccfg_op_t op)
3590 xmlNodePtr cursor;
3591 xmlChar *type;
3592 int e;
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"));
3602 return (-1);
3605 xmlFree(type);
3607 switch (op) {
3608 case SVCCFG_OP_IMPORT:
3609 if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
3610 semerr(gettext("document is not a manifest.\n"));
3611 return (-1);
3613 break;
3614 case SVCCFG_OP_APPLY:
3615 if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
3616 semerr(gettext("document is not a profile.\n"));
3617 return (-1);
3619 break;
3620 case SVCCFG_OP_RESTORE:
3621 if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) {
3622 semerr(gettext("document is not an archive.\n"));
3623 return (-1);
3625 break;
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"));
3631 return (-1);
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))
3640 continue;
3642 e = lxml_xlate_element(cursor->name);
3644 switch (e) {
3645 case SC_XI_INCLUDE:
3646 continue;
3648 case SC_SERVICE_BUNDLE:
3649 if (lxml_get_bundle(bundle, bundle_type, cursor, op))
3650 return (-1);
3651 break;
3652 case SC_SERVICE:
3653 if (lxml_get_service(bundle, cursor, op) != 0)
3654 return (-1);
3655 break;
3659 return (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)
3670 xmlDocPtr document;
3671 xmlNodePtr cursor;
3672 xmlDtdPtr dtd = NULL;
3673 xmlValidCtxtPtr vcp;
3674 boolean_t do_validate;
3675 char *dtdpath = NULL;
3676 int r;
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));
3683 return (-1);
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);
3692 if (do_validate)
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"));
3700 return (-1);
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"));
3710 return (-1);
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"));
3721 return (-1);
3724 if ((cursor = xmlDocGetRootElement(document)) == NULL) {
3725 semerr(gettext("document is empty\n"));
3726 xmlFreeDoc(document);
3727 return (-1);
3730 if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) {
3731 semerr(gettext("document is not a service bundle\n"));
3732 xmlFreeDoc(document);
3733 return (-1);
3737 if (dtdpath != NULL) {
3738 dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
3739 if (dtd == NULL) {
3740 semerr(gettext("Could not parse DTD \"%s\".\n"),
3741 dtdpath);
3742 return (-1);
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 "
3753 "in document\n"));
3754 return (-1);
3757 if (do_validate) {
3758 vcp = xmlNewValidCtxt();
3759 if (vcp == NULL)
3760 uu_die(gettext("could not allocate memory"));
3761 vcp->warning = xmlParserValidityWarning;
3762 vcp->error = xmlParserValidityError;
3764 r = xmlValidateDocument(vcp, document);
3766 xmlFreeValidCtxt(vcp);
3768 if (r == 0) {
3769 semerr(gettext("Document is not valid.\n"));
3770 xmlFreeDoc(document);
3771 return (-1);
3775 #ifdef DEBUG
3776 lxml_dump(0, cursor);
3777 #endif /* DEBUG */
3779 r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op);
3781 xmlFreeDoc(document);
3783 return (r);
3787 lxml_inventory(const char *filename)
3789 bundle_t *b;
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);
3797 return (-1);
3800 svcs = uu_list_walk_start(b->sc_bundle_services, 0);
3801 if (svcs == NULL)
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);
3809 if (insts == NULL)
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,
3814 inst->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);
3830 return (0);