svccfg_xml.c revision 80148899834a4078a2bd348504aa2d6de9752837
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* 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>
#include <assert.h>
#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>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/param.h>
#include "manifest_hash.h"
#include "svccfg.h"
/*
* snprintf(3C) format strings for constructing property names that include
* the locale designation. Use %s to indicate where the locale should go.
*
* 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";
/*
* The following list must be kept in the same order as that of
* element_t array
*/
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 */
"dependent", /* SC_DEPENDENT */
"description", /* SC_DESCRIPTION */
"doc_link", /* SC_DOC_LINK */
"documentation", /* SC_DOCUMENTATION */
"enabled", /* SC_ENABLED */
"exec_method", /* SC_EXEC_METHOD */
"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 */
"method_credential", /* SC_METHOD_CREDENTIAL */
"method_profile", /* SC_METHOD_PROFILE */
"method_environment", /* SC_METHOD_ENVIRONMENT */
"envvar", /* SC_METHOD_ENVVAR */
"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 */
"service_fmri", /* SC_SERVICE_FMRI */
"single_instance", /* SC_INSTANCE_SINGLE */
"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 */
};
/*
* The following list must be kept in the same order as that of
* element_t array
*/
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 */
"", /* SC_DEPENDENT */
"", /* SC_DESCRIPTION */
"", /* SC_DOC_LINK */
"", /* SC_DOCUMENTATION */
"", /* SC_ENABLED */
"", /* SC_EXEC_METHOD */
"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 */
"", /* SC_METHOD_CREDENTIAL */
"", /* SC_METHOD_PROFILE */
"", /* SC_METHOD_ENVIRONMENT */
"", /* SC_METHOD_ENVVAR */
"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 */
"", /* SC_SERVICE_FMRI */
"", /* SC_INSTANCE_SINGLE */
"", /* SC_STABILITY */
"", /* SC_TEMPLATE */
"time", /* SC_TIME */
"", /* SC_UNITS */
"uri", /* SC_URI */
"ustring", /* SC_USTRING */
"", /* SC_VALUE */
"", /* SC_VALUE_NODE */
"", /* SC_VALUES */
"", /* SC_VISIBILITY */
"", /* SC_XI_FALLBACK */
"" /* SC_XI_INCLUDE */
};
int
lxml_init()
{
if (getenv("SVCCFG_NOVALIDATE") == NULL) {
/*
* DTD validation, with line numbers.
*/
(void) xmlLineNumbersDefault(1);
xmlLoadExtDtdDefaultValue |= XML_DETECT_IDS;
xmlLoadExtDtdDefaultValue |= XML_COMPLETE_ATTRS;
}
return (0);
}
static bundle_type_t
lxml_xlate_bundle_type(xmlChar *type)
{
if (xmlStrcmp(type, (const xmlChar *)"manifest") == 0)
return (SVCCFG_MANIFEST);
if (xmlStrcmp(type, (const xmlChar *)"profile") == 0)
return (SVCCFG_PROFILE);
if (xmlStrcmp(type, (const xmlChar *)"archive") == 0)
return (SVCCFG_ARCHIVE);
return (SVCCFG_UNKNOWN_BUNDLE);
}
static service_type_t
lxml_xlate_service_type(xmlChar *type)
{
if (xmlStrcmp(type, (const xmlChar *)"service") == 0)
return (SVCCFG_SERVICE);
if (xmlStrcmp(type, (const xmlChar *)"restarter") == 0)
return (SVCCFG_RESTARTER);
if (xmlStrcmp(type, (const xmlChar *)"milestone") == 0)
return (SVCCFG_MILESTONE);
return (SVCCFG_UNKNOWN_SERVICE);
}
static element_t
lxml_xlate_element(const xmlChar *tag)
{
int i;
for (i = 0; i < sizeof (lxml_elements) / sizeof (char *); i++)
if (xmlStrcmp(tag, (const xmlChar *)lxml_elements[i]) == 0)
return ((element_t)i);
return ((element_t)-1);
}
static uint_t
lxml_xlate_boolean(const xmlChar *value)
{
if (xmlStrcmp(value, (const xmlChar *)true) == 0)
return (1);
if (xmlStrcmp(value, (const xmlChar *)false) == 0)
return (0);
uu_die(gettext("illegal boolean value \"%s\"\n"), value);
/*NOTREACHED*/
}
static scf_type_t
lxml_element_to_type(element_t type)
{
switch (type) {
case SC_ASTRING: return (SCF_TYPE_ASTRING);
case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN);
case SC_COUNT: return (SCF_TYPE_COUNT);
case SC_FMRI: return (SCF_TYPE_FMRI);
case SC_HOST: return (SCF_TYPE_HOST);
case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME);
case SC_INTEGER: return (SCF_TYPE_INTEGER);
case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4);
case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6);
case SC_OPAQUE: return (SCF_TYPE_OPAQUE);
case SC_TIME: return (SCF_TYPE_TIME);
case SC_URI: return (SCF_TYPE_URI);
case SC_USTRING: return (SCF_TYPE_USTRING);
default:
uu_die(gettext("unknown value type (%d)\n"), type);
}
/* NOTREACHED */
}
static scf_type_t
lxml_element_to_scf_type(element_t type)
{
switch (type) {
case SC_ASTRING: return (SCF_TYPE_ASTRING);
case SC_BOOLEAN: return (SCF_TYPE_BOOLEAN);
case SC_COUNT: return (SCF_TYPE_COUNT);
case SC_FMRI: return (SCF_TYPE_FMRI);
case SC_HOST: return (SCF_TYPE_HOST);
case SC_HOSTNAME: return (SCF_TYPE_HOSTNAME);
case SC_INTEGER: return (SCF_TYPE_INTEGER);
case SC_NET_ADDR_V4: return (SCF_TYPE_NET_ADDR_V4);
case SC_NET_ADDR_V6: return (SCF_TYPE_NET_ADDR_V6);
case SC_OPAQUE: return (SCF_TYPE_OPAQUE);
case SC_TIME: return (SCF_TYPE_TIME);
case SC_URI: return (SCF_TYPE_URI);
case SC_USTRING: return (SCF_TYPE_USTRING);
default:
uu_die(gettext("unknown value type (%d)\n"), 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)
{
xmlChar *val;
property_t *p;
int r;
val = xmlGetProp(n, (xmlChar *)attr);
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
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 ||
xmlStrcmp(n->name, (xmlChar *)"comment") == 0) ? 1 : 0);
}
static int
lxml_validate_string_value(scf_type_t type, const char *v)
{
static scf_value_t *scf_value = NULL;
static scf_handle_t *scf_hndl = NULL;
if (scf_hndl == NULL && (scf_hndl = scf_handle_create(SCF_VERSION)) ==
NULL)
return (-1);
if (scf_value == NULL && (scf_value = scf_value_create(scf_hndl)) ==
NULL)
return (-1);
return (scf_value_set_from_string(scf_value, type, v));
}
static void
lxml_free_str(value_t *val)
{
free(val->sc_u.sc_string);
}
static value_t *
lxml_make_value(element_t type, const xmlChar *value)
{
value_t *v;
char *endptr;
scf_type_t scf_type = SCF_TYPE_INVALID;
v = internal_value_new();
v->sc_type = lxml_element_to_type(type);
switch (type) {
case SC_COUNT:
/*
* Although an SC_COUNT represents a uint64_t the use
* of a negative value is acceptable due to the usage
* established by inetd(1M).
*/
errno = 0;
v->sc_u.sc_count = strtoull((char *)value, &endptr, 10);
if (errno != 0 || endptr == (char *)value || *endptr)
uu_die(gettext("illegal value \"%s\" for "
"%s (%s)\n"), (char *)value,
lxml_prop_types[type],
(errno) ? strerror(errno) :
gettext("Illegal character"));
break;
case SC_INTEGER:
errno = 0;
v->sc_u.sc_integer = strtoll((char *)value, &endptr, 10);
if (errno != 0 || *endptr)
uu_die(gettext("illegal value \"%s\" for "
"%s (%s)\n"), (char *)value,
lxml_prop_types[type],
(errno) ? strerror(errno) : "Illegal character");
break;
case SC_OPAQUE:
case SC_HOST:
case SC_HOSTNAME:
case SC_NET_ADDR_V4:
case SC_NET_ADDR_V6:
case SC_FMRI:
case SC_URI:
case SC_TIME:
case SC_ASTRING:
case SC_USTRING:
scf_type = lxml_element_to_scf_type(type);
if ((v->sc_u.sc_string = strdup((char *)value)) == NULL)
uu_die(gettext("string duplication failed (%s)\n"),
strerror(errno));
if (lxml_validate_string_value(scf_type,
v->sc_u.sc_string) != 0)
uu_die(gettext("illegal value \"%s\" for "
"%s (%s)\n"), (char *)value,
lxml_prop_types[type],
(scf_error()) ? scf_strerror(scf_error()) :
gettext("Illegal format"));
v->sc_free = lxml_free_str;
break;
case SC_BOOLEAN:
v->sc_u.sc_count = lxml_xlate_boolean(value);
break;
default:
uu_die(gettext("unknown value type (%d)\n"), type);
break;
}
return (v);
}
static int
lxml_get_value(property_t *prop, element_t vtype, xmlNodePtr value)
{
xmlNodePtr cursor;
for (cursor = value->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
xmlChar *assigned_value;
value_t *v;
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_VALUE_NODE:
if ((assigned_value = xmlGetProp(cursor,
(xmlChar *)value_attr)) == NULL)
uu_die(gettext("no value on value node?\n"));
break;
default:
uu_die(gettext("value list contains illegal element "
"\'%s\'\n"), cursor->name);
break;
}
v = lxml_make_value(vtype, assigned_value);
xmlFree(assigned_value);
internal_attach_value(prop, v);
}
return (0);
}
static int
lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
{
property_t *p;
element_t r;
value_t *v;
xmlChar *type, *val, *override;
p = internal_property_new();
p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr);
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) || (*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)
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);
p->sc_value_type = lxml_element_to_type(r);
val = xmlGetProp(propval, (xmlChar *)value_attr);
if (val == NULL)
uu_die(gettext("property value missing for property '%s/%s'\n"),
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);
p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
xmlFree(override);
return (internal_attach_property(pgrp, p));
}
static int
lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
{
property_t *p;
xmlNodePtr cursor;
element_t r;
xmlChar *type, *override;
p = internal_property_new();
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) ||
(*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)
break;
}
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);
}
p->sc_value_type = lxml_element_to_type(r);
for (cursor = property->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (r = lxml_xlate_element(cursor->name)) {
case SC_ASTRING:
case SC_BOOLEAN:
case SC_COUNT:
case SC_FMRI:
case SC_HOST:
case SC_HOSTNAME:
case SC_INTEGER:
case SC_NET_ADDR_V4:
case SC_NET_ADDR_V6:
case SC_OPAQUE:
case SC_TIME:
case SC_URI:
case SC_USTRING:
if (strcmp(lxml_prop_types[r], (const char *)type) != 0)
uu_die(gettext("property \'%s\' "
"type-to-list mismatch\n"),
p->sc_property_name);
(void) lxml_get_value(p, r, cursor);
break;
default:
uu_die(gettext("unknown value list type: %s\n"),
cursor->name);
break;
}
}
xmlFree(type);
override = xmlGetProp(property, (xmlChar *)override_attr);
p->sc_property_override = (xmlStrcmp(override, (xmlChar *)true) == 0);
xmlFree(override);
return (internal_attach_property(pgrp, p));
}
static int
lxml_get_pgroup_stability(pgroup_t *pgrp, xmlNodePtr stab)
{
return (new_str_prop_from_attr(pgrp, SCF_PROPERTY_STABILITY,
SCF_TYPE_ASTRING, stab, value_attr));
}
/*
* Property groups can go on any of a service, an instance, or a template.
*/
static int
lxml_get_pgroup(entity_t *entity, xmlNodePtr pgroup)
{
pgroup_t *pg;
xmlNodePtr cursor;
xmlChar *name, *type, *delete;
/*
* property group attributes:
* name: string
* type: string | framework | application
*/
name = xmlGetProp(pgroup, (xmlChar *)name_attr);
type = xmlGetProp(pgroup, (xmlChar *)type_attr);
pg = internal_pgroup_find_or_create(entity, (char *)name, (char *)type);
xmlFree(name);
xmlFree(type);
/*
* Walk the children of this lxml_elements, which are a stability
* element, property elements, or propval elements.
*/
for (cursor = pgroup->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_STABILITY:
(void) lxml_get_pgroup_stability(pg, cursor);
break;
case SC_PROPERTY:
(void) lxml_get_property(pg, cursor);
break;
case SC_PROPVAL:
(void) lxml_get_propval(pg, cursor);
break;
default:
abort();
break;
}
}
delete = xmlGetProp(pgroup, (xmlChar *)delete_attr);
pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
xmlFree(delete);
return (0);
}
/*
* Dependency groups, execution methods can go on either a service or an
* instance.
*/
static int
lxml_get_method_profile(pgroup_t *pg, xmlNodePtr profile)
{
property_t *p;
p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
1, (uint64_t)1);
if (internal_attach_property(pg, p) != 0)
return (-1);
return (new_str_prop_from_attr(pg, SCF_PROPERTY_PROFILE,
SCF_TYPE_ASTRING, profile, name_attr));
}
static int
lxml_get_method_credential(pgroup_t *pg, xmlNodePtr cred)
{
property_t *p;
p = internal_property_create(SCF_PROPERTY_USE_PROFILE, SCF_TYPE_BOOLEAN,
1, (uint64_t)0);
if (internal_attach_property(pg, p) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_USER, SCF_TYPE_ASTRING,
cred, "user", NULL) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_GROUP, SCF_TYPE_ASTRING,
cred, "group", NULL) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_SUPP_GROUPS,
SCF_TYPE_ASTRING, cred, "supp_groups", NULL) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PRIVILEGES,
SCF_TYPE_ASTRING, cred, "privileges", NULL) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_LIMIT_PRIVILEGES,
SCF_TYPE_ASTRING, cred, "limit_privileges", NULL) != 0)
return (-1);
return (0);
}
static char *
lxml_get_envvar(xmlNodePtr envvar)
{
char *name;
char *value;
char *ret;
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 "
"\"%s\".\n"), name);
if (strstr(name, "SMF_") == name)
uu_die(gettext("Invalid environment variable "
"\"%s\"; \"SMF_\" prefix is reserved.\n"), name);
ret = uu_msprintf("%s=%s", name, value);
xmlFree(name);
xmlFree(value);
return (ret);
}
static int
lxml_get_method_environment(pgroup_t *pg, xmlNodePtr environment)
{
property_t *p;
xmlNodePtr cursor;
value_t *val;
p = internal_property_create(SCF_PROPERTY_ENVIRONMENT,
SCF_TYPE_ASTRING, 0);
for (cursor = environment->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
char *tmp;
if (lxml_ignorable_block(cursor))
continue;
if (lxml_xlate_element(cursor->name) != SC_METHOD_ENVVAR)
uu_die(gettext("illegal element \"%s\" on "
"method environment for \"%s\"\n"),
cursor->name, pg->sc_pgroup_name);
if ((tmp = lxml_get_envvar(cursor)) == NULL)
uu_die(gettext("Out of memory\n"));
val = internal_value_new();
val->sc_u.sc_string = tmp;
val->sc_type = SCF_TYPE_ASTRING;
val->sc_free = lxml_free_str;
internal_attach_value(p, val);
}
if (internal_attach_property(pg, p) != 0) {
internal_property_free(p);
return (-1);
}
return (0);
}
static int
lxml_get_method_context(pgroup_t *pg, xmlNodePtr ctx)
{
xmlNodePtr cursor;
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_WORKING_DIRECTORY,
SCF_TYPE_ASTRING, ctx, "working_directory", NULL) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_PROJECT,
SCF_TYPE_ASTRING, ctx, "project", NULL) != 0)
return (-1);
if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_RESOURCE_POOL,
SCF_TYPE_ASTRING, ctx, "resource_pool", NULL) != 0)
return (-1);
for (cursor = ctx->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_METHOD_CREDENTIAL:
(void) lxml_get_method_credential(pg, cursor);
break;
case SC_METHOD_PROFILE:
(void) lxml_get_method_profile(pg, cursor);
break;
case SC_METHOD_ENVIRONMENT:
(void) lxml_get_method_environment(pg, cursor);
break;
default:
semerr(gettext("illegal element \'%s\' in method "
"context\n"), (char *)cursor);
break;
}
}
return (0);
}
static int
lxml_get_entity_method_context(entity_t *entity, xmlNodePtr ctx)
{
pgroup_t *pg;
pg = internal_pgroup_find_or_create(entity, SCF_PG_METHOD_CONTEXT,
(char *)scf_group_framework);
return (lxml_get_method_context(pg, ctx));
}
static int
lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
{
pgroup_t *pg;
property_t *p;
xmlChar *name, *timeout, *delete;
xmlNodePtr cursor;
int r = 0;
name = xmlGetProp(emeth, (xmlChar *)name_attr);
pg = internal_pgroup_find_or_create(entity, (char *)name,
(char *)SCF_GROUP_METHOD);
xmlFree(name);
if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
emeth, type_attr) != 0 ||
new_str_prop_from_attr(pg, SCF_PROPERTY_EXEC, SCF_TYPE_ASTRING,
emeth, "exec") != 0)
return (-1);
timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr);
if (timeout != NULL) {
uint64_t u_timeout;
char *endptr;
/*
* Although an SC_COUNT represents a uint64_t the use
* of a negative value is acceptable due to the usage
* established by inetd(1M).
*/
errno = 0;
u_timeout = strtoull((char *)timeout, &endptr, 10);
if (errno != 0 || endptr == (char *)timeout || *endptr)
uu_die(gettext("illegal value \"%s\" for "
"timeout_seconds (%s)\n"),
(char *)timeout, (errno) ? strerror(errno):
gettext("Illegal character"));
p = internal_property_create(SCF_PROPERTY_TIMEOUT,
SCF_TYPE_COUNT, 1, u_timeout);
r = internal_attach_property(pg, p);
xmlFree(timeout);
}
if (r != 0)
return (-1);
/*
* There is a possibility that a method context also exists, in which
* case the following attributes are defined: project, resource_pool,
* working_directory, profile, user, group, privileges, limit_privileges
*/
for (cursor = emeth->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_STABILITY:
if (lxml_get_pgroup_stability(pg, cursor) != 0)
return (-1);
break;
case SC_METHOD_CONTEXT:
(void) lxml_get_method_context(pg, cursor);
break;
case SC_PROPVAL:
(void) lxml_get_propval(pg, cursor);
break;
case SC_PROPERTY:
(void) lxml_get_property(pg, cursor);
break;
default:
uu_die(gettext("illegal element \"%s\" on "
"execution method \"%s\"\n"), cursor->name,
pg->sc_pgroup_name);
break;
}
}
delete = xmlGetProp(emeth, (xmlChar *)delete_attr);
pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
xmlFree(delete);
return (0);
}
static int
lxml_get_dependency(entity_t *entity, xmlNodePtr dependency)
{
pgroup_t *pg;
property_t *p;
xmlNodePtr cursor;
xmlChar *name;
xmlChar *delete;
/*
* dependency attributes:
* name: string
* grouping: require_all | require_any | exclude_all | optional_all
* reset_on: string (error | restart | refresh | none)
* type: service / path /host
*/
name = xmlGetProp(dependency, (xmlChar *)name_attr);
pg = internal_pgroup_find_or_create(entity, (char *)name,
(char *)SCF_GROUP_DEPENDENCY);
xmlFree(name);
if (new_str_prop_from_attr(pg, SCF_PROPERTY_TYPE, SCF_TYPE_ASTRING,
dependency, type_attr) != 0)
return (-1);
if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
SCF_TYPE_ASTRING, dependency, "restart_on") != 0)
return (-1);
if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
dependency, "grouping") != 0)
return (-1);
p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 0);
if (internal_attach_property(pg, p) != 0)
return (-1);
for (cursor = dependency->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
xmlChar *value;
value_t *v;
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_STABILITY:
if (lxml_get_pgroup_stability(pg, cursor) != 0)
return (-1);
break;
case SC_SERVICE_FMRI:
value = xmlGetProp(cursor, (xmlChar *)value_attr);
if (value != NULL) {
if (lxml_validate_string_value(SCF_TYPE_FMRI,
(char *)value) != 0)
uu_die(gettext("illegal value \"%s\" "
"for %s (%s)\n"), (char *)value,
lxml_prop_types[SC_FMRI],
(scf_error()) ?
scf_strerror(scf_error()) :
gettext("Illegal format"));
v = internal_value_new();
v->sc_type = SCF_TYPE_FMRI;
v->sc_u.sc_string = (char *)value;
internal_attach_value(p, v);
}
break;
case SC_PROPVAL:
(void) lxml_get_propval(pg, cursor);
break;
case SC_PROPERTY:
(void) lxml_get_property(pg, cursor);
break;
default:
uu_die(gettext("illegal element \"%s\" on "
"dependency group \"%s\"\n"), cursor->name, name);
break;
}
}
delete = xmlGetProp(dependency, (xmlChar *)delete_attr);
pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
xmlFree(delete);
return (0);
}
/*
* Dependents are hairy. They should cause a dependency pg to be created in
* another service, but we can't do that here; we'll have to wait until the
* import routines. So for now we'll add the dependency group that should go
* in the other service to the entity's dependent list.
*/
static int
lxml_get_dependent(entity_t *entity, xmlNodePtr dependent)
{
xmlChar *name, *or;
xmlNodePtr sf;
xmlChar *fmri, *delete;
pgroup_t *pg;
property_t *p;
xmlNodePtr n;
char *myfmri;
name = xmlGetProp(dependent, (xmlChar *)name_attr);
if (internal_pgroup_find(entity, (char *)name, NULL) != NULL) {
semerr(gettext("Property group and dependent of entity %s "
"have same name \"%s\".\n"), entity->sc_name, name);
xmlFree(name);
return (-1);
}
or = xmlGetProp(dependent, (xmlChar *)override_attr);
pg = internal_pgroup_new();
pg->sc_pgroup_name = (char *)name;
pg->sc_pgroup_type = (char *)SCF_GROUP_DEPENDENCY;
pg->sc_pgroup_override = (xmlStrcmp(or, (xmlChar *)true) == 0);
xmlFree(or);
if (internal_attach_dependent(entity, pg) != 0) {
xmlFree(name);
internal_pgroup_free(pg);
return (-1);
}
for (sf = dependent->children; sf != NULL; sf = sf->next)
if (xmlStrcmp(sf->name, (xmlChar *)"service_fmri") == 0)
break;
assert(sf != NULL);
fmri = xmlGetProp(sf, (xmlChar *)value_attr);
pg->sc_pgroup_fmri = (char *)fmri;
if (new_str_prop_from_attr(pg, SCF_PROPERTY_RESTART_ON,
SCF_TYPE_ASTRING, dependent, "restart_on") != 0)
return (-1);
if (new_str_prop_from_attr(pg, SCF_PROPERTY_GROUPING, SCF_TYPE_ASTRING,
dependent, "grouping") != 0)
return (-1);
myfmri = safe_malloc(max_scf_fmri_len + 1);
if (entity->sc_etype == SVCCFG_SERVICE_OBJECT) {
if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s",
entity->sc_name) < 0)
bad_error("snprintf", errno);
} else {
assert(entity->sc_etype == SVCCFG_INSTANCE_OBJECT);
if (snprintf(myfmri, max_scf_fmri_len + 1, "svc:/%s:%s",
entity->sc_parent->sc_name, entity->sc_name) < 0)
bad_error("snprintf", errno);
}
p = internal_property_create(SCF_PROPERTY_ENTITIES, SCF_TYPE_FMRI, 1,
myfmri);
if (internal_attach_property(pg, p) != 0)
return (-1);
/* Create a property to serve as a do-not-export flag. */
p = internal_property_create("external", SCF_TYPE_BOOLEAN, 1,
(uint64_t)1);
if (internal_attach_property(pg, p) != 0)
return (-1);
for (n = sf->next; n != NULL; n = n->next) {
if (lxml_ignorable_block(n))
continue;
switch (lxml_xlate_element(n->name)) {
case SC_STABILITY:
if (new_str_prop_from_attr(pg,
SCF_PROPERTY_ENTITY_STABILITY, SCF_TYPE_ASTRING, n,
value_attr) != 0)
return (-1);
break;
case SC_PROPVAL:
(void) lxml_get_propval(pg, n);
break;
case SC_PROPERTY:
(void) lxml_get_property(pg, n);
break;
default:
uu_die(gettext("unexpected element %s.\n"), n->name);
}
}
/* Go back and fill in defaults. */
if (internal_property_find(pg, SCF_PROPERTY_TYPE) == NULL) {
p = internal_property_create(SCF_PROPERTY_TYPE,
SCF_TYPE_ASTRING, 1, "service");
if (internal_attach_property(pg, p) != 0)
return (-1);
}
delete = xmlGetProp(dependent, (xmlChar *)delete_attr);
pg->sc_pgroup_delete = (xmlStrcmp(delete, (xmlChar *)true) == 0);
xmlFree(delete);
pg = internal_pgroup_find_or_create(entity, "dependents",
(char *)scf_group_framework);
p = internal_property_create((char *)name, SCF_TYPE_FMRI, 1, fmri);
if (internal_attach_property(pg, p) != 0)
return (-1);
return (0);
}
static int
lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
{
pgroup_t *pg;
property_t *p;
xmlChar *stabval;
if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) ||
(*stabval == 0)) {
uu_warn(gettext("no stability value found\n"));
stabval = (xmlChar *)strdup("External");
}
pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
(char *)scf_group_framework);
p = internal_property_create(SCF_PROPERTY_ENTITY_STABILITY,
SCF_TYPE_ASTRING, 1, stabval);
return (internal_attach_property(pg, p));
}
static int
lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
{
pgroup_t *pg;
property_t *p;
xmlChar *restarter;
xmlNode *cursor;
int r;
/*
* Go find child. Child is a service_fmri element. value attribute
* contains restarter FMRI.
*/
pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
(char *)scf_group_framework);
/*
* Walk its child elements, as appropriate.
*/
for (cursor = rstr->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_SERVICE_FMRI:
restarter = xmlGetProp(cursor, (xmlChar *)value_attr);
break;
default:
uu_die(gettext("illegal element \"%s\" on restarter "
"element for \"%s\"\n"), cursor->name,
entity->sc_name);
break;
}
}
p = internal_property_create(SCF_PROPERTY_RESTARTER, SCF_TYPE_FMRI, 1,
restarter);
r = internal_attach_property(pg, p);
if (r != 0) {
internal_property_free(p);
return (-1);
}
return (0);
}
/*
* 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,
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_attr)) == NULL) ||
(*val == 0)) {
if (((val = xmlGetProp(loctext,
(xmlChar *)lang_attr)) == NULL) || (*val == 0)) {
val = (xmlChar *)"unknown";
}
}
_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) {
if (strcmp("text", (const char *)cursor->name) == 0) {
break;
} else if (strcmp("comment", (const char *)cursor->name) != 0) {
uu_die(gettext("illegal element \"%s\" on loctext "
"element for \"%s\"\n"), cursor->name,
service->sc_name);
}
}
if (cursor == NULL) {
uu_die(gettext("loctext element has no content for \"%s\"\n"),
service->sc_name);
}
/*
* Remove leading and trailing whitespace.
*/
if ((stripped = strdup((const char *)cursor->content)) == NULL)
uu_die(gettext("Out of memory\n"));
for (; isspace(*stripped); stripped++)
;
for (cp = stripped + strlen(stripped) - 1; isspace(*cp); cp--)
;
*(cp + 1) = '\0';
p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1,
stripped);
r = internal_attach_property(pg, p);
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_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container,
const char *pn_format, const char *source)
{
xmlNodePtr cursor;
/*
* 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 = 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, pn_format,
source))
return (-1);
break;
default:
uu_die(gettext("illegal element \"%s\" on %s element "
"for \"%s\"\n"), cursor->name, container->name,
service->sc_name);
break;
}
}
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_cardinality_attribute(entity_t *service, xmlNodePtr cursor,
const char *attr_name, const char *prop_name, uint64_t *value,
property_t **propp)
{
char *c;
property_t *p;
xmlChar *val;
uint64_t count;
char *endptr;
*propp = NULL;
val = xmlGetProp(cursor, (xmlChar *)attr_name);
if (val == NULL)
return (0);
if (*val == 0) {
xmlFree(val);
return (0);
}
/*
* Make sure that the string at val doesn't have a leading minus
* sign. The strtoull() call below does not catch this problem.
*/
for (c = (char *)val; *c != 0; c++) {
if (isspace(*c))
continue;
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 *
lxml_label_to_groupname(const char *prefix, const char *in)
{
char *out, *cp;
size_t len, piece_len;
out = uu_zalloc(2 * scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1);
if (out == NULL)
return (NULL);
(void) strcpy(out, prefix);
(void) strcat(out, in);
len = strlen(out);
if (len > max_scf_name_len) {
/* Use the first half and the second half. */
piece_len = (max_scf_name_len - 2) / 2;
(void) strncpy(out + piece_len, "..", 2);
(void) strcpy(out + piece_len + 2, out + (len - piece_len));
len = strlen(out);
}
/*
* Translate non-property characters to '_'.
*/
for (cp = out; *cp != '\0'; ++cp) {
if (!(isalnum(*cp) || *cp == '_' || *cp == '-'))
*cp = '_';
}
*cp = '\0';
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)
{
pgroup_t *pg;
char *pgname;
xmlChar *title;
/*
* Fetch title attribute, convert to something sanitized, and create
* property group.
*/
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);
/*
* Each attribute is an astring property within the group.
*/
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);
}
static int
lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
{
pgroup_t *pg;
char *pgname;
xmlChar *name;
/*
* Fetch name attribute, convert name to something sanitized, and create
* property group.
*/
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, 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);
}
static int
lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
{
xmlNodePtr cursor;
for (cursor = documentation->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_MANPAGE:
(void) lxml_get_tm_manpage(service, cursor);
break;
case SC_DOC_LINK:
(void) lxml_get_tm_doclink(service, cursor);
break;
default:
uu_die(gettext("illegal element \"%s\" on template "
"for service \"%s\"\n"),
cursor->name, service->sc_name);
}
}
return (0);
}
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;
for (cursor = templ->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_tm_common_name(service, cursor);
break;
case SC_DESCRIPTION:
(void) lxml_get_tm_description(service, cursor);
break;
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"),
cursor->name, service->sc_name);
}
}
return (0);
}
static int
lxml_get_default_instance(entity_t *service, xmlNodePtr definst)
{
entity_t *i;
xmlChar *enabled;
pgroup_t *pg;
property_t *p;
char *package;
uint64_t enabled_val = 0;
i = internal_instance_new("default");
if ((enabled = xmlGetProp(definst, (xmlChar *)enabled_attr)) != NULL) {
enabled_val = (strcmp(true, (const char *)enabled) == 0) ?
1 : 0;
xmlFree(enabled);
}
/*
* New general property group with enabled boolean property set.
*/
pg = internal_pgroup_new();
(void) internal_attach_pgroup(i, pg);
pg->sc_pgroup_name = (char *)scf_pg_general;
pg->sc_pgroup_type = (char *)scf_group_framework;
pg->sc_pgroup_flags = 0;
p = internal_property_create(SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN, 1,
enabled_val);
(void) internal_attach_property(pg, p);
/*
* Add general/package property if PKGINST is set.
*/
if ((package = getenv("PKGINST")) != NULL) {
p = internal_property_create(SCF_PROPERTY_PACKAGE,
SCF_TYPE_ASTRING, 1, package);
(void) internal_attach_property(pg, p);
}
return (internal_attach_entity(service, i));
}
/*
* Translate an instance element into an internal property tree, added to
* service. If op is SVCCFG_OP_APPLY (i.e., apply a profile), set the
* enabled property to override.
*/
static int
lxml_get_instance(entity_t *service, xmlNodePtr inst, bundle_type_t bt,
svccfg_op_t op)
{
entity_t *i;
pgroup_t *pg;
property_t *p;
xmlNodePtr cursor;
xmlChar *enabled;
int r, e_val;
/*
* Fetch its attributes, as appropriate.
*/
i = internal_instance_new((char *)xmlGetProp(inst,
(xmlChar *)name_attr));
/*
* Note that this must be done before walking the children so that
* sc_fmri is set in case we enter lxml_get_dependent().
*/
r = internal_attach_entity(service, i);
if (r != 0)
return (r);
enabled = xmlGetProp(inst, (xmlChar *)enabled_attr);
if (enabled == NULL) {
if (bt == SVCCFG_MANIFEST) {
semerr(gettext("Instance \"%s\" missing attribute "
"\"%s\".\n"), i->sc_name, enabled_attr);
return (-1);
}
} else { /* enabled != NULL */
if (strcmp(true, (const char *)enabled) != 0 &&
strcmp(false, (const char *)enabled) != 0) {
xmlFree(enabled);
semerr(gettext("Invalid enabled value\n"));
return (-1);
}
pg = internal_pgroup_new();
(void) internal_attach_pgroup(i, pg);
pg->sc_pgroup_name = (char *)scf_pg_general;
pg->sc_pgroup_type = (char *)scf_group_framework;
pg->sc_pgroup_flags = 0;
e_val = (strcmp(true, (const char *)enabled) == 0);
p = internal_property_create(SCF_PROPERTY_ENABLED,
SCF_TYPE_BOOLEAN, 1, (uint64_t)e_val);
p->sc_property_override = (op == SVCCFG_OP_APPLY);
(void) internal_attach_property(pg, p);
xmlFree(enabled);
}
/*
* Walk its child elements, as appropriate.
*/
for (cursor = inst->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_RESTARTER:
(void) lxml_get_restarter(i, cursor);
break;
case SC_DEPENDENCY:
(void) lxml_get_dependency(i, cursor);
break;
case SC_DEPENDENT:
(void) lxml_get_dependent(i, cursor);
break;
case SC_METHOD_CONTEXT:
(void) lxml_get_entity_method_context(i, cursor);
break;
case SC_EXEC_METHOD:
(void) lxml_get_exec_method(i, cursor);
break;
case SC_PROPERTY_GROUP:
(void) lxml_get_pgroup(i, cursor);
break;
case SC_TEMPLATE:
if (lxml_get_template(i, cursor) != 0)
return (-1);
break;
default:
uu_die(gettext(
"illegal element \"%s\" on instance \"%s\"\n"),
cursor->name, i->sc_name);
break;
}
}
return (0);
}
/* ARGSUSED1 */
static int
lxml_get_single_instance(entity_t *entity, xmlNodePtr si)
{
pgroup_t *pg;
property_t *p;
int r;
pg = internal_pgroup_find_or_create(entity, (char *)scf_pg_general,
(char *)scf_group_framework);
p = internal_property_create(SCF_PROPERTY_SINGLE_INSTANCE,
SCF_TYPE_BOOLEAN, 1, (uint64_t)1);
r = internal_attach_property(pg, p);
if (r != 0) {
internal_property_free(p);
return (-1);
}
return (0);
}
/*
* Translate a service element into an internal instance/property tree, added
* to bundle. If op is SVCCFG_OP_APPLY, allow only instance subelements.
*/
static int
lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
{
pgroup_t *pg;
property_t *p;
entity_t *s;
xmlNodePtr cursor;
xmlChar *type;
xmlChar *version;
int e;
/*
* Fetch attributes, as appropriate.
*/
s = internal_service_new((char *)xmlGetProp(svc,
(xmlChar *)name_attr));
version = xmlGetProp(svc, (xmlChar *)version_attr);
s->sc_u.sc_service.sc_service_version = atol((const char *)version);
xmlFree(version);
type = xmlGetProp(svc, (xmlChar *)type_attr);
s->sc_u.sc_service.sc_service_type = lxml_xlate_service_type(type);
xmlFree(type);
/*
* Now that the service is created create the manifest
* property group and add the property value of the service.
*/
if (svc->doc->name != NULL &&
bundle->sc_bundle_type == SVCCFG_MANIFEST) {
char *buf, *base, *fname;
pg = internal_pgroup_create_strict(s, SCF_PG_MANIFESTFILES,
SCF_GROUP_FRAMEWORK);
if (pg == NULL) {
uu_die(gettext("Property group for prop_pattern, "
"\"%s\", already exists in %s\n"),
SCF_PG_MANIFESTFILES, s->sc_name);
}
buf = mhash_filename_to_propname(svc->doc->name, B_FALSE);
/*
* Must remove the PKG_INSTALL_ROOT, point to the correct
* directory after install
*/
base = getenv("PKG_INSTALL_ROOT");
fname = safe_strdup(svc->doc->name +
((base != NULL) ? strlen(base) : 0));
p = internal_property_create(buf, SCF_TYPE_ASTRING, 1, fname);
(void) internal_attach_property(pg, p);
}
/*
* Walk its child elements, as appropriate.
*/
for (cursor = svc->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
e = lxml_xlate_element(cursor->name);
switch (e) {
case SC_INSTANCE:
if (lxml_get_instance(s, cursor,
bundle->sc_bundle_type, op) != 0)
return (-1);
break;
case SC_TEMPLATE:
if (lxml_get_template(s, cursor) != 0)
return (-1);
break;
case SC_STABILITY:
(void) lxml_get_entity_stability(s, cursor);
break;
case SC_DEPENDENCY:
(void) lxml_get_dependency(s, cursor);
break;
case SC_DEPENDENT:
(void) lxml_get_dependent(s, cursor);
break;
case SC_RESTARTER:
(void) lxml_get_restarter(s, cursor);
break;
case SC_EXEC_METHOD:
(void) lxml_get_exec_method(s, cursor);
break;
case SC_METHOD_CONTEXT:
(void) lxml_get_entity_method_context(s, cursor);
break;
case SC_PROPERTY_GROUP:
(void) lxml_get_pgroup(s, cursor);
break;
case SC_INSTANCE_CREATE_DEFAULT:
(void) lxml_get_default_instance(s, cursor);
break;
case SC_INSTANCE_SINGLE:
(void) lxml_get_single_instance(s, cursor);
break;
default:
uu_die(gettext(
"illegal element \"%s\" on service \"%s\"\n"),
cursor->name, s->sc_name);
break;
}
}
return (internal_attach_service(bundle, s));
}
#ifdef DEBUG
void
lxml_dump(int g, xmlNodePtr p)
{
if (p && p->name) {
printf("%d %s\n", g, p->name);
for (p = p->xmlChildrenNode; p != NULL; p = p->next)
lxml_dump(g + 1, p);
}
}
#endif /* DEBUG */
static int
lxml_is_known_dtd(const xmlChar *dtdname)
{
if (dtdname == NULL ||
strcmp(MANIFEST_DTD_PATH, (const char *)dtdname) != 0)
return (0);
return (1);
}
static int
lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
xmlNodePtr subbundle, svccfg_op_t op)
{
xmlNodePtr cursor;
xmlChar *type;
int e;
/*
* 1. Get bundle attributes.
*/
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) {
semerr(gettext("included bundle of different type.\n"));
return (-1);
}
xmlFree(type);
switch (op) {
case SVCCFG_OP_IMPORT:
if (bundle->sc_bundle_type != SVCCFG_MANIFEST) {
semerr(gettext("document is not a manifest.\n"));
return (-1);
}
break;
case SVCCFG_OP_APPLY:
if (bundle->sc_bundle_type != SVCCFG_PROFILE) {
semerr(gettext("document is not a profile.\n"));
return (-1);
}
break;
case SVCCFG_OP_RESTORE:
if (bundle->sc_bundle_type != SVCCFG_ARCHIVE) {
semerr(gettext("document is not an archive.\n"));
return (-1);
}
break;
}
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);
}
/*
* 2. Get services, descend into each one and build state.
*/
for (cursor = subbundle->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
e = lxml_xlate_element(cursor->name);
switch (e) {
case SC_XI_INCLUDE:
continue;
case SC_SERVICE_BUNDLE:
if (lxml_get_bundle(bundle, bundle_type, cursor, op))
return (-1);
break;
case SC_SERVICE:
if (lxml_get_service(bundle, cursor, op) != 0)
return (-1);
break;
}
}
return (0);
}
/*
* Load an XML tree from filename and translate it into an internal service
* tree bundle. Require that the bundle be of appropriate type for the
* operation: archive for RESTORE, manifest for IMPORT, profile for APPLY.
*/
int
lxml_get_bundle_file(bundle_t *bundle, const char *filename, svccfg_op_t op)
{
xmlDocPtr document;
xmlNodePtr cursor;
xmlDtdPtr dtd = NULL;
xmlValidCtxtPtr vcp;
boolean_t do_validate;
char *dtdpath = NULL;
int r;
/*
* Verify we can read the file before we try to parse it.
*/
if (access(filename, R_OK | F_OK) == -1) {
semerr(gettext("unable to open file: %s\n"), strerror(errno));
return (-1);
}
/*
* Until libxml2 addresses DTD-based validation with XInclude, we don't
* validate service profiles (i.e. the apply path).
*/
do_validate = (op != SVCCFG_OP_APPLY) &&
(getenv("SVCCFG_NOVALIDATE") == NULL);
if (do_validate)
dtdpath = getenv("SVCCFG_DTD");
if (dtdpath != NULL)
xmlLoadExtDtdDefaultValue = 0;
if ((document = xmlReadFile(filename, NULL, 0)) == NULL) {
semerr(gettext("couldn't parse document\n"));
return (-1);
}
document->name = strdup(filename);
/*
* Verify that this is a document type we understand.
*/
if ((dtd = xmlGetIntSubset(document)) == NULL) {
semerr(gettext("document has no DTD\n"));
return (-1);
}
if (!lxml_is_known_dtd(dtd->SystemID)) {
semerr(gettext("document DTD unknown; not service bundle?\n"));
return (-1);
}
if ((cursor = xmlDocGetRootElement(document)) == NULL) {
semerr(gettext("document is empty\n"));
xmlFreeDoc(document);
return (-1);
}
if (xmlStrcmp(cursor->name, (const xmlChar *)"service_bundle") != 0) {
semerr(gettext("document is not a service bundle\n"));
xmlFreeDoc(document);
return (-1);
}
if (dtdpath != NULL) {
dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
if (dtd == NULL) {
semerr(gettext("Could not parse DTD \"%s\".\n"),
dtdpath);
return (-1);
}
if (document->extSubset != NULL)
xmlFreeDtd(document->extSubset);
document->extSubset = dtd;
}
if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
semerr(gettext("couldn't handle XInclude statements "
"in document\n"));
return (-1);
}
if (do_validate) {
vcp = xmlNewValidCtxt();
if (vcp == NULL)
uu_die(gettext("could not allocate memory"));
vcp->warning = xmlParserValidityWarning;
vcp->error = xmlParserValidityError;
r = xmlValidateDocument(vcp, document);
xmlFreeValidCtxt(vcp);
if (r == 0) {
semerr(gettext("Document is not valid.\n"));
xmlFreeDoc(document);
return (-1);
}
}
#ifdef DEBUG
lxml_dump(0, cursor);
#endif /* DEBUG */
r = lxml_get_bundle(bundle, SVCCFG_UNKNOWN_BUNDLE, cursor, op);
xmlFreeDoc(document);
return (r);
}
int
lxml_inventory(const char *filename)
{
bundle_t *b;
uu_list_walk_t *svcs, *insts;
entity_t *svc, *inst;
b = internal_bundle_new();
if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) != 0) {
internal_bundle_free(b);
return (-1);
}
svcs = uu_list_walk_start(b->sc_bundle_services, 0);
if (svcs == NULL)
uu_die(gettext("Couldn't walk services"));
while ((svc = uu_list_walk_next(svcs)) != NULL) {
uu_list_t *inst_list;
inst_list = svc->sc_u.sc_service.sc_service_instances;
insts = uu_list_walk_start(inst_list, 0);
if (insts == NULL)
uu_die(gettext("Couldn't walk instances"));
while ((inst = uu_list_walk_next(insts)) != NULL)
(void) printf("svc:/%s:%s\n", svc->sc_name,
inst->sc_name);
uu_list_walk_end(insts);
}
uu_list_walk_end(svcs);
svcs = uu_list_walk_start(b->sc_bundle_services, 0);
while ((svc = uu_list_walk_next(svcs)) != NULL) {
(void) fputs("svc:/", stdout);
(void) puts(svc->sc_name);
}
uu_list_walk_end(svcs);
internal_bundle_free(b);
return (0);
}