summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/svccfg/svccfg_xml.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/svc/svccfg/svccfg_xml.c')
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_xml.c1501
1 files changed, 1411 insertions, 90 deletions
diff --git a/usr/src/cmd/svc/svccfg/svccfg_xml.c b/usr/src/cmd/svc/svccfg/svccfg_xml.c
index 930a61bdd4..2b04a0a1df 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_xml.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_xml.c
@@ -19,11 +19,18 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * XML document manipulation routines
+ *
+ * These routines provide translation to and from the internal representation to
+ * XML. Directionally-oriented verbs are with respect to the external source,
+ * so lxml_get_service() fetches a service from the XML file into the
+ * internal representation.
+ */
#include <libxml/parser.h>
#include <libxml/xinclude.h>
@@ -32,7 +39,10 @@
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
+#include <libscf.h>
+#include <libscf_priv.h>
#include <libuutil.h>
+#include <sasl/saslutil.h>
#include <stdlib.h>
#include <string.h>
@@ -43,20 +53,46 @@
#include "svccfg.h"
/*
- * XML document manipulation routines
+ * snprintf(3C) format strings for constructing property names that include
+ * the locale designation. Use %s to indicate where the locale should go.
*
- * These routines provide translation to and from the internal representation to
- * XML. Directionally-oriented verbs are with respect to the external source,
- * so lxml_get_service() fetches a service from the XML file into the
- * internal representation.
+ * The VALUE_* symbols are an exception. The firs %s will be replaced with
+ * "value_". The second %s will be replaced by the name of the value and
+ * %%s will be replaced by the locale designation. These formats are
+ * processed twice by snprintf(3C). The first time captures the value name
+ * and the second time captures the locale.
*/
-
+#define LOCALE_ONLY_FMT ("%s")
+#define COMMON_NAME_FMT ("common_name_%s")
+#define DESCRIPTION_FMT ("description_%s")
+#define UNITS_FMT ("units_%s")
+#define VALUE_COMMON_NAME_FMT ("%s%s_common_name_%%s")
+#define VALUE_DESCRIPTION_FMT ("%s%s_description_%%s")
+
+/* Attribute names */
const char * const delete_attr = "delete";
const char * const enabled_attr = "enabled";
+const char * const lang_attr = "lang";
+const char * const manpath_attr = "manpath";
+const char * const max_attr = "max";
+const char * const min_attr = "min";
const char * const name_attr = "name";
const char * const override_attr = "override";
+const char * const required_attr = "required";
+const char * const section_attr = "section";
+const char * const set_attr = "set";
+const char * const target_attr = "target";
+const char * const timeout_seconds_attr = "timeout_seconds";
+const char * const title_attr = "title";
const char * const type_attr = "type";
+const char * const uri_attr = "uri";
const char * const value_attr = "value";
+const char * const version_attr = "version";
+const char * const xml_lang_attr = "xml:lang";
+
+/* Attribute values */
+const char * const all_value = "all";
+
const char * const true = "true";
const char * const false = "false";
@@ -67,7 +103,10 @@ const char * const false = "false";
static const char *lxml_elements[] = {
"astring_list", /* SC_ASTRING */
"boolean_list", /* SC_BOOLEAN */
+ "cardinality", /* SC_CARDINALITY */
+ "choices", /* SC_CHOICES */
"common_name", /* SC_COMMON_NAME */
+ "constraints", /* SC_CONSTRAINTS */
"count_list", /* SC_COUNT */
"create_default_instance", /* SC_INSTANCE_CREATE_DEFAULT */
"dependency", /* SC_DEPENDENCY */
@@ -80,8 +119,10 @@ static const char *lxml_elements[] = {
"fmri_list", /* SC_FMRI */
"host_list", /* SC_HOST */
"hostname_list", /* SC_HOSTNAME */
+ "include_values", /* SC_INCLUDE_VALUES */
"instance", /* SC_INSTANCE */
"integer_list", /* SC_INTEGER */
+ "internal_separators", /* SC_INTERNAL_SEPARATORS */
"loctext", /* SC_LOCTEXT */
"manpage", /* SC_MANPAGE */
"method_context", /* SC_METHOD_CONTEXT */
@@ -92,9 +133,12 @@ static const char *lxml_elements[] = {
"net_address_v4_list", /* SC_NET_ADDR_V4 */
"net_address_v6_list", /* SC_NET_ADDR_V6 */
"opaque_list", /* SC_OPAQUE */
+ "pg_pattern", /* SC_PG_PATTERN */
+ "prop_pattern", /* SC_PROP_PATTERN */
"property", /* SC_PROPERTY */
"property_group", /* SC_PROPERTY_GROUP */
"propval", /* SC_PROPVAL */
+ "range", /* SC_RANGE */
"restarter", /* SC_RESTARTER */
"service", /* SC_SERVICE */
"service_bundle", /* SC_SERVICE_BUNDLE */
@@ -103,9 +147,13 @@ static const char *lxml_elements[] = {
"stability", /* SC_STABILITY */
"template", /* SC_TEMPLATE */
"time_list", /* SC_TIME */
+ "units", /* SC_UNITS */
"uri_list", /* SC_URI */
"ustring_list", /* SC_USTRING */
+ "value", /* SC_VALUE */
"value_node", /* SC_VALUE_NODE */
+ "values", /* SC_VALUES */
+ "visibility", /* SC_VISIBILITY */
"xi:fallback", /* SC_XI_FALLBACK */
"xi:include" /* SC_XI_INCLUDE */
};
@@ -117,7 +165,10 @@ static const char *lxml_elements[] = {
static const char *lxml_prop_types[] = {
"astring", /* SC_ASTRING */
"boolean", /* SC_BOOLEAN */
+ "", /* SC_CARDINALITY */
+ "", /* SC_CHOICES */
"", /* SC_COMMON_NAME */
+ "", /* SC_CONSTRAINTS */
"count", /* SC_COUNT */
"", /* SC_INSTANCE_CREATE_DEFAULT */
"", /* SC_DEPENDENCY */
@@ -130,8 +181,10 @@ static const char *lxml_prop_types[] = {
"fmri", /* SC_FMRI */
"host", /* SC_HOST */
"hostname", /* SC_HOSTNAME */
+ "", /* SC_INCLUDE_VALUES */
"", /* SC_INSTANCE */
"integer", /* SC_INTEGER */
+ "", /* SC_INTERNAL_SEPARATORS */
"", /* SC_LOCTEXT */
"", /* SC_MANPAGE */
"", /* SC_METHOD_CONTEXT */
@@ -142,9 +195,12 @@ static const char *lxml_prop_types[] = {
"net_address_v4", /* SC_NET_ADDR_V4 */
"net_address_v6", /* SC_NET_ADDR_V6 */
"opaque", /* SC_OPAQUE */
+ "", /* SC_PG_PATTERN */
+ "", /* SC_PROP_PATTERN */
"", /* SC_PROPERTY */
"", /* SC_PROPERTY_GROUP */
"", /* SC_PROPVAL */
+ "", /* SC_RANGE */
"", /* SC_RESTARTER */
"", /* SC_SERVICE */
"", /* SC_SERVICE_BUNDLE */
@@ -153,10 +209,14 @@ static const char *lxml_prop_types[] = {
"", /* SC_STABILITY */
"", /* SC_TEMPLATE */
"time", /* SC_TIME */
+ "", /* SC_UNITS */
"uri", /* SC_URI */
"ustring", /* SC_USTRING */
- "" /* SC_VALUE_NODE */
- "" /* SC_XI_FALLBACK */
+ "", /* SC_VALUE */
+ "", /* SC_VALUE_NODE */
+ "", /* SC_VALUES */
+ "", /* SC_VISIBILITY */
+ "", /* SC_XI_FALLBACK */
"" /* SC_XI_INCLUDE */
};
@@ -280,6 +340,49 @@ lxml_element_to_scf_type(element_t type)
/* NOTREACHED */
}
+/*
+ * Create a SCF_TYPE_BOOLEAN property name pname and attach it to the
+ * property group at pgrp. The value of the property will be set from the
+ * attribute named attr. attr must have a value of 0, 1, true or false.
+ *
+ * Zero is returned on success. An error is indicated by -1. It indicates
+ * that either the attribute had an invalid value or that we could not
+ * attach the property to pgrp. The attribute should not have an invalid
+ * value if the DTD is correctly written.
+ */
+static int
+new_bool_prop_from_attr(pgroup_t *pgrp, const char *pname, xmlNodePtr n,
+ const char *attr)
+{
+ uint64_t bool;
+ xmlChar *val;
+ property_t *p;
+ int r;
+
+ val = xmlGetProp(n, (xmlChar *)attr);
+ if (val == NULL)
+ return (0);
+
+ if ((xmlStrcmp(val, (xmlChar *)"0") == 0) ||
+ (xmlStrcmp(val, (xmlChar *)"false") == 0)) {
+ bool = 0;
+ } else if ((xmlStrcmp(val, (xmlChar *)"1") == 0) ||
+ (xmlStrcmp(val, (xmlChar *)"true") == 0)) {
+ bool = 1;
+ } else {
+ xmlFree(val);
+ return (-1);
+ }
+ xmlFree(val);
+ p = internal_property_create(pname, SCF_TYPE_BOOLEAN, 1, bool);
+ r = internal_attach_property(pgrp, p);
+
+ if (r != 0)
+ internal_property_free(p);
+
+ return (r);
+}
+
static int
new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
xmlNodePtr n, const char *attr)
@@ -300,6 +403,39 @@ new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
}
static int
+new_opt_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
+ xmlNodePtr n, const char *attr, const char *dflt)
+{
+ xmlChar *val;
+ property_t *p;
+ int r;
+
+ val = xmlGetProp(n, (xmlChar *)attr);
+ if (val == NULL) {
+ if (dflt == NULL) {
+ /*
+ * A missing attribute is considered to be a
+ * success in this function, because many of the
+ * attributes are optional. Missing non-optional
+ * attributes will be detected later when template
+ * validation is done.
+ */
+ return (0);
+ } else {
+ val = (xmlChar *)dflt;
+ }
+ }
+
+ p = internal_property_create(pname, ty, 1, val);
+ r = internal_attach_property(pgrp, p);
+
+ if (r != 0)
+ internal_property_free(p);
+
+ return (r);
+}
+
+static int
lxml_ignorable_block(xmlNodePtr n)
{
return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 ||
@@ -446,12 +582,12 @@ lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
p = internal_property_new();
p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr);
- if (p->sc_property_name == NULL)
+ if ((p->sc_property_name == NULL) || (*p->sc_property_name == 0))
uu_die(gettext("property name missing in group '%s'\n"),
pgrp->sc_pgroup_name);
type = xmlGetProp(propval, (xmlChar *)type_attr);
- if (type == NULL)
+ if ((type == NULL) || (*type == 0))
uu_die(gettext("property type missing for property '%s/%s'\n"),
pgrp->sc_pgroup_name, p->sc_property_name);
@@ -459,6 +595,7 @@ lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
break;
}
+ xmlFree(type);
if (r >= sizeof (lxml_prop_types) / sizeof (char *))
uu_die(gettext("property type invalid for property '%s/%s'\n"),
pgrp->sc_pgroup_name, p->sc_property_name);
@@ -471,6 +608,7 @@ lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
pgrp->sc_pgroup_name, p->sc_property_name);
v = lxml_make_value(r, val);
+ xmlFree(val);
internal_attach_value(p, v);
override = xmlGetProp(propval, (xmlChar *)override_attr);
@@ -490,15 +628,17 @@ lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
p = internal_property_new();
- if ((p->sc_property_name = (char *)xmlGetProp(property,
- (xmlChar *)name_attr)) == NULL)
+ if (((p->sc_property_name = (char *)xmlGetProp(property,
+ (xmlChar *)name_attr)) == NULL) || (*p->sc_property_name == 0))
uu_die(gettext("property name missing in group \'%s\'\n"),
pgrp->sc_pgroup_name);
- if ((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL)
+ if (((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL) ||
+ (*type == 0)) {
uu_die(gettext("property type missing for "
"property \'%s/%s\'\n"), pgrp->sc_pgroup_name,
p->sc_property_name);
+ }
for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); r++) {
if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
@@ -674,8 +814,8 @@ lxml_get_envvar(xmlNodePtr envvar)
char *value;
char *ret;
- name = (char *)xmlGetProp(envvar, (xmlChar *)"name");
- value = (char *)xmlGetProp(envvar, (xmlChar *)"value");
+ name = (char *)xmlGetProp(envvar, (xmlChar *)name_attr);
+ value = (char *)xmlGetProp(envvar, (xmlChar *)value_attr);
if (strlen(name) == 0 || strchr(name, '=') != NULL)
uu_die(gettext("Invalid environment variable "
@@ -803,7 +943,7 @@ lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
emeth, "exec") != 0)
return (-1);
- timeout = xmlGetProp(emeth, (xmlChar *)"timeout_seconds");
+ timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr);
if (timeout != NULL) {
uint64_t u_timeout;
char *endptr;
@@ -1093,7 +1233,8 @@ lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
property_t *p;
xmlChar *stabval;
- if ((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) {
+ if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) ||
+ (*stabval == 0)) {
uu_warn(gettext("no stability value found\n"));
stabval = (xmlChar *)strdup("External");
}
@@ -1156,28 +1297,45 @@ lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
return (0);
}
-static void
-sanitize_locale(uchar_t *locale)
-{
- for (; *locale != '\0'; locale++)
- if (!isalnum(*locale) && *locale != '_')
- *locale = '_';
-}
-
+/*
+ * Add a property containing the localized text from the manifest. The
+ * property is added to the property group at pg. The name of the created
+ * property is based on the format at pn_format. This is an snprintf(3C)
+ * format containing a single %s conversion specification. At conversion
+ * time, the %s is replaced by the locale designation.
+ *
+ * source is the source element and it is only used for error messages.
+ */
static int
-lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext)
+lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext,
+ const char *pn_format, const char *source)
{
+ int extra;
xmlNodePtr cursor;
xmlChar *val;
char *stripped, *cp;
property_t *p;
+ char *prop_name;
int r;
- if ((val = xmlGetProp(loctext, (xmlChar *)"xml:lang")) == NULL)
- if ((val = xmlGetProp(loctext, (xmlChar *)"lang")) == NULL)
+ if (((val = xmlGetProp(loctext, (xmlChar *)xml_lang_attr)) == NULL) ||
+ (*val == 0)) {
+ if (((val = xmlGetProp(loctext,
+ (xmlChar *)lang_attr)) == NULL) || (*val == 0)) {
val = (xmlChar *)"unknown";
+ }
+ }
- sanitize_locale(val);
+ _scf_sanitize_locale((char *)val);
+ prop_name = safe_malloc(max_scf_name_len + 1);
+ if ((extra = snprintf(prop_name, max_scf_name_len + 1, pn_format,
+ val)) >= max_scf_name_len + 1) {
+ extra -= max_scf_name_len;
+ uu_die(gettext("%s attribute is %d characters too long for "
+ "%s in %s\n"),
+ xml_lang_attr, extra, source, service->sc_name);
+ }
+ xmlFree(val);
for (cursor = loctext->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
@@ -1207,45 +1365,54 @@ lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext)
;
*(cp + 1) = '\0';
- p = internal_property_create((const char *)val, SCF_TYPE_USTRING, 1,
+ p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1,
stripped);
r = internal_attach_property(pg, p);
- if (r != 0)
+ if (r != 0) {
internal_property_free(p);
+ free(prop_name);
+ }
return (r);
}
+/*
+ * This function processes all loctext elements in the current XML element
+ * designated by container. A property is created for each loctext element
+ * and added to the property group at pg. The name of the property is
+ * derived from the loctext language designation using the format at
+ * pn_format. pn_format should be an snprintf format string containing one
+ * %s which is replaced by the language designation.
+ *
+ * The function returns 0 on success and -1 if it is unable to attach the
+ * newly created property to pg.
+ */
static int
-lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
+lxml_get_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container,
+ const char *pn_format, const char *source)
{
xmlNodePtr cursor;
- pgroup_t *pg;
-
- /*
- * Create the property group, if absent.
- */
- pg = internal_pgroup_find_or_create(service,
- (char *)SCF_PG_TM_COMMON_NAME, (char *)SCF_GROUP_TEMPLATE);
/*
- * Iterate through one or more loctext elements. The locale is the
- * property name; the contents are the ustring value for the property.
+ * Iterate through one or more loctext elements. The locale is
+ * used to generate the property name; the contents are the ustring
+ * value for the property.
*/
- for (cursor = common_name->xmlChildrenNode; cursor != NULL;
+ for (cursor = container->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_LOCTEXT:
- if (lxml_get_loctext(service, pg, cursor))
+ if (lxml_get_loctext(service, pg, cursor, pn_format,
+ source))
return (-1);
break;
default:
- uu_die(gettext("illegal element \"%s\" on common_name "
- "element for \"%s\"\n"), cursor->name,
+ uu_die(gettext("illegal element \"%s\" on %s element "
+ "for \"%s\"\n"), cursor->name, container->name,
service->sc_name);
break;
}
@@ -1254,41 +1421,170 @@ lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
return (0);
}
+/*
+ * Obtain the specified cardinality attribute and place it in a property
+ * named prop_name. The converted attribute is placed at *value, and the
+ * newly created property is returned to propp. NULL is returned to propp
+ * if the attribute is not provided in the manifest.
+ *
+ * 0 is returned upon success, and -1 indicates that the manifest contained
+ * an invalid cardinality value.
+ */
static int
-lxml_get_tm_description(entity_t *service, xmlNodePtr description)
+lxml_get_cardinality_attribute(entity_t *service, xmlNodePtr cursor,
+ const char *attr_name, const char *prop_name, uint64_t *value,
+ property_t **propp)
{
- xmlNodePtr cursor;
- pgroup_t *pg;
+ char *c;
+ property_t *p;
+ xmlChar *val;
+ uint64_t count;
+ char *endptr;
- /*
- * Create the property group, if absent.
- */
- pg = internal_pgroup_find_or_create(service,
- (char *)SCF_PG_TM_DESCRIPTION, (char *)SCF_GROUP_TEMPLATE);
+ *propp = NULL;
+ val = xmlGetProp(cursor, (xmlChar *)attr_name);
+ if (val == NULL)
+ return (0);
+ if (*val == 0) {
+ xmlFree(val);
+ return (0);
+ }
/*
- * Iterate through one or more loctext elements. The locale is the
- * property name; the contents are the ustring value for the property.
+ * Make sure that the string at val doesn't have a leading minus
+ * sign. The strtoull() call below does not catch this problem.
*/
- for (cursor = description->xmlChildrenNode; cursor != NULL;
- cursor = cursor->next) {
- if (lxml_ignorable_block(cursor))
+ for (c = (char *)val; *c != 0; c++) {
+ if (isspace(*c))
continue;
-
- switch (lxml_xlate_element(cursor->name)) {
- case SC_LOCTEXT:
- if (lxml_get_loctext(service, pg, cursor))
- return (-1);
- break;
- default:
- uu_die(gettext("illegal element \"%s\" on description "
- "element for \"%s\"\n"), cursor->name,
- service->sc_name);
+ if (isdigit(*c))
break;
+ semerr(gettext("\"%c\" is not a legal character in the %s "
+ "attribute of the %s element in %s.\n"), *c,
+ attr_name, prop_name, service->sc_name);
+ xmlFree(val);
+ return (-1);
+ }
+ errno = 0;
+ count = strtoull((char *)val, &endptr, 10);
+ if (errno != 0 || endptr == (char *)val || *endptr) {
+ semerr(gettext("\"%s\" is not a legal number for the %s "
+ "attribute of the %s element in %s.\n"), (char *)val,
+ attr_name, prop_name, service->sc_name);
+ xmlFree(val);
+ return (-1);
+ }
+
+ xmlFree(val);
+
+ /* Value is valid. Create the property. */
+ p = internal_property_create(prop_name, SCF_TYPE_COUNT, 1, count);
+ *value = count;
+ *propp = p;
+ return (0);
+}
+
+/*
+ * The cardinality is specified by two attributes max and min at cursor.
+ * Both are optional, but if present they must be unsigned integers.
+ */
+static int
+lxml_get_tm_cardinality(entity_t *service, pgroup_t *pg, xmlNodePtr cursor)
+{
+ int min_attached = 0;
+ int compare = 1;
+ property_t *min_prop;
+ property_t *max_prop;
+ uint64_t max;
+ uint64_t min;
+ int r;
+
+ r = lxml_get_cardinality_attribute(service, cursor, min_attr,
+ SCF_PROPERTY_TM_CARDINALITY_MIN, &min, &min_prop);
+ if (r != 0)
+ return (r);
+ if (min_prop == NULL)
+ compare = 0;
+ r = lxml_get_cardinality_attribute(service, cursor, max_attr,
+ SCF_PROPERTY_TM_CARDINALITY_MAX, &max, &max_prop);
+ if (r != 0)
+ goto errout;
+ if ((max_prop != NULL) && (compare == 1)) {
+ if (max < min) {
+ semerr(gettext("Cardinality max is less than min for "
+ "the %s element in %s.\n"), pg->sc_pgroup_name,
+ service->sc_fmri);
+ goto errout;
}
}
+ /* Attach the properties to the property group. */
+ if (min_prop) {
+ if (internal_attach_property(pg, min_prop) == 0) {
+ min_attached = 1;
+ } else {
+ goto errout;
+ }
+ }
+ if (max_prop) {
+ if (internal_attach_property(pg, max_prop) != 0) {
+ if (min_attached)
+ internal_detach_property(pg, min_prop);
+ goto errout;
+ }
+ }
return (0);
+
+errout:
+ if (min_prop)
+ internal_property_free(min_prop);
+ if (max_prop)
+ internal_property_free(max_prop);
+ return (-1);
+}
+
+/*
+ * Get the common_name which is present as localized text at common_name in
+ * the manifest. The common_name is stored as the value of a property in
+ * the property group whose name is SCF_PG_TM_COMMON_NAME and type is
+ * SCF_GROUP_TEMPLATE. This property group will be created in service if
+ * it is not already there.
+ */
+static int
+lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
+{
+ pgroup_t *pg;
+
+ /*
+ * Create the property group, if absent.
+ */
+ pg = internal_pgroup_find_or_create(service, SCF_PG_TM_COMMON_NAME,
+ SCF_GROUP_TEMPLATE);
+
+ return (lxml_get_all_loctext(service, pg, common_name, LOCALE_ONLY_FMT,
+ "common_name"));
+}
+
+/*
+ * Get the description which is present as localized text at description in
+ * the manifest. The description is stored as the value of a property in
+ * the property group whose name is SCF_PG_TM_DESCRIPTION and type is
+ * SCF_GROUP_TEMPLATE. This property group will be created in service if
+ * it is not already there.
+ */
+static int
+lxml_get_tm_description(entity_t *service, xmlNodePtr description)
+{
+ pgroup_t *pg;
+
+ /*
+ * Create the property group, if absent.
+ */
+ pg = internal_pgroup_find_or_create(service, SCF_PG_TM_DESCRIPTION,
+ SCF_GROUP_TEMPLATE);
+
+ return (lxml_get_all_loctext(service, pg, description,
+ LOCALE_ONLY_FMT, "description"));
}
static char *
@@ -1329,6 +1625,108 @@ lxml_label_to_groupname(const char *prefix, const char *in)
return (out);
}
+/*
+ * If *p is NULL, astring_prop_value() first creates a property with the
+ * name specified in prop_name. The address of the newly created property
+ * is placed in *p.
+ *
+ * In either case, newly created property or existing property, a new
+ * SCF_TYPE_ASTRING value will created and attached to the property at *p.
+ * The value of the newly created property is prop_value.
+ *
+ * free_flag is used to indicate whether or not the memory at prop_value
+ * should be freed when the property is freed by a call to
+ * internal_property_free().
+ */
+static void
+astring_prop_value(property_t **p, const char *prop_name, char *prop_value,
+ boolean_t free_flag)
+{
+ value_t *v;
+
+ if (*p == NULL) {
+ /* Create the property */
+ *p = internal_property_new();
+ (*p)->sc_property_name = (char *)prop_name;
+ (*p)->sc_value_type = SCF_TYPE_ASTRING;
+ }
+
+ /* Add the property value to the property's list of values. */
+ v = internal_value_new();
+ v->sc_type = SCF_TYPE_ASTRING;
+ if (free_flag == B_TRUE)
+ v->sc_free = lxml_free_str;
+ v->sc_u.sc_string = prop_value;
+ internal_attach_value(*p, v);
+}
+
+/*
+ * If p points to a null pointer, create an internal_separators property
+ * saving the address at p. For each character at seps create a property
+ * value and attach it to the property at p.
+ */
+static void
+seps_to_prop_values(property_t **p, xmlChar *seps)
+{
+ value_t *v;
+ char val_str[2];
+
+ if (*p == NULL) {
+ *p = internal_property_new();
+ (*p)->sc_property_name =
+ (char *)SCF_PROPERTY_INTERNAL_SEPARATORS;
+ (*p)->sc_value_type = SCF_TYPE_ASTRING;
+ }
+
+ /* Add the values to the property's list. */
+ val_str[1] = 0; /* Terminate the string. */
+ for (; *seps != 0; seps++) {
+ v = internal_value_new();
+ v->sc_type = (*p)->sc_value_type;
+ v->sc_free = lxml_free_str;
+ val_str[0] = *seps;
+ v->sc_u.sc_string = strdup(val_str);
+ if (v->sc_u.sc_string == NULL)
+ uu_die(gettext("Out of memory\n"));
+ internal_attach_value(*p, v);
+ }
+}
+
+/*
+ * Create an internal_separators property and attach it to the property
+ * group at pg. The separator characters are provided in the text nodes
+ * that are the children of seps. Each separator character is stored as a
+ * property value in the internal_separators property.
+ */
+static int
+lxml_get_tm_internal_seps(entity_t *service, pgroup_t *pg, xmlNodePtr seps)
+{
+ xmlNodePtr cursor;
+ property_t *prop = NULL;
+ int r;
+
+ for (cursor = seps->xmlChildrenNode; cursor != NULL;
+ cursor = cursor->next) {
+ if (strcmp("text", (const char *)cursor->name) == 0) {
+ seps_to_prop_values(&prop, cursor->content);
+ } else if (strcmp("comment", (const char *)cursor->name) != 0) {
+ uu_die(gettext("illegal element \"%s\" on %s element "
+ "for \"%s\"\n"), cursor->name, seps->name,
+ service->sc_name);
+ }
+ }
+ if (prop == NULL) {
+ semerr(gettext("The %s element in %s had an empty list of "
+ "separators.\n"), (const char *)seps->name,
+ service->sc_name);
+ return (-1);
+ }
+ r = internal_attach_property(pg, prop);
+ if (r != 0)
+ internal_property_free(prop);
+ return (r);
+}
+
static int
lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
{
@@ -1340,9 +1738,10 @@ lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
* Fetch title attribute, convert to something sanitized, and create
* property group.
*/
- title = xmlGetProp(manpage, (xmlChar *)"title");
+ title = xmlGetProp(manpage, (xmlChar *)title_attr);
pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX,
(const char *)title);
+ xmlFree(title);
pg = internal_pgroup_find_or_create(service, pgname,
(char *)SCF_GROUP_TEMPLATE);
@@ -1350,12 +1749,12 @@ lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
/*
* Each attribute is an astring property within the group.
*/
- if (new_str_prop_from_attr(pg, "title", SCF_TYPE_ASTRING, manpage,
- "title") != 0 ||
- new_str_prop_from_attr(pg, "section", SCF_TYPE_ASTRING, manpage,
- "section") != 0 ||
- new_str_prop_from_attr(pg, "manpath", SCF_TYPE_ASTRING, manpage,
- "manpath") != 0)
+ if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_TITLE,
+ SCF_TYPE_ASTRING, manpage, title_attr) != 0 ||
+ new_str_prop_from_attr(pg, SCF_PROPERTY_TM_SECTION,
+ SCF_TYPE_ASTRING, manpage, section_attr) != 0 ||
+ new_str_prop_from_attr(pg, SCF_PROPERTY_TM_MANPATH,
+ SCF_TYPE_ASTRING, manpage, manpath_attr) != 0)
return (-1);
return (0);
@@ -1372,21 +1771,22 @@ lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
* Fetch name attribute, convert name to something sanitized, and create
* property group.
*/
- name = xmlGetProp(doc_link, (xmlChar *)"name");
+ name = xmlGetProp(doc_link, (xmlChar *)name_attr);
pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX,
(const char *)name);
pg = internal_pgroup_find_or_create(service, pgname,
(char *)SCF_GROUP_TEMPLATE);
+ xmlFree(name);
/*
* Each attribute is an astring property within the group.
*/
- if (new_str_prop_from_attr(pg, "name", SCF_TYPE_ASTRING, doc_link,
- "name") != 0 ||
- new_str_prop_from_attr(pg, "uri", SCF_TYPE_ASTRING, doc_link,
- "uri") != 0)
+ if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, SCF_TYPE_ASTRING,
+ doc_link, name_attr) != 0 ||
+ new_str_prop_from_attr(pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING,
+ doc_link, uri_attr) != 0)
return (-1);
return (0);
@@ -1420,6 +1820,919 @@ lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
}
static int
+lxml_get_prop_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
+{
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
+ SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
+ SCF_TYPE_ASTRING, cursor, type_attr, "") != 0) {
+ return (-1);
+ }
+ if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
+ required_attr) != 0)
+ return (-1);
+ return (0);
+}
+
+static int
+lxml_get_tm_include_values(entity_t *service, pgroup_t *pg,
+ xmlNodePtr include_values, const char *prop_name)
+{
+ boolean_t attach_to_pg = B_FALSE;
+ property_t *p;
+ int r = 0;
+ char *type;
+
+ /* Get the type attribute of the include_values element. */
+ type = (char *)xmlGetProp(include_values, (const xmlChar *)type_attr);
+ if ((type == NULL) || (*type == 0)) {
+ uu_die(gettext("%s element requires a %s attribute in the %s "
+ "service.\n"), include_values->name, type_attr,
+ service->sc_name);
+ }
+
+ /* Add the type to the values of the prop_name property. */
+ p = internal_property_find(pg, prop_name);
+ if (p == NULL)
+ attach_to_pg = B_TRUE;
+ astring_prop_value(&p, prop_name, type, B_FALSE);
+ if (attach_to_pg == B_TRUE) {
+ r = internal_attach_property(pg, p);
+ if (r != 0)
+ internal_property_free(p);
+ }
+ return (r);
+}
+
+#define RC_MIN 0
+#define RC_MAX 1
+#define RC_COUNT 2
+
+/*
+ * Verify that the strings at min and max are valid numeric strings. Also
+ * verify that max is numerically >= min.
+ *
+ * 0 is returned if the range is valid, and -1 is returned if it is not.
+ */
+static int
+verify_range(entity_t *service, xmlNodePtr range, char *min, char *max)
+{
+ char *c;
+ int i;
+ int is_signed = 0;
+ int inverted = 0;
+ const char *limit[RC_COUNT];
+ char *strings[RC_COUNT];
+ uint64_t urange[RC_COUNT]; /* unsigned range. */
+ int64_t srange[RC_COUNT]; /* signed range. */
+
+ strings[RC_MIN] = min;
+ strings[RC_MAX] = max;
+ limit[RC_MIN] = min_attr;
+ limit[RC_MAX] = max_attr;
+
+ /* See if the range is signed. */
+ for (i = 0; (i < RC_COUNT) && (is_signed == 0); i++) {
+ c = strings[i];
+ while (isspace(*c)) {
+ c++;
+ }
+ if (*c == '-')
+ is_signed = 1;
+ }
+
+ /* Attempt to convert the strings. */
+ for (i = 0; i < RC_COUNT; i++) {
+ errno = 0;
+ if (is_signed) {
+ srange[i] = strtoll(strings[i], &c, 0);
+ } else {
+ urange[i] = strtoull(strings[i], &c, 0);
+ }
+ if ((errno != 0) || (c == strings[i]) || (*c != 0)) {
+ /* Conversion failed. */
+ uu_die(gettext("Unable to convert %s for the %s "
+ "element in service %s.\n"), limit[i],
+ (char *)range->name, service->sc_name);
+ }
+ }
+
+ /* Make sure that min is <= max */
+ if (is_signed) {
+ if (srange[RC_MAX] < srange[RC_MIN])
+ inverted = 1;
+ } else {
+ if (urange[RC_MAX] < urange[RC_MIN])
+ inverted = 1;
+ }
+ if (inverted != 0) {
+ semerr(gettext("Maximum less than minimum for the %s element "
+ "in service %s.\n"), (char *)range->name,
+ service->sc_name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * This, function creates a property named prop_name. The range element
+ * should have two attributes -- min and max. The property value then
+ * becomes the concatenation of their value separated by a comma. The
+ * property is then attached to the property group at pg.
+ *
+ * If pg already contains a property with a name of prop_name, it is only
+ * necessary to create a new value and attach it to the existing property.
+ */
+static int
+lxml_get_tm_range(entity_t *service, pgroup_t *pg, xmlNodePtr range,
+ const char *prop_name)
+{
+ boolean_t attach_to_pg = B_FALSE;
+ char *max;
+ char *min;
+ property_t *p;
+ char *prop_value;
+ int r = 0;
+
+ /* Get max and min from the XML description. */
+ max = (char *)xmlGetProp(range, (xmlChar *)max_attr);
+ if ((max == NULL) || (*max == 0)) {
+ uu_die(gettext("%s element is missing the %s attribute in "
+ "service %s.\n"), (char *)range->name, max_attr,
+ service->sc_name);
+ }
+ min = (char *)xmlGetProp(range, (xmlChar *)min_attr);
+ if ((min == NULL) || (*min == 0)) {
+ uu_die(gettext("%s element is missing the %s attribute in "
+ "service %s.\n"), (char *)range->name, min_attr,
+ service->sc_name);
+ }
+ if (verify_range(service, range, min, max) != 0) {
+ xmlFree(min);
+ xmlFree(max);
+ return (-1);
+ }
+
+ /* Property value is concatenation of min and max. */
+ prop_value = safe_malloc(max_scf_value_len + 1);
+ if (snprintf(prop_value, max_scf_value_len + 1, "%s,%s", min, max) >=
+ max_scf_value_len + 1) {
+ uu_die(gettext("min and max are too long for the %s element "
+ "of %s.\n"), (char *)range->name, service->sc_name);
+ }
+ xmlFree(min);
+ xmlFree(max);
+
+ /*
+ * If necessary create the property and attach it to the property
+ * group.
+ */
+ p = internal_property_find(pg, prop_name);
+ if (p == NULL)
+ attach_to_pg = B_TRUE;
+ astring_prop_value(&p, prop_name, prop_value, B_TRUE);
+ if (attach_to_pg == B_TRUE) {
+ r = internal_attach_property(pg, p);
+ if (r != 0) {
+ internal_property_free(p);
+ }
+ }
+ return (r);
+}
+
+/*
+ * Determine how many plain characters are represented by count Base32
+ * encoded characters. 5 plain text characters are converted to 8 Base32
+ * characters.
+ */
+static size_t
+encoded_count_to_plain(size_t count)
+{
+ return (5 * ((count + 7) / 8));
+}
+
+/*
+ * The value element contains 0 or 1 common_name element followed by 0 or 1
+ * description element. It also has a required attribute called "name".
+ * The common_name and description are stored as property values in pg.
+ * The property names are:
+ * value_<name>_common_name_<lang>
+ * value_<name>_description_<lang>
+ *
+ * The <name> portion of the preceeding proper names requires more
+ * explanation. Ideally it would just the name attribute of this value
+ * element. Unfortunately, the name attribute can contain characters that
+ * are not legal in a property name. Thus, we base 32 encode the name
+ * attribute and use that for <name>.
+ *
+ * There are cases where the caller needs to know the name, so it is
+ * returned through the name_value pointer if it is not NULL.
+ *
+ * Parameters:
+ * service - Information about the service that is being
+ * processed. This function only uses this parameter
+ * for producing error messages.
+ *
+ * pg - The property group to receive the newly created
+ * properties.
+ *
+ * value - Pointer to the value element in the XML tree.
+ *
+ * name_value - Address to receive the value of the name attribute.
+ * The caller must free the memory.
+ */
+static int
+lxml_get_tm_value_element(entity_t *service, pgroup_t *pg, xmlNodePtr value,
+ char **name_value)
+{
+ char *common_name_fmt;
+ xmlNodePtr cursor;
+ char *description_fmt;
+ char *encoded_value = NULL;
+ size_t extra;
+ char *value_name;
+ int r = 0;
+
+ common_name_fmt = safe_malloc(max_scf_name_len + 1);
+ description_fmt = safe_malloc(max_scf_name_len + 1);
+
+ /*
+ * Get the value of our name attribute, so that we can use it to
+ * construct property names.
+ */
+ value_name = (char *)xmlGetProp(value, (xmlChar *)name_attr);
+ /* The value name must be present, but it can be empty. */
+ if (value_name == NULL) {
+ uu_die(gettext("%s element requires a %s attribute in the %s "
+ "service.\n"), (char *)value->name, name_attr,
+ service->sc_name);
+ }
+
+ /*
+ * The value_name may contain characters that are not valid in in a
+ * property name. So we will encode value_name and then use the
+ * encoded value in the property name.
+ */
+ encoded_value = safe_malloc(max_scf_name_len + 1);
+ if (scf_encode32(value_name, strlen(value_name), encoded_value,
+ max_scf_name_len + 1, &extra, SCF_ENCODE32_PAD) != 0) {
+ extra = encoded_count_to_plain(extra - max_scf_name_len);
+ uu_die(gettext("Constructed property name is %u characters "
+ "too long for value \"%s\" in the %s service.\n"),
+ extra, value_name, service->sc_name);
+ }
+ if ((extra = snprintf(common_name_fmt, max_scf_name_len + 1,
+ VALUE_COMMON_NAME_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
+ encoded_value)) >= max_scf_name_len + 1) {
+ extra = encoded_count_to_plain(extra - max_scf_name_len);
+ uu_die(gettext("Name attribute is "
+ "%u characters too long for %s in service %s\n"),
+ extra, (char *)value->name, service->sc_name);
+ }
+ if ((extra = snprintf(description_fmt, max_scf_name_len + 1,
+ VALUE_DESCRIPTION_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
+ encoded_value)) >= max_scf_name_len + 1) {
+ extra = encoded_count_to_plain(extra - max_scf_name_len);
+ uu_die(gettext("Name attribute is "
+ "%u characters too long for %s in service %s\n"),
+ extra, (char *)value->name, service->sc_name);
+ }
+
+ for (cursor = value->xmlChildrenNode;
+ cursor != NULL;
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_COMMON_NAME:
+ r = lxml_get_all_loctext(service, pg, cursor,
+ common_name_fmt, (const char *)cursor->name);
+ break;
+ case SC_DESCRIPTION:
+ r = lxml_get_all_loctext(service, pg, cursor,
+ description_fmt, (const char *)cursor->name);
+ break;
+ default:
+ uu_die(gettext("\"%s\" is an illegal element in %s "
+ "of service %s\n"), (char *)cursor->name,
+ (char *)value->name, service->sc_name);
+ }
+ if (r != 0)
+ break;
+ }
+
+ free(description_fmt);
+ free(common_name_fmt);
+ if (r == 0) {
+ *name_value = safe_strdup(value_name);
+ }
+ xmlFree(value_name);
+ free(encoded_value);
+ return (r);
+}
+
+static int
+lxml_get_tm_choices(entity_t *service, pgroup_t *pg, xmlNodePtr choices)
+{
+ xmlNodePtr cursor;
+ char *name_value;
+ property_t *name_prop = NULL;
+ int r = 0;
+
+ for (cursor = choices->xmlChildrenNode;
+ (cursor != NULL) && (r == 0);
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_INCLUDE_VALUES:
+ (void) lxml_get_tm_include_values(service, pg, cursor,
+ SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES);
+ break;
+ case SC_RANGE:
+ r = lxml_get_tm_range(service, pg, cursor,
+ SCF_PROPERTY_TM_CHOICES_RANGE);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_VALUE:
+ r = lxml_get_tm_value_element(service, pg, cursor,
+ &name_value);
+ if (r == 0) {
+ /*
+ * There is no need to free the memory
+ * associated with name_value, because the
+ * property value will end up pointing to
+ * the memory.
+ */
+ astring_prop_value(&name_prop,
+ SCF_PROPERTY_TM_CHOICES_NAME, name_value,
+ B_TRUE);
+ } else {
+ goto out;
+ }
+ break;
+ default:
+ uu_die(gettext("%s is an invalid element of "
+ "choices for service %s.\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ /* Attach the name property if we created one. */
+ if ((r == 0) && (name_prop != NULL)) {
+ r = internal_attach_property(pg, name_prop);
+ }
+ if ((r != 0) && (name_prop != NULL)) {
+ internal_property_free(name_prop);
+ }
+
+ return (r);
+}
+
+static int
+lxml_get_tm_constraints(entity_t *service, pgroup_t *pg, xmlNodePtr constraints)
+{
+ xmlNodePtr cursor;
+ char *name_value;
+ property_t *name_prop = NULL;
+ int r = 0;
+
+ for (cursor = constraints->xmlChildrenNode;
+ (cursor != NULL) && (r == 0);
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_RANGE:
+ r = lxml_get_tm_range(service, pg, cursor,
+ SCF_PROPERTY_TM_CONSTRAINT_RANGE);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_VALUE:
+ r = lxml_get_tm_value_element(service, pg, cursor,
+ &name_value);
+ if (r == 0) {
+ /*
+ * There is no need to free the memory
+ * associated with name_value, because the
+ * property value will end up pointing to
+ * the memory.
+ */
+ astring_prop_value(&name_prop,
+ SCF_PROPERTY_TM_CONSTRAINT_NAME, name_value,
+ B_TRUE);
+ } else {
+ goto out;
+ }
+ break;
+ default:
+ uu_die(gettext("%s is an invalid element of "
+ "constraints for service %s.\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ /* Attach the name property if we created one. */
+ if ((r == 0) && (name_prop != NULL)) {
+ r = internal_attach_property(pg, name_prop);
+ }
+ if ((r != 0) && (name_prop != NULL)) {
+ internal_property_free(name_prop);
+ }
+
+ return (r);
+}
+
+/*
+ * The values element contains one or more value elements.
+ */
+static int
+lxml_get_tm_values(entity_t *service, pgroup_t *pg, xmlNodePtr values)
+{
+ xmlNodePtr cursor;
+ char *name_value;
+ property_t *name_prop = NULL;
+ int r = 0;
+
+ for (cursor = values->xmlChildrenNode;
+ (cursor != NULL) && (r == 0);
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ if (lxml_xlate_element(cursor->name) != SC_VALUE) {
+ uu_die(gettext("\"%s\" is an illegal element in the "
+ "%s element of %s\n"), (char *)cursor->name,
+ (char *)values->name, service->sc_name);
+ }
+ r = lxml_get_tm_value_element(service, pg, cursor, &name_value);
+ if (r == 0) {
+ /*
+ * There is no need to free the memory
+ * associated with name_value, because the
+ * property value will end up pointing to
+ * the memory.
+ */
+ astring_prop_value(&name_prop,
+ SCF_PROPERTY_TM_VALUES_NAME, name_value,
+ B_TRUE);
+ }
+ }
+
+ /* Attach the name property if we created one. */
+ if ((r == 0) && (name_prop != NULL)) {
+ r = internal_attach_property(pg, name_prop);
+ }
+ if ((r != 0) && (name_prop != NULL)) {
+ internal_property_free(name_prop);
+ }
+
+ return (r);
+}
+
+/*
+ * This function processes a prop_pattern element within a pg_pattern XML
+ * element. First it creates a property group to hold the prop_pattern
+ * information. The name of this property group is the concatenation of:
+ * - SCF_PG_TM_PROP_PATTERN_PREFIX
+ * - The unique part of the property group name of the enclosing
+ * pg_pattern. The property group name of the enclosing pg_pattern
+ * is passed to us in pgpat_name. The unique part, is the part
+ * following SCF_PG_TM_PG_PATTERN_PREFIX.
+ * - The name of this prop_pattern element.
+ *
+ * After creating the property group, the prop_pattern attributes are saved
+ * as properties in the PG. Finally, the prop_pattern elements are
+ * processed and added to the PG.
+ */
+static int
+lxml_get_tm_prop_pattern(entity_t *service, xmlNodePtr prop_pattern,
+ const char *pgpat_name)
+{
+ xmlNodePtr cursor;
+ int extra;
+ pgroup_t *pg;
+ property_t *p;
+ char *pg_name;
+ size_t prefix_len;
+ xmlChar *prop_pattern_name;
+ int r;
+ const char *unique;
+ value_t *v;
+
+ /* Find the unique part of the pg_pattern property group name. */
+ prefix_len = strlen(SCF_PG_TM_PG_PAT_BASE);
+ assert(strncmp(pgpat_name, SCF_PG_TM_PG_PAT_BASE, prefix_len) == 0);
+ unique = pgpat_name + prefix_len;
+
+ /*
+ * We need to get the value of the name attribute first. The
+ * prop_pattern name as well as the name of the enclosing
+ * pg_pattern both constitute part of the name of the property
+ * group that we will create.
+ */
+ prop_pattern_name = xmlGetProp(prop_pattern, (xmlChar *)name_attr);
+ if ((prop_pattern_name == NULL) || (*prop_pattern_name == 0)) {
+ semerr(gettext("prop_pattern name is missing for %s\n"),
+ service->sc_name);
+ return (-1);
+ }
+ if (uu_check_name((const char *)prop_pattern_name,
+ UU_NAME_DOMAIN) != 0) {
+ semerr(gettext("prop_pattern name, \"%s\", for %s is not "
+ "valid.\n"), prop_pattern_name, service->sc_name);
+ xmlFree(prop_pattern_name);
+ return (-1);
+ }
+ pg_name = safe_malloc(max_scf_name_len + 1);
+ if ((extra = snprintf(pg_name, max_scf_name_len + 1, "%s%s_%s",
+ SCF_PG_TM_PROP_PATTERN_PREFIX, unique,
+ (char *)prop_pattern_name)) >= max_scf_name_len + 1) {
+ uu_die(gettext("prop_pattern name, \"%s\", for %s is %d "
+ "characters too long\n"), (char *)prop_pattern_name,
+ service->sc_name, extra - max_scf_name_len);
+ }
+
+ /*
+ * Create the property group, the property referencing the pg_pattern
+ * name, and add the prop_pattern attributes to the property group.
+ */
+ pg = internal_pgroup_create_strict(service, pg_name,
+ SCF_GROUP_TEMPLATE_PROP_PATTERN);
+ if (pg == NULL) {
+ uu_die(gettext("Property group for prop_pattern, \"%s\", "
+ "already exists in %s\n"), prop_pattern_name,
+ service->sc_name);
+ }
+
+ p = internal_property_create(SCF_PROPERTY_TM_PG_PATTERN,
+ SCF_TYPE_ASTRING, 1, safe_strdup(pgpat_name));
+ /*
+ * Unfortunately, internal_property_create() does not set the free
+ * function for the value, so we'll set it now.
+ */
+ v = uu_list_first(p->sc_property_values);
+ v->sc_free = lxml_free_str;
+ if (internal_attach_property(pg, p) != 0)
+ internal_property_free(p);
+
+
+ r = lxml_get_prop_pattern_attributes(pg, prop_pattern);
+ if (r != 0)
+ goto out;
+
+ /*
+ * Now process the elements of prop_pattern
+ */
+ for (cursor = prop_pattern->xmlChildrenNode;
+ cursor != NULL;
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_CARDINALITY:
+ r = lxml_get_tm_cardinality(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_CHOICES:
+ r = lxml_get_tm_choices(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_COMMON_NAME:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ COMMON_NAME_FMT, (const char *)cursor->name);
+ break;
+ case SC_CONSTRAINTS:
+ r = lxml_get_tm_constraints(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_DESCRIPTION:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ DESCRIPTION_FMT, (const char *)cursor->name);
+ break;
+ case SC_INTERNAL_SEPARATORS:
+ r = lxml_get_tm_internal_seps(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_UNITS:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ UNITS_FMT, "units");
+ break;
+ case SC_VALUES:
+ (void) lxml_get_tm_values(service, pg, cursor);
+ break;
+ case SC_VISIBILITY:
+ /*
+ * The visibility element is empty, so we only need
+ * to proccess the value attribute.
+ */
+ (void) new_str_prop_from_attr(pg,
+ SCF_PROPERTY_TM_VISIBILITY, SCF_TYPE_ASTRING,
+ cursor, value_attr);
+ break;
+ default:
+ uu_die(gettext("illegal element \"%s\" in prop_pattern "
+ "for service \"%s\"\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ xmlFree(prop_pattern_name);
+ free(pg_name);
+ return (r);
+}
+
+/*
+ * Get the pg_pattern attributes and save them as properties in the
+ * property group at pg. The pg_pattern element accepts four attributes --
+ * name, type, required and target.
+ */
+static int
+lxml_get_pg_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
+{
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
+ SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
+ SCF_TYPE_ASTRING, cursor, type_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TARGET,
+ SCF_TYPE_ASTRING, cursor, target_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
+ required_attr) != 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * There are several restrictions on the pg_pattern attributes that cannot
+ * be specifed in the service bundle DTD. This function verifies that
+ * those restrictions have been satisfied. The restrictions are:
+ *
+ * - The target attribute may have a value of "instance" only when the
+ * template block is in a service declaration.
+ *
+ * - The target attribute may have a value of "delegate" only when the
+ * template block applies to a restarter.
+ *
+ * - The target attribute may have a value of "all" only when the
+ * template block applies to the master restarter.
+ *
+ * The function returns 0 on success and -1 on failure.
+ */
+static int
+verify_pg_pattern_attributes(entity_t *s, pgroup_t *pg)
+{
+ int is_restarter;
+ property_t *target;
+ value_t *v;
+
+ /* Find the value of the target property. */
+ target = internal_property_find(pg, SCF_PROPERTY_TM_TARGET);
+ if (target == NULL) {
+ uu_die(gettext("pg_pattern is missing the %s attribute "
+ "in %s\n"), target_attr, s->sc_name);
+ return (-1);
+ }
+ v = uu_list_first(target->sc_property_values);
+ assert(v != NULL);
+ assert(v->sc_type == SCF_TYPE_ASTRING);
+
+ /*
+ * If target has a value of instance, the template must be in a
+ * service object.
+ */
+ if (strcmp(v->sc_u.sc_string, "instance") == 0) {
+ if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
+ uu_warn(gettext("pg_pattern %s attribute may only "
+ "have a value of \"instance\" when it is in a "
+ "service declaration.\n"), target_attr);
+ return (-1);
+ }
+ }
+
+ /*
+ * If target has a value of "delegate", the template must be in a
+ * restarter.
+ */
+ if (strcmp(v->sc_u.sc_string, "delegate") == 0) {
+ is_restarter = 0;
+ if ((s->sc_etype == SVCCFG_SERVICE_OBJECT) &&
+ (s->sc_u.sc_service.sc_service_type == SVCCFG_RESTARTER)) {
+ is_restarter = 1;
+ }
+ if ((s->sc_etype == SVCCFG_INSTANCE_OBJECT) &&
+ (s->sc_parent->sc_u.sc_service.sc_service_type ==
+ SVCCFG_RESTARTER)) {
+ is_restarter = 1;
+ }
+ if (is_restarter == 0) {
+ uu_warn(gettext("pg_pattern %s attribute has a "
+ "value of \"delegate\" but is not in a "
+ "restarter service\n"), target_attr);
+ return (-1);
+ }
+ }
+
+ /*
+ * If target has a value of "all", the template must be in the
+ * global (SCF_SERVICE_GLOBAL) service.
+ */
+ if (strcmp(v->sc_u.sc_string, all_value) == 0) {
+ if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
+ uu_warn(gettext("pg_pattern %s attribute has a "
+ "value of \"%s\" but is not in a "
+ "service entity.\n"), target_attr, all_value);
+ return (-1);
+ }
+ if (strcmp(s->sc_fmri, SCF_SERVICE_GLOBAL) != 0) {
+ uu_warn(gettext("pg_pattern %s attribute has a "
+ "value of \"%s\" but is in the \"%s\" service. "
+ "pg_patterns with target \"%s\" are only allowed "
+ "in the global service.\n"),
+ target_attr, all_value, s->sc_fmri, all_value);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+lxml_get_tm_pg_pattern(entity_t *service, xmlNodePtr pg_pattern)
+{
+ xmlNodePtr cursor;
+ int out_len;
+ xmlChar *name;
+ pgroup_t *pg = NULL;
+ char *pg_name;
+ int r = -1;
+ xmlChar *type;
+
+ pg_name = safe_malloc(max_scf_name_len + 1);
+
+ /*
+ * Get the name and type attributes. Their presence or absence
+ * determines whcih prefix we will use for the property group name.
+ * There are four cases -- neither attribute is present, both are
+ * present, only name is present or only type is present.
+ */
+ name = xmlGetProp(pg_pattern, (xmlChar *)name_attr);
+ type = xmlGetProp(pg_pattern, (xmlChar *)type_attr);
+ if ((name == NULL) || (*name == 0)) {
+ if ((type == NULL) || (*type == 0)) {
+ /* PG name contains only the prefix in this case */
+ if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
+ max_scf_name_len + 1) >= max_scf_name_len + 1) {
+ uu_die(gettext("Unable to create pg_pattern "
+ "property for %s\n"), service->sc_name);
+ }
+ } else {
+ /*
+ * If we have a type and no name, the type becomes
+ * part of the pg_pattern property group name.
+ */
+ if ((out_len = snprintf(pg_name, max_scf_name_len + 1,
+ "%s%s", SCF_PG_TM_PG_PATTERN_T_PREFIX, type)) >=
+ max_scf_name_len + 1) {
+ uu_die(gettext("pg_pattern type is for %s is "
+ "%d bytes too long\n"), service->sc_name,
+ out_len - max_scf_name_len);
+ }
+ }
+ } else {
+ const char *prefix;
+
+ /* Make sure that the name is valid. */
+ if (uu_check_name((const char *)name, UU_NAME_DOMAIN) != 0) {
+ semerr(gettext("pg_pattern name attribute, \"%s\", "
+ "for %s is invalid\n"), name, service->sc_name);
+ goto out;
+ }
+
+ /*
+ * As long as the pg_pattern has a name, it becomes part of
+ * the name of the pg_pattern property group name. We
+ * merely need to pick the appropriate prefix.
+ */
+ if ((type == NULL) || (*type == 0)) {
+ prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX;
+ } else {
+ prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX;
+ }
+ if ((out_len = snprintf(pg_name, max_scf_name_len + 1, "%s%s",
+ prefix, name)) >= max_scf_name_len + 1) {
+ uu_die(gettext("pg_pattern property group name "
+ "for %s is %d bytes too long\n"), service->sc_name,
+ out_len - max_scf_name_len);
+ }
+ }
+
+ /*
+ * Create the property group for holding this pg_pattern
+ * information, and capture the pg_pattern attributes.
+ */
+ pg = internal_pgroup_create_strict(service, pg_name,
+ SCF_GROUP_TEMPLATE_PG_PATTERN);
+ if (pg == NULL) {
+ if ((name == NULL) || (*name == 0)) {
+ if ((type == NULL) ||(*type == 0)) {
+ semerr(gettext("pg_pattern with empty name and "
+ "type is not unique in %s\n"),
+ service->sc_name);
+ } else {
+ semerr(gettext("pg_pattern with empty name and "
+ "type \"%s\" is not unique in %s\n"),
+ type, service->sc_name);
+ }
+ } else {
+ if ((type == NULL) || (*type == 0)) {
+ semerr(gettext("pg_pattern with name \"%s\" "
+ "and empty type is not unique in %s\n"),
+ name, service->sc_name);
+ } else {
+ semerr(gettext("pg_pattern with name \"%s\" "
+ "and type \"%s\" is not unique in %s\n"),
+ name, type, service->sc_name);
+ }
+ }
+ goto out;
+ }
+
+ /*
+ * Get the pg_pattern attributes from the manifest and verify
+ * that they satisfy our restrictions.
+ */
+ r = lxml_get_pg_pattern_attributes(pg, pg_pattern);
+ if (r != 0)
+ goto out;
+ if (verify_pg_pattern_attributes(service, pg) != 0) {
+ semerr(gettext("Invalid pg_pattern attributes in %s\n"),
+ service->sc_name);
+ r = -1;
+ goto out;
+ }
+
+ /*
+ * Now process all of the elements of pg_pattern.
+ */
+ for (cursor = pg_pattern->xmlChildrenNode;
+ cursor != NULL;
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_COMMON_NAME:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ COMMON_NAME_FMT, (const char *)cursor->name);
+ break;
+ case SC_DESCRIPTION:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ DESCRIPTION_FMT, (const char *)cursor->name);
+ break;
+ case SC_PROP_PATTERN:
+ r = lxml_get_tm_prop_pattern(service, cursor,
+ pg_name);
+ if (r != 0)
+ goto out;
+ break;
+ default:
+ uu_die(gettext("illegal element \"%s\" in pg_pattern "
+ "for service \"%s\"\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ if ((r != 0) && (pg != NULL)) {
+ internal_detach_pgroup(service, pg);
+ internal_pgroup_free(pg);
+ }
+ free(pg_name);
+ xmlFree(name);
+ xmlFree(type);
+
+ return (r);
+}
+
+static int
lxml_get_template(entity_t *service, xmlNodePtr templ)
{
xmlNodePtr cursor;
@@ -1439,6 +2752,10 @@ lxml_get_template(entity_t *service, xmlNodePtr templ)
case SC_DOCUMENTATION:
(void) lxml_get_tm_documentation(service, cursor);
break;
+ case SC_PG_PATTERN:
+ if (lxml_get_tm_pg_pattern(service, cursor) != 0)
+ return (-1);
+ break;
default:
uu_die(gettext("illegal element \"%s\" on template "
"for service \"%s\"\n"),
@@ -1581,7 +2898,8 @@ lxml_get_instance(entity_t *service, xmlNodePtr inst, svccfg_op_t op)
(void) lxml_get_pgroup(i, cursor);
break;
case SC_TEMPLATE:
- (void) lxml_get_template(i, cursor);
+ if (lxml_get_template(i, cursor) != 0)
+ return (-1);
break;
default:
uu_die(gettext(
@@ -1636,7 +2954,7 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
s = internal_service_new((char *)xmlGetProp(svc,
(xmlChar *)name_attr));
- version = xmlGetProp(svc, (xmlChar *)"version");
+ version = xmlGetProp(svc, (xmlChar *)version_attr);
s->sc_u.sc_service.sc_service_version = atol((const char *)version);
xmlFree(version);
@@ -1664,10 +2982,12 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
switch (e) {
case SC_INSTANCE:
- (void) lxml_get_instance(s, cursor, op);
+ if (lxml_get_instance(s, cursor, op) != 0)
+ return (-1);
break;
case SC_TEMPLATE:
- (void) lxml_get_template(s, cursor);
+ if (lxml_get_template(s, cursor) != 0)
+ return (-1);
break;
case SC_STABILITY:
(void) lxml_get_entity_stability(s, cursor);
@@ -1741,7 +3061,7 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
/*
* 1. Get bundle attributes.
*/
- type = xmlGetProp(subbundle, (xmlChar *)"type");
+ type = xmlGetProp(subbundle, (xmlChar *)type_attr);
bundle->sc_bundle_type = lxml_xlate_bundle_type(type);
if (bundle->sc_bundle_type != bundle_type &&
bundle_type != SVCCFG_UNKNOWN_BUNDLE) {
@@ -1772,8 +3092,8 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
break;
}
- if ((bundle->sc_bundle_name = xmlGetProp(subbundle,
- (xmlChar *)"name")) == NULL) {
+ if (((bundle->sc_bundle_name = xmlGetProp(subbundle,
+ (xmlChar *)name_attr)) == NULL) || (*bundle->sc_bundle_name == 0)) {
semerr(gettext("service bundle lacks name attribute\n"));
return (-1);
}
@@ -1797,7 +3117,8 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
return (-1);
break;
case SC_SERVICE:
- (void) lxml_get_service(bundle, cursor, op);
+ if (lxml_get_service(bundle, cursor, op) != 0)
+ return (-1);
break;
}
}