svccfg_xml.c revision 9e9ae1fc5a2cf0c35d578ad826dff4763cd0a605
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <libxml/xinclude.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
#include <libuutil.h>
#include <stdlib.h>
#include <string.h>
#include "svccfg.h"
/*
* 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.
*/
const char * const delete_attr = "delete";
const char * const enabled_attr = "enabled";
const char * const name_attr = "name";
const char * const override_attr = "override";
const char * const type_attr = "type";
const char * const value_attr = "value";
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 */
"common_name", /* SC_COMMON_NAME */
"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 */
"instance", /* SC_INSTANCE */
"integer_list", /* SC_INTEGER */
"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 */
"property", /* SC_PROPERTY */
"property_group", /* SC_PROPERTY_GROUP */
"propval", /* SC_PROPVAL */
"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 */
"uri_list", /* SC_URI */
"ustring_list", /* SC_USTRING */
"value_node", /* SC_VALUE_NODE */
"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_COMMON_NAME */
"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_INSTANCE */
"integer", /* SC_INTEGER */
"", /* 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_PROPERTY */
"", /* SC_PROPERTY_GROUP */
"", /* SC_PROPVAL */
"", /* SC_RESTARTER */
"", /* SC_SERVICE */
"", /* SC_SERVICE_BUNDLE */
"", /* SC_SERVICE_FMRI */
"", /* SC_INSTANCE_SINGLE */
"", /* SC_STABILITY */
"", /* SC_TEMPLATE */
"time", /* SC_TIME */
"uri", /* SC_URI */
"ustring", /* SC_USTRING */
"" /* SC_VALUE_NODE */
"" /* SC_XI_FALLBACK */
"" /* SC_XI_INCLUDE */
};
int
{
/*
* DTD validation, with line numbers.
*/
}
return (0);
}
static bundle_type_t
{
return (SVCCFG_MANIFEST);
return (SVCCFG_PROFILE);
return (SVCCFG_ARCHIVE);
return (SVCCFG_UNKNOWN_BUNDLE);
}
static service_type_t
{
return (SVCCFG_SERVICE);
return (SVCCFG_RESTARTER);
return (SVCCFG_MILESTONE);
return (SVCCFG_UNKNOWN_SERVICE);
}
static element_t
{
int i;
for (i = 0; i < sizeof (lxml_elements) / sizeof (char *); i++)
return ((element_t)i);
return ((element_t)-1);
}
static uint_t
{
return (1);
return (0);
/*NOTREACHED*/
}
static scf_type_t
{
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:
}
/* NOTREACHED */
}
static scf_type_t
{
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:
}
/* NOTREACHED */
}
static int
xmlNodePtr n, const char *attr)
{
property_t *p;
int r;
r = internal_attach_property(pgrp, p);
if (r != 0)
return (r);
}
static int
{
}
static int
{
NULL)
return (-1);
NULL)
return (-1);
}
static void
{
}
static value_t *
{
value_t *v;
char *endptr;
v = internal_value_new();
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;
"%s (%s)\n"), (char *)value,
gettext("Illegal character"));
break;
case SC_INTEGER:
errno = 0;
"%s (%s)\n"), (char *)value,
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:
"%s (%s)\n"), (char *)value,
gettext("Illegal format"));
v->sc_free = lxml_free_str;
break;
case SC_BOOLEAN:
break;
default:
break;
}
return (v);
}
static int
{
value_t *v;
if (lxml_ignorable_block(cursor))
continue;
case SC_VALUE_NODE:
break;
default:
break;
}
}
return (0);
}
static int
{
property_t *p;
element_t r;
value_t *v;
p = internal_property_new();
if (p->sc_property_name == NULL)
for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); ++r) {
break;
}
if (r >= sizeof (lxml_prop_types) / sizeof (char *))
p->sc_value_type = lxml_element_to_type(r);
v = lxml_make_value(r, val);
internal_attach_value(p, v);
return (internal_attach_property(pgrp, p));
}
static int
{
property_t *p;
element_t r;
p = internal_property_new();
p->sc_property_name);
if (lxml_ignorable_block(cursor))
continue;
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:
"type-to-list mismatch\n"),
p->sc_property_name);
p->sc_value_type = lxml_element_to_type(r);
(void) lxml_get_value(p, r, cursor);
break;
default:
break;
}
}
return (internal_attach_property(pgrp, p));
}
static int
{
}
/*
* Property groups can go on any of a service, an instance, or a template.
*/
static int
{
/*
* property group attributes:
* name: string
* type: string | framework | application
*/
/*
* Walk the children of this lxml_elements, which are a stability
* element, property elements, or propval elements.
*/
if (lxml_ignorable_block(cursor))
continue;
case SC_STABILITY:
break;
case SC_PROPERTY:
break;
case SC_PROPVAL:
break;
default:
abort();
break;
}
}
return (0);
}
/*
* Dependency groups, execution methods can go on either a service or an
* instance.
*/
static int
{
property_t *p;
if (internal_attach_property(pg, p) != 0)
return (-1);
}
static int
{
property_t *p;
1, (uint64_t)0);
if (internal_attach_property(pg, p) != 0)
return (-1);
cred, "user") != 0)
return (-1);
cred, "group") != 0)
return (-1);
return (-1);
return (-1);
return (-1);
return (0);
}
static char *
{
char *name;
char *value;
char *ret;
"\"%s\".\n"), name);
"\"%s\"; \"SMF_\" prefix is reserved.\n"), name);
return (ret);
}
static int
{
property_t *p;
SCF_TYPE_ASTRING, 0);
char *tmp;
if (lxml_ignorable_block(cursor))
continue;
"method environment for \"%s\"\n"),
val = internal_value_new();
internal_attach_value(p, val);
}
if (internal_attach_property(pg, p) != 0) {
return (-1);
}
return (0);
}
static int
{
return (-1);
ctx, "project") != 0)
return (-1);
return (-1);
if (lxml_ignorable_block(cursor))
continue;
case SC_METHOD_CREDENTIAL:
break;
case SC_METHOD_PROFILE:
break;
case SC_METHOD_ENVIRONMENT:
break;
default:
"context\n"), (char *)cursor);
break;
}
}
return (0);
}
static int
{
(char *)scf_group_framework);
}
static int
{
property_t *p;
int r = 0;
(char *)SCF_GROUP_METHOD);
emeth, "exec") != 0)
return (-1);
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;
"timeout_seconds (%s)\n"),
gettext("Illegal character"));
r = internal_attach_property(pg, p);
}
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
*/
if (lxml_ignorable_block(cursor))
continue;
case SC_STABILITY:
return (-1);
break;
case SC_METHOD_CONTEXT:
break;
case SC_PROPVAL:
break;
case SC_PROPERTY:
break;
default:
pg->sc_pgroup_name);
break;
}
}
return (0);
}
static int
{
property_t *p;
/*
* dependency attributes:
* name: string
* grouping: require_all | require_any | exclude_all | optional_all
* reset_on: string (error | restart | refresh | none)
* type: service / path /host
*/
(char *)SCF_GROUP_DEPENDENCY);
dependency, type_attr) != 0)
return (-1);
return (-1);
dependency, "grouping") != 0)
return (-1);
if (internal_attach_property(pg, p) != 0)
return (-1);
value_t *v;
if (lxml_ignorable_block(cursor))
continue;
case SC_STABILITY:
return (-1);
break;
case SC_SERVICE_FMRI:
(char *)value) != 0)
"for %s (%s)\n"), (char *)value,
(scf_error()) ?
scf_strerror(scf_error()) :
gettext("Illegal format"));
v = internal_value_new();
v->sc_type = SCF_TYPE_FMRI;
internal_attach_value(p, v);
}
break;
case SC_PROPVAL:
break;
case SC_PROPERTY:
break;
default:
break;
}
}
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
{
property_t *p;
xmlNodePtr n;
char *myfmri;
return (-1);
}
pg = internal_pgroup_new();
return (-1);
}
break;
return (-1);
dependent, "grouping") != 0)
return (-1);
} else {
}
myfmri);
if (internal_attach_property(pg, p) != 0)
return (-1);
/* Create a property to serve as a do-not-export flag. */
(uint64_t)1);
if (internal_attach_property(pg, p) != 0)
return (-1);
if (lxml_ignorable_block(n))
continue;
switch (lxml_xlate_element(n->name)) {
case SC_STABILITY:
if (new_str_prop_from_attr(pg,
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:
}
}
/* Go back and fill in defaults. */
if (internal_attach_property(pg, p) != 0)
return (-1);
}
(char *)scf_group_framework);
if (internal_attach_property(pg, p) != 0)
return (-1);
return (0);
}
static int
{
property_t *p;
}
(char *)scf_group_framework);
return (internal_attach_property(pg, p));
}
static int
{
property_t *p;
int r;
/*
* Go find child. Child is a service_fmri element. value attribute
* contains restarter FMRI.
*/
(char *)scf_group_framework);
/*
* Walk its child elements, as appropriate.
*/
if (lxml_ignorable_block(cursor))
continue;
case SC_SERVICE_FMRI:
break;
default:
break;
}
}
r = internal_attach_property(pg, p);
if (r != 0) {
return (-1);
}
return (0);
}
static void
{
*locale = '_';
}
static int
{
property_t *p;
int r;
break;
}
}
}
/*
* Remove leading and trailing whitespace.
*/
stripped);
r = internal_attach_property(pg, p);
if (r != 0)
return (r);
}
static int
{
/*
* Create the property group, if absent.
*/
(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.
*/
if (lxml_ignorable_block(cursor))
continue;
case SC_LOCTEXT:
return (-1);
break;
default:
break;
}
}
return (0);
}
static int
{
/*
* Create the property group, if absent.
*/
(char *)SCF_PG_TM_DESCRIPTION, (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.
*/
if (lxml_ignorable_block(cursor))
continue;
case SC_LOCTEXT:
return (-1);
break;
default:
break;
}
}
return (0);
}
static char *
{
return (NULL);
if (len > max_scf_name_len) {
/* Use the first half and the second half. */
}
/*
* Translate non-property characters to '_'.
*/
*cp = '_';
}
*cp = '\0';
return (out);
}
static int
{
char *pgname;
/*
* Fetch title attribute, convert to something sanitized, and create
* property group.
*/
(const char *)title);
(char *)SCF_GROUP_TEMPLATE);
/*
* Each attribute is an astring property within the group.
*/
"title") != 0 ||
"section") != 0 ||
"manpath") != 0)
return (-1);
return (0);
}
static int
{
char *pgname;
/*
* Fetch name attribute, convert name to something sanitized, and create
* property group.
*/
(const char *)name);
(char *)SCF_GROUP_TEMPLATE);
/*
* Each attribute is an astring property within the group.
*/
"name") != 0 ||
"uri") != 0)
return (-1);
return (0);
}
static int
{
if (lxml_ignorable_block(cursor))
continue;
case SC_MANPAGE:
break;
case SC_DOC_LINK:
break;
default:
"for service \"%s\"\n"),
}
}
return (0);
}
static int
{
if (lxml_ignorable_block(cursor))
continue;
case SC_COMMON_NAME:
break;
case SC_DESCRIPTION:
break;
case SC_DOCUMENTATION:
break;
default:
"for service \"%s\"\n"),
}
}
return (0);
}
static int
{
entity_t *i;
property_t *p;
char *package;
uint64_t enabled_val = 0;
i = internal_instance_new("default");
1 : 0;
}
/*
* New general property group with enabled boolean property set.
*/
pg = internal_pgroup_new();
(void) internal_attach_pgroup(i, pg);
pg->sc_pgroup_flags = 0;
(void) internal_attach_property(pg, p);
/*
*/
(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 apply is true, forbid subelements and set the enabled property
* to override.
*/
static int
{
entity_t *i;
property_t *p;
int r;
/*
* Fetch its attributes, as appropriate.
*/
/*
* 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);
/*
* New general property group with enabled boolean property set.
*/
pg = internal_pgroup_new();
(void) internal_attach_pgroup(i, pg);
pg->sc_pgroup_flags = 0;
p->sc_property_override = apply;
(void) internal_attach_property(pg, p);
/*
* Walk its child elements, as appropriate.
*/
if (lxml_ignorable_block(cursor))
continue;
if (apply) {
"elements in profiles.\n"), i->sc_name,
return (-1);
}
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:
(void) lxml_get_template(i, cursor);
break;
default:
"illegal element \"%s\" on instance \"%s\"\n"),
break;
}
}
return (0);
}
/* ARGSUSED1 */
static int
{
property_t *p;
int r;
(char *)scf_group_framework);
r = internal_attach_property(pg, p);
if (r != 0) {
return (-1);
}
return (0);
}
/*
* to bundle. If apply is true, allow only instance subelements.
*/
static int
{
entity_t *s;
int e;
/*
* Fetch attributes, as appropriate.
*/
/*
* Walk its child elements, as appropriate.
*/
if (lxml_ignorable_block(cursor))
continue;
if (apply && e != SC_INSTANCE) {
"non-instance element \"%s\" in a profile.\n"),
return (-1);
}
switch (e) {
case SC_INSTANCE:
break;
case SC_TEMPLATE:
(void) lxml_get_template(s, cursor);
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;
(void) lxml_get_default_instance(s, cursor);
break;
case SC_INSTANCE_SINGLE:
(void) lxml_get_single_instance(s, cursor);
break;
default:
"illegal element \"%s\" on service \"%s\"\n"),
break;
}
}
return (internal_attach_service(bundle, s));
}
#ifdef DEBUG
void
lxml_dump(int g, xmlNodePtr p)
{
if (p && p->name) {
lxml_dump(g + 1, p);
}
}
#endif /* DEBUG */
static int
{
return (0);
return (1);
}
static int
{
int e;
/*
* 1. Get bundle attributes.
*/
return (-1);
}
if (!apply) {
return (-1);
}
} else {
return (-1);
}
}
return (-1);
}
/*
* 2. Get services, descend into each one and build state.
*/
if (lxml_ignorable_block(cursor))
continue;
switch (e) {
case SC_XI_INCLUDE:
continue;
case SC_SERVICE_BUNDLE:
return (-1);
break;
case SC_SERVICE:
break;
}
}
return (0);
}
/*
* Load an XML tree from filename and translate it into an internal service
* tree bundle. If apply is false, require that the the bundle be of type
* manifest, or type profile otherwise.
*/
int
{
int r;
/*
* Until libxml2 addresses DTD-based validation with XInclude, we don't
* validate service profiles (i.e. the apply path).
*/
if (do_validate)
return (-1);
}
/*
* Verify that this is a document type we understand.
*/
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
dtdpath);
return (-1);
}
}
"in document\n"));
return (-1);
}
if (do_validate) {
vcp = xmlNewValidCtxt();
if (r == 0) {
return (-1);
}
}
#ifdef DEBUG
#endif /* DEBUG */
return (r);
}
int
lxml_inventory(const char *filename)
{
bundle_t *b;
b = internal_bundle_new();
if (lxml_get_bundle_file(b, filename, 0) != 0) {
return (-1);
}
}
}
return (0);
}