summaryrefslogtreecommitdiff
path: root/usr/src/cmd/svc/svccfg
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/svc/svccfg')
-rw-r--r--usr/src/cmd/svc/svccfg/Makefile2
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.h99
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.l1
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg.y28
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_engine.c44
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_help.c14
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_internal.c303
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_libscf.c990
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_main.c5
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_tmpl.c4036
-rw-r--r--usr/src/cmd/svc/svccfg/svccfg_xml.c1501
11 files changed, 6842 insertions, 181 deletions
diff --git a/usr/src/cmd/svc/svccfg/Makefile b/usr/src/cmd/svc/svccfg/Makefile
index f87147fa8b..5cdb524581 100644
--- a/usr/src/cmd/svc/svccfg/Makefile
+++ b/usr/src/cmd/svc/svccfg/Makefile
@@ -23,7 +23,6 @@
# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
MYPROG = svccfg
PROG = $(MYPROG)
@@ -32,6 +31,7 @@ SRCS = svccfg_main.c \
svccfg_engine.c \
svccfg_internal.c \
svccfg_libscf.c \
+ svccfg_tmpl.c \
svccfg_xml.c \
svccfg_help.c
diff --git a/usr/src/cmd/svc/svccfg/svccfg.h b/usr/src/cmd/svc/svccfg/svccfg.h
index e63c24c111..bf73e5202f 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.h
+++ b/usr/src/cmd/svc/svccfg/svccfg.h
@@ -72,16 +72,20 @@ extern int lex_lineno;
* lxml_prop_types[]
*/
typedef enum element {
- SC_ASTRING = 0x0, SC_BOOLEAN, SC_COMMON_NAME, SC_COUNT,
- SC_INSTANCE_CREATE_DEFAULT, SC_DEPENDENCY, SC_DEPENDENT, SC_DESCRIPTION,
- SC_DOC_LINK, SC_DOCUMENTATION, SC_ENABLED, SC_EXEC_METHOD, SC_FMRI,
- SC_HOST, SC_HOSTNAME, SC_INSTANCE, SC_INTEGER, SC_LOCTEXT, SC_MANPAGE,
- SC_METHOD_CONTEXT, SC_METHOD_CREDENTIAL, SC_METHOD_PROFILE,
- SC_METHOD_ENVIRONMENT, SC_METHOD_ENVVAR, SC_NET_ADDR_V4, SC_NET_ADDR_V6,
- 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, SC_TIME, SC_URI, SC_USTRING, SC_VALUE_NODE,
- SC_XI_FALLBACK, SC_XI_INCLUDE
+ SC_ASTRING = 0, SC_BOOLEAN, SC_CARDINALITY, SC_CHOICES,
+ SC_COMMON_NAME, SC_CONSTRAINTS, SC_COUNT,
+ SC_INSTANCE_CREATE_DEFAULT, SC_DEPENDENCY, SC_DEPENDENT,
+ SC_DESCRIPTION, SC_DOC_LINK, SC_DOCUMENTATION, SC_ENABLED,
+ SC_EXEC_METHOD, SC_FMRI, SC_HOST, SC_HOSTNAME, SC_INCLUDE_VALUES,
+ SC_INSTANCE, SC_INTEGER, SC_INTERNAL_SEPARATORS, SC_LOCTEXT, SC_MANPAGE,
+ SC_METHOD_CONTEXT, SC_METHOD_CREDENTIAL,
+ SC_METHOD_PROFILE, SC_METHOD_ENVIRONMENT, SC_METHOD_ENVVAR,
+ SC_NET_ADDR_V4, SC_NET_ADDR_V6, 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, SC_TIME, SC_UNITS,
+ SC_URI, SC_USTRING, SC_VALUE, SC_VALUE_NODE, SC_VALUES,
+ SC_VISIBILITY, SC_XI_FALLBACK, SC_XI_INCLUDE
} element_t;
typedef enum bundle_type {
@@ -101,8 +105,7 @@ typedef enum service_type {
} service_type_t;
typedef enum entity_type {
- SVCCFG_SERVICE_OBJECT = 0x0, SVCCFG_INSTANCE_OBJECT,
- SVCCFG_TEMPLATE_OBJECT
+ SVCCFG_SERVICE_OBJECT = 0x0, SVCCFG_INSTANCE_OBJECT
} entity_type_t;
enum import_state {
@@ -120,6 +123,40 @@ typedef enum svccfg_op {
SVCCFG_OP_RESTORE
} svccfg_op_t;
+/*
+ * Return values for functions that validate an entity against the templates.
+ */
+typedef enum tmpl_validate_status {
+ TVS_SUCCESS = 0,
+ /*
+ * Either conversion of ASTRING property value to a number failed,
+ * or base 32 decoding of a property value failed.
+ */
+ TVS_BAD_CONVERSION,
+ /* Template is defective. */
+ TVS_BAD_TEMPLATE,
+ /* Template type spec is invalid. */
+ TVS_INVALID_TYPE_SPECIFICATION,
+ /* Property group is missing a type specification. */
+ TVS_MISSING_PG_TYPE,
+ /* Template with required == true is missing type specification. */
+ TVS_MISSING_TYPE_SPECIFICATION,
+ /* No match was found for specified item. */
+ TVS_NOMATCH,
+ /* Validation error occurred */
+ TVS_VALIDATION,
+ /* Validation error that should not inhibit import. */
+ TVS_WARN,
+ /* Could not validate because of fatal errors. */
+ TVS_FATAL = -1
+} tmpl_validate_status_t;
+
+/*
+ * The composed_pg structure is used for templates validation. It is
+ * defined in svccfg_tmpl.c
+ */
+typedef struct composed_pg composed_pg_t;
+
typedef struct entity {
uu_list_node_t sc_node;
entity_type_t sc_etype;
@@ -138,21 +175,28 @@ typedef struct entity {
uu_list_t *sc_service_instances;
service_type_t sc_service_type;
uint_t sc_service_version;
-
- struct entity *sc_service_template;
+ /* Following used by template validation */
+ struct entity *sc_restarter;
+ struct entity *sc_global;
} sc_service;
struct {
- uint_t sc_instance_dummy;
+ uu_avl_t *sc_composed;
+ /* Following used by template validation */
+ struct entity *sc_instance_restarter;
} sc_instance;
- struct {
- uint_t sc_template_dummy;
- } sc_template;
} sc_u;
} entity_t;
+/*
+ * sc_pgroup_composed is only used for templates validation of properties.
+ * It is created in build_composed_property_groups() and destroyed in
+ * composed_pg_destroy(). It will only be set for property groups that are
+ * part of an instance -- not for service property groups.
+ */
typedef struct pgroup {
uu_list_node_t sc_node;
uu_list_t *sc_pgroup_props;
+ composed_pg_t *sc_pgroup_composed; /* Composed properties */
const char *sc_pgroup_name;
const char *sc_pgroup_type;
@@ -168,6 +212,7 @@ typedef struct pgroup {
typedef struct property {
uu_list_node_t sc_node;
+ uu_avl_node_t sc_composed_node; /* Composed props linkage */
uu_list_t *sc_property_values;
char *sc_property_name;
@@ -205,6 +250,11 @@ typedef struct scf_callback {
int sc_err;
} scf_callback_t;
+/*
+ * Collection of template validation errors.
+ */
+typedef struct tmpl_errors tmpl_errors_t;
+
#ifndef NDEBUG
#define bad_error(func, err) { \
(void) fprintf(stderr, "%s:%d: %s() failed with unexpected " \
@@ -300,6 +350,8 @@ pgroup_t *internal_pgroup_find(entity_t *, const char *, const char *);
pgroup_t *internal_dependent_find(entity_t *, const char *);
pgroup_t *internal_pgroup_find_or_create(entity_t *, const char *,
const char *);
+pgroup_t *internal_pgroup_create_strict(entity_t *, const char *,
+ const char *);
property_t *internal_property_new(void);
void internal_property_free(property_t *);
property_t *internal_property_find(pgroup_t *, const char *);
@@ -309,12 +361,15 @@ value_t *internal_value_new(void);
int internal_attach_service(bundle_t *, entity_t *);
int internal_attach_entity(entity_t *, entity_t *);
int internal_attach_pgroup(entity_t *, pgroup_t *);
+void internal_detach_pgroup(entity_t *, pgroup_t *);
int internal_attach_dependent(entity_t *, pgroup_t *);
int internal_attach_property(pgroup_t *, property_t *);
+void internal_detach_property(pgroup_t *, property_t *);
void internal_attach_value(property_t *, value_t *);
int load_init(void);
void load_fini(void);
+int load_instance(const char *, const char *, entity_t **);
int load_pg_attrs(const scf_propertygroup_t *, pgroup_t **);
int load_pg(const scf_propertygroup_t *, pgroup_t **, const char *,
const char *);
@@ -340,6 +395,7 @@ void lscf_delhash(char *, int);
void lscf_listprop(const char *);
void lscf_addprop(char *, const char *, const uu_list_t *);
void lscf_delprop(char *);
+int lscf_describe(uu_list_t *, int);
void lscf_listsnap();
void lscf_selectsnap(const char *);
void lscf_revert(const char *);
@@ -366,6 +422,13 @@ int engine_cmd_getc(engine_state_t *);
int engine_cmd_ungetc(engine_state_t *, char);
void engine_cmd_nputs(engine_state_t *, char *, size_t);
+void tmpl_errors_destroy(tmpl_errors_t *);
+void tmpl_errors_print(FILE *, tmpl_errors_t *, const char *);
+void tmpl_init(void);
+void tmpl_property_fini(property_t *);
+void tmpl_property_init(property_t *);
+tmpl_validate_status_t tmpl_validate_bundle(bundle_t *, tmpl_errors_t **);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/cmd/svc/svccfg/svccfg.l b/usr/src/cmd/svc/svccfg/svccfg.l
index 4bf42e18b3..96cf8fd9e9 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.l
+++ b/usr/src/cmd/svc/svccfg/svccfg.l
@@ -104,6 +104,7 @@ extern int yyerror(const char *);
<INITIAL>setprop { BEGIN WORD; return (SCC_SETPROP); }
<INITIAL>delprop { BEGIN WORD; return (SCC_DELPROP); }
<INITIAL>editprop { BEGIN WORD; return (SCC_EDITPROP); }
+<INITIAL>describe { BEGIN WORD; return (SCC_DESCRIBE); }
<INITIAL>addpropvalue { BEGIN WORD; return (SCC_ADDPROPVALUE); }
<INITIAL>delpropvalue { BEGIN WORD; return (SCC_DELPROPVALUE); }
<INITIAL>setenv { BEGIN WORD; return (SCC_SETENV); }
diff --git a/usr/src/cmd/svc/svccfg/svccfg.y b/usr/src/cmd/svc/svccfg/svccfg.y
index 2cc0b04ab7..ad16849bc7 100644
--- a/usr/src/cmd/svc/svccfg/svccfg.y
+++ b/usr/src/cmd/svc/svccfg/svccfg.y
@@ -47,6 +47,7 @@ uu_list_pool_t *string_pool;
%token SCC_LIST SCC_ADD SCC_DELETE SCC_SELECT SCC_UNSELECT
%token SCC_LISTPG SCC_ADDPG SCC_DELPG SCC_DELHASH
%token SCC_LISTPROP SCC_SETPROP SCC_DELPROP SCC_EDITPROP
+%token SCC_DESCRIBE
%token SCC_ADDPROPVALUE SCC_DELPROPVALUE SCC_SETENV SCC_UNSETENV
%token SCC_LISTSNAP SCC_SELECTSNAP SCC_REVERT SCC_REFRESH
%token SCS_REDIRECT SCS_NEWLINE SCS_EQUALS SCS_LPAREN SCS_RPAREN
@@ -96,6 +97,7 @@ command : terminator
| setprop_cmd
| delprop_cmd
| editprop_cmd
+ | describe_cmd
| addpropvalue_cmd
| delpropvalue_cmd
| setenv_cmd
@@ -130,11 +132,10 @@ unknown_cmd : SCV_WORD terminator
validate_cmd : SCC_VALIDATE SCV_WORD terminator
{
- bundle_t *b = internal_bundle_new();
- lxml_get_bundle_file(b, $2, SVCCFG_OP_IMPORT);
- (void) internal_bundle_free(b);
+ lscf_validate($2);
free($2);
}
+ | SCC_VALIDATE terminator { lscf_validate_fmri(NULL); }
| SCC_VALIDATE error terminator { synerr(SCC_VALIDATE); return(0); }
import_cmd : SCC_IMPORT string_list terminator
@@ -410,6 +411,26 @@ delprop_cmd : SCC_DELPROP SCV_WORD terminator
editprop_cmd : SCC_EDITPROP terminator { lscf_editprop(); }
| SCC_EDITPROP error terminator { synerr(SCC_EDITPROP); return(0); }
+describe_cmd : SCC_DESCRIBE string_list terminator
+ {
+ string_list_t *slp;
+ void *cookie = NULL;
+
+ if (lscf_describe($2, 1) == -2) {
+ synerr(SCC_DESCRIBE);
+ return(0);
+ }
+
+ while ((slp = uu_list_teardown($2, &cookie)) != NULL) {
+ free(slp->str);
+ free(slp);
+ }
+
+ uu_list_destroy($2);
+ }
+ | SCC_DESCRIBE terminator { lscf_describe(NULL, 0); }
+ | SCC_DESCRIBE error terminator { synerr(SCC_DESCRIBE); return(0); }
+
addpropvalue_cmd : SCC_ADDPROPVALUE SCV_WORD string terminator
{
lscf_addpropvalue($2, NULL, $3);
@@ -559,3 +580,4 @@ command_token : SCC_VALIDATE { $$ = SCC_VALIDATE; }
| SCC_SELECTSNAP { $$ = SCC_SELECTSNAP; }
| SCC_REVERT { $$ = SCC_REVERT; }
| SCC_REFRESH { $$ = SCC_REFRESH; }
+ | SCC_DESCRIBE { $$ = SCC_DESCRIBE; }
diff --git a/usr/src/cmd/svc/svccfg/svccfg_engine.c b/usr/src/cmd/svc/svccfg/svccfg_engine.c
index e610814274..2a907fd7a8 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_engine.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_engine.c
@@ -220,6 +220,7 @@ static struct cmd_info {
{ "setprop", CS_SVC | CS_INST, NULL },
{ "delprop", CS_SVC | CS_INST, NULL },
{ "editprop", CS_SVC | CS_INST, NULL },
+ { "describe", CS_SVC | CS_INST | CS_SNAP, NULL },
{ "listsnap", CS_INST | CS_SNAP, NULL },
{ "selectsnap", CS_INST | CS_SNAP, NULL },
{ "revert", CS_INST | CS_SNAP, NULL },
@@ -511,8 +512,10 @@ engine_import(uu_list_t *args)
uchar_t hash[MHASH_SIZE];
char **argv;
string_list_t *slp;
- boolean_t verify = B_FALSE;
+ boolean_t validate = B_FALSE;
+ tmpl_validate_status_t vr;
uint_t flags = SCI_GENERALLAST;
+ tmpl_errors_t *errs;
argc = uu_list_numnodes(args);
if (argc < 1)
@@ -542,7 +545,7 @@ engine_import(uu_list_t *args)
break;
case 'V':
- verify = B_TRUE;
+ validate = B_TRUE;
break;
case '?':
@@ -563,11 +566,20 @@ engine_import(uu_list_t *args)
file = argv[optind];
free(argv);
+ /* If we're in interactive mode, force strict validation. */
+ if (est->sc_cmd_flags & SC_CMD_IACTIVE)
+ validate = B_TRUE;
+
lscf_prep_hndl();
ret = mhash_test_file(g_hndl, file, 0, &pname, hash);
- if (ret != MHASH_NEWFILE)
+ if (ret != MHASH_NEWFILE) {
+ if (ret == MHASH_FAILURE)
+ semerr(gettext("Could not hash file %s\n"), file);
+ else if (g_verbose && ret == MHASH_RECONCILED)
+ warn(gettext("No changes were necessary.\n"));
return (ret);
+ }
/* Load */
b = internal_bundle_new();
@@ -577,9 +589,28 @@ engine_import(uu_list_t *args)
return (-1);
}
+ /* Validate */
+ if ((vr = tmpl_validate_bundle(b, &errs)) != TVS_SUCCESS) {
+ char *prefix;
+
+ if ((validate == 0) || (vr == TVS_WARN)) {
+ prefix = gettext("Warning: ");
+ } else {
+ prefix = "";
+ }
+ tmpl_errors_print(stderr, errs, prefix);
+ if (validate && (vr != TVS_WARN)) {
+ tmpl_errors_destroy(errs);
+ semerr(gettext("Import failed.\n"));
+ return (-1);
+ }
+ }
+ tmpl_errors_destroy(errs);
+
/* Import */
if (lscf_bundle_import(b, file, flags) != 0) {
internal_bundle_free(b);
+ semerr(gettext("Import failed.\n"));
return (-1);
}
@@ -602,10 +633,6 @@ engine_import(uu_list_t *args)
free(pname);
}
- /* Verify */
- if (verify)
- warn(gettext("import -V not implemented.\n"));
-
return (0);
}
@@ -731,7 +758,8 @@ help(int com)
"Manifest commands: inventory validate import export "
"archive\n"
"Profile commands: apply extract\n"
- "Entity commands: list select unselect add delete\n"
+ "Entity commands: list select unselect add delete "
+ "describe\n"
"Snapshot commands: listsnap selectsnap revert\n"
"Instance commands: refresh\n"
"Property group commands: listpg addpg delpg\n"
diff --git a/usr/src/cmd/svc/svccfg/svccfg_help.c b/usr/src/cmd/svc/svccfg/svccfg_help.c
index 0a03767614..65b7c5872d 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_help.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_help.c
@@ -29,10 +29,14 @@
#include "svccfg_grammar.h"
struct help_message help_messages[] = {
- { SCC_VALIDATE, "validate file\n\n"
- "Process a manifest file without changing the repository."
+ { SCC_VALIDATE, "validate [file | fmri]\n\n"
+"Validate a manifest file without changing the repository.\n"
+"Validate an instance FMRI against the template specifications."
+ },
+ { SCC_IMPORT, "import [-V] file\n\n"
+"Import a manifest into the repository. With -V force strict adherence\n"
+"to the template specifications."
},
- { SCC_IMPORT, "import file\n\nImport a manifest into the repository." },
{ SCC_EXPORT, "export [-a] {service | pattern} [> file]\n\n"
"Print a manifest for service to file, or standard output if not specified."
},
@@ -98,6 +102,10 @@ struct help_message help_messages[] = {
{ SCC_EDITPROP, "editprop\n\n"
"Invoke $EDITOR to edit the properties of the currently selected entity."
},
+ { SCC_DESCRIBE, "describe [-v] [-t] [propertygroup/property]\n\n"
+"Describe the current properties. With -v, describe verbosely. With -t,\n"
+"show only template data, not current properties."
+ },
{ SCC_ADDPROPVALUE, "addpropvalue pg/name [type:] value\n\n"
"Add the given value to the named property."
},
diff --git a/usr/src/cmd/svc/svccfg/svccfg_internal.c b/usr/src/cmd/svc/svccfg/svccfg_internal.c
index 74d206f67a..583d3d575f 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_internal.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_internal.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <assert.h>
#include <errno.h>
#include <libintl.h>
@@ -33,6 +31,8 @@
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
+#include <unistd.h>
+#include <libscf_priv.h>
#include "svccfg.h"
@@ -248,6 +248,10 @@ internal_bundle_new()
b->sc_bundle_type = SVCCFG_UNKNOWN_BUNDLE;
b->sc_bundle_services = uu_list_create(entity_pool, b, 0);
+ if (b->sc_bundle_services == NULL) {
+ uu_die(gettext("Unable to create list for bundle services. "
+ "%s\n"), uu_strerror(uu_error()));
+ }
return (b);
}
@@ -266,27 +270,50 @@ internal_bundle_free(bundle_t *b)
}
entity_t *
-internal_service_new(const char *name)
+internal_entity_new(entity_type_t entity)
{
- entity_t *s;
+ entity_t *e;
- if ((s = uu_zalloc(sizeof (entity_t))) == NULL)
+ if ((e = uu_zalloc(sizeof (entity_t))) == NULL)
uu_die(gettext("couldn't allocate memory"));
- uu_list_node_init(s, &s->sc_node, entity_pool);
+ uu_list_node_init(e, &e->sc_node, entity_pool);
+
+ e->sc_etype = entity;
+ e->sc_pgroups = uu_list_create(pgroup_pool, e, 0);
+ if (e->sc_pgroups == NULL) {
+ uu_die(gettext("Unable to create list for entity property "
+ "groups. %s\n"), uu_strerror(uu_error()));
+ }
+
+ return (e);
+}
+
+entity_t *
+internal_service_new(const char *name)
+{
+ entity_t *s;
+
+ s = internal_entity_new(SVCCFG_SERVICE_OBJECT);
s->sc_name = name;
s->sc_fmri = uu_msprintf("svc:/%s", name);
if (s->sc_fmri == NULL)
uu_die(gettext("couldn't allocate memory"));
- s->sc_etype = SVCCFG_SERVICE_OBJECT;
- s->sc_pgroups = uu_list_create(pgroup_pool, s, 0);
s->sc_dependents = uu_list_create(pgroup_pool, s, 0);
+ if (s->sc_dependents == NULL) {
+ uu_die(gettext("Unable to create list for service dependents. "
+ "%s\n"), uu_strerror(uu_error()));
+ }
s->sc_u.sc_service.sc_service_type = SVCCFG_UNKNOWN_SERVICE;
s->sc_u.sc_service.sc_service_instances = uu_list_create(entity_pool, s,
0);
+ if (s->sc_u.sc_service.sc_service_instances == NULL) {
+ uu_die(gettext("Unable to create list for service instances. "
+ "%s\n"), uu_strerror(uu_error()));
+ }
return (s);
}
@@ -298,6 +325,11 @@ internal_service_free(entity_t *s)
pgroup_t *pg;
void *cookie;
+ if (s->sc_u.sc_service.sc_restarter != NULL)
+ internal_instance_free(s->sc_u.sc_service.sc_restarter);
+ if (s->sc_u.sc_service.sc_global != NULL)
+ internal_instance_free(s->sc_u.sc_service.sc_global);
+
cookie = NULL;
while ((pg = uu_list_teardown(s->sc_pgroups, &cookie)) != NULL)
internal_pgroup_free(pg);
@@ -310,6 +342,7 @@ internal_service_free(entity_t *s)
while ((inst = uu_list_teardown(s->sc_u.sc_service.sc_service_instances,
&cookie)) != NULL)
internal_instance_free(inst);
+ uu_free((void *)s->sc_fmri);
free(s);
}
@@ -319,16 +352,14 @@ internal_instance_new(const char *name)
{
entity_t *i;
- if ((i = uu_zalloc(sizeof (entity_t))) == NULL)
- uu_die(gettext("couldn't allocate memory"));
-
- uu_list_node_init(i, &i->sc_node, entity_pool);
-
+ i = internal_entity_new(SVCCFG_INSTANCE_OBJECT);
i->sc_name = name;
/* Can't set i->sc_fmri until we're attached to a service. */
- i->sc_etype = SVCCFG_INSTANCE_OBJECT;
- i->sc_pgroups = uu_list_create(pgroup_pool, i, 0);
i->sc_dependents = uu_list_create(pgroup_pool, i, 0);
+ if (i->sc_dependents == NULL) {
+ uu_die(gettext("Unable to create list for instance "
+ "dependents. %s\n"), uu_strerror(uu_error()));
+ }
return (i);
}
@@ -338,33 +369,22 @@ internal_instance_free(entity_t *i)
{
pgroup_t *pg;
void *cookie = NULL;
+ entity_t *rs;
+ rs = i->sc_u.sc_instance.sc_instance_restarter;
+ if (rs != NULL)
+ internal_instance_free(rs);
while ((pg = uu_list_teardown(i->sc_pgroups, &cookie)) != NULL)
internal_pgroup_free(pg);
cookie = NULL;
while ((pg = uu_list_teardown(i->sc_dependents, &cookie)) != NULL)
internal_pgroup_free(pg);
+ uu_free((void *)i->sc_fmri);
free(i);
}
-entity_t *
-internal_template_new()
-{
- entity_t *t;
-
- if ((t = uu_zalloc(sizeof (entity_t))) == NULL)
- uu_die(gettext("couldn't allocate memory"));
-
- uu_list_node_init(t, &t->sc_node, entity_pool);
-
- t->sc_etype = SVCCFG_TEMPLATE_OBJECT;
- t->sc_pgroups = uu_list_create(pgroup_pool, t, 0);
-
- return (t);
-}
-
pgroup_t *
internal_pgroup_new()
{
@@ -376,6 +396,10 @@ internal_pgroup_new()
uu_list_node_init(p, &p->sc_node, pgroup_pool);
p->sc_pgroup_props = uu_list_create(property_pool, p, UU_LIST_SORTED);
+ if (p->sc_pgroup_props == NULL) {
+ uu_die(gettext("Unable to create list for properties. %s\n"),
+ uu_strerror(uu_error()));
+ }
p->sc_pgroup_name = "<unset>";
p->sc_pgroup_type = "<unset>";
@@ -388,6 +412,12 @@ internal_pgroup_free(pgroup_t *pg)
property_t *prop;
void *cookie = NULL;
+ /*
+ * Templates validation code should clean up this reference when
+ * the validation is finished.
+ */
+ assert(pg->sc_pgroup_composed == NULL);
+
while ((prop = uu_list_teardown(pg->sc_pgroup_props, &cookie)) != NULL)
internal_property_free(prop);
@@ -427,14 +457,20 @@ internal_pgroup_find(entity_t *e, const char *name, const char *type)
return (find_pgroup(e->sc_pgroups, name, type));
}
-pgroup_t *
-internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type)
+static pgroup_t *
+internal_pgroup_create_common(entity_t *e, const char *name, const char *type,
+ boolean_t unique)
{
pgroup_t *pg;
pg = internal_pgroup_find(e, name, type);
- if (pg != NULL)
- return (pg);
+ if (pg != NULL) {
+ if (unique == B_TRUE) {
+ return (NULL);
+ } else {
+ return (pg);
+ }
+ }
pg = internal_pgroup_new();
(void) internal_attach_pgroup(e, pg);
@@ -448,6 +484,18 @@ internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type)
return (pg);
}
+pgroup_t *
+internal_pgroup_find_or_create(entity_t *e, const char *name, const char *type)
+{
+ return (internal_pgroup_create_common(e, name, type, B_FALSE));
+}
+
+pgroup_t *
+internal_pgroup_create_strict(entity_t *e, const char *name, const char *type)
+{
+ return (internal_pgroup_create_common(e, name, type, B_TRUE));
+}
+
property_t *
internal_property_new()
{
@@ -459,8 +507,14 @@ internal_property_new()
uu_list_node_init(p, &p->sc_node, property_pool);
p->sc_property_values = uu_list_create(value_pool, p, 0);
+ if (p->sc_property_values == NULL) {
+ uu_die(gettext("Unable to create list for property values. "
+ "%s\n"), uu_strerror(uu_error()));
+ }
p->sc_property_name = "<unset>";
+ tmpl_property_init(p);
+
return (p);
}
@@ -470,6 +524,8 @@ internal_property_free(property_t *p)
value_t *val;
void *cookie = NULL;
+ tmpl_property_fini(p);
+
while ((val = uu_list_teardown(p->sc_property_values, &cookie)) !=
NULL) {
if (val->sc_free != NULL)
@@ -587,11 +643,6 @@ internal_attach_service(bundle_t *bndl, entity_t *svc)
int
internal_attach_entity(entity_t *svc, entity_t *ent)
{
- if (ent->sc_etype == SVCCFG_TEMPLATE_OBJECT) {
- svc->sc_u.sc_service.sc_service_template = ent;
- return (0);
- }
-
if (svc->sc_etype != SVCCFG_SERVICE_OBJECT)
uu_die(gettext("bad entity attach: %s is not a service\n"),
svc->sc_name);
@@ -628,6 +679,12 @@ internal_attach_pgroup(entity_t *ent, pgroup_t *pgrp)
return (0);
}
+void
+internal_detach_pgroup(entity_t *ent, pgroup_t *pgrp)
+{
+ uu_list_remove(ent->sc_pgroups, pgrp);
+}
+
int
internal_attach_dependent(entity_t *ent, pgroup_t *pg)
{
@@ -667,6 +724,12 @@ internal_attach_property(pgroup_t *pgrp, property_t *prop)
}
void
+internal_detach_property(pgroup_t *pgrp, property_t *prop)
+{
+ uu_list_remove(pgrp->sc_pgroup_props, prop);
+}
+
+void
internal_attach_value(property_t *prop, value_t *val)
{
(void) uu_list_append(prop->sc_property_values, val);
@@ -683,9 +746,11 @@ internal_attach_value(property_t *prop, value_t *val)
static char *loadbuf = NULL;
static size_t loadbuf_sz;
+static scf_propertygroup_t *load_pgroup = NULL;
static scf_property_t *load_prop = NULL;
static scf_value_t *load_val = NULL;
static scf_iter_t *load_propiter = NULL, *load_valiter = NULL;
+static scf_iter_t *load_pgiter = NULL;
/*
* Initialize the global state for the load_*() routines.
@@ -705,6 +770,8 @@ load_init(void)
if ((load_prop = scf_property_create(g_hndl)) == NULL ||
(load_val = scf_value_create(g_hndl)) == NULL ||
+ (load_pgroup = scf_pg_create(g_hndl)) == NULL ||
+ (load_pgiter = scf_iter_create(g_hndl)) == NULL ||
(load_propiter = scf_iter_create(g_hndl)) == NULL ||
(load_valiter = scf_iter_create(g_hndl)) == NULL) {
load_fini();
@@ -721,6 +788,10 @@ load_fini(void)
load_propiter = NULL;
scf_iter_destroy(load_valiter);
load_valiter = NULL;
+ scf_iter_destroy(load_pgiter);
+ load_pgiter = NULL;
+ scf_pg_destroy(load_pgroup);
+ load_pgroup = NULL;
scf_value_destroy(load_val);
load_val = NULL;
scf_property_destroy(load_prop);
@@ -1091,6 +1162,158 @@ out:
}
/*
+ * Load the instance for fmri from the repository into memory. The
+ * property groups that define the instances pg_patterns and prop_patterns
+ * are also loaded.
+ *
+ * Returns 0 on success and non-zero on failure.
+ */
+int
+load_instance(const char *fmri, const char *name, entity_t **inst_ptr)
+{
+ entity_t *e = NULL;
+ scf_instance_t *inst;
+ pgroup_t *ipg;
+ int rc;
+ char *type = NULL;
+ ssize_t tsize;
+
+ assert(inst_ptr != NULL);
+
+ if ((inst = scf_instance_create(g_hndl)) == NULL) {
+ switch (scf_error()) {
+ case SCF_ERROR_NO_MEMORY:
+ case SCF_ERROR_NO_RESOURCES:
+ rc = EAGAIN;
+ goto errout;
+ default:
+ bad_error("scf_instance_create", scf_error());
+ }
+ }
+ if (scf_handle_decode_fmri(g_hndl, fmri, NULL, NULL, inst, NULL, NULL,
+ SCF_DECODE_FMRI_EXACT|SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ rc = ECONNABORTED;
+ goto errout;
+ case SCF_ERROR_DELETED:
+ case SCF_ERROR_NOT_FOUND:
+ rc = ENOENT;
+ goto errout;
+ default:
+ bad_error("scf_handle_decode_fmri", scf_error());
+ }
+ }
+ if (scf_iter_instance_pgs_composed(load_pgiter, inst, NULL) != 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_DELETED:
+ rc = ECANCELED;
+ goto errout;
+ case SCF_ERROR_CONNECTION_BROKEN:
+ rc = ECONNABORTED;
+ goto errout;
+ default:
+ bad_error("scf_iter_instance_pgs_composed",
+ scf_error());
+ }
+ }
+
+ tsize = scf_limit(SCF_LIMIT_MAX_PG_TYPE_LENGTH);
+ type = uu_zalloc(tsize);
+ if (type == NULL) {
+ rc = ENOMEM;
+ goto errout;
+ }
+
+ /*
+ * Initialize our entity structure.
+ */
+ e = internal_instance_new(name);
+ if (e == NULL) {
+ rc = ENOMEM;
+ goto errout;
+ }
+ e->sc_fmri = uu_strdup(fmri);
+ if (e->sc_fmri == NULL) {
+ rc = ENOMEM;
+ goto errout;
+ }
+
+ /*
+ * Walk through the property group's of the instance and capture
+ * the property groups that are of type
+ * SCF_GROUP_TEMPLATE_PG_PATTERN and
+ * SCF_GROUP_TEMPLATE_PROP_PATTERN. In other words grab the
+ * pg_pattern and prop_pattern property groups.
+ */
+ while ((rc = scf_iter_next_pg(load_pgiter, load_pgroup)) == 1) {
+ if (scf_pg_get_type(load_pgroup, type, tsize) <= 0) {
+ switch (scf_error()) {
+ case SCF_ERROR_DELETED:
+ rc = ENOENT;
+ break;
+ case SCF_ERROR_CONNECTION_BROKEN:
+ rc = ECONNABORTED;
+ break;
+ default:
+ bad_error("scf_pg_get_type", scf_error());
+ }
+ goto errout;
+ }
+ if ((strcmp(type, SCF_GROUP_TEMPLATE_PG_PATTERN) != 0) &&
+ (strcmp(type, SCF_GROUP_TEMPLATE_PROP_PATTERN) != 0)) {
+ continue;
+ }
+ if ((rc = load_pg(load_pgroup, &ipg, fmri, NULL)) != 0) {
+ switch (rc) {
+ case ECANCELED:
+ case ECONNABORTED:
+ case EACCES:
+ case ENOMEM:
+ break;
+ default:
+ bad_error("load_pg", rc);
+ }
+ goto errout;
+ }
+ if (internal_attach_pgroup(e, ipg) != 0) {
+ rc = EBADF;
+ goto errout;
+ }
+ }
+ if (rc == -1) {
+ /* Error in iteration. */
+ switch (scf_error()) {
+ case SCF_ERROR_CONNECTION_BROKEN:
+ rc = ECONNABORTED;
+ break;
+ case SCF_ERROR_DELETED:
+ rc = ENOENT;
+ break;
+ case SCF_ERROR_NO_RESOURCES:
+ rc = EAGAIN;
+ break;
+ default:
+ bad_error("scf_iter_next_pg", scf_error());
+ }
+ goto errout;
+ }
+
+ *inst_ptr = e;
+ scf_instance_destroy(inst);
+ return (0);
+
+errout:
+ if (type != NULL)
+ uu_free(type);
+ if (inst != NULL)
+ scf_instance_destroy(inst);
+ if (e != NULL)
+ internal_instance_free(e);
+ return (rc);
+}
+
+/*
* These functions compare internal property groups and properties (pgroup_t
* & property_t). They return 1 if the given structures are equal and
* 0 otherwise. Some will report the differences between the two structures.
diff --git a/usr/src/cmd/svc/svccfg/svccfg_libscf.c b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
index 9c133a37c4..797994faf7 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_libscf.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_libscf.c
@@ -32,12 +32,14 @@
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <inttypes.h>
#include <libintl.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libtecla.h>
#include <libuutil.h>
#include <limits.h>
+#include <locale.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
@@ -63,6 +65,13 @@
#define HASH_PG_FLAGS 0
#define HASH_PROP "md5sum"
+/*
+ * Indentation used in the output of the describe subcommand.
+ */
+#define TMPL_VALUE_INDENT " "
+#define TMPL_INDENT " "
+#define TMPL_INDENT_2X " "
+#define TMPL_CHOICE_INDENT " "
/*
* These are the classes of elements which may appear as children of service
@@ -128,7 +137,6 @@ const char * const snap_lastimport = "last-import";
const char * const snap_previous = "previous";
const char * const snap_running = "running";
-
scf_handle_t *g_hndl = NULL; /* only valid after lscf_prep_hndl() */
ssize_t max_scf_fmri_len;
@@ -361,6 +369,21 @@ entity_destroy(void *ent, int issvc)
scf_instance_destroy(ent);
}
+static int
+get_pg(const char *pg_name, scf_propertygroup_t *pg)
+{
+ int ret;
+
+ if (cur_level != NULL)
+ ret = scf_snaplevel_get_pg(cur_level, pg_name, pg);
+ else if (cur_inst != NULL)
+ ret = scf_instance_get_pg(cur_inst, pg_name, pg);
+ else
+ ret = scf_service_get_pg(cur_svc, pg_name, pg);
+
+ return (ret);
+}
+
/*
* Find a snaplevel in a snapshot. If get_svc is true, find the service
* snaplevel. Otherwise find the instance snaplevel.
@@ -5805,7 +5828,12 @@ lscf_service_import(void *v, void *pvt)
}
/* create temporary service */
- r = snprintf(imp_tsname, max_scf_name_len + 1, "TEMP/%s", s->sc_name);
+ /*
+ * the size of the buffer was reduced to max_scf_name_len to prevent
+ * hitting bug 6681151. After the bug fix, the size of the buffer
+ * should be restored to its original value (max_scf_name_len +1)
+ */
+ r = snprintf(imp_tsname, max_scf_name_len, "TEMP/%s", s->sc_name);
if (r < 0)
bad_error("snprintf", errno);
if (r > max_scf_name_len) {
@@ -9419,6 +9447,152 @@ select_callback(void *unused, scf_walkinfo_t *wip)
return (0);
}
+static int
+validate_callback(void *fmri_p, scf_walkinfo_t *wip)
+{
+ char **fmri = fmri_p;
+
+ *fmri = strdup(wip->fmri);
+ if (*fmri == NULL)
+ uu_die(gettext("Out of memory.\n"));
+
+ return (0);
+}
+
+/*
+ * validate [fmri]
+ * Perform the validation of an FMRI instance.
+ */
+void
+lscf_validate_fmri(const char *fmri)
+{
+ int ret = 0;
+ size_t inst_sz;
+ char *inst_fmri = NULL;
+ scf_tmpl_errors_t *errs = NULL;
+ char *snapbuf = NULL;
+
+ lscf_prep_hndl();
+
+ if (fmri == NULL) {
+ inst_sz = max_scf_fmri_len + 1;
+ inst_fmri = safe_malloc(inst_sz);
+
+ if (cur_snap != NULL) {
+ snapbuf = safe_malloc(max_scf_name_len + 1);
+ if (scf_snapshot_get_name(cur_snap, snapbuf,
+ max_scf_name_len + 1) < 0)
+ scfdie();
+ }
+ if (cur_inst == NULL) {
+ semerr(gettext("No instance selected\n"));
+ goto cleanup;
+ } else if (scf_instance_to_fmri(cur_inst, inst_fmri,
+ inst_sz) >= inst_sz) {
+ /* sanity check. Should never get here */
+ uu_die(gettext("Unexpected error! file %s, line %d\n"),
+ __FILE__, __LINE__);
+ }
+ } else {
+ scf_error_t scf_err;
+ int err = 0;
+
+ if ((scf_err = scf_walk_fmri(g_hndl, 1, (char **)&fmri, 0,
+ validate_callback, &inst_fmri, &err, semerr)) != 0) {
+ uu_warn("Failed to walk instances: %s\n",
+ scf_strerror(scf_err));
+ goto cleanup;
+ }
+ if (err != 0)
+ /* error message displayed by scf_walk_fmri */
+ goto cleanup;
+ }
+
+ ret = scf_tmpl_validate_fmri(g_hndl, inst_fmri, snapbuf, &errs,
+ SCF_TMPL_VALIDATE_FLAG_CURRENT);
+ if (ret == -1) {
+ if (scf_error() == SCF_ERROR_TEMPLATE_INVALID) {
+ warn(gettext("Template data for %s is invalid. "
+ "Consider reverting to a previous snapshot or "
+ "restoring original configuration.\n"), inst_fmri);
+ } else {
+ uu_warn("%s: %s\n",
+ gettext("Error validating the instance"),
+ scf_strerror(scf_error()));
+ }
+ } else if (ret == 1 && errs != NULL) {
+ scf_tmpl_error_t *err = NULL;
+ char *msg;
+ size_t len = 256; /* initial error buffer size */
+ int flag = (est->sc_cmd_flags & SC_CMD_IACTIVE) ?
+ SCF_TMPL_STRERROR_HUMAN : 0;
+
+ msg = safe_malloc(len);
+
+ while ((err = scf_tmpl_next_error(errs)) != NULL) {
+ int ret;
+
+ if ((ret = scf_tmpl_strerror(err, msg, len,
+ flag)) >= len) {
+ len = ret + 1;
+ msg = realloc(msg, len);
+ if (msg == NULL)
+ uu_die(gettext(
+ "Out of memory.\n"));
+ (void) scf_tmpl_strerror(err, msg, len,
+ flag);
+ }
+ (void) fprintf(stderr, "%s\n", msg);
+ }
+ if (msg != NULL)
+ free(msg);
+ }
+ if (errs != NULL)
+ scf_tmpl_errors_destroy(errs);
+cleanup:
+ free(inst_fmri);
+ free(snapbuf);
+}
+
+static void
+lscf_validate_file(const char *filename)
+{
+ tmpl_errors_t *errs;
+
+ bundle_t *b = internal_bundle_new();
+ if (lxml_get_bundle_file(b, filename, SVCCFG_OP_IMPORT) == 0) {
+ if (tmpl_validate_bundle(b, &errs) != TVS_SUCCESS) {
+ tmpl_errors_print(stderr, errs, "");
+ semerr(gettext("Validation failed.\n"));
+ }
+ tmpl_errors_destroy(errs);
+ }
+ (void) internal_bundle_free(b);
+}
+
+/*
+ * validate [fmri|file]
+ */
+void
+lscf_validate(const char *arg)
+{
+ const char *str;
+
+ if (strncmp(arg, SCF_FMRI_FILE_PREFIX,
+ sizeof (SCF_FMRI_FILE_PREFIX) - 1) == 0) {
+ str = arg + sizeof (SCF_FMRI_FILE_PREFIX) - 1;
+ lscf_validate_file(str);
+ } else if (strncmp(arg, SCF_FMRI_SVC_PREFIX,
+ sizeof (SCF_FMRI_SVC_PREFIX) - 1) == 0) {
+ str = arg + sizeof (SCF_FMRI_SVC_PREFIX) - 1;
+ lscf_validate_fmri(str);
+ } else if (access(arg, R_OK | F_OK) == 0) {
+ lscf_validate_file(arg);
+ } else {
+ lscf_validate_fmri(arg);
+ }
+}
+
void
lscf_select(const char *fmri)
{
@@ -10731,16 +10905,640 @@ list_prop_info(const scf_property_t *prop, const char *name, size_t len)
uu_die(gettext("Could not output newline"));
}
+/*
+ * Outputs template property group info for the describe subcommand.
+ * If 'templates' == 2, verbose output is printed in the format expected
+ * for describe -v, which includes all templates fields. If pg is
+ * not NULL, we're describing the template data, not an existing property
+ * group, and formatting should be appropriate for describe -t.
+ */
+static void
+list_pg_tmpl(scf_pg_tmpl_t *pgt, scf_propertygroup_t *pg, int templates)
+{
+ char *buf;
+ uint8_t required;
+ scf_property_t *stability_prop;
+ scf_value_t *stability_val;
+
+ if (templates == 0)
+ return;
+
+ if ((stability_prop = scf_property_create(g_hndl)) == NULL ||
+ (stability_val = scf_value_create(g_hndl)) == NULL)
+ scfdie();
+
+ if (templates == 2 && pg != NULL) {
+ if (scf_pg_get_property(pg, SCF_PROPERTY_STABILITY,
+ stability_prop) == 0) {
+ if (prop_check_type(stability_prop,
+ SCF_TYPE_ASTRING) == 0 &&
+ prop_get_val(stability_prop, stability_val) == 0) {
+ char *stability;
+
+ stability = safe_malloc(max_scf_value_len + 1);
+
+ if (scf_value_get_astring(stability_val,
+ stability, max_scf_value_len + 1) == -1 &&
+ scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("stability"), stability);
+
+ free(stability);
+ }
+ } else if (scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+ }
+
+ scf_property_destroy(stability_prop);
+ scf_value_destroy(stability_val);
+
+ if (pgt == NULL)
+ return;
+
+ if (pg == NULL || templates == 2) {
+ /* print type info only if scf_tmpl_pg_name succeeds */
+ if (scf_tmpl_pg_name(pgt, &buf) != -1) {
+ if (pg != NULL)
+ safe_printf("%s", TMPL_INDENT);
+ safe_printf("%s: ", gettext("name"));
+ safe_printf("%s\n", buf);
+ free(buf);
+ }
+
+ /* print type info only if scf_tmpl_pg_type succeeds */
+ if (scf_tmpl_pg_type(pgt, &buf) != -1) {
+ if (pg != NULL)
+ safe_printf("%s", TMPL_INDENT);
+ safe_printf("%s: ", gettext("type"));
+ safe_printf("%s\n", buf);
+ free(buf);
+ }
+ }
+
+ if (templates == 2 && scf_tmpl_pg_required(pgt, &required) == 0)
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("required"),
+ required ? "true" : "false");
+
+ if (templates == 2 && scf_tmpl_pg_target(pgt, &buf) > 0) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("target"),
+ buf);
+ free(buf);
+ }
+
+ if (templates == 2 && scf_tmpl_pg_common_name(pgt, NULL, &buf) > 0) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("common name"),
+ buf);
+ free(buf);
+ }
+
+ if (scf_tmpl_pg_description(pgt, NULL, &buf) > 0) {
+ if (templates == 2)
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("description"), buf);
+ else
+ safe_printf("%s%s\n", TMPL_INDENT, buf);
+ free(buf);
+ }
+
+}
+
+/*
+ * With as_value set to true, indent as appropriate for the value level.
+ * If false, indent to appropriate level for inclusion in constraint
+ * or choice printout.
+ */
+static void
+print_template_value_details(scf_prop_tmpl_t *prt, const char *val_buf,
+ int as_value)
+{
+ char *buf;
+
+ if (scf_tmpl_value_common_name(prt, NULL, val_buf, &buf) > 0) {
+ if (as_value == 0)
+ safe_printf("%s", TMPL_CHOICE_INDENT);
+ else
+ safe_printf("%s", TMPL_INDENT);
+ safe_printf("%s: %s\n", gettext("value common name"), buf);
+ free(buf);
+ }
+
+ if (scf_tmpl_value_description(prt, NULL, val_buf, &buf) > 0) {
+ if (as_value == 0)
+ safe_printf("%s", TMPL_CHOICE_INDENT);
+ else
+ safe_printf("%s", TMPL_INDENT);
+ safe_printf("%s: %s\n", gettext("value description"), buf);
+ free(buf);
+ }
+}
+
+static void
+print_template_value(scf_prop_tmpl_t *prt, const char *val_buf)
+{
+ safe_printf("%s%s: ", TMPL_VALUE_INDENT, gettext("value"));
+ /* This is to be human-readable, so don't use CHARS_TO_QUOTE */
+ safe_printf("%s\n", val_buf);
+
+ print_template_value_details(prt, val_buf, 1);
+}
+
+static void
+print_template_constraints(scf_prop_tmpl_t *prt, int verbose)
+{
+ int i, printed = 0;
+ scf_values_t values;
+ scf_count_ranges_t c_ranges;
+ scf_int_ranges_t i_ranges;
+
+ printed = 0;
+ i = 0;
+ if (scf_tmpl_value_name_constraints(prt, &values) == 0) {
+ safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
+ gettext("value constraints"));
+ printed++;
+ for (i = 0; i < values.value_count; ++i) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("value name"), values.values_as_strings[i]);
+ if (verbose == 1)
+ print_template_value_details(prt,
+ values.values_as_strings[i], 0);
+ }
+
+ scf_values_destroy(&values);
+ }
+
+ if (scf_tmpl_value_count_range_constraints(prt, &c_ranges) == 0) {
+ if (printed++ == 0)
+ safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
+ gettext("value constraints"));
+ for (i = 0; i < c_ranges.scr_num_ranges; ++i) {
+ safe_printf("%s%s: %llu to %llu\n", TMPL_INDENT,
+ gettext("range"), c_ranges.scr_min[i],
+ c_ranges.scr_max[i]);
+ }
+ scf_count_ranges_destroy(&c_ranges);
+ } else if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED &&
+ scf_tmpl_value_int_range_constraints(prt, &i_ranges) == 0) {
+ if (printed++ == 0)
+ safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
+ gettext("value constraints"));
+ for (i = 0; i < i_ranges.sir_num_ranges; ++i) {
+ safe_printf("%s%s: %lld to %lld\n", TMPL_INDENT,
+ gettext("range"), i_ranges.sir_min[i],
+ i_ranges.sir_max[i]);
+ }
+ scf_int_ranges_destroy(&i_ranges);
+ }
+}
+
+static void
+print_template_choices(scf_prop_tmpl_t *prt, int verbose)
+{
+ int i = 0, printed = 0;
+ scf_values_t values;
+ scf_count_ranges_t c_ranges;
+ scf_int_ranges_t i_ranges;
+
+ printed = 0;
+ if (scf_tmpl_value_name_choices(prt, &values) == 0) {
+ safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
+ gettext("value constraints"));
+ printed++;
+ for (i = 0; i < values.value_count; i++) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("value name"), values.values_as_strings[i]);
+ if (verbose == 1)
+ print_template_value_details(prt,
+ values.values_as_strings[i], 0);
+ }
+
+ scf_values_destroy(&values);
+ }
+
+ if (scf_tmpl_value_count_range_choices(prt, &c_ranges) == 0) {
+ for (i = 0; i < c_ranges.scr_num_ranges; ++i) {
+ if (printed++ == 0)
+ safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
+ gettext("value choices"));
+ safe_printf("%s%s: %llu to %llu\n", TMPL_INDENT,
+ gettext("range"), c_ranges.scr_min[i],
+ c_ranges.scr_max[i]);
+ }
+ scf_count_ranges_destroy(&c_ranges);
+ } else if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED &&
+ scf_tmpl_value_int_range_choices(prt, &i_ranges) == 0) {
+ for (i = 0; i < i_ranges.sir_num_ranges; ++i) {
+ if (printed++ == 0)
+ safe_printf("%s%s:\n", TMPL_VALUE_INDENT,
+ gettext("value choices"));
+ safe_printf("%s%s: %lld to %lld\n", TMPL_INDENT,
+ gettext("range"), i_ranges.sir_min[i],
+ i_ranges.sir_max[i]);
+ }
+ scf_int_ranges_destroy(&i_ranges);
+ }
+}
+
+static void
+list_values_by_template(scf_prop_tmpl_t *prt)
+{
+ print_template_constraints(prt, 1);
+ print_template_choices(prt, 1);
+}
+
+static void
+list_values_tmpl(scf_prop_tmpl_t *prt, scf_property_t *prop)
+{
+ char *val_buf;
+ scf_iter_t *iter;
+ scf_value_t *val;
+ int ret;
+
+ if ((iter = scf_iter_create(g_hndl)) == NULL ||
+ (val = scf_value_create(g_hndl)) == NULL)
+ scfdie();
+
+ if (scf_iter_property_values(iter, prop) != SCF_SUCCESS)
+ scfdie();
+
+ val_buf = safe_malloc(max_scf_value_len + 1);
+
+ while ((ret = scf_iter_next_value(iter, val)) == 1) {
+ if (scf_value_get_as_string(val, val_buf,
+ max_scf_value_len + 1) < 0)
+ scfdie();
+
+ print_template_value(prt, val_buf);
+ }
+ if (ret != 0 && scf_error() != SCF_ERROR_PERMISSION_DENIED)
+ scfdie();
+ free(val_buf);
+
+ print_template_constraints(prt, 0);
+ print_template_choices(prt, 0);
+
+}
+
+/*
+ * Outputs property info for the describe subcommand
+ * Verbose output if templates == 2, -v option of svccfg describe
+ * Displays template data if prop is not NULL, -t option of svccfg describe
+ */
+static void
+list_prop_tmpl(scf_prop_tmpl_t *prt, scf_property_t *prop, int templates)
+{
+ char *buf;
+ uint8_t u_buf;
+ int i;
+ uint64_t min, max;
+ scf_values_t values;
+
+ if (prt == NULL || templates == 0)
+ return;
+
+ if (prop == NULL) {
+ safe_printf("%s%s: ", TMPL_VALUE_INDENT, gettext("name"));
+ if (scf_tmpl_prop_name(prt, &buf) > 0) {
+ safe_printf("%s\n", buf);
+ free(buf);
+ } else
+ safe_printf("(%s)\n", gettext("any"));
+ }
+
+ if (prop == NULL || templates == 2) {
+ if (prop != NULL)
+ safe_printf("%s", TMPL_INDENT);
+ else
+ safe_printf("%s", TMPL_VALUE_INDENT);
+ safe_printf("%s: ", gettext("type"));
+ if ((buf = _scf_read_tmpl_prop_type_as_string(prt)) != NULL) {
+ safe_printf("%s\n", buf);
+ free(buf);
+ } else
+ safe_printf("(%s)\n", gettext("any"));
+ }
+
+ if (templates == 2 && scf_tmpl_prop_required(prt, &u_buf) == 0)
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("required"),
+ u_buf ? "true" : "false");
+
+ if (templates == 2 && scf_tmpl_prop_common_name(prt, NULL, &buf) > 0) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("common name"),
+ buf);
+ free(buf);
+ }
+
+ if (templates == 2 && scf_tmpl_prop_units(prt, NULL, &buf) > 0) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("units"),
+ buf);
+ free(buf);
+ }
+
+ if (scf_tmpl_prop_description(prt, NULL, &buf) > 0) {
+ safe_printf("%s%s\n", TMPL_INDENT, buf);
+ free(buf);
+ }
+
+ if (templates == 2 && scf_tmpl_prop_visibility(prt, &u_buf) == 0)
+ safe_printf("%s%s: %s\n", TMPL_INDENT, gettext("visibility"),
+ scf_tmpl_visibility_to_string(u_buf));
+
+ if (templates == 2 && scf_tmpl_prop_cardinality(prt, &min, &max) == 0) {
+ safe_printf("%s%s: %" PRIu64 "\n", TMPL_INDENT,
+ gettext("minimum number of values"), min);
+ if (max == ULLONG_MAX) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("maximum number of values"),
+ gettext("unlimited"));
+ } else {
+ safe_printf("%s%s: %" PRIu64 "\n", TMPL_INDENT,
+ gettext("maximum number of values"), max);
+ }
+ }
+
+ if (templates == 2 && scf_tmpl_prop_internal_seps(prt, &values) == 0) {
+ for (i = 0; i < values.value_count; i++) {
+ if (i == 0) {
+ safe_printf("%s%s:", TMPL_INDENT,
+ gettext("internal separators"));
+ }
+ safe_printf(" \"%s\"", values.values_as_strings[i]);
+ }
+ safe_printf("\n");
+ }
+
+ if (templates != 2)
+ return;
+
+ if (prop != NULL)
+ list_values_tmpl(prt, prop);
+ else
+ list_values_by_template(prt);
+}
+
+static char *
+read_astring(scf_propertygroup_t *pg, const char *prop_name)
+{
+ char *rv;
+
+ rv = _scf_read_single_astring_from_pg(pg, prop_name);
+ if (rv == NULL) {
+ switch (scf_error()) {
+ case SCF_ERROR_NOT_FOUND:
+ break;
+ default:
+ scfdie();
+ }
+ }
+ return (rv);
+}
+
static void
-listprop(const char *pattern, int only_pgs)
+display_documentation(scf_iter_t *iter, scf_propertygroup_t *pg)
+{
+ size_t doc_len;
+ size_t man_len;
+ char *pg_name;
+ char *text = NULL;
+ int rv;
+
+ doc_len = strlen(SCF_PG_TM_DOC_PREFIX);
+ man_len = strlen(SCF_PG_TM_MAN_PREFIX);
+ pg_name = safe_malloc(max_scf_name_len + 1);
+ while ((rv = scf_iter_next_pg(iter, pg)) == 1) {
+ if (scf_pg_get_name(pg, pg_name, max_scf_name_len + 1) == -1) {
+ scfdie();
+ }
+ if (strncmp(pg_name, SCF_PG_TM_DOC_PREFIX, doc_len) == 0) {
+ /* Display doc_link and and uri */
+ safe_printf("%s%s:\n", TMPL_INDENT,
+ gettext("doc_link"));
+ text = read_astring(pg, SCF_PROPERTY_TM_NAME);
+ if (text != NULL) {
+ safe_printf("%s%s%s: %s\n", TMPL_INDENT,
+ TMPL_INDENT, gettext("name"), text);
+ uu_free(text);
+ }
+ text = read_astring(pg, SCF_PROPERTY_TM_URI);
+ if (text != NULL) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT_2X,
+ gettext("uri"), text);
+ uu_free(text);
+ }
+ } else if (strncmp(pg_name, SCF_PG_TM_MAN_PREFIX,
+ man_len) == 0) {
+ /* Display manpage title, section and path */
+ safe_printf("%s%s:\n", TMPL_INDENT,
+ gettext("manpage"));
+ text = read_astring(pg, SCF_PROPERTY_TM_TITLE);
+ if (text != NULL) {
+ safe_printf("%s%s%s: %s\n", TMPL_INDENT,
+ TMPL_INDENT, gettext("title"), text);
+ uu_free(text);
+ }
+ text = read_astring(pg, SCF_PROPERTY_TM_SECTION);
+ if (text != NULL) {
+ safe_printf("%s%s%s: %s\n", TMPL_INDENT,
+ TMPL_INDENT, gettext("section"), text);
+ uu_free(text);
+ }
+ text = read_astring(pg, SCF_PROPERTY_TM_MANPATH);
+ if (text != NULL) {
+ safe_printf("%s%s%s: %s\n", TMPL_INDENT,
+ TMPL_INDENT, gettext("manpath"), text);
+ uu_free(text);
+ }
+ }
+ }
+ if (rv == -1)
+ scfdie();
+
+done:
+ free(pg_name);
+}
+
+static void
+list_entity_tmpl(int templates)
+{
+ char *common_name = NULL;
+ char *description = NULL;
+ char *locale = NULL;
+ scf_iter_t *iter;
+ scf_propertygroup_t *pg;
+ scf_property_t *prop;
+ int r;
+ scf_value_t *val;
+
+ if ((pg = scf_pg_create(g_hndl)) == NULL ||
+ (prop = scf_property_create(g_hndl)) == NULL ||
+ (val = scf_value_create(g_hndl)) == NULL ||
+ (iter = scf_iter_create(g_hndl)) == NULL)
+ scfdie();
+
+ locale = setlocale(LC_MESSAGES, NULL);
+
+ if (get_pg(SCF_PG_TM_COMMON_NAME, pg) == 0) {
+ common_name = safe_malloc(max_scf_value_len + 1);
+
+ /* Try both the current locale and the "C" locale. */
+ if (scf_pg_get_property(pg, locale, prop) == 0 ||
+ (scf_error() == SCF_ERROR_NOT_FOUND &&
+ scf_pg_get_property(pg, "C", prop) == 0)) {
+ if (prop_get_val(prop, val) == 0 &&
+ scf_value_get_ustring(val, common_name,
+ max_scf_value_len + 1) != -1) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("common name"), common_name);
+ }
+ }
+ }
+
+ /*
+ * Do description, manpages, and doc links if templates == 2.
+ */
+ if (templates == 2) {
+ /* Get the description. */
+ if (get_pg(SCF_PG_TM_DESCRIPTION, pg) == 0) {
+ description = safe_malloc(max_scf_value_len + 1);
+
+ /* Try both the current locale and the "C" locale. */
+ if (scf_pg_get_property(pg, locale, prop) == 0 ||
+ (scf_error() == SCF_ERROR_NOT_FOUND &&
+ scf_pg_get_property(pg, "C", prop) == 0)) {
+ if (prop_get_val(prop, val) == 0 &&
+ scf_value_get_ustring(val, description,
+ max_scf_value_len + 1) != -1) {
+ safe_printf("%s%s: %s\n", TMPL_INDENT,
+ gettext("description"),
+ description);
+ }
+ }
+ }
+
+ /* Process doc_link & manpage elements. */
+ if (cur_level != NULL) {
+ r = scf_iter_snaplevel_pgs_typed(iter, cur_level,
+ SCF_GROUP_TEMPLATE);
+ } else if (cur_inst != NULL) {
+ r = scf_iter_instance_pgs_typed(iter, cur_inst,
+ SCF_GROUP_TEMPLATE);
+ } else {
+ r = scf_iter_service_pgs_typed(iter, cur_svc,
+ SCF_GROUP_TEMPLATE);
+ }
+ if (r == 0) {
+ display_documentation(iter, pg);
+ }
+ }
+
+ free(common_name);
+ free(description);
+ scf_pg_destroy(pg);
+ scf_property_destroy(prop);
+ scf_value_destroy(val);
+ scf_iter_destroy(iter);
+}
+
+static void
+listtmpl(const char *pattern, int templates)
+{
+ scf_pg_tmpl_t *pgt;
+ scf_prop_tmpl_t *prt;
+ char *snapbuf = NULL;
+ char *fmribuf;
+ char *pg_name = NULL, *prop_name = NULL;
+ ssize_t prop_name_size;
+ char *qual_prop_name;
+ char *search_name;
+ int listed = 0;
+
+ if ((pgt = scf_tmpl_pg_create(g_hndl)) == NULL ||
+ (prt = scf_tmpl_prop_create(g_hndl)) == NULL)
+ scfdie();
+
+ fmribuf = safe_malloc(max_scf_name_len + 1);
+ qual_prop_name = safe_malloc(max_scf_name_len + 1);
+
+ if (cur_snap != NULL) {
+ snapbuf = safe_malloc(max_scf_name_len + 1);
+ if (scf_snapshot_get_name(cur_snap, snapbuf,
+ max_scf_name_len + 1) < 0)
+ scfdie();
+ }
+
+ if (cur_inst != NULL) {
+ if (scf_instance_to_fmri(cur_inst, fmribuf,
+ max_scf_name_len + 1) < 0)
+ scfdie();
+ } else if (cur_svc != NULL) {
+ if (scf_service_to_fmri(cur_svc, fmribuf,
+ max_scf_name_len + 1) < 0)
+ scfdie();
+ } else
+ abort();
+
+ /* If pattern is specified, we want to list only those items. */
+ while (scf_tmpl_iter_pgs(pgt, fmribuf, snapbuf, NULL, NULL) == 1) {
+ listed = 0;
+ if (pattern == NULL || (scf_tmpl_pg_name(pgt, &pg_name) > 0 &&
+ fnmatch(pattern, pg_name, 0) == 0)) {
+ list_pg_tmpl(pgt, NULL, templates);
+ listed++;
+ }
+
+ scf_tmpl_prop_reset(prt);
+
+ while (scf_tmpl_iter_props(pgt, prt, NULL) == 0) {
+ search_name = NULL;
+ prop_name_size = scf_tmpl_prop_name(prt, &prop_name);
+ if ((prop_name_size > 0) && (pg_name != NULL)) {
+ if (snprintf(qual_prop_name,
+ max_scf_name_len + 1, "%s/%s",
+ pg_name, prop_name) >=
+ max_scf_name_len + 1) {
+ prop_name_size = -1;
+ } else {
+ search_name = qual_prop_name;
+ }
+ }
+ if (listed > 0 || pattern == NULL ||
+ (prop_name_size > 0 &&
+ fnmatch(pattern, search_name,
+ FNM_PATHNAME) == 0))
+ list_prop_tmpl(prt, NULL, templates);
+ if (prop_name != NULL) {
+ free(prop_name);
+ prop_name = NULL;
+ }
+ }
+ if (pg_name != NULL) {
+ free(pg_name);
+ pg_name = NULL;
+ }
+ }
+
+ scf_tmpl_prop_destroy(prt);
+ scf_tmpl_pg_destroy(pgt);
+ free(snapbuf);
+ free(fmribuf);
+ free(qual_prop_name);
+}
+
+static void
+listprop(const char *pattern, int only_pgs, int templates)
{
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_iter_t *iter, *piter;
char *pgnbuf, *prnbuf, *ppnbuf;
+ scf_pg_tmpl_t *pgt, *pgtp;
+ scf_prop_tmpl_t *prt;
void **objects;
char **names;
+ void **tmpls;
int allocd, i;
int ret;
@@ -10755,7 +11553,9 @@ listprop(const char *pattern, int only_pgs)
if ((pg = scf_pg_create(g_hndl)) == NULL ||
(prop = scf_property_create(g_hndl)) == NULL ||
(iter = scf_iter_create(g_hndl)) == NULL ||
- (piter = scf_iter_create(g_hndl)) == NULL)
+ (piter = scf_iter_create(g_hndl)) == NULL ||
+ (prt = scf_tmpl_prop_create(g_hndl)) == NULL ||
+ (pgt = scf_tmpl_pg_create(g_hndl)) == NULL)
scfdie();
prnbuf = safe_malloc(max_scf_name_len + 1);
@@ -10767,16 +11567,15 @@ listprop(const char *pattern, int only_pgs)
else
ret = scf_iter_service_pgs(iter, cur_svc);
if (ret != 0) {
- if (scf_error() == SCF_ERROR_DELETED)
- scfdie();
return;
}
/*
* We want to only list items which match pattern, and we want the
* second column to line up, so during the first pass we'll save
- * matching items & their names in objects and names, computing the
- * maximum name length as we go, and then we'll print them out.
+ * matching items, their names, and their templates in objects,
+ * names, and tmpls, computing the maximum name length as we go,
+ * and then we'll print them out.
*
* Note: We always keep an extra slot available so the array can be
* NULL-terminated.
@@ -10785,9 +11584,12 @@ listprop(const char *pattern, int only_pgs)
allocd = 1;
objects = safe_malloc(sizeof (*objects));
names = safe_malloc(sizeof (*names));
+ tmpls = safe_malloc(sizeof (*tmpls));
while ((ret = scf_iter_next_pg(iter, pg)) == 1) {
int new_pg = 0;
+ int print_props = 0;
+ pgtp = NULL;
pgnlen = scf_pg_get_name(pg, NULL, 0);
if (pgnlen < 0)
@@ -10800,6 +11602,14 @@ listprop(const char *pattern, int only_pgs)
scfdie();
assert(szret <= pgnlen);
+ if (scf_tmpl_get_by_pg(pg, pgt, NULL) == -1) {
+ if (scf_error() != SCF_ERROR_NOT_FOUND)
+ scfdie();
+ pgtp = NULL;
+ } else {
+ pgtp = pgt;
+ }
+
if (pattern == NULL ||
fnmatch(pattern, pgnbuf, 0) == 0) {
if (i+1 >= allocd) {
@@ -10808,17 +11618,27 @@ listprop(const char *pattern, int only_pgs)
sizeof (*objects) * allocd);
names =
realloc(names, sizeof (*names) * allocd);
- if (objects == NULL || names == NULL)
+ tmpls = realloc(tmpls,
+ sizeof (*tmpls) * allocd);
+ if (objects == NULL || names == NULL ||
+ tmpls == NULL)
uu_die(gettext("Out of memory"));
}
objects[i] = pg;
names[i] = pgnbuf;
+
+ if (pgtp == NULL)
+ tmpls[i] = NULL;
+ else
+ tmpls[i] = pgt;
+
++i;
if (pgnlen > max_len)
max_len = pgnlen;
new_pg = 1;
+ print_props = 1;
}
if (only_pgs) {
@@ -10826,6 +11646,9 @@ listprop(const char *pattern, int only_pgs)
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
+ pgt = scf_tmpl_pg_create(g_hndl);
+ if (pgt == NULL)
+ scfdie();
} else
free(pgnbuf);
@@ -10850,7 +11673,7 @@ listprop(const char *pattern, int only_pgs)
prnbuf) < 0)
uu_die("snprintf");
- if (pattern == NULL ||
+ if (pattern == NULL || print_props == 1 ||
fnmatch(pattern, ppnbuf, 0) == 0) {
if (i+1 >= allocd) {
allocd *= 2;
@@ -10858,19 +11681,38 @@ listprop(const char *pattern, int only_pgs)
sizeof (*objects) * allocd);
names = realloc(names,
sizeof (*names) * allocd);
- if (objects == NULL || names == NULL)
- uu_die(gettext("Out of "
- "memory"));
+ tmpls = realloc(tmpls,
+ sizeof (*tmpls) * allocd);
+ if (objects == NULL || names == NULL ||
+ tmpls == NULL)
+ uu_die(gettext(
+ "Out of memory"));
}
objects[i] = prop;
names[i] = ppnbuf;
+
+ if (pgtp != NULL) {
+ if (scf_tmpl_get_by_prop(pgt, prnbuf,
+ prt, NULL) < 0) {
+ if (scf_error() !=
+ SCF_ERROR_NOT_FOUND)
+ scfdie();
+ tmpls[i] = NULL;
+ } else {
+ tmpls[i] = prt;
+ }
+ } else {
+ tmpls[i] = NULL;
+ }
+
++i;
if (prnlen > max_len)
max_len = prnlen;
prop = scf_property_create(g_hndl);
+ prt = scf_tmpl_prop_create(g_hndl);
} else {
free(ppnbuf);
}
@@ -10880,6 +11722,9 @@ listprop(const char *pattern, int only_pgs)
pg = scf_pg_create(g_hndl);
if (pg == NULL)
scfdie();
+ pgt = scf_tmpl_pg_create(g_hndl);
+ if (pgt == NULL)
+ scfdie();
} else
free(pgnbuf);
}
@@ -10889,26 +11734,37 @@ listprop(const char *pattern, int only_pgs)
objects[i] = NULL;
scf_pg_destroy(pg);
+ scf_tmpl_pg_destroy(pgt);
scf_property_destroy(prop);
+ scf_tmpl_prop_destroy(prt);
for (i = 0; objects[i] != NULL; ++i) {
if (strchr(names[i], '/') == NULL) {
/* property group */
pg = (scf_propertygroup_t *)objects[i];
+ pgt = (scf_pg_tmpl_t *)tmpls[i];
list_pg_info(pg, names[i], max_len);
+ list_pg_tmpl(pgt, pg, templates);
free(names[i]);
scf_pg_destroy(pg);
+ if (pgt != NULL)
+ scf_tmpl_pg_destroy(pgt);
} else {
/* property */
prop = (scf_property_t *)objects[i];
+ prt = (scf_prop_tmpl_t *)tmpls[i];
list_prop_info(prop, names[i], max_len);
+ list_prop_tmpl(prt, prop, templates);
free(names[i]);
scf_property_destroy(prop);
+ if (prt != NULL)
+ scf_tmpl_prop_destroy(prt);
}
}
free(names);
free(objects);
+ free(tmpls);
}
void
@@ -10916,7 +11772,7 @@ lscf_listpg(const char *pattern)
{
lscf_prep_hndl();
- listprop(pattern, 1);
+ listprop(pattern, 1, 0);
}
/*
@@ -11061,7 +11917,7 @@ lscf_listprop(const char *pattern)
{
lscf_prep_hndl();
- listprop(pattern, 0);
+ listprop(pattern, 0, 0);
}
int
@@ -12808,6 +13664,110 @@ lscf_refresh(void)
free(fmribuf);
}
+/*
+ * describe [-v] [-t] [pg/prop]
+ */
+int
+lscf_describe(uu_list_t *args, int hasargs)
+{
+ int ret = 0;
+ size_t i;
+ int argc;
+ char **argv = NULL;
+ string_list_t *slp;
+ int do_verbose = 0;
+ int do_templates = 0;
+ char *pattern = NULL;
+
+ lscf_prep_hndl();
+
+ if (hasargs != 0) {
+ argc = uu_list_numnodes(args);
+ if (argc < 1)
+ goto usage;
+
+ argv = calloc(argc + 1, sizeof (char *));
+ if (argv == NULL)
+ uu_die(gettext("Out of memory.\n"));
+
+ for (slp = uu_list_first(args), i = 0;
+ slp != NULL;
+ slp = uu_list_next(args, slp), ++i)
+ argv[i] = slp->str;
+
+ argv[i] = NULL;
+
+ /*
+ * We start optind = 0 because our list of arguments
+ * starts at argv[0]
+ */
+ optind = 0;
+ opterr = 0;
+ for (;;) {
+ ret = getopt(argc, argv, "vt");
+ if (ret == -1)
+ break;
+
+ switch (ret) {
+ case 'v':
+ do_verbose = 1;
+ break;
+
+ case 't':
+ do_templates = 1;
+ break;
+
+ case '?':
+ goto usage;
+
+ default:
+ bad_error("getopt", ret);
+ }
+ }
+
+ pattern = argv[optind];
+ }
+
+ if (cur_inst == NULL && cur_svc == NULL) {
+ semerr(emsg_entity_not_selected);
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * list_entity_tmpl(), listprop() and listtmpl() produce verbose
+ * output if their last parameter is set to 2. Less information is
+ * produced if the parameter is set to 1.
+ */
+ if (pattern == NULL) {
+ if (do_verbose == 1)
+ list_entity_tmpl(2);
+ else
+ list_entity_tmpl(1);
+ }
+
+ if (do_templates == 0) {
+ if (do_verbose == 1)
+ listprop(pattern, 0, 2);
+ else
+ listprop(pattern, 0, 1);
+ } else {
+ if (do_verbose == 1)
+ listtmpl(pattern, 2);
+ else
+ listtmpl(pattern, 1);
+ }
+
+ ret = 0;
+out:
+ if (argv != NULL)
+ free(argv);
+ return (ret);
+usage:
+ ret = -2;
+ goto out;
+}
+
#ifndef NATIVE_BUILD
/* ARGSUSED */
CPL_MATCH_FN(complete_select)
diff --git a/usr/src/cmd/svc/svccfg/svccfg_main.c b/usr/src/cmd/svc/svccfg/svccfg_main.c
index 97893f86f4..aee16b5da2 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_main.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_main.c
@@ -19,12 +19,10 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* svccfg - modify service configuration repository
*/
@@ -180,6 +178,7 @@ initialize(int argc, char *argv[])
internal_init();
engine_init();
lscf_init(); /* must follow engine_init() */
+ tmpl_init();
}
int
diff --git a/usr/src/cmd/svc/svccfg/svccfg_tmpl.c b/usr/src/cmd/svc/svccfg/svccfg_tmpl.c
new file mode 100644
index 0000000000..215a9d79a3
--- /dev/null
+++ b/usr/src/cmd/svc/svccfg/svccfg_tmpl.c
@@ -0,0 +1,4036 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This file provides the code that allows svccfg(1M) to validate a
+ * manifest against the template specifications. svccfg uses the
+ * validation facilities for the import and validate subcommands.
+ *
+ * There are three entry points -- tmpl_validate_bundle(),
+ * tmpl_errors_print() and tmpl_errors_destroy(). svccfg calls
+ * tmpl_validate_bundle() to validate a bundle. tmpl_validate_bundle()
+ * returns a pointer to a tmpl_errors_t. This is a pointer to information
+ * about any validation errors that were found. If an error was detected,
+ * svccfg calls tmpl_errors_print() to print the error information. Once
+ * the error information is printed, svccfg calls tmpl_errors_destroy() to
+ * free the memory associated with the tmpl_errors_t.
+ *
+ * libscf's scf_tmpl.c performs similar checks to the ones described in
+ * this paragraph. Any changes to the algorithms in this file should also
+ * be infcorporated into scf_tmpl.c. The reason that there are two bodies
+ * of code is that they work on different data structures.
+ * tmpl_validate_bundle() validates each instance of each service in the
+ * bundle. The following checks are performed on each instance:
+ *
+ * 1. Verify template consistency.
+ * A. No conflicting definitions of "pg_pattern" are allowed
+ * within a single instance.
+ * B. Templates at a narrow target (e.g. instance) which define
+ * property groups already templated at a broad target
+ * (e.g. delegate or all) are strongly discouraged.
+ * C. Developers may not define a template which specifies a
+ * single prop_pattern name with differing types on the same
+ * target entity.
+ * D. If a pg_pattern has a required attribute with a value of
+ * true, then its name and type attributes must be
+ * specified.
+ * E. If a prop_pattern has a required attribute with a value
+ * of true, then its type attribute must be specified.
+ * F. If a prop_pattern has an include_values element make sure
+ * that the appropriate constraints or values element has
+ * also been declared.
+ * 2. Validate that each property group in the instance is in
+ * conformance with the template specifications.
+ * A. Verify that the types of the PG and the pg_pattern are
+ * compatible.
+ * B. Verify properties of the PG against the prop_patterns in
+ * the template.
+ * o Verify property's type.
+ * o Verify cardinality.
+ * o Vefiy that property values satisfy the constraints
+ * imposed by the prop_pattern.
+ * C. Verify that required properties are present.
+ * 3. Verify that all required property groups are present in the
+ * insance.
+ *
+ * tmpl_validate_bundle() is called after svccfg has processed the manifest
+ * file. The manifest is represented in memory by a set of entity_t,
+ * pgroup_t, property_t and value_t structures. These structures are
+ * defined in svccfg.h.
+ *
+ * tmpl_validate_bundle() calls tmpl_validate_service() for each service in
+ * the bundle, and tmpl_validate_service() then calls
+ * tmpl_validate_instance() for each instance in the service.
+ * tmpl_validate_instance() is the function that does the real work of
+ * validation against the template specification.
+ *
+ * Subsystems:
+ * ==========
+ *
+ * General Templates:
+ * -----------------
+ * In order to perform the validation specified by 1.B above, we need to
+ * load the templates specifications for the global service and the
+ * instance's restarter. This information is loaded from the repository
+ * and it is held in memory using the entity_t, pgroup_t, property_t and
+ * value_t hierarchy of structures. When a service is processed,
+ * load_general_templates() is called to load the information for the
+ * global service and restarter that is declared at the service level. The
+ * sc_service.sc_global and sc_service.sc_restarter members of the
+ * service's entity_t point to the information for the global and restarter
+ * services.
+ *
+ * The instance portion of a manifest can declare an instance specific
+ * restarter. If this is the case, load_instance_restarter() will load the
+ * information for that restarter, and it is saved in the
+ * sc_instance.sc_instance_restarter member of the entity_t that represents
+ * the instance.
+ *
+ * Composed Properties:
+ * -------------------
+ * We need the ability to process the composed properties of an instance.
+ * That is to say if an instance defines a property, it overrides any
+ * definition in the service. Otherwise, the service's definition is
+ * inherited in the instance.
+ *
+ * In an entity_t, the sc_instance.sc_composed member points to a
+ * uu_avl tree of composed property groups (composed_pg_t) for the
+ * instance. The composed_pg_t has two members, cpg_instance_pg and
+ * cpg_service_pg, that point to the instance and service property group
+ * definitions respectively. Either of these may be NULL indicating that
+ * only an instance or service definition exists in the manifest.
+ *
+ * In the case where both the instance and the service define a property
+ * group, the properties must be composed. This is done by
+ * compose_props(). The compose_pg_t holds the composed properties in a
+ * uu_avl_tree at cpf_compose_props. This is a tree of property_t
+ * structures. If a property is defined in both the instance and service
+ * property group, the tree will hold the instance definition. If the
+ * property is defined at only one level, the tree will hold the property_t
+ * for that level. Thus, the tree is truly a set of composed properties of
+ * the property group.
+ *
+ * Property Group Iteration:
+ * ------------------------
+ * A number of functions must iterate through an instance's property groups
+ * looking for the ones that define a pg_pattern or a prop_pattern. To be
+ * specific, the iteration starts with the composed view of the instance.
+ * It then proceeds through the restarter information and finally moves on
+ * to the global service. The pg_iter_t structure holds the information
+ * that is needed to implement this type of iteration. pg_iter_create()
+ * creates one of these iterators, and pg_iter_destroy() frees the memory
+ * associated with the pg_iter_t. next_pattern_pg(), is used to step
+ * through the iteration.
+ *
+ * Error Reporting:
+ * ---------------
+ * While performing the templates validation checks, svccfg collects
+ * information for all the errors that it finds. Because of this you will
+ * see many places in the code where a loop is not exited when an error is
+ * encountered. We note that fact that an error has occurred, but continue
+ * in the loop to see if there are more validation errors. The error code
+ * of the last error that is encountered is returned. This works, because
+ * the callers of tmpl_validate_bundle() only look to see whether or not
+ * the return code is TVS_SUCCESS.
+ *
+ * The information for reporting the errors is collected in a tmpl_errors_t
+ * structure, and tmpl_validate_bundle() returns the address of this
+ * structure. The caller of tmpl_validate_bundle() can then call
+ * tmpl_errors_print() to display the error information to the user.
+ *
+ * There are two categories of errors. Some errors are seen when
+ * processing the information in the manifest. This type of error is only
+ * seen by svccfg when it is importing or validating a manifest. The other
+ * type of error consists of template validation errors. These errors can
+ * be seen when processing a manifest or when performing a templates
+ * validation of the information associated with an FMRI in the the
+ * repository. tmpl_errors_add_im() is used to capture error information
+ * about the first type of error, and add_scf_error() is used to capture
+ * error information about the second type of error.
+ *
+ * The distinction is important when printing the error information. The
+ * fuctions for printing the first type of error reside in this file, since
+ * these errors will only be seen by the functions in this file. The
+ * functions for printing the template validation errors reside in libscf,
+ * because these errors are of a more general nature.
+ *
+ * Thus, tmpl_errors_t has two lists -- one for each type of error.
+ * te_list is a list of im_tmpl_error_t structures that represent the first
+ * type of error. te_scf is a list of tv_errors_t structures that hold
+ * information about general template validation errors.
+ * tmpl_errors_print() processes both lists to print information about all
+ * errors. In tmpl_errors_print() im_tmpl_error_print() is used to print
+ * the errors that are specific to this file. scf_tmpl_strerror() provides
+ * the errors messages for general templates errors.
+ *
+ * As was mentioned in the previous paragraph, im_tmpl_error_print() is
+ * responsible for printing the errors that are specific to this file.
+ * Based on the error code, it dispatches to one of
+ * im_perror_bad_conversion(), im_perror_bad_template(),
+ * im_perror_invalid_type(), im_perror_missing_pg_type() or
+ * im_perror_missing_type(). The rest of the im_perror_* functions provide
+ * services to these error specific functions by printing common
+ * information.
+ *
+ * im_perror_item() is the heart of this message printing subsystem. It is
+ * called directly or indirectly by all of the other im_perror_* functions.
+ * im_perror_item() prints a single item of error information. If svccfg
+ * is running in interactive mode, im_perror_item() prints each item on a
+ * single line, so that they are readable by a human. In non-interactive
+ * mode, all items are printed on a single line separated by semi-colons.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libintl.h>
+#include <limits.h>
+#include <libscf.h>
+#include <libscf_priv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include "svccfg.h"
+
+/*
+ * Clear error_info_t structure.
+ */
+#define CLEAR_ERROR_INFO(ei) ((void) memset((ei), 0, sizeof (error_info_t)))
+
+/*
+ * Retrieve the property group pointer from the composed_pg structure.
+ */
+#define CPG2PG(cpg) (cpg->cpg_instance_pg ? cpg->cpg_instance_pg : \
+ cpg->cpg_service_pg)
+
+/*
+ * Convert a pointer to an empty string into a NULL pointer.
+ */
+#define EMPTY_TO_NULL(p) (((p) && (*(p) == 0)) ? NULL : (p))
+
+/* uu_avl and uu_list debugging bits. */
+#ifdef NDEBUG
+#define TMPL_DEBUG_AVL_POOL UU_DEFAULT
+#define TMPL_DEBUG_LIST UU_DEFAULT
+#define TMPL_DEBUG_LIST_POOL UU_DEFAULT
+#define TMPL_DEBUG_TREE UU_DEFAULT
+#else
+#define TMPL_DEBUG_AVL_POOL UU_AVL_POOL_DEBUG
+#define TMPL_DEBUG_LIST UU_LIST_DEBUG
+#define TMPL_DEBUG_LIST_POOL UU_LIST_POOL_DEBUG
+#define TMPL_DEBUG_TREE UU_AVL_DEBUG
+#endif /* NDEBUG */
+
+/*
+ * Structures and enums that are used in producing error messages:
+ *
+ * error_info_t is used to pass information about an error to
+ * tmpl_errors_add_im() and add_scf_error(). tmpl_errors_add_im() collects
+ * the error information and stores it in an im_tmpl_error_t. The
+ * im_tmpl_error_t is linked into the tmpl_errors_t, so that the captured
+ * information can be used later when error messages are printed.
+ *
+ * tv_errors_t is used to keep track of error information for general
+ * template errors that are known by libscf. add_scf_error() captures the
+ * error information for use in this structure.
+ */
+/*
+ * enum to designate the type of data that is held in a err_info structure.
+ */
+typedef enum err_info_type {
+ EIT_NONE, /* No values in the structure */
+ EIT_BAD_TEMPLATE, /* Reason that template is bad */
+ EIT_CARDINALITY, /* Ranges for property cardinality */
+ EIT_INCLUDE_VALUES, /* include_values type attribute */
+ EIT_MISSING_PG, /* Name of missing pg */
+ EIT_MISSING_PROP, /* Name of missing property */
+ EIT_PATTERN_CONFLICT, /* Conflicting pattern definition */
+ EIT_PROP_TYPE, /* Value with invalid type */
+ EIT_RANGE /* Value that is out of range */
+} err_info_type_t;
+
+/*
+ * Structure to hold information that will be used in generating error
+ * messages.
+ */
+typedef struct error_info {
+ err_info_type_t ei_type; /* Type of information stored here */
+ union {
+ /* EIT_BAD_TEMPLATE */
+ struct {
+ const char *ei_reason;
+ } ei_bad_template;
+ /* EIT_CARDINALITY */
+ struct {
+ uint64_t ei_min;
+ uint64_t ei_max;
+ uint64_t ei_count; /* Number of prop values */
+ } ei_cardinality;
+ /* EIT_INCLUDE_VALUES */
+ struct {
+ const char *ei_type;
+ } ei_inc_values;
+ /* EIT_MISSING_PG */
+ struct {
+ const char *ei_pg_name; /* Name of missing pg */
+ const char *ei_pg_type; /* Type of missing pg */
+ } ei_missing_pg;
+ /* EIT_MISSING_PROP */
+ struct {
+ const char *ei_prop_name; /* Name of prop */
+ } ei_missing_prop;
+ /* EIT_PATTERN_CONFLICT */
+ struct {
+ pgroup_t *ei_pattern; /* Conficting pattern */
+ } ei_pattern_conflict;
+ /* EIT_PROP_TYPE */
+ struct {
+ scf_type_t ei_actual;
+ scf_type_t ei_specified;
+ } ei_prop_type;
+ /* EIT_RANGE */
+ struct {
+ scf_type_t ei_rtype;
+ int64_t ei_ivalue;
+ uint64_t ei_uvalue;
+ } ei_range;
+ } ei_u;
+} error_info_t;
+
+/*
+ * Structure with information about a template violation. This structure
+ * is for use with in memory representations of the manifest and template.
+ * See scf_tmpl_error_t for use with repository representations. Some of
+ * the pointers may be NULL for some types of errors.
+ */
+typedef struct im_tmpl_error {
+ tmpl_validate_status_t ite_type; /* Type of error */
+ entity_t *ite_entity; /* Instance or service */
+ pgroup_t *ite_pg; /* Non-conforming prop. group */
+ pgroup_t *ite_pg_pattern; /* Violated pg_pattern */
+ property_t *ite_prop; /* Non-conforming property */
+ pgroup_t *ite_prop_pattern; /* Violated prop_pattern */
+ value_t *ite_value; /* Non-conforming value */
+ error_info_t ite_einfo; /* Extra error information */
+ uu_list_node_t ite_node; /* Node to link us in a list. */
+} im_tmpl_error_t;
+
+/*
+ * This structure holds the data that will be used by scf_tmpl_strerror()
+ * for printing template validation errors.
+ */
+typedef struct tv_errors {
+ scf_tmpl_errors_t *tve_errors; /* Errors for scf_tmpl_strerror() */
+ uu_list_node_t tve_node; /* Linkage in a list. */
+} tv_errors_t;
+
+/*
+ * Structure to collect template validation errors.
+ */
+struct tmpl_errors {
+ uu_list_t *te_list; /* List of im_tmpl_error_t */
+ im_tmpl_error_t *te_next; /* Next error to present */
+ uu_list_t *te_scf; /* Errors for scf_tmpl_strerror() */
+ tv_errors_t *te_cur_scf; /* Current member of te_scf */
+};
+
+/* End of structures used in error processing. */
+
+/*
+ * Property group types that are of interest to us. See pgroup_type().
+ */
+typedef enum pg_type {
+ NORMAL_PG,
+ PG_PATTERN_PG,
+ PROP_PATTERN_PG
+} pg_type_t;
+
+/*
+ * Structure to keep track of a set of ASTRING property values for a
+ * property. The consumer may wish to have the ASTRING property values
+ * converted to a numeric form which is the reason for the av_v union.
+ * This structure is returned by av_get_values() and is accessed by
+ * av_get_integer(), av_get_string() and av_get_unsigned().
+ */
+typedef struct avalues {
+ uint_t av_count; /* Number of values */
+ scf_type_t av_type; /* Type of value representation */
+ union {
+ uint64_t *av_unsigned; /* Count & boolean values */
+ int64_t *av_integer; /* Integer values */
+ const char **av_string; /* String values */
+ } av_v; /* Container for the values */
+} avalues_t;
+
+/*
+ * composed_pg_t contains the information that is needed to compose a
+ * property group. See the section on Composed Properties in the block
+ * comment at the beginning of this file. The composed_pg structures are
+ * linked into a uu_avl tree. The tree is at sc_instance.sc_composed in
+ * the entity_t.
+ */
+struct composed_pg {
+ /*
+ * Property group is uniquely identified by its name and type.
+ * These two elements point to the name and type in a pgroup_t
+ * (either service or instance), so they do not need to be
+ * allocated or freed.
+ */
+ const char *cpg_name;
+ const char *cpg_type;
+
+ /* References to the actual property group definitions. */
+ pgroup_t *cpg_instance_pg;
+ pgroup_t *cpg_service_pg;
+
+ /* Composed properties of the property group. */
+ uu_avl_t *cpg_composed_props;
+
+ uu_avl_node_t cpg_node; /* Linkage for AVL tree */
+};
+
+/*
+ * Prefixes for standard property names. Used in
+ * include_values_support().
+ */
+typedef struct prop_prefix {
+ const char *pp_prefix;
+ size_t pp_size;
+} prop_prefix_t;
+
+/*
+ * Store a legal range for a property allowing for either signed or
+ * unsigned ranges. It is used to store a range from a template
+ * constraint element of a prop_pattern. The structure is returned by
+ * get_ranges() and is used by value_in_range() to validate the values of a
+ * property.
+ */
+typedef struct range {
+ union {
+ struct {
+ uint64_t rng_min;
+ uint64_t rng_max;
+ } rng_unsigned;
+ struct {
+ int64_t rng_min;
+ int64_t rng_max;
+ } rng_signed;
+ } rng_u;
+} range_t;
+
+/*
+ * This enum defines the levels where templates can be defined. See the
+ * pg_iter structure below.
+ */
+typedef enum tmpl_level {
+ TL_NOLEVEL = 0, /* No level yet specified. */
+ TL_INSTANCE, /* Instance templates. */
+ TL_COMPOSED, /* Composed instance. */
+ TL_SERVICE, /* Service wide templates. */
+ TL_RESTARTER, /* Templates from restarter manifest. */
+ TL_GLOBAL /* SMF wide templates. */
+} tmpl_level_t;
+
+/*
+ * pg_iter is a structure that allows us to iterate through property groups
+ * in an instance followed by the property groups of the instance's
+ * service, the instance's restarter and finally the global service. See
+ * the Property Group Iteration section of the block comment at the
+ * beginning of this file.
+ */
+typedef struct pg_iter {
+ entity_t *pgi_entity; /* Entity being searched */
+ const char *pgi_restrict; /* Only return PGs of this type */
+ tmpl_level_t pgi_level; /* Current level */
+ entity_t *pgi_service; /* Service being processed. */
+ union {
+ pgroup_t *pgi_pg;
+ composed_pg_t *pgi_cpg;
+ } pgi_current; /* Current property group. */
+} pg_iter_t;
+
+/*
+ * enum to distinguish between pg_patterns and prop_patterns. It is used
+ * in the ptrn_info_t structure. See below.
+ */
+typedef enum ptrn_type {
+ PG_PATTERN,
+ PROP_PATTERN
+} ptrn_type_t;
+
+/*
+ * Structure of information about a pg_pattern or a prop_pattern. It is
+ * used for template consistency checks. gather_pattern() is used to
+ * gather information for all the pg_patterns or prop_patterns in an
+ * instance. It allocates a ptrn_info_t for each of these and adds them to
+ * an avl tree that is held by tmpl_consistency().
+ */
+typedef struct ptrn_info {
+ ptrn_type_t pi_ptrn_type;
+ pgroup_t *pi_ptrnpg; /* pgroup_t defining the pattern. */
+ const char *pi_name; /* Name attribute. */
+ const char *pi_type; /* Type attribute. */
+ const char *pi_target; /* Target attribute - only PG_PATTERN */
+ const char *pi_pgp_name; /* Name of the pg pattern. Only */
+ /* used for PROP_PATTERN. */
+ pgroup_t *pi_enc_pgp; /* PG of the pg_pattern that holds */
+ /* the prop_pattern defined by this */
+ /* structure. Only used for */
+ /* PROP_PATTERN. */
+ uu_avl_node_t pi_link; /* Linkage into AVL tree */
+} ptrn_info_t;
+
+static const char *emesg_nomem;
+
+/*
+ * Pool for trees of composed property groups.
+ */
+static uu_avl_pool_t *composed_pg_pool;
+
+/*
+ * Pool for trees of composed properties.
+ */
+static uu_avl_pool_t *composed_prop_pool;
+
+/*
+ * Pool for lists of errors in the internal representation.
+ */
+static uu_list_pool_t *inmem_errors_pool;
+
+/*
+ * Pool for trees of pg_pattern info structures (ptrn_info_t).
+ */
+static uu_avl_pool_t *ptrn_info_pool;
+
+/*
+ * Pool for lists of template errors in the libscf representation.
+ */
+static uu_list_pool_t *tv_errors_pool;
+
+/*
+ * Property name prefixes for constraints and values.
+ */
+static const char *constraint_prefixes[] = {
+ SCF_PROPERTY_TM_CONSTRAINT_NAME,
+ SCF_PROPERTY_TM_CONSTRAINT_RANGE,
+ NULL
+};
+static const char *value_prefixes[] = {
+ SCF_PROPERTY_TM_VALUE_PREFIX,
+ NULL
+};
+
+/*
+ * Function to compare two composed_pg structures.
+ */
+/* ARGSUSED2 */
+static int
+composed_pg_compare(const void *left, const void *right, void *unused)
+{
+ composed_pg_t *l = (composed_pg_t *)left;
+ composed_pg_t *r = (composed_pg_t *)right;
+ int rc;
+
+ if ((rc = strcmp(l->cpg_name, r->cpg_name)) == 0) {
+ rc = strcmp(l->cpg_type, r->cpg_type);
+ }
+ return (rc);
+}
+
+/* ARGSUSED2 */
+static int
+composed_prop_compare(const void *left, const void *right, void *unused)
+{
+ property_t *l = (property_t *)left;
+ property_t *r = (property_t *)right;
+
+ return (strcmp(l->sc_property_name, r->sc_property_name));
+}
+
+static composed_pg_t *
+composed_pg_create()
+{
+ composed_pg_t *cpg;
+
+ cpg = safe_malloc(sizeof (*cpg));
+ uu_avl_node_init(cpg, &cpg->cpg_node, composed_pg_pool);
+ return (cpg);
+}
+
+static void
+composed_pg_destroy(composed_pg_t *cpg)
+{
+ void *marker = NULL;
+ pgroup_t *pg;
+
+ if (cpg == NULL)
+ return;
+ /* Tear down composed property tree if we have one. */
+ if ((cpg->cpg_composed_props != NULL)) {
+ while (uu_avl_teardown(cpg->cpg_composed_props, &marker) !=
+ NULL) {
+ /*
+ * Nothing to do other than getting the property
+ * out of the list. This cleans up the property's
+ * uu_avl_node.
+ */
+ }
+ uu_avl_destroy(cpg->cpg_composed_props);
+ }
+
+ /* Clean up any pgroup_t references to us. */
+ if ((pg = cpg->cpg_instance_pg) != NULL) {
+ assert((pg->sc_pgroup_composed == NULL) ||
+ (pg->sc_pgroup_composed == cpg));
+ pg->sc_pgroup_composed = NULL;
+ }
+
+ uu_avl_node_fini(cpg, &cpg->cpg_node, composed_pg_pool);
+ free(cpg);
+}
+
+/*
+ * Walk the property group at pg, and add its properties to the AVL tree at
+ * tree.
+ */
+static void
+grow_props_tree(pgroup_t *pg, uu_avl_t *tree)
+{
+ uu_avl_index_t marker;
+ property_t *prop;
+
+ for (prop = uu_list_first(pg->sc_pgroup_props);
+ prop != NULL;
+ prop = uu_list_next(pg->sc_pgroup_props, prop)) {
+ if (uu_avl_find(tree, prop, NULL, &marker) == NULL) {
+ /*
+ * If there was no match, insert the property into
+ * the tree. If we do get a match, there is
+ * nothing to do. That is because we rely on our
+ * caller to process the instance properties first,
+ * and the instance properties override the service
+ * properties.
+ */
+ uu_avl_insert(tree, prop, marker);
+ }
+ }
+}
+
+/*
+ * The composed properties are stored in a uu_avl_tree. First we populate
+ * the tree with properties from the instance level property group. Then,
+ * we'll add the properties from the service level property group.
+ */
+static void
+compose_props(composed_pg_t *cpg)
+{
+ uu_avl_t *tree;
+
+ tree = uu_avl_create(composed_prop_pool, cpg, TMPL_DEBUG_TREE);
+ if (tree == NULL) {
+ uu_die(gettext("composed_pool tree creation failed: %s\n"),
+ uu_strerror(uu_error()));
+ }
+ cpg->cpg_composed_props = tree;
+
+ /*
+ * compose_props() is only called when there is both an instance
+ * and a service definition of the property group. This implies
+ * that neither cpg->cpg_instance_pg nor cpg->cpg_service_pg can be
+ * NULL.
+ */
+ /*
+ * First add instance properties to the tree.
+ */
+ assert(cpg->cpg_instance_pg != NULL);
+ grow_props_tree(cpg->cpg_instance_pg, tree);
+
+ /*
+ * Add service properties to the tree.
+ */
+ assert(cpg->cpg_service_pg != NULL);
+ grow_props_tree(cpg->cpg_service_pg, tree);
+}
+
+/*
+ * This function is a utility for build_composed_instance().
+ */
+static void
+build_composed_property_groups(entity_t *inst, uu_avl_t *tree)
+{
+ composed_pg_t *cpg;
+ uu_avl_index_t marker;
+ composed_pg_t *match;
+ pgroup_t *pg;
+ entity_t *svc;
+
+ /* First capture the instance property groups. */
+ for (pg = uu_list_first(inst->sc_pgroups);
+ pg != NULL;
+ pg = uu_list_next(inst->sc_pgroups, pg)) {
+ cpg = composed_pg_create();
+ cpg->cpg_name = pg->sc_pgroup_name;
+ cpg->cpg_type = pg->sc_pgroup_type;
+ cpg->cpg_instance_pg = pg;
+ match = uu_avl_find(tree, cpg, NULL, &marker);
+ /* Since we do the instance first, there should be no match. */
+ assert(match == NULL);
+ uu_avl_insert(tree, cpg, marker);
+ pg->sc_pgroup_composed = cpg;
+ }
+
+ /* Now capture the service property groups. */
+ svc = inst->sc_parent;
+ cpg = NULL;
+ for (pg = uu_list_first(svc->sc_pgroups);
+ pg != NULL;
+ pg = uu_list_next(svc->sc_pgroups, pg)) {
+ if (cpg == NULL)
+ cpg = composed_pg_create();
+ cpg->cpg_name = pg->sc_pgroup_name;
+ cpg->cpg_type = pg->sc_pgroup_type;
+ cpg->cpg_service_pg = pg;
+ match = uu_avl_find(tree, cpg, NULL, &marker);
+ if (match == NULL) {
+ uu_avl_insert(tree, cpg, marker);
+ /* Get new composed_pg_t next at top of loop. */
+ cpg = NULL;
+ } else {
+ /*
+ * Already have a composed_pg from instance
+ * processing. Just add the pointer to the service
+ * pg and compose the properties.
+ */
+ match->cpg_service_pg = pg;
+ compose_props(match);
+ }
+ }
+ if (cpg != NULL)
+ composed_pg_destroy(cpg);
+}
+
+static void
+build_composed_instance(entity_t *inst)
+{
+ uu_avl_t *tree;
+
+ assert(inst->sc_etype == SVCCFG_INSTANCE_OBJECT);
+
+ if (inst->sc_u.sc_instance.sc_composed == NULL) {
+ tree = uu_avl_create(composed_pg_pool, inst, TMPL_DEBUG_TREE);
+ if (tree == NULL) {
+ uu_die(gettext("composed_instance tree creation "
+ "failed: %s\n"), uu_strerror(uu_error()));
+ }
+ inst->sc_u.sc_instance.sc_composed = tree;
+ }
+ build_composed_property_groups(inst,
+ inst->sc_u.sc_instance.sc_composed);
+}
+
+static void
+demolish_composed_instance(entity_t *inst)
+{
+ composed_pg_t *cpg;
+ void *marker = NULL;
+ uu_avl_t *tree;
+
+ tree = inst->sc_u.sc_instance.sc_composed;
+ if (tree == NULL)
+ return;
+
+ marker = NULL;
+ while ((cpg = uu_avl_teardown(tree, &marker)) != NULL) {
+ composed_pg_destroy(cpg);
+ }
+ uu_avl_destroy(tree);
+
+ inst->sc_u.sc_instance.sc_composed = NULL;
+}
+/*
+ * Return the number of values in prop.
+ */
+static size_t
+count_prop_values(property_t *prop)
+{
+ return (uu_list_numnodes(prop->sc_property_values));
+}
+
+static int
+is_numeric_type(scf_type_t type)
+{
+ if (type == SCF_TYPE_BOOLEAN)
+ return (1);
+ if (type == SCF_TYPE_COUNT)
+ return (1);
+ if (type == SCF_TYPE_INTEGER)
+ return (1);
+ return (0);
+}
+
+static pg_type_t
+pgroup_type(pgroup_t *pg)
+{
+ if (strcmp(pg->sc_pgroup_type, SCF_GROUP_TEMPLATE_PG_PATTERN) == 0)
+ return (PG_PATTERN_PG);
+ if (strcmp(pg->sc_pgroup_type, SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0)
+ return (PROP_PATTERN_PG);
+ return (NORMAL_PG);
+}
+
+/*
+ * Search the property group at pg for a property named name. If the
+ * property group has a tree of composed properties, the tree will be
+ * searched for the property. Otherwise, the property group's linked list
+ * will be searched.
+ */
+static property_t *
+property_find(pgroup_t *pg, const char *name)
+{
+ composed_pg_t *cpg;
+ property_t look;
+
+ cpg = pg->sc_pgroup_composed;
+
+ if ((cpg == NULL) || (cpg->cpg_composed_props == NULL)) {
+ /* This is not a composed property group. */
+ return (internal_property_find(pg, name));
+ }
+
+ /*
+ * This is a composed property group, so look for the property in
+ * the AVL tree.
+ */
+ look.sc_property_name = (char *)name;
+ return (uu_avl_find(cpg->cpg_composed_props, &look, NULL, NULL));
+}
+
+/*
+ * Functions for manipulating the avalues structure.
+ */
+
+/*
+ * Free allocated memory referenced by the avalues structure. Then, free
+ * the structure itself.
+ */
+static void
+av_destroy(avalues_t *av)
+{
+ if (av == NULL)
+ return;
+ switch (av->av_type) {
+ case SCF_TYPE_BOOLEAN:
+ case SCF_TYPE_COUNT:
+ uu_free(av->av_v.av_unsigned);
+ break;
+ case SCF_TYPE_INTEGER:
+ uu_free(av->av_v.av_integer);
+ break;
+ default:
+ /*
+ * We don't need to free the strings that are referenced by
+ * av_string. The strings are held in propery_t structures
+ * that will be freed at a later time.
+ */
+ uu_free(av->av_v.av_string);
+ break;
+ }
+ uu_free(av);
+}
+/*
+ * Allocate and inialize an avalues structure. count represents the
+ * number of values the structure is expected to hold. type specifies how
+ * the consumer of the property values would like to see them represented.
+ * See comments for the av_get_values() more details on how type is used.
+ *
+ * The returned structure must be freed by calling av_destroy().
+ *
+ * NULL is returned if memory allocation fails.
+ */
+static avalues_t *
+av_create(size_t count, scf_type_t type)
+{
+ uint_t alloc_failed = 0;
+ avalues_t *av;
+
+ av = uu_zalloc(sizeof (*av));
+ if (av == NULL)
+ return (NULL);
+ av->av_count = count;
+ av->av_type = type;
+ switch (type) {
+ case SCF_TYPE_BOOLEAN:
+ case SCF_TYPE_COUNT:
+ av->av_v.av_unsigned = uu_zalloc(count * sizeof (uint64_t));
+ if (av->av_v.av_unsigned == NULL)
+ alloc_failed = 1;
+ break;
+ case SCF_TYPE_INTEGER:
+ av->av_v.av_integer = uu_zalloc(count * sizeof (int64_t));
+ if (av->av_v.av_integer == NULL)
+ alloc_failed = 1;
+ break;
+ default:
+ av->av_v.av_string = uu_zalloc(count * sizeof (char *));
+ if (av->av_v.av_string == NULL)
+ alloc_failed = 1;
+ }
+ if (alloc_failed) {
+ av_destroy(av);
+ return (NULL);
+ }
+ return (av);
+}
+
+/*
+ * Return the ith integer value in av.
+ */
+static int64_t
+av_get_integer(avalues_t *av, uint_t i)
+{
+ assert(av->av_type == SCF_TYPE_INTEGER);
+ assert(i < av->av_count);
+ return (*(av->av_v.av_integer + i));
+}
+
+/*
+ * Return the ith string value in av.
+ */
+static const char *
+av_get_string(avalues_t *av, uint_t i)
+{
+ assert(is_numeric_type(av->av_type) == 0);
+ assert(i < av->av_count);
+ return (*(av->av_v.av_string + i));
+}
+
+/*
+ * Return the ith unsigned value in av.
+ */
+static uint64_t
+av_get_unsigned(avalues_t *av, uint_t i)
+{
+ assert((av->av_type == SCF_TYPE_BOOLEAN) ||
+ (av->av_type == SCF_TYPE_COUNT));
+ assert(i < av->av_count);
+ return (*(av->av_v.av_unsigned + i));
+}
+
+/*
+ * Store the value in the ith slot of the av structure. If av is being
+ * used to store numeric values, the string at value will be converted to
+ * the appropriate numeric form.
+ */
+static tmpl_validate_status_t
+av_set_value(avalues_t *av, uint_t i, const char *value)
+{
+ char *endptr;
+ int64_t n;
+ uint64_t un;
+
+ if (is_numeric_type(av->av_type)) {
+ switch (av->av_type) {
+ case SCF_TYPE_BOOLEAN:
+ case SCF_TYPE_COUNT:
+ un = strtoull(value, &endptr, 0);
+ if ((endptr == value) || (*endptr != 0)) {
+ return (TVS_BAD_CONVERSION);
+ }
+ *(av->av_v.av_unsigned + i) = un;
+ break;
+ case SCF_TYPE_INTEGER:
+ n = strtoll(value, &endptr, 0);
+ if ((endptr == value) || (*endptr != 0)) {
+ return (TVS_BAD_CONVERSION);
+ }
+ *(av->av_v.av_integer + i) = n;
+ }
+ } else {
+ *(av->av_v.av_string + i) = value;
+ }
+
+ return (TVS_SUCCESS);
+}
+
+/*
+ * Find the property whose name is prop_name in the property group at pg.
+ * Read all the values of this property and return them in an avalues
+ * structure placing the address of the structure in *values. The caller
+ * must free the structure by calling av_destroy().
+ *
+ * The type parameter is used to indicate the type of information that the
+ * caller would like to consume. If it is one of the numeric types, the
+ * property value will be converted to the appropriate numeric type before
+ * placing it in the avalues struct. Decoding will be done before the
+ * conversion if necessary.
+ */
+static tmpl_validate_status_t
+av_get_values(pgroup_t *pg, const char *prop_name, scf_type_t type,
+ avalues_t **values)
+{
+ avalues_t *av;
+ uint_t i;
+ property_t *prop;
+ tmpl_validate_status_t rc;
+ value_t *v;
+
+ prop = property_find(pg, prop_name);
+ if (prop == NULL) {
+ return (TVS_NOMATCH);
+ }
+ assert(prop->sc_value_type == SCF_TYPE_ASTRING);
+ av = av_create(count_prop_values(prop), type);
+ if (av == NULL)
+ uu_die(emesg_nomem);
+
+ /* Collect the values. */
+ for ((v = uu_list_first(prop->sc_property_values)), i = 0;
+ v != NULL;
+ (v = uu_list_next(prop->sc_property_values, v)), i++) {
+ assert(i < av->av_count);
+ assert(v->sc_type == SCF_TYPE_ASTRING);
+ rc = av_set_value(av, i, v->sc_u.sc_string);
+ if (rc != TVS_SUCCESS) {
+ av_destroy(av);
+ return (rc);
+ }
+ }
+ *values = av;
+ return (TVS_SUCCESS);
+}
+
+/*
+ * Find the property in pg whose name is prop_name. Return a pointer to
+ * the first astring value in that property.
+ *
+ * NULL is returned if there is no property named prop_name or if it does
+ * not have an astring value.
+ */
+static const char *
+find_astring_value_in_pg(pgroup_t *pg, const char *prop_name)
+{
+ property_t *prop;
+ value_t *v;
+
+ prop = property_find(pg, prop_name);
+ if (prop == NULL)
+ return (NULL);
+ if (prop->sc_value_type != SCF_TYPE_ASTRING)
+ return (NULL);
+ v = uu_list_first(prop->sc_property_values);
+ if (v == NULL)
+ return (NULL);
+ assert(v->sc_type == SCF_TYPE_ASTRING);
+ return (v->sc_u.sc_string);
+}
+/*
+ * Find the first property value of type SCF_TYPE_COUNT in the property at
+ * prop. Return the value to count.
+ */
+static tmpl_validate_status_t
+find_count_value(property_t *prop, uint64_t *count)
+{
+ value_t *value;
+
+ assert(prop->sc_value_type == SCF_TYPE_COUNT);
+ value = uu_list_first(prop->sc_property_values);
+ if (value == NULL)
+ return (TVS_NOMATCH);
+ *count = value->sc_u.sc_count;
+ return (TVS_SUCCESS);
+}
+
+/*
+ * pattern is a property group representing a pg_pattern or a
+ * prop_pattern. This function returns the name specification from the
+ * pg_pattern or prop_pattern.
+ */
+static const char *
+find_name_specification(pgroup_t *pattern)
+{
+ return (find_astring_value_in_pg(pattern, SCF_PROPERTY_TM_NAME));
+}
+
+/*
+ * pattern is a property group representing a pg_pattern or a prop_pattern.
+ * This function returns the type specification from the pg_pattern or
+ * prop_pattern.
+ */
+static const char *
+find_type_specification(pgroup_t *pattern)
+{
+ return (find_astring_value_in_pg(pattern, SCF_PROPERTY_TM_TYPE));
+}
+
+/*
+ * Find the FMRI of the restarter for the entity, e. The restarter is the
+ * value of the "restarter" property in the "general" property group.
+ */
+static const char *
+find_restarter(entity_t *e)
+{
+ pgroup_t *pg;
+ property_t *prop;
+ value_t *v;
+
+ pg = internal_pgroup_find(e, scf_pg_general, scf_group_framework);
+ if (pg != NULL) {
+ prop = property_find(pg, SCF_PROPERTY_RESTARTER);
+ if ((prop != NULL) && (prop->sc_value_type == SCF_TYPE_FMRI)) {
+ v = uu_list_first(prop->sc_property_values);
+ if (v != NULL)
+ return (v->sc_u.sc_string);
+ }
+ }
+
+ /*
+ * Didn't find the restarter.
+ */
+ return (NULL);
+}
+
+/*
+ * prop_pattern points to a prop_pattern. This function finds the
+ * cardinality specification in the prop_pattern and returns the minimum
+ * and maximum values of the cardinality.
+ *
+ * Returns TVS_NOMATCH if either the cardinality minimum or maximum are
+ * missing.
+ */
+static tmpl_validate_status_t
+get_cardinality(pgroup_t *prop_pattern, uint64_t *min, uint64_t *max)
+{
+ property_t *prop;
+ tmpl_validate_status_t rc;
+
+ assert(strcmp(prop_pattern->sc_pgroup_type,
+ SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0);
+
+ prop = property_find(prop_pattern,
+ SCF_PROPERTY_TM_CARDINALITY_MIN);
+ if (prop == NULL)
+ return (TVS_NOMATCH);
+ rc = find_count_value(prop, min);
+ if (rc != TVS_SUCCESS)
+ return (rc);
+
+ prop = property_find(prop_pattern,
+ SCF_PROPERTY_TM_CARDINALITY_MAX);
+ if (prop == NULL)
+ return (TVS_NOMATCH);
+ rc = find_count_value(prop, max);
+
+ return (rc);
+}
+
+/*
+ * Ranges are represented as ASTRING values in the property at range_prop.
+ * The minimum and maximum of the range are separated by a comma.
+ *
+ * range_prop can contain multiple range values, so we return a pointer to
+ * an allocated array of range_t in ranges. This array must be freed by
+ * the caller using free(). count receives the number of range_t
+ * structures that are allocated.
+ *
+ * type tells us whether the range values should be treated as signed or
+ * unsigned. It must be SCF_TYPE_COUNT or SCF_TYPE_INTEGER.
+ */
+static tmpl_validate_status_t
+get_ranges(property_t *range_prop, scf_type_t type, range_t **ranges,
+ uint_t *count)
+{
+ char *endptr;
+ char *endptr2;
+ range_t *r;
+ value_t *value;
+
+ *count = uu_list_numnodes(range_prop->sc_property_values);
+ assert(*count != 0);
+ r = safe_malloc(*count * sizeof (*r));
+ *ranges = r;
+ for (value = uu_list_first(range_prop->sc_property_values);
+ value != NULL;
+ value = uu_list_next(range_prop->sc_property_values, value)) {
+ assert(value->sc_type == SCF_TYPE_ASTRING);
+
+ /* First get the minimum */
+ errno = 0;
+ if (type == SCF_TYPE_INTEGER) {
+ r->rng_u.rng_signed.rng_min =
+ strtoll(value->sc_u.sc_string, &endptr, 0);
+ } else {
+ r->rng_u.rng_unsigned.rng_min =
+ strtoull(value->sc_u.sc_string, &endptr, 0);
+ }
+ if ((errno != 0) || (endptr == value->sc_u.sc_string))
+ goto badtemplate;
+ if (*endptr != ',')
+ goto badtemplate;
+
+ /* Now get the maximum */
+ endptr++;
+ if (type == SCF_TYPE_INTEGER) {
+ r->rng_u.rng_signed.rng_max =
+ strtoll(endptr, &endptr2, 0);
+ } else {
+ r->rng_u.rng_unsigned.rng_max =
+ strtoull(endptr, &endptr2, 0);
+ }
+ if ((errno != 0) || (endptr2 == endptr) ||
+ (*endptr2 != 0))
+ goto badtemplate;
+ r++;
+ }
+
+ return (TVS_SUCCESS);
+
+badtemplate:
+ free(*ranges);
+ *ranges = NULL;
+ return (TVS_BAD_TEMPLATE);
+}
+
+static tv_errors_t *
+tv_errors_create(const char *fmri)
+{
+ tv_errors_t *ste;
+
+ ste = safe_malloc(sizeof (*ste));
+ uu_list_node_init(ste, &ste->tve_node, tv_errors_pool);
+ ste->tve_errors = _scf_create_errors(fmri, 1);
+ if (ste->tve_errors == NULL)
+ uu_die(emesg_nomem);
+
+ return (ste);
+}
+
+static void
+destroy_scf_errors(tv_errors_t *ste)
+{
+ scf_tmpl_errors_destroy(ste->tve_errors);
+ uu_list_node_fini(ste, &ste->tve_node, tv_errors_pool);
+ free(ste);
+}
+
+/*
+ * Given a property group and the name of a property within that property
+ * group, generate the name of the property group that holds the
+ * prop_pattern information for the property. The address of the generated
+ * name is returned to prop_pattern_pg_name. The memory holding the
+ * generated name must be freed using uu_free().
+ */
+static tmpl_validate_status_t
+gen_prop_pattern_pg_name(pgroup_t *pg_pattern, const char *prop_name,
+ char **prop_pattern_pg_name)
+{
+ ssize_t limit;
+ char *name;
+ size_t prefix_size;
+ const char *unique;
+
+ limit = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
+ assert(limit > 0);
+
+ /* Get the unique part of the pg_pattern's property group name. */
+ prefix_size = strlen(SCF_PG_TM_PG_PAT_BASE);
+ assert(strncmp(pg_pattern->sc_pgroup_name, SCF_PG_TM_PG_PAT_BASE,
+ prefix_size) == 0);
+ unique = pg_pattern->sc_pgroup_name + prefix_size;
+
+ /* Construct the prop pattern property group name. */
+ *prop_pattern_pg_name = NULL;
+ name = uu_zalloc(limit);
+ if (name == NULL)
+ uu_die(emesg_nomem);
+ if (snprintf(name, limit, "%s%s_%s", SCF_PG_TM_PROP_PATTERN_PREFIX,
+ unique, prop_name) >= limit) {
+ uu_free(name);
+ return (TVS_BAD_TEMPLATE);
+ }
+ *prop_pattern_pg_name = name;
+ return (TVS_SUCCESS);
+}
+
+/*
+ * Error message printing functions:
+ */
+
+/*
+ * Flags for use by im_perror_item.
+ */
+#define IPI_NOT_FIRST 0x1 /* Not first item to be displayed. */
+
+/*
+ * Print a single item of information about a validation failure. This
+ * function takes care of printing the appropriate decoration before the
+ * first item and between subsequent items.
+ *
+ * Parameters:
+ * out Stream to receive the output.
+ * desc Text describing the items
+ * item Address of the item to be displayed
+ * type Type of the item
+ * flags Used by im_perror_item to keep track of where it
+ * is. Caller should set flags to 0 before calling
+ * this function with the first item.
+ */
+static void
+im_perror_item(FILE *out, const char *desc, void *item, scf_type_t type,
+ int *flags)
+{
+ const char *cp;
+ const char *first_sep;
+ int64_t ival;
+ const char *subsequent_sep;
+ uint64_t uval;
+
+ /* Nothing to print if item is NULL. */
+ if (item == NULL)
+ return;
+
+ assert(type != SCF_TYPE_INVALID);
+
+ /* Establish separators for environment. */
+ if (est->sc_cmd_flags & SC_CMD_IACTIVE) {
+ /* Interactive mode - make messages readable */
+ first_sep = ":\n\t";
+ subsequent_sep = "\n\t";
+ } else {
+ /* Non-interactive - one line messages. */
+ first_sep = ": ";
+ subsequent_sep = "; ";
+ }
+
+ /* Print separator and description */
+ if (*flags & IPI_NOT_FIRST) {
+ (void) fprintf(out, subsequent_sep);
+ } else {
+ (void) fprintf(out, first_sep);
+ *flags |= IPI_NOT_FIRST;
+ }
+ (void) fprintf(out, "%s=", desc);
+
+ switch (type) {
+ case SCF_TYPE_BOOLEAN:
+ uval = *((uint64_t *)item);
+ if (uval) {
+ (void) fprintf(out, "\"%s\"", gettext("true"));
+ } else {
+ (void) fprintf(out, "\"%s\"", gettext("false"));
+ }
+ break;
+ case SCF_TYPE_COUNT:
+ uval = *((uint64_t *)item);
+ (void) fprintf(out, "%" PRIu64, uval);
+ break;
+ case SCF_TYPE_INTEGER:
+ ival = *((int64_t *)item);
+ (void) fprintf(out, "%" PRIi64, ival);
+ break;
+ default:
+ /*
+ * Treat everything else as a string, but escape any
+ * internal quotes.
+ */
+ (void) fputc('\"', out);
+ cp = (const char *)item;
+ while (*cp != 0) {
+ if (*cp == '\"') {
+ (void) fprintf(out, "\\\"");
+ } else {
+ (void) fputc(*cp, out);
+ }
+ cp++;
+ }
+ (void) fputc('\"', out);
+ break;
+ }
+}
+
+/*
+ * Print erroneous FMRI.
+ */
+static void
+im_perror_fmri(FILE *out, im_tmpl_error_t *i, int *flags)
+{
+ if (i->ite_entity != NULL) {
+ im_perror_item(out, "FMRI", (void *)i->ite_entity->sc_fmri,
+ SCF_TYPE_FMRI, flags);
+ }
+}
+
+/*
+ * Print erroneous property group name.
+ */
+static void
+im_perror_pg_name(FILE *out, im_tmpl_error_t *i, int *flags)
+{
+ if (i->ite_pg != NULL) {
+ im_perror_item(out, gettext("Property group"),
+ (void *)i->ite_pg->sc_pgroup_name, SCF_TYPE_ASTRING,
+ flags);
+ }
+}
+
+/*
+ * If srcflag is 1, print the template source of the pg_pattern or
+ * prop_pattern at pattern. Always print the name and type of the pattern.
+ */
+static void
+im_perror_pattern_info(FILE *out, pgroup_t *pattern, int *flags, int srcflag)
+{
+ void *c;
+ const char *name_string;
+ const char *type_string;
+
+ if (pattern == NULL)
+ return;
+ switch (pgroup_type(pattern)) {
+ case PG_PATTERN_PG:
+ name_string = gettext("pg_pattern name");
+ type_string = gettext("pg_pattern type");
+ break;
+ case PROP_PATTERN_PG:
+ name_string = gettext("prop_pattern name");
+ type_string = gettext("prop_pattern type");
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+ if (srcflag) {
+ im_perror_item(out, gettext("Template source"),
+ (void *)pattern->sc_parent->sc_fmri, SCF_TYPE_FMRI, flags);
+ }
+ c = (void *)find_name_specification(pattern);
+ im_perror_item(out, name_string,
+ (c == NULL) ? "" : c, SCF_TYPE_ASTRING, flags);
+ c = (void *)find_type_specification(pattern);
+ im_perror_item(out, type_string,
+ (c == NULL) ? "" : c, SCF_TYPE_ASTRING, flags);
+}
+
+/*
+ * Print information about the template specifications that were violated,
+ * so that the user can find the specification.
+ */
+static void
+im_perror_template_info(FILE *out, im_tmpl_error_t *i, int *flags)
+{
+ pgroup_t *pg_pattern = i->ite_pg_pattern;
+ pgroup_t *prop_pattern = i->ite_prop_pattern;
+ int srcflag = 1;
+
+ if (pg_pattern != NULL) {
+ im_perror_pattern_info(out, pg_pattern, flags, srcflag);
+ srcflag = 0;
+ }
+ if (prop_pattern != NULL) {
+ im_perror_pattern_info(out, prop_pattern, flags, srcflag);
+ }
+}
+
+/* Print error message for TVS_BAD_CONVERSION errors. */
+static void
+im_perror_bad_conversion(FILE *out, im_tmpl_error_t *i, const char *prefix)
+{
+ int flags = 0;
+
+ (void) fprintf(out, gettext("%sUnable to convert property value"),
+ prefix);
+ im_perror_fmri(out, i, &flags);
+ im_perror_pg_name(out, i, &flags);
+ im_perror_item(out, gettext("Property"),
+ (void *)i->ite_prop->sc_property_name, SCF_TYPE_ASTRING, &flags);
+ im_perror_template_info(out, i, &flags);
+ (void) fputc('\n', out);
+}
+
+/* Print error message for TVS_BAD_TEMPLATE errors. */
+static void
+im_perror_bad_template(FILE *out, im_tmpl_error_t *i, const char *prefix)
+{
+ int flags = 0;
+
+ assert(i->ite_einfo.ei_type == EIT_BAD_TEMPLATE);
+ (void) fprintf(out, gettext("%sInvalid template - %s"), prefix,
+ i->ite_einfo.ei_u.ei_bad_template.ei_reason);
+ im_perror_fmri(out, i, &flags);
+ im_perror_template_info(out, i, &flags);
+ (void) fputc('\n', out);
+}
+
+/*
+ * Print error message for TVS_INVALID_TYPE_SPECIFICATION errors. This
+ * error occurs if a prop_pattern has an invalid type specification. Thus,
+ * it is an indication of an invalid template rather than a violation of a
+ * template.
+ */
+static void
+im_perror_invalid_type(FILE *out, im_tmpl_error_t *i, const char *prefix)
+{
+ int flags = 0;
+ const char *prop_pattern_name;
+
+ (void) fprintf(out, gettext("%sInvalid type in prop_pattern"), prefix);
+ im_perror_pg_name(out, i, &flags);
+ if (i->ite_prop_pattern != NULL) {
+ prop_pattern_name =
+ find_name_specification(i->ite_prop_pattern);
+ im_perror_item(out, gettext("prop_pattern name"),
+ (void *)prop_pattern_name, SCF_TYPE_ASTRING, &flags);
+ }
+ im_perror_template_info(out, i, &flags);
+ (void) fputc('\n', out);
+}
+
+/*
+ * Print error message for TVS_MISSING_PG_TYPE errors. In this case the
+ * template specifies a type, but the property group itself has no type.
+ */
+static void
+im_perror_missing_pg_type(FILE *out, im_tmpl_error_t *i, const char *prefix)
+{
+ int flags = 0;
+ const char *type_spec;
+
+ (void) fprintf(out, gettext("%sProperty group has no type"), prefix);
+ im_perror_fmri(out, i, &flags);
+ im_perror_pg_name(out, i, &flags);
+ if (i->ite_pg_pattern != NULL) {
+ type_spec = find_type_specification(i->ite_pg_pattern);
+ im_perror_item(out, gettext("Type specified in pg_pattern"),
+ (void *)type_spec, SCF_TYPE_ASTRING, &flags);
+ }
+ (void) fputc('\n', out);
+}
+
+/*
+ * Print error message for TVS_MISSING_TYPE_SPECIFICATION errors. A
+ * property group has a "required" attribute of true, but it does not have
+ * a type specification.
+ */
+static void
+im_perror_missing_type(FILE *out, im_tmpl_error_t *i, const char *prefix)
+{
+ int flags = 0;
+ const char *pg_pattern_name;
+
+ (void) fprintf(out, gettext("%sPg_pattern with true required attribute "
+ "is missing the type attribute"), prefix);
+ im_perror_fmri(out, i, &flags);
+ if (i->ite_pg_pattern != NULL) {
+ pg_pattern_name = find_name_specification(i->ite_pg_pattern);
+ im_perror_item(out, gettext("Pg_pattern name"),
+ (void *)pg_pattern_name, SCF_TYPE_ASTRING, &flags);
+ }
+ im_perror_template_info(out, i, &flags);
+ (void) fputc('\n', out);
+}
+
+static void
+im_tmpl_error_print(FILE *out, im_tmpl_error_t *ite, const char *prefix)
+{
+ switch (ite->ite_type) {
+ case TVS_BAD_CONVERSION:
+ im_perror_bad_conversion(out, ite, prefix);
+ break;
+ case TVS_BAD_TEMPLATE:
+ im_perror_bad_template(out, ite, prefix);
+ break;
+ case TVS_INVALID_TYPE_SPECIFICATION:
+ im_perror_invalid_type(out, ite, prefix);
+ break;
+ case TVS_MISSING_PG_TYPE:
+ im_perror_missing_pg_type(out, ite, prefix);
+ break;
+ case TVS_MISSING_TYPE_SPECIFICATION:
+ im_perror_missing_type(out, ite, prefix);
+ break;
+ case TVS_NOMATCH:
+ /*
+ * TVS_NOMATCH should be handled where it occurs. Thus,
+ * there are no error messages associated with it.
+ */
+ assert(0);
+ abort();
+ break;
+ case TVS_SUCCESS:
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+static char *
+int64_to_str(int64_t i)
+{
+ char *c;
+ const char *fmt;
+ int size;
+
+ fmt = "%" PRIi64;
+ size = snprintf(NULL, 0, fmt, i) + 1;
+ c = safe_malloc(size);
+ (void) snprintf(c, size, fmt, i);
+ return (c);
+}
+
+static char *
+uint64_to_str(uint64_t u)
+{
+ char *c;
+ const char *fmt;
+ int size;
+
+ fmt = "%" PRIu64;
+ size = snprintf(NULL, 0, fmt, u) + 1;
+ c = safe_malloc(size);
+ (void) snprintf(c, size, fmt, u);
+ return (c);
+}
+
+/*
+ * Convert the value to a string. The returned value must be freed using
+ * free(3C).
+ */
+static const char *
+value_to_string(value_t *v)
+{
+ char *c;
+
+ if (is_numeric_type(v->sc_type)) {
+ switch (v->sc_type) {
+ case SCF_TYPE_BOOLEAN:
+ if (v->sc_u.sc_count == 0) {
+ c = gettext("false");
+ } else {
+ c = gettext("true");
+ }
+ break;
+ case SCF_TYPE_COUNT:
+ c = uint64_to_str(v->sc_u.sc_count);
+ return (c);
+ case SCF_TYPE_INTEGER:
+ c = int64_to_str(v->sc_u.sc_integer);
+ return (c);
+ }
+ } else {
+ c = v->sc_u.sc_string;
+ }
+
+ return (safe_strdup(c));
+}
+
+/*
+ * Subscripts for common error data.
+ */
+#define ED_PG_NAME 0
+#define ED_PROP_NAME 1
+#define ED_TMPL_FMRI 2
+#define ED_TMPL_PG_NAME 3
+#define ED_TMPL_PG_TYPE 4
+#define ED_TMPL_PROP_NAME 5
+#define ED_TMPL_PROP_TYPE 6
+#define ED_COUNT 7
+
+/*
+ * This function converts the error information specified by the function
+ * parameters. It converts it to form needed by _scf_tmpl_add_error().
+ * _scf_tmpl_add_error() requires that the error information be in the form
+ * of allocated strings that can be freed when it is done with them. Thus,
+ * the bulk of this function is devoted to producing those allocated
+ * strings.
+ *
+ * Once the strings are ready, we call _scf_tmpl_add_error() to add an
+ * new error structure to errs.
+ */
+static int
+add_scf_error(tmpl_errors_t *errs, scf_tmpl_error_type_t ec,
+ pgroup_t *pg_pattern, pgroup_t *pg, pgroup_t *prop_pattern,
+ property_t *prop, value_t *val, error_info_t *einfo)
+{
+ const char *actual = NULL;
+ char *c;
+ pgroup_t *conflict;
+ const char *ed[ED_COUNT];
+ const char *ev1 = NULL;
+ const char *ev2 = NULL;
+ int i;
+ scf_type_t prop_type;
+ int rc;
+
+ (void) memset(ed, 0, sizeof (ed));
+
+ /* Set values that are common to most error types. */
+ if (pg != NULL) {
+ ed[ED_PG_NAME] = pg->sc_pgroup_name;
+ }
+ if (prop != NULL) {
+ ed[ED_PROP_NAME] = prop->sc_property_name;
+ }
+ if (pg_pattern == NULL) {
+ if (prop_pattern != NULL) {
+ ed[ED_TMPL_FMRI] = prop_pattern->sc_parent->sc_fmri;
+ }
+ } else {
+ ed[ED_TMPL_FMRI] = pg_pattern->sc_parent->sc_fmri;
+ ed[ED_TMPL_PG_NAME] = find_name_specification(pg_pattern);
+ ed[ED_TMPL_PG_TYPE] = find_type_specification(pg_pattern);
+ }
+ if (prop_pattern != NULL) {
+ ed[ED_TMPL_PROP_NAME] = find_name_specification(prop_pattern);
+ ed[ED_TMPL_PROP_TYPE] = find_type_specification(prop_pattern);
+ }
+
+ /*
+ * All of the strings that we've found must be strduped. This is
+ * so that scf_tmpl_errors_destroy() can free them. We cannot use
+ * the flag argument of _scf_create_errors() to indicate that the
+ * strings should not be freed. The flag argument is an all or
+ * nothing thing. In the code below we need to convert integers to
+ * strings, and this requires memory allocation. Since we have to
+ * allocate memory for that data, we need to allocate it for every
+ * thing.
+ */
+ for (i = 0; i < ED_COUNT; i++) {
+ if (ed[i] == NULL)
+ continue;
+ ed[i] = safe_strdup(ed[i]);
+ }
+
+ /* actual, ev1 and ev2 are error code specific. */
+ switch (ec) {
+ case SCF_TERR_CARDINALITY_VIOLATION:
+ assert(einfo != NULL);
+ assert(einfo->ei_type == EIT_CARDINALITY);
+ ev1 = uint64_to_str(einfo->ei_u.ei_cardinality.ei_min);
+ ev2 = uint64_to_str(einfo->ei_u.ei_cardinality.ei_max);
+ actual = uint64_to_str(einfo->ei_u.ei_cardinality.ei_count);
+ break;
+ case SCF_TERR_WRONG_PG_TYPE:
+ /* Specified type. */
+ if (pg_pattern != NULL) {
+ ev1 = find_type_specification(pg_pattern);
+ if (ev1 != NULL) {
+ ev1 = safe_strdup(ev1);
+ }
+ }
+ /* Actual type. */
+ if (pg != NULL) {
+ actual = pg->sc_pgroup_type;
+ if (actual != NULL) {
+ actual = safe_strdup(actual);
+ }
+ }
+ break;
+ case SCF_TERR_WRONG_PROP_TYPE:
+ assert(einfo->ei_type == EIT_PROP_TYPE);
+ prop_type = einfo->ei_u.ei_prop_type.ei_specified;
+ ev1 = safe_strdup(scf_type_to_string(prop_type));
+ prop_type = einfo->ei_u.ei_prop_type.ei_actual;
+ actual = safe_strdup(scf_type_to_string(prop_type));
+ break;
+ case SCF_TERR_VALUE_CONSTRAINT_VIOLATED:
+ actual = value_to_string(val);
+ break;
+ case SCF_TERR_MISSING_PG:
+ assert(einfo->ei_type == EIT_MISSING_PG);
+ ev1 = safe_strdup(einfo->ei_u.ei_missing_pg.ei_pg_name);
+ ev2 = safe_strdup(einfo->ei_u.ei_missing_pg.ei_pg_type);
+ break;
+ case SCF_TERR_MISSING_PROP:
+ assert(einfo->ei_type == EIT_MISSING_PROP);
+ ev1 = safe_strdup(einfo->ei_u.ei_missing_prop.ei_prop_name);
+ break;
+ case SCF_TERR_RANGE_VIOLATION:
+ assert(einfo->ei_type == EIT_RANGE);
+ if (einfo->ei_u.ei_range.ei_rtype == SCF_TYPE_COUNT) {
+ c = uint64_to_str(einfo->ei_u.ei_range.ei_uvalue);
+ } else {
+ c = int64_to_str(einfo->ei_u.ei_range.ei_ivalue);
+ }
+ actual = c;
+ break;
+ case SCF_TERR_PG_PATTERN_CONFLICT:
+ case SCF_TERR_PROP_PATTERN_CONFLICT:
+ case SCF_TERR_GENERAL_REDEFINE:
+ assert(einfo->ei_type == EIT_PATTERN_CONFLICT);
+ conflict = einfo->ei_u.ei_pattern_conflict.ei_pattern;
+ ev1 = safe_strdup(conflict->sc_parent->sc_fmri);
+ ev2 = find_name_specification(conflict);
+ if (ev2 != NULL)
+ ev2 = safe_strdup(ev2);
+ actual = find_type_specification(conflict);
+ if (actual != NULL)
+ actual = safe_strdup(actual);
+ break;
+ case SCF_TERR_INCLUDE_VALUES:
+ assert(einfo->ei_type == EIT_INCLUDE_VALUES);
+ ev1 = safe_strdup(einfo->ei_u.ei_inc_values.ei_type);
+ break;
+ case SCF_TERR_PG_PATTERN_INCOMPLETE:
+ case SCF_TERR_PROP_PATTERN_INCOMPLETE:
+ break;
+ default:
+ assert(0);
+ abort();
+ };
+
+ rc = _scf_tmpl_add_error(errs->te_cur_scf->tve_errors, ec,
+ ed[ED_PG_NAME], ed[ED_PROP_NAME], ev1, ev2, actual,
+ ed[ED_TMPL_FMRI], ed[ED_TMPL_PG_NAME], ed[ED_TMPL_PG_TYPE],
+ ed[ED_TMPL_PROP_NAME], ed[ED_TMPL_PROP_TYPE]);
+
+ return (rc);
+}
+
+/*
+ * Create and initialize a new im_tmpl_error structure and add it to the
+ * list of errors in errs. The rest of the parameters are used to
+ * initialize the im_tmpl_error structure.
+ */
+static tmpl_validate_status_t
+tmpl_errors_add_im(tmpl_errors_t *errs, tmpl_validate_status_t ec, entity_t *e,
+ pgroup_t *pg_pattern, pgroup_t *pg, pgroup_t *prop_pattern,
+ property_t *prop, value_t *val, error_info_t *einfo)
+{
+ im_tmpl_error_t *ite;
+ int result;
+
+ ite = uu_zalloc(sizeof (*ite));
+ if (ite == NULL)
+ uu_die(emesg_nomem);
+ uu_list_node_init(ite, &ite->ite_node, inmem_errors_pool);
+ ite->ite_type = ec;
+ ite->ite_entity = e;
+ ite->ite_pg = pg;
+ ite->ite_pg_pattern = pg_pattern;
+ ite->ite_prop = prop;
+ ite->ite_prop_pattern = prop_pattern;
+ ite->ite_value = val;
+ if (einfo != NULL)
+ ite->ite_einfo = *einfo;
+
+ result = uu_list_insert_after(errs->te_list, NULL, ite);
+ assert(result == 0);
+ return (TVS_SUCCESS);
+}
+
+/*
+ * pattern must point to a pg_pattern or a prop_pattern. This function
+ * finds the property named required and returns the property's value. If
+ * the property does not exist, false is return since it is the default.
+ */
+static int
+is_required(pgroup_t *pattern)
+{
+ property_t *required;
+ value_t *value;
+
+ assert((strcmp(pattern->sc_pgroup_type,
+ SCF_GROUP_TEMPLATE_PG_PATTERN) == 0) ||
+ (strcmp(pattern->sc_pgroup_type,
+ SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0));
+
+ required = property_find(pattern, SCF_PROPERTY_TM_REQUIRED);
+
+ /* Default if there is no required property is false. */
+ if (required == NULL)
+ return (0);
+
+ /* Retrieve the value of the required property. */
+ value = uu_list_first(required->sc_property_values);
+ if (value == NULL)
+ return (0);
+ if (value->sc_type == SCF_TYPE_BOOLEAN)
+ return (value->sc_u.sc_count == 0 ? 0 : 1);
+
+ /* No boolean property values, so return false. */
+ return (0);
+}
+
+/*
+ * Load the service's restarter instance and the global instance from the
+ * repository. This will allow us to use their templates in validating the
+ * service.
+ *
+ * There is no function to unload the general templates. The memory that
+ * is allocated by load_general_templates() will be freed automatically in
+ * internal_service_free() which is called by internal_bundle_free().
+ */
+static void
+load_general_templates(entity_t *svc)
+{
+ const char *restarter;
+ int is_global = 0;
+ int r;
+
+ assert(svc->sc_etype == SVCCFG_SERVICE_OBJECT);
+
+ /*
+ * If e is the global service, we only need to load the restarter.
+ */
+ if ((strcmp(svc->sc_fmri, SCF_INSTANCE_GLOBAL) == 0) ||
+ (strcmp(svc->sc_fmri, SCF_SERVICE_GLOBAL) == 0)) {
+ is_global = 1;
+ }
+
+ /*
+ * Load the templates for the service's restarter.
+ */
+ restarter = find_restarter(svc);
+ if (restarter == NULL)
+ restarter = SCF_SERVICE_STARTD;
+ if ((r = load_instance(restarter, "restarter",
+ &svc->sc_u.sc_service.sc_restarter)) != 0) {
+ /*
+ * During initial manifest import, restarter may
+ * not be in the repository yet. In this case we
+ * continue on without it.
+ */
+ if (r == ENOMEM)
+ uu_die(emesg_nomem);
+ else
+ svc->sc_u.sc_service.sc_restarter = NULL;
+ }
+ if (is_global == 0) {
+ if ((r = load_instance(SCF_INSTANCE_GLOBAL, "global",
+ &svc->sc_u.sc_service.sc_global)) != 0) {
+ /*
+ * During initial manifest import, global may not be in
+ * the repository yet.
+ */
+ if (r == ENOMEM)
+ uu_die(emesg_nomem);
+ else
+ svc->sc_u.sc_service.sc_global = NULL;
+ }
+ }
+}
+
+/*
+ * Load the instance specific restarter if one is declared.
+ *
+ * There is no corresponding unload_instance_restarter() function because
+ * it is not needed. The memory will be freed in internal_instance_free()
+ * when internal_bundle_free() is called.
+ */
+static void
+load_instance_restarter(entity_t *i)
+{
+ const char *restarter;
+ int r;
+
+ assert(i->sc_etype == SVCCFG_INSTANCE_OBJECT);
+
+ restarter = find_restarter(i);
+ if (restarter == NULL) {
+ /* No instance specific restarter */
+ return;
+ }
+ r = load_instance(restarter, "instance_restarter",
+ &i->sc_u.sc_instance.sc_instance_restarter);
+ if (r != 0) {
+ /*
+ * During initial manifest import, the restarter may not be
+ * in the repository yet. In this case we continue on
+ * without it.
+ */
+ if (r == ENOMEM)
+ uu_die(emesg_nomem);
+ }
+}
+
+/*
+ * Find the next property after current in the property group at pg. If
+ * the property group contains a tree of composed properties, that tree is
+ * walked. Otherwise, we walk through the uu_list at sc_pgroup_props.
+ */
+static property_t *
+next_property(pgroup_t *pg, property_t *current)
+{
+ composed_pg_t *cpg;
+ property_t *prop;
+
+ cpg = pg->sc_pgroup_composed;
+ if ((cpg != NULL) && (cpg->cpg_composed_props != NULL)) {
+ /* Walk through composed property list. */
+ if (current) {
+ prop = uu_avl_next(cpg->cpg_composed_props, current);
+ } else {
+ prop = uu_avl_first(cpg->cpg_composed_props);
+ }
+ } else {
+ /* No composition available, so walk the list of properties */
+ if (current) {
+ prop = uu_list_next(pg->sc_pgroup_props, current);
+ } else {
+ prop = uu_list_first(pg->sc_pgroup_props);
+ }
+ }
+
+ return (prop);
+}
+
+static ptrn_info_t *
+ptrn_info_create(pgroup_t *pat)
+{
+ entity_t *e;
+ ptrn_info_t *info;
+ composed_pg_t *match;
+ composed_pg_t cpg;
+
+ info = safe_malloc(sizeof (*info));
+
+ switch (pgroup_type(pat)) {
+ case PG_PATTERN_PG:
+ info->pi_ptrn_type = PG_PATTERN;
+ break;
+ case PROP_PATTERN_PG:
+ info->pi_ptrn_type = PROP_PATTERN;
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+ info->pi_ptrnpg = pat;
+ info->pi_name = find_name_specification(pat);
+ info->pi_name = EMPTY_TO_NULL(info->pi_name);
+ info->pi_type = find_type_specification(pat);
+ info->pi_type = EMPTY_TO_NULL(info->pi_type);
+ if (info->pi_ptrn_type == PG_PATTERN) {
+ info->pi_target = find_astring_value_in_pg(pat,
+ SCF_PROPERTY_TM_TARGET);
+ if (info->pi_target == NULL)
+ info->pi_target = SCF_TM_TARGET_THIS;
+ }
+ if (info->pi_ptrn_type == PROP_PATTERN) {
+ info->pi_pgp_name = find_astring_value_in_pg(pat,
+ SCF_PROPERTY_TM_PG_PATTERN);
+ assert((info->pi_pgp_name != NULL) &&
+ (*(info->pi_pgp_name) != 0));
+
+ /*
+ * Find the property group that defines the pg_pattern that
+ * holds this prop_pattern.
+ */
+ e = pat->sc_parent;
+ if (e->sc_etype == SVCCFG_INSTANCE_OBJECT) {
+ (void) memset(&cpg, 0, sizeof (cpg));
+ cpg.cpg_name = info->pi_pgp_name;
+ cpg.cpg_type = SCF_GROUP_TEMPLATE_PG_PATTERN;
+ match = uu_avl_find(e->sc_u.sc_instance.sc_composed,
+ &cpg, NULL, NULL);
+ assert(match != NULL);
+ info->pi_enc_pgp = CPG2PG(match);
+ } else {
+ info->pi_enc_pgp = internal_pgroup_find(e,
+ info->pi_pgp_name, SCF_GROUP_TEMPLATE_PG_PATTERN);
+ }
+ assert(info->pi_enc_pgp != NULL);
+ }
+ uu_avl_node_init(info, &info->pi_link, ptrn_info_pool);
+ return (info);
+}
+
+static void
+ptrn_info_destroy(ptrn_info_t *info)
+{
+ if (info == NULL)
+ return;
+ uu_avl_node_fini(info, &info->pi_link, ptrn_info_pool);
+ free(info);
+}
+
+/*
+ * Walk through the property groups of the instance or service at e looking
+ * for definitions of pg_patterns or prop_patterns as specified by type.
+ * For each property group that matches type create a ptrn_info_t and add
+ * it to the avl tree at tree. If duplicates are found add an error entry
+ * to errs.
+ */
+static tmpl_validate_status_t
+gather_pattern(entity_t *e, ptrn_type_t type, uu_avl_t *tree,
+ tmpl_errors_t *errs)
+{
+ error_info_t einfo;
+ ptrn_info_t *info = NULL;
+ uu_avl_index_t marker;
+ ptrn_info_t *match;
+ pgroup_t *pg;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ const char *selector;
+
+ switch (type) {
+ case PG_PATTERN:
+ selector = SCF_GROUP_TEMPLATE_PG_PATTERN;
+ break;
+ case PROP_PATTERN:
+ selector = SCF_GROUP_TEMPLATE_PROP_PATTERN;
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ for (pg = uu_list_first(e->sc_pgroups);
+ pg != NULL;
+ pg = uu_list_next(e->sc_pgroups, pg)) {
+ if (strcmp(pg->sc_pgroup_type, selector) != 0) {
+ continue;
+ }
+ if (info != NULL) {
+ /* Get rid of old structure. */
+ ptrn_info_destroy(info);
+ }
+ info = ptrn_info_create(pg);
+ match = uu_avl_find(tree, info, NULL, &marker);
+ if (match == NULL) {
+ /* No match. Insert the info. */
+ uu_avl_insert(tree, info, marker);
+ info = NULL;
+ continue;
+ }
+
+ /* Got a match. Determine if it is a conflict. */
+ if ((info->pi_name == NULL) ||
+ (info->pi_type == NULL) ||
+ (match->pi_name == NULL) ||
+ (match->pi_type == NULL)) {
+ /* No conflicts if any wild cards. */
+ continue;
+ }
+
+ /*
+ * Name already matches, or we wouldn't have gotten
+ * here. Make sure that the type also matches.
+ */
+ if (strcmp(info->pi_type, match->pi_type) == 0) {
+ continue;
+ }
+
+ /*
+ * If we get to this point we have a conflict, and
+ * we need to generate the correct type of error.
+ */
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_PATTERN_CONFLICT;
+ einfo.ei_u.ei_pattern_conflict.ei_pattern =
+ match->pi_ptrnpg;
+ if (type == PG_PATTERN) {
+ rc = TVS_VALIDATION;
+ if (add_scf_error(errs, SCF_TERR_PG_PATTERN_CONFLICT,
+ info->pi_ptrnpg, NULL, NULL, NULL, NULL,
+ &einfo) != 0) {
+ /*
+ * If we can no longer accumulate
+ * errors, break out of the loop.
+ */
+ break;
+ }
+ } else {
+ /*
+ * Possible conflicting prop_pattern. See if the
+ * prop_patterns are declared in the same
+ * pg_pattern.
+ */
+ if ((info->pi_pgp_name == NULL) ||
+ (match->pi_pgp_name == NULL)) {
+ continue;
+ }
+ if (strcmp(info->pi_pgp_name, match->pi_pgp_name) != 0)
+ continue;
+
+ /* It is a real conflict. */
+ rc = TVS_VALIDATION;
+ if (add_scf_error(errs, SCF_TERR_PROP_PATTERN_CONFLICT,
+ info->pi_enc_pgp, NULL, info->pi_ptrnpg, NULL, NULL,
+ &einfo) != 0) {
+ /*
+ * If we can no longer accumulate
+ * errors, break out of the loop.
+ */
+ break;
+ }
+ }
+ }
+
+ ptrn_info_destroy(info);
+ return (rc);
+}
+
+/*
+ * Free the pg_iter structure.
+ */
+static void
+pg_iter_destroy(pg_iter_t *i)
+{
+ if (i == NULL)
+ return;
+
+ uu_free(i);
+}
+
+/*
+ * Create a property group iterator for the instance at e. This iterator
+ * will walk through the composed property groups of the instance. It will
+ * then step through the property groups of the instance's restarter and
+ * finally the global service. If you wish to iterate over a specific type
+ * of property group, set restriction to point the the desired type.
+ * Otherwise set restriction to NULL.
+ *
+ * The returned interator must be freed by calling pg_iter_destroy(). NULL
+ * is returned if we are unable to allocate the necessary memory.
+ */
+static pg_iter_t *
+pg_iter_create(entity_t *e, const char *restriction)
+{
+ pg_iter_t *i;
+
+ assert(e->sc_etype == SVCCFG_INSTANCE_OBJECT);
+
+ i = uu_zalloc(sizeof (*i));
+ if (i == NULL)
+ return (NULL);
+
+ i->pgi_entity = e;
+ i->pgi_restrict = restriction;
+ i->pgi_level = TL_COMPOSED;
+ i->pgi_service = e->sc_parent;
+
+ return (i);
+}
+
+/*
+ * Return the next property group in the iteration. NULL is returned if we
+ * reach the end of the list. The iterator will automatically proceed from
+ * most specific to most general levels.
+ */
+static pgroup_t *
+next_pattern_pg(pg_iter_t *i)
+{
+ composed_pg_t *cpg;
+ entity_t *e;
+ pgroup_t *pg;
+ uu_avl_t *composed_tree;
+
+ assert(i->pgi_level != TL_NOLEVEL);
+
+ while (i->pgi_entity != NULL) {
+ if (i->pgi_level == TL_COMPOSED) {
+ composed_tree =
+ i->pgi_entity->sc_u.sc_instance.sc_composed;
+ cpg = i->pgi_current.pgi_cpg;
+ if (cpg == NULL) {
+ cpg = uu_avl_first(composed_tree);
+ } else {
+ cpg = uu_avl_next(composed_tree, cpg);
+ }
+ if (cpg == NULL) {
+ pg = NULL;
+ } else {
+ pg = CPG2PG(cpg);
+ i->pgi_current.pgi_cpg = cpg;
+ }
+ } else {
+ pg = i->pgi_current.pgi_pg;
+ if (pg == NULL) {
+ pg = uu_list_first(i->pgi_entity->sc_pgroups);
+ } else {
+ pg = uu_list_next(i->pgi_entity->sc_pgroups,
+ pg);
+ }
+ i->pgi_current.pgi_pg = pg;
+ }
+
+ if (pg == NULL) {
+ /*
+ * End of the list. Reset current and break out of
+ * the loop.
+ */
+ (void) memset(&i->pgi_current, 0,
+ sizeof (i->pgi_current));
+ break;
+ }
+
+ /*
+ * If this iteration is for a specific type, verify that
+ * this pg is of that type.
+ */
+ if (i->pgi_restrict) {
+ if (strcmp(pg->sc_pgroup_type, i->pgi_restrict) != 0) {
+ continue;
+ }
+ }
+
+ return (pg);
+ }
+
+ /*
+ * End of the list in the current level. Move up to the next
+ * level.
+ */
+ switch (i->pgi_level) {
+ case TL_COMPOSED:
+ /* Skip service if we've finished a composed instance. */
+ e = i->pgi_entity;
+ if (e->sc_u.sc_instance.sc_instance_restarter == NULL) {
+ /* Use service restarter */
+ i->pgi_entity =
+ i->pgi_service->sc_u.sc_service.sc_restarter;
+ } else {
+ /* Use instance restarter */
+ i->pgi_entity =
+ e->sc_u.sc_instance.sc_instance_restarter;
+ }
+ i->pgi_level = TL_RESTARTER;
+ break;
+ case TL_RESTARTER:
+ i->pgi_entity = i->pgi_service->sc_u.sc_service.sc_global;
+ i->pgi_level = TL_GLOBAL;
+ break;
+ case TL_GLOBAL:
+ i->pgi_level = TL_NOLEVEL;
+ return (NULL);
+ default:
+ assert(0);
+ abort();
+ }
+
+ /* Go process the next level. */
+ return (next_pattern_pg(i));
+}
+
+/*
+ * Compare two pattern info structures (ptrn_info_t). If both structures
+ * have names, the comparison is based on the name. If only one has a
+ * name, the structure with no name will be first. If neither structure
+ * has a name, the comparison is based on their types using similar wild
+ * card logic.
+ */
+/* ARGSUSED2 */
+static int
+ptrn_info_compare(const void *left, const void *right, void *unused)
+{
+ ptrn_info_t *l = (ptrn_info_t *)left;
+ ptrn_info_t *r = (ptrn_info_t *)right;
+
+ if ((l->pi_name != NULL) && (r->pi_name != NULL))
+ return (strcmp(l->pi_name, r->pi_name));
+ if ((l->pi_name == NULL) && (r->pi_name == NULL)) {
+ /* No names, so we need to compare types. */
+ if ((l->pi_type != NULL) && (r->pi_type != NULL))
+ return (strcmp(l->pi_type, r->pi_type));
+ if ((l->pi_type == NULL) && (r->pi_type == NULL))
+ return (0);
+
+ /* If we get here, exactly one of the types is NULL */
+ if (l->pi_type == NULL)
+ return (-1);
+ return (1);
+ }
+
+ /* If we get here, exactly one of the names is NULL */
+ if (l->pi_name == NULL)
+ return (-1);
+ return (1);
+}
+
+/*
+ * The target of a pg_pattern in combination with the level at which the
+ * pg_pattern was defined determines whether or not it should be applied.
+ * The following combinations should be ignored, and all others should be
+ * applied.
+ *
+ * Target Level
+ * ------ -----
+ * this TL_RESTARTER, TL_GLOBAL
+ * "this" only applies if the pg_pattern was defined
+ * at the instance or service level
+ * delegate TL_INSTANCE, TL_SERVICE
+ * Only restarters and the global service can
+ * delegate.
+ * instance TL_INSTANCE, TL_RESTARTER, TL_GLOBAL
+ * Only the service level can specify the "instance"
+ * target.
+ * all TL_INSTANCE, TL_SERVICE, TL_RESTARTER
+ * Only the global service can specify the "all"
+ * target.
+ *
+ * Return Values:
+ * 1 apply the pg_pattern
+ * 0 ignore the pg_pattern
+ */
+static int
+target_check(const char *target, tmpl_level_t level)
+{
+ if ((target == NULL) || (*target == 0)) {
+ /* Default is this */
+ target = SCF_TM_TARGET_THIS;
+ }
+ if (strcmp(target, SCF_TM_TARGET_THIS) == 0) {
+ if ((level == TL_RESTARTER) ||
+ (level == TL_GLOBAL)) {
+ return (0);
+ } else {
+ return (1);
+ }
+ }
+ if (strcmp(target, SCF_TM_TARGET_DELEGATE) == 0) {
+ if ((level == TL_INSTANCE) ||
+ (level == TL_SERVICE)) {
+ return (0);
+ } else {
+ return (1);
+ }
+ }
+ if (strcmp(target, SCF_TM_TARGET_INSTANCE) == 0) {
+ /*
+ * Note that the test is inverted from the other cases.
+ * This is because there is only one instance where apply
+ * is the correct thing to do.
+ */
+ if (level == TL_SERVICE) {
+ return (1);
+ } else {
+ return (0);
+ }
+ }
+ if (strcmp(target, SCF_TM_TARGET_ALL) == 0) {
+ if ((level == TL_INSTANCE) ||
+ (level == TL_SERVICE) ||
+ (level == TL_RESTARTER)) {
+ return (0);
+ }
+ }
+ return (1);
+}
+
+static int
+pg_target_check(pgroup_t *pg_pattern, tmpl_level_t level)
+{
+ const char *target;
+
+ target = find_astring_value_in_pg(pg_pattern, SCF_PROPERTY_TM_TARGET);
+ if (level == TL_COMPOSED) {
+ switch (pg_pattern->sc_parent->sc_etype) {
+ case SVCCFG_INSTANCE_OBJECT:
+ level = TL_INSTANCE;
+ break;
+ case SVCCFG_SERVICE_OBJECT:
+ level = TL_SERVICE;
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+ }
+ return (target_check(target, level));
+}
+
+/*
+ * Find the prop_pattern's type sepcification and convert it to the
+ * appropriate scf_type.
+ */
+static tmpl_validate_status_t
+prop_pattern_type(pgroup_t *pattern, scf_type_t *type)
+{
+ const char *type_spec;
+
+ assert(strcmp(pattern->sc_pgroup_type,
+ SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0);
+
+ type_spec = find_type_specification(pattern);
+ if ((type_spec == NULL) || (*type_spec == 0))
+ return (TVS_MISSING_TYPE_SPECIFICATION);
+ *type = scf_string_to_type(type_spec);
+ return (TVS_SUCCESS);
+}
+
+/*
+ * This function is analagous to scf_property_is_type(3SCF), but it works
+ * on the in memory representation of the property.
+ *
+ * RETURNS:
+ * 0 The property at prop does not have the specified
+ * type.
+ * non-zero The property at prop does have the specified type.
+ */
+static int
+property_is_type(property_t *prop, scf_type_t type)
+{
+ return (prop->sc_value_type == type);
+}
+
+/*
+ * This function generates a property group name for a template's
+ * pg_pattern. The name and type of the pg_pattern are used to construct
+ * the name, but either or both may be null. A pointer to the constructed
+ * name is returned, and the referenced memory must be freed using
+ * free(3c). NULL is returned if we are unable to allocate enough memory.
+ */
+static char *
+gen_pg_pattern_pg_name(const char *name, const char *type)
+{
+ char *pg_name;
+ char *rv = NULL;
+ ssize_t name_size;
+
+ name_size = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
+ pg_name = safe_malloc(name_size);
+ rv = pg_name;
+
+ /*
+ * There are four cases -- name and type are both null, name and
+ * type are both non-null, only name is present or only type is
+ * present.
+ */
+ if ((name == NULL) || (*name == 0)) {
+ if ((type == NULL) || (*type == 0)) {
+ /*
+ * Name and type are both null, so the PG name
+ * contains only the prefix.
+ */
+ if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
+ name_size) >= name_size) {
+ rv = NULL;
+ }
+ } else {
+ /*
+ * If we have a type and no name, the type becomes
+ * part of the pg_pattern property group name.
+ */
+ if (snprintf(pg_name, name_size, "%s%s",
+ SCF_PG_TM_PG_PATTERN_T_PREFIX, type) >=
+ name_size) {
+ rv = NULL;
+ }
+ }
+ } else {
+ /*
+ * 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.
+ */
+ const char *prefix;
+ if ((type == NULL) || (*type == 0)) {
+ prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX;
+ } else {
+ prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX;
+ }
+ if (snprintf(pg_name, name_size, "%s%s", prefix, name) >=
+ name_size) {
+ rv = NULL;
+ }
+ }
+
+ if (rv == NULL) {
+ /* Name was too big. */
+ free(pg_name);
+ }
+ return (rv);
+}
+
+/*
+ * pinfo contains information about a prop_pattern. An include_values
+ * element with a type of type has been included in the prop_pattern
+ * specification. We need to determine if the prop_pattern also contains
+ * constraints or values specifications as determined by type. Thus, we
+ * search the prop_pattern for properties whose names start with the
+ * correct prefix.
+ */
+static tmpl_validate_status_t
+include_values_support(ptrn_info_t *pinfo, const char *type,
+ tmpl_errors_t *errs)
+{
+ error_info_t einfo;
+ int i;
+ const char **prefixes;
+ const char *pfx;
+ property_t *prop;
+ pgroup_t *ptrn;
+
+ if (strcmp(type, "constraints") == 0) {
+ prefixes = constraint_prefixes;
+ } else if (strcmp(type, "values") == 0) {
+ prefixes = value_prefixes;
+ } else {
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_BAD_TEMPLATE;
+ einfo.ei_u.ei_bad_template.ei_reason = gettext("include_values "
+ "type must be \"constraints\" or \"values\"");
+ (void) tmpl_errors_add_im(errs, TVS_BAD_TEMPLATE,
+ pinfo->pi_ptrnpg->sc_parent, pinfo->pi_enc_pgp,
+ NULL, pinfo->pi_ptrnpg, NULL, NULL, &einfo);
+ return (TVS_BAD_TEMPLATE);
+ }
+
+ /*
+ * Now see if the prop_pattern has a property whose name starts
+ * with one of these prefixes.
+ */
+ ptrn = pinfo->pi_ptrnpg;
+ for (prop = uu_list_first(ptrn->sc_pgroup_props);
+ prop != NULL;
+ prop = uu_list_next(ptrn->sc_pgroup_props, prop)) {
+ for (pfx = prefixes[0], i = 0;
+ pfx != NULL;
+ ++i, pfx = prefixes[i]) {
+ if (strncmp(prop->sc_property_name, pfx,
+ strlen(pfx)) == 0) {
+ return (TVS_SUCCESS);
+ }
+ }
+ }
+
+ /* No match found. Generate error */
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_INCLUDE_VALUES;
+ einfo.ei_u.ei_inc_values.ei_type = type;
+ (void) add_scf_error(errs, SCF_TERR_INCLUDE_VALUES, pinfo->pi_enc_pgp,
+ NULL, ptrn, NULL, NULL, &einfo);
+
+ return (TVS_VALIDATION);
+}
+
+/*
+ * Walk through the prop_patterns in tree, looking for any that have an
+ * include_values, SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES, property. For
+ * the prop_patterns with the include values property, verify that the
+ * prop_pattern has constraint or values declarations as specified by the
+ * include_values property.
+ */
+static tmpl_validate_status_t
+tmpl_include_values_check(uu_avl_t *tree, tmpl_errors_t *errs)
+{
+ ptrn_info_t *info;
+ property_t *iv;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ value_t *v;
+
+ for (info = uu_avl_first(tree);
+ info != NULL;
+ info = uu_avl_next(tree, info)) {
+ iv = internal_property_find(info->pi_ptrnpg,
+ SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES);
+ if (iv == NULL)
+ continue;
+ for (v = uu_list_first(iv->sc_property_values);
+ v != NULL;
+ v = uu_list_next(iv->sc_property_values, v)) {
+ assert(is_numeric_type(v->sc_type) == 0);
+ r = include_values_support(info, v->sc_u.sc_string,
+ errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+ }
+ }
+ return (rc);
+}
+
+/*
+ * Verify that there are no conflicting definitions of pg_pattern or
+ * prop_pattern. Two patterns are said to be in conflict if they have the
+ * same name and differing types. There is a caveat, however. Empty
+ * pattern names or types are considered to be wild cards. There is no
+ * conflict if a pattern has a wild card.
+ */
+static tmpl_validate_status_t
+tmpl_pattern_conflict(entity_t *inst, uu_avl_t *tree, ptrn_type_t type,
+ tmpl_errors_t *errs)
+{
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc;
+
+ /* First walk the instance. */
+ rc = gather_pattern(inst, type, tree, errs);
+
+ /* Now walk the service */
+ r = gather_pattern(inst->sc_parent, type, tree, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ return (rc);
+}
+
+static tmpl_validate_status_t
+tmpl_required_attr_present(uu_avl_t *tree, tmpl_errors_t *errs)
+{
+ ptrn_info_t *pinfo;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ int reported_name;
+ int rv;
+
+ for (pinfo = uu_avl_first(tree);
+ pinfo != NULL;
+ pinfo = uu_avl_next(tree, pinfo)) {
+ if (is_required(pinfo->pi_ptrnpg) == 0) {
+ /* Nothing to check if pattern is not required. */
+ continue;
+ }
+
+ /*
+ * For pg_pattern both name and type are optional unless
+ * the required attribute has a value of true. For
+ * prop_patterns only the type is optional, but it must be
+ * provided if the required attribute has a value of true.
+ */
+ reported_name = 0;
+ if ((pinfo->pi_ptrn_type == PG_PATTERN) &&
+ (pinfo->pi_name == NULL)) {
+ rc = TVS_VALIDATION;
+ if (add_scf_error(errs, SCF_TERR_PG_PATTERN_INCOMPLETE,
+ pinfo->pi_ptrnpg,
+ NULL, NULL, NULL, NULL, NULL) != 0) {
+ /*
+ * If we're unable to report errors, break
+ * out of the loop.
+ */
+ break;
+ }
+ /*
+ * Don't report the error twice if both name and
+ * type are missing. One error message is
+ * adequate.
+ */
+ reported_name = 1;
+ }
+ if ((pinfo->pi_type == NULL) && (reported_name == 0)) {
+ rc = TVS_VALIDATION;
+ if (pinfo->pi_ptrn_type == PG_PATTERN) {
+ rv = add_scf_error(errs,
+ SCF_TERR_PG_PATTERN_INCOMPLETE,
+ pinfo->pi_ptrnpg,
+ NULL, NULL, NULL, NULL, NULL);
+ } else {
+ rv = add_scf_error(errs,
+ SCF_TERR_PROP_PATTERN_INCOMPLETE,
+ pinfo->pi_enc_pgp, NULL, pinfo->pi_ptrnpg,
+ NULL, NULL, NULL);
+ }
+ /* If we're unable to log errors, break out of loop. */
+ if (rv != 0)
+ break;
+ }
+ }
+ return (rc);
+}
+
+/*
+ * Look for pg_pattern definitions in general. general is either the
+ * restarter serivce for inst or it is the global service. tree contains
+ * the ptrn_info_t structures describing the pg_patterns for an instance.
+ * For each general pg_pattern, see if the instance contains an overriding
+ * definition in tree. If it does generate an error entry.
+ *
+ * If a redefinition is found, TVS_WARN is returned. This is because a
+ * redefinition is not sufficient reason to inhibit the import operation.
+ */
+static tmpl_validate_status_t
+tmpl_scan_general(entity_t *general, uu_avl_t *tree,
+ tmpl_level_t level, tmpl_errors_t *errs)
+{
+ tmpl_level_t cur_level;
+ error_info_t einfo;
+ pgroup_t *pg;
+ ptrn_info_t *ginfo = NULL;
+ ptrn_info_t *match;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+
+ /*
+ * General services may not be in repository yet. It depends on
+ * the order that manifests are imported.
+ */
+ if (general == NULL)
+ return (TVS_SUCCESS);
+
+ for (pg = uu_list_first(general->sc_pgroups);
+ pg != NULL;
+ pg = uu_list_next(general->sc_pgroups, pg)) {
+ if (strcmp(pg->sc_pgroup_type,
+ SCF_GROUP_TEMPLATE_PG_PATTERN) != 0) {
+ /* Not a pg_pattern */
+ continue;
+ }
+ if (ginfo != NULL)
+ ptrn_info_destroy(ginfo);
+ ginfo = ptrn_info_create(pg);
+ match = uu_avl_find(tree, ginfo, NULL, NULL);
+ if (match != NULL) {
+ /* See if global pg_pattern is targeted at us. */
+ if (target_check(ginfo->pi_target, level) == 0)
+ continue;
+
+ /*
+ * See if the match applies to us. If we happen to
+ * be a restarter, the pg_pattern could have a
+ * target of delegate. That wouldn't apply to this
+ * instance, it would only apply to our delegates.
+ * Cases such as this are not a redefinition.
+ */
+ if (match->pi_ptrnpg->sc_parent->sc_etype ==
+ SVCCFG_INSTANCE_OBJECT) {
+ cur_level = TL_INSTANCE;
+ } else {
+ cur_level = TL_SERVICE;
+ }
+ if (target_check(match->pi_target, cur_level) == 0)
+ continue;
+
+ /*
+ * Instance or service overrides a general
+ * definition. We need to issue a warning message.
+ */
+ rc = TVS_WARN;
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_PATTERN_CONFLICT;
+ einfo.ei_u.ei_pattern_conflict.ei_pattern = pg;
+ if (add_scf_error(errs, SCF_TERR_GENERAL_REDEFINE,
+ match->pi_ptrnpg, NULL, NULL, NULL, NULL,
+ &einfo) != 0) {
+ /*
+ * No need to continue the search if we
+ * cannot record errors.
+ */
+ break;
+ }
+ }
+ }
+
+ if (ginfo != NULL)
+ ptrn_info_destroy(ginfo);
+ return (rc);
+}
+
+/*
+ * tree contains the pg_pattern definitions for the instance at inst. See
+ * if these pg_patterns redefine any pg_patterns in the instance's
+ * restarter or in the global service. TVS_WARN is returned if a
+ * redefinition is encountered.
+ */
+static tmpl_validate_status_t
+tmpl_level_redefine(entity_t *inst, uu_avl_t *tree, tmpl_errors_t *errs)
+{
+ entity_t *restarter;
+ entity_t *svc = inst->sc_parent;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc;
+
+ restarter = inst->sc_u.sc_instance.sc_instance_restarter;
+ if (restarter == NULL) {
+ /* No instance restarter. Use the service restarter */
+ restarter = svc->sc_u.sc_service.sc_restarter;
+ }
+ rc = tmpl_scan_general(restarter, tree, TL_RESTARTER, errs);
+ r = tmpl_scan_general(svc->sc_u.sc_service.sc_global, tree,
+ TL_GLOBAL, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+ return (rc);
+}
+
+/*
+ * Perform the following consistency checks on the template specifications
+ * themselves:
+ *
+ * - No conflicting definitions of `pg_pattern` are allowed within a
+ * single instance.
+ *
+ * - Templates at a narrow target (e.g. instance) which define
+ * property groups already templated at a broad target
+ * (e.g. restarter or all) are strongly discouraged.
+ *
+ * - Developers may not define a template which specifies a single
+ * prop_pattern name with differing types on the same target
+ * entity.
+ *
+ * - If a pg_pattern has a required attribute with a value of true,
+ * then its name and type attributes must be specified.
+ *
+ * - If a prop_pattern has a required attribute with a value of true,
+ * then its type attribute must be specified.
+ *
+ * - If a prop_pattern has an include values make sure that the
+ * appropriate constraints or values element has also been
+ * declared.
+ */
+static tmpl_validate_status_t
+tmpl_consistency(entity_t *inst, tmpl_errors_t *errs)
+{
+ void *marker = NULL;
+ ptrn_info_t *info;
+ uu_avl_t *tree;
+ tmpl_validate_status_t rc;
+ tmpl_validate_status_t r;
+
+ /* Allocate the tree. */
+ tree = uu_avl_create(ptrn_info_pool, NULL, TMPL_DEBUG_TREE);
+ if (tree == NULL) {
+ uu_die(gettext("pg_info tree creation failed: %s\n"),
+ uu_strerror(uu_error()));
+ }
+
+ rc = tmpl_pattern_conflict(inst, tree, PG_PATTERN, errs);
+
+ /*
+ * The tree now contains the instance and service pg_patterns.
+ * Check to see if they override any pg_pattern definitions in the
+ * restarter and global services.
+ */
+ r = tmpl_level_redefine(inst, tree, errs);
+ if (r != TVS_SUCCESS) {
+ /*
+ * tmpl_level_redefine() can return a warning. Don't
+ * override a serious error with a warning.
+ */
+ if (r == TVS_WARN) {
+ if (rc == TVS_SUCCESS)
+ rc = r;
+ } else {
+ rc = r;
+ }
+ }
+
+ /*
+ * If the pg_pattern has a required attribute with a value of true,
+ * then it must also have name and type attributes.
+ */
+ r = tmpl_required_attr_present(tree, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ /* Empty the tree, so that we can reuse it for prop_patterns. */
+ while ((info = uu_avl_teardown(tree, &marker)) != NULL) {
+ ptrn_info_destroy(info);
+ }
+
+ r = tmpl_pattern_conflict(inst, tree, PROP_PATTERN, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ /*
+ * If a prop_pattern has required attribute with a value of true,
+ * then it must also have a type attribute.
+ */
+ r = tmpl_required_attr_present(tree, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ /*
+ * Insure that include_values have the constraint for values
+ * elements that are needed.
+ */
+ r = tmpl_include_values_check(tree, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ /* Tear down the tree. */
+ marker = NULL;
+ while ((info = uu_avl_teardown(tree, &marker)) != NULL) {
+ ptrn_info_destroy(info);
+ }
+ uu_avl_destroy(tree);
+
+ return (rc);
+}
+
+/*
+ * Release memory associated with the tmpl_errors structure and then free
+ * the structure itself.
+ */
+void
+tmpl_errors_destroy(tmpl_errors_t *te)
+{
+ im_tmpl_error_t *ite;
+ tv_errors_t *ste;
+ void *marker = NULL;
+
+ if (te == NULL)
+ return;
+ if (te->te_list) {
+ while ((ite = uu_list_teardown(te->te_list, &marker)) != NULL) {
+ uu_list_node_fini(ite, &ite->ite_node,
+ inmem_errors_pool);
+ uu_free(ite);
+ }
+ uu_list_destroy(te->te_list);
+ }
+ if (te->te_scf) {
+ marker = NULL;
+ while ((ste = uu_list_teardown(te->te_scf, &marker)) != NULL) {
+ destroy_scf_errors(ste);
+ }
+ uu_list_destroy(te->te_scf);
+ }
+ uu_free(te);
+}
+
+/*
+ * Allocate and initialize a tmpl_errors structure. The address of the
+ * structure is returned, unless we are unable to allocate enough memory.
+ * In the case of memory allocation failures, NULL is returned.
+ *
+ * The allocated structure should be freed by calling
+ * tmpl_errors_destroy().
+ */
+static tmpl_errors_t *
+tmpl_errors_create()
+{
+ tmpl_errors_t *te;
+
+ te = uu_zalloc(sizeof (*te));
+ if (te == NULL)
+ return (NULL);
+ te->te_list = uu_list_create(inmem_errors_pool, NULL, TMPL_DEBUG_LIST);
+ if (te->te_list == NULL) {
+ uu_free(te);
+ return (NULL);
+ }
+ te->te_scf = uu_list_create(tv_errors_pool, NULL, TMPL_DEBUG_LIST);
+ if (te->te_scf == NULL) {
+ tmpl_errors_destroy(te);
+ return (NULL);
+ }
+
+ return (te);
+}
+
+void
+tmpl_errors_print(FILE *out, tmpl_errors_t *errs, const char *prefix)
+{
+ scf_tmpl_error_t *cur;
+ size_t buf_size = 4096;
+ im_tmpl_error_t *ite;
+ char *s = NULL;
+ scf_tmpl_errors_t *scferrs;
+ tv_errors_t *scft;
+ int interactive = (est->sc_cmd_flags & SC_CMD_IACTIVE) ?
+ SCF_TMPL_STRERROR_HUMAN : 0;
+
+ for (ite = uu_list_first(errs->te_list);
+ ite != NULL;
+ ite = uu_list_next(errs->te_list, ite)) {
+ im_tmpl_error_print(out, ite, prefix);
+ }
+
+ /* Now handle the errors that can be printed via libscf. */
+ s = safe_malloc(buf_size);
+ for (scft = uu_list_first(errs->te_scf);
+ scft != NULL;
+ scft = uu_list_next(errs->te_scf, scft)) {
+ scferrs = scft->tve_errors;
+ if (_scf_tmpl_error_set_prefix(scferrs, prefix) != 0)
+ uu_die(emesg_nomem);
+ while ((cur = scf_tmpl_next_error(scferrs)) != NULL) {
+ (void) scf_tmpl_strerror(cur, s, buf_size, interactive);
+ (void) fputs(s, out);
+ (void) fputc('\n', out);
+ }
+ }
+
+ free(s);
+}
+
+/*
+ * This function finds the prop_pattern for the property, prop. e is the
+ * instance where the search for the prop_pattern will start. pg_pattern
+ * is the address of the pg_pattern that holds the prop_pattern.
+ */
+static tmpl_validate_status_t
+tmpl_find_prop_pattern(entity_t *inst, pgroup_t *pg_pattern,
+ property_t *prop, pgroup_t **prop_pattern)
+{
+ pgroup_t *candidate;
+ pg_iter_t *iter = NULL;
+ char *prop_pattern_name = NULL;
+ tmpl_validate_status_t rc;
+
+ /*
+ * Get the name of the property group that holds the prop_pattern
+ * definition.
+ */
+ rc = gen_prop_pattern_pg_name(pg_pattern,
+ prop->sc_property_name, &prop_pattern_name);
+ if (rc != TVS_SUCCESS)
+ goto out;
+
+ /* Find the property group. */
+ iter = pg_iter_create(inst, SCF_GROUP_TEMPLATE_PROP_PATTERN);
+ if (iter == NULL)
+ goto out;
+ while ((candidate = next_pattern_pg(iter)) != NULL) {
+ const char *c;
+
+ if (strcmp(prop_pattern_name, candidate->sc_pgroup_name) != 0)
+ continue;
+ c = find_astring_value_in_pg(candidate,
+ SCF_PROPERTY_TM_PG_PATTERN);
+ if (c == NULL)
+ continue;
+ if (strcmp(pg_pattern->sc_pgroup_name, c) == 0)
+ break;
+ }
+ *prop_pattern = candidate;
+ if (candidate == NULL)
+ rc = TVS_NOMATCH;
+
+out:
+ pg_iter_destroy(iter);
+ uu_free((void *)prop_pattern_name);
+ return (rc);
+}
+
+/*
+ * Indexes for pg_pattern property group names. Indexes are arranged
+ * from most specific to least specific.
+ */
+#define PGN_BOTH 0 /* both name and type */
+#define PGN_NAME 1 /* name only */
+#define PGN_TYPE 2 /* type only */
+#define PGN_NEITHER 3 /* neither name nor type */
+#define PGN_MAX 4 /* Size of array */
+
+/*
+ * Given an instance entity, e, and a propety group, pg, within the
+ * instance; return the address of the pg_pattern for the property group.
+ * The address of the pg_pattern is placed at pgp. NULL indicates that no
+ * pg_pattern was specified.
+ */
+static tmpl_validate_status_t
+tmpl_find_pg_pattern(entity_t *e, pgroup_t *pg, pgroup_t **pgp)
+{
+ pgroup_t *cpg; /* candidate property group */
+ int i;
+ pg_iter_t *iter = NULL;
+ char *pg_names[PGN_MAX];
+ pgroup_t *pg_patterns[PGN_MAX];
+ tmpl_validate_status_t rv = TVS_SUCCESS;
+
+ (void) memset(pg_patterns, 0, sizeof (pg_patterns));
+ *pgp = NULL;
+
+ /* Generate candidate names for pg_pattern property groups. */
+ pg_names[PGN_BOTH] = gen_pg_pattern_pg_name(pg->sc_pgroup_name,
+ pg->sc_pgroup_type);
+ pg_names[PGN_NAME] = gen_pg_pattern_pg_name(pg->sc_pgroup_name,
+ NULL);
+ pg_names[PGN_TYPE] = gen_pg_pattern_pg_name(NULL,
+ pg->sc_pgroup_type);
+ pg_names[PGN_NEITHER] = gen_pg_pattern_pg_name(NULL, NULL);
+ for (i = 0; i < PGN_MAX; i++) {
+ if (pg_names[i] == NULL) {
+ rv = TVS_BAD_TEMPLATE;
+ goto errout;
+ }
+ }
+
+ /* Search for property groups that match these names */
+ iter = pg_iter_create(e, SCF_GROUP_TEMPLATE_PG_PATTERN);
+ if (iter == NULL) {
+ uu_die(emesg_nomem);
+ }
+ while ((cpg = next_pattern_pg(iter)) != NULL) {
+ if (pg_target_check(cpg, iter->pgi_level) == 0)
+ continue;
+
+ /* See if we have a name match. */
+ for (i = 0; i < PGN_MAX; i++) {
+ if (strcmp(cpg->sc_pgroup_name, pg_names[i]) == 0) {
+ /*
+ * If we already have a lower level
+ * pg_pattern, keep it.
+ */
+ if (pg_patterns[i] == NULL)
+ pg_patterns[i] = cpg;
+ break;
+ }
+ }
+ }
+
+ /* Find the most specific pg_pattern. */
+ for (i = 0; i < PGN_MAX; i++) {
+ if (pg_patterns[i] != NULL) {
+ *pgp = pg_patterns[i];
+ break;
+ }
+ }
+errout:
+ for (i = 0; i < PGN_MAX; i++) {
+ free(pg_names[i]);
+ }
+ pg_iter_destroy(iter);
+ return (rv);
+}
+
+/*
+ * Initialize structures that are required for validation using
+ * templates specifications.
+ */
+void
+tmpl_init(void)
+{
+ emesg_nomem = gettext("Out of memory.\n");
+
+ composed_pg_pool = uu_avl_pool_create("composed_pg",
+ sizeof (composed_pg_t), offsetof(composed_pg_t, cpg_node),
+ composed_pg_compare, TMPL_DEBUG_AVL_POOL);
+ if (composed_pg_pool == NULL) {
+ uu_die(gettext("composed_pg pool creation failed: %s\n"),
+ uu_strerror(uu_error()));
+ }
+ composed_prop_pool = uu_avl_pool_create("composed_prop",
+ sizeof (property_t), offsetof(property_t, sc_composed_node),
+ composed_prop_compare, TMPL_DEBUG_AVL_POOL);
+ if (composed_prop_pool == NULL) {
+ uu_die(gettext("composed_prop pool creation failed. %s\n"),
+ uu_strerror(uu_error()));
+ }
+ ptrn_info_pool = uu_avl_pool_create("ptrn_info", sizeof (ptrn_info_t),
+ offsetof(ptrn_info_t, pi_link), ptrn_info_compare,
+ TMPL_DEBUG_AVL_POOL);
+ if (ptrn_info_pool == NULL) {
+ uu_die(gettext("pg_pattern info pool creation failed: %s\n"),
+ uu_strerror(uu_error()));
+ }
+ inmem_errors_pool = uu_list_pool_create("errors-internal",
+ sizeof (im_tmpl_error_t), offsetof(im_tmpl_error_t,
+ ite_node), NULL, TMPL_DEBUG_LIST_POOL);
+ if (inmem_errors_pool == NULL) {
+ uu_die(gettext("inmem_errors_pool pool creation failed: "
+ "%s\n"), uu_strerror(uu_error()));
+ }
+ tv_errors_pool = uu_list_pool_create("scf-terrors",
+ sizeof (tv_errors_t), offsetof(tv_errors_t, tve_node),
+ NULL, TMPL_DEBUG_LIST_POOL);
+ if (tv_errors_pool == NULL) {
+ uu_die(gettext("tv_errors_pool pool creation failed: %s\n"),
+ uu_strerror(uu_error()));
+ }
+}
+
+/*
+ * Clean up the composed property node in the property.
+ */
+void
+tmpl_property_fini(property_t *p)
+{
+ uu_avl_node_fini(p, &p->sc_composed_node, composed_prop_pool);
+}
+
+/*
+ * Initialize the composed property node in the property.
+ */
+void
+tmpl_property_init(property_t *p)
+{
+ uu_avl_node_init(p, &p->sc_composed_node, composed_prop_pool);
+}
+
+/*
+ * Use the cardinality specification in the prop_pattern to verify the
+ * cardinality of the property at prop. The cardinality of the property is
+ * the number of values that it has.
+ *
+ * pg is the property group that holds prop, and pg_pattern is the
+ * pg_pattern for the property group. pg and pg_pattern are only used for
+ * error reporting.
+ */
+static tmpl_validate_status_t
+tmpl_validate_cardinality(pgroup_t *prop_pattern, property_t *prop,
+ pgroup_t *pg, pgroup_t *pg_pattern, tmpl_errors_t *errs)
+{
+ size_t count;
+ uint64_t max;
+ uint64_t min;
+ tmpl_validate_status_t rc;
+ error_info_t einfo;
+
+ assert(strcmp(prop_pattern->sc_pgroup_type,
+ SCF_GROUP_TEMPLATE_PROP_PATTERN) == 0);
+
+ rc = get_cardinality(prop_pattern, &min, &max);
+ switch (rc) {
+ case TVS_NOMATCH:
+ /* Nothing to check. */
+ return (TVS_SUCCESS);
+ case TVS_SUCCESS:
+ /* Process the limits. */
+ break;
+ default:
+ return (rc);
+ }
+
+ if ((min == 0) && (max == ULLONG_MAX)) {
+ /* Any number of values is permitted. No need to count. */
+ return (TVS_SUCCESS);
+ }
+
+ count = count_prop_values(prop);
+ if ((count < min) || (count > max)) {
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_CARDINALITY;
+ einfo.ei_u.ei_cardinality.ei_min = min;
+ einfo.ei_u.ei_cardinality.ei_max = max;
+ einfo.ei_u.ei_cardinality.ei_count = count;
+ (void) add_scf_error(errs, SCF_TERR_CARDINALITY_VIOLATION,
+ pg_pattern, pg, prop_pattern, prop, NULL, &einfo);
+ return (TVS_VALIDATION);
+ }
+
+ return (TVS_SUCCESS);
+}
+
+/*
+ * Iterate over pg_patterns in the entity, e. If the pg_pattern's required
+ * attribute is true, verify that the entity contains the corresponding
+ * property group.
+ */
+static tmpl_validate_status_t
+tmpl_required_pg_present(entity_t *e, tmpl_errors_t *errs)
+{
+ composed_pg_t cpg;
+ composed_pg_t *match;
+ error_info_t einfo;
+ pg_iter_t *iter;
+ pgroup_t *pg;
+ const char *pg_name;
+ const char *pg_type;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ uu_avl_t *tree;
+
+ assert(e->sc_etype == SVCCFG_INSTANCE_OBJECT);
+
+ iter = pg_iter_create(e, SCF_GROUP_TEMPLATE_PG_PATTERN);
+ if (iter == NULL)
+ uu_die(emesg_nomem);
+
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_MISSING_PG;
+
+ while ((pg = next_pattern_pg(iter)) != NULL) {
+ if (is_required(pg) == 0) {
+ /* If pg is not required, there is nothing to check. */
+ continue;
+ }
+ pg_name = find_astring_value_in_pg(pg, SCF_PROPERTY_TM_NAME);
+ pg_type = find_astring_value_in_pg(pg, SCF_PROPERTY_TM_TYPE);
+ if (pg_target_check(pg, iter->pgi_level) == 0)
+ continue;
+ einfo.ei_u.ei_missing_pg.ei_pg_name = pg_name;
+ einfo.ei_u.ei_missing_pg.ei_pg_type = pg_type;
+ tree = e->sc_u.sc_instance.sc_composed;
+ (void) memset(&cpg, 0, sizeof (cpg));
+ cpg.cpg_name = pg_name;
+ cpg.cpg_type = pg_type;
+ match = uu_avl_find(tree, &cpg, NULL, NULL);
+ if (match == NULL) {
+ rc = TVS_VALIDATION;
+ if (add_scf_error(errs, SCF_TERR_MISSING_PG, pg,
+ NULL, NULL, NULL, NULL, &einfo) != 0) {
+ break;
+ }
+ }
+ }
+
+ pg_iter_destroy(iter);
+ return (rc);
+}
+
+/*
+ * Verify that the property group, pg, contains property declarations for
+ * all required properties. Unfortunately, there is no direct way to find
+ * the prop_patterns for a given property group. Therefore, we need to
+ * scan the entity at e looking for property groups with a type of
+ * SCF_GROUP_TEMPLATE_PROP_PATTERN. That is, we scan the entity looking
+ * for all prop_patterns. When we find a prop_pattern, we look at the
+ * value of its pg_pattern property to see if it matches the name of the
+ * pg_pattern. If they match, this is a prop_pattern that is of interest
+ * to us.
+ *
+ * When we find an interesting prop_pattern, we see if it's required
+ * property is true. If it is, we verify that the property group at pg
+ * contains the specified property.
+ */
+static tmpl_validate_status_t
+tmpl_required_props_present(entity_t *e, pgroup_t *pg, pgroup_t *pg_pattern,
+ tmpl_errors_t *errs)
+{
+ error_info_t einfo;
+ pg_iter_t *iter;
+ const char *prop_name;
+ const char *prop_pg_pattern_name;
+ pgroup_t *prop_pattern;
+ scf_tmpl_error_type_t ec;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+
+ /*
+ * Scan the entity's property groups looking for ones with a type
+ * of SCF_GROUP_TEMPLATE_PROP_PATTERN.
+ */
+ iter = pg_iter_create(e, SCF_GROUP_TEMPLATE_PROP_PATTERN);
+ if (iter == NULL)
+ uu_die(emesg_nomem);
+ CLEAR_ERROR_INFO(&einfo);
+ for (prop_pattern = next_pattern_pg(iter);
+ prop_pattern != NULL;
+ prop_pattern = next_pattern_pg(iter)) {
+ /*
+ * Find the pg_pattern property in this prop_pattern.
+ * Verify that its value matches the name of the
+ * pg_pattern.
+ */
+ prop_pg_pattern_name = find_astring_value_in_pg(prop_pattern,
+ SCF_PROPERTY_TM_PG_PATTERN);
+ assert(prop_pg_pattern_name != NULL);
+ if (strcmp(pg_pattern->sc_pgroup_name,
+ prop_pg_pattern_name) != 0) {
+ continue;
+ }
+
+ /* If the property is required, see if it is in the pg. */
+ if (is_required(prop_pattern) == 0)
+ continue;
+ prop_name = find_astring_value_in_pg(prop_pattern,
+ SCF_PROPERTY_TM_NAME);
+ assert(prop_name != NULL);
+ if (property_find(pg, prop_name) == NULL) {
+ ec = SCF_TERR_MISSING_PROP;
+ rc = TVS_VALIDATION;
+ einfo.ei_type = EIT_MISSING_PROP;
+ einfo.ei_u.ei_missing_prop.ei_prop_name = prop_name;
+ if (add_scf_error(errs, ec, pg_pattern, pg,
+ prop_pattern, NULL, NULL, &einfo) != 0) {
+ /*
+ * If we can no longer accumulate errors,
+ * break out of the loop.
+ */
+ break;
+ }
+ }
+ }
+
+ pg_iter_destroy(iter);
+ return (rc);
+}
+
+/*
+ * Check the value at v to see if it falls within any of the ranges at r.
+ * count is the number of ranges at r, and type tells whether to treat the
+ * value as signed or unsigned.
+ *
+ * Return 1 if the value falls within one of the ranges. Otherwise return
+ * 0.
+ */
+static int
+value_in_range(value_t *v, scf_type_t type, range_t *r, size_t count)
+{
+ for (; count > 0; --count, r++) {
+ if (type == SCF_TYPE_COUNT) {
+ if ((v->sc_u.sc_count >=
+ r->rng_u.rng_unsigned.rng_min) &&
+ (v->sc_u.sc_count <=
+ r->rng_u.rng_unsigned.rng_max))
+ return (1);
+ } else {
+ if ((v->sc_u.sc_integer >=
+ r->rng_u.rng_signed.rng_min) &&
+ (v->sc_u.sc_integer <=
+ r->rng_u.rng_signed.rng_max))
+ return (1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * If the template prop_pattern at pattern contains a constraint_range
+ * property, use the specified range to validate all the numeric property
+ * values of the property at prop.
+ *
+ * pg is the property group that holds prop, and pg_pattern is the
+ * pg_pattern for the property group. pg and pg_pattern are only used for
+ * error reporting.
+ */
+static tmpl_validate_status_t
+tmpl_validate_value_range(pgroup_t *pattern, property_t *prop, pgroup_t *pg,
+ pgroup_t *pg_pattern, tmpl_errors_t *errs)
+{
+ uint_t count;
+ error_info_t einfo;
+ property_t *range_prop;
+ range_t *ranges;
+ tmpl_validate_status_t rc;
+ scf_type_t type;
+ value_t *v;
+
+ /* Get the range constraints if they exist. */
+ if ((range_prop = property_find(pattern,
+ SCF_PROPERTY_TM_CONSTRAINT_RANGE)) == NULL) {
+ /* No range to check. */
+ return (TVS_SUCCESS);
+ }
+ type = prop->sc_value_type;
+ if ((type != SCF_TYPE_COUNT) && (type != SCF_TYPE_INTEGER)) {
+ rc = TVS_BAD_TEMPLATE;
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_BAD_TEMPLATE;
+ einfo.ei_u.ei_bad_template.ei_reason =
+ gettext("Property does not have correct type for "
+ "a range specification");
+ (void) tmpl_errors_add_im(errs, rc, pg_pattern->sc_parent,
+ pg_pattern, pg, pattern, prop, NULL, &einfo);
+ return (rc);
+ }
+ if ((rc = get_ranges(range_prop, prop->sc_value_type, &ranges,
+ &count)) != TVS_SUCCESS) {
+ rc = TVS_BAD_TEMPLATE;
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_BAD_TEMPLATE;
+ einfo.ei_u.ei_bad_template.ei_reason = gettext("Illegal range "
+ "value");
+ (void) tmpl_errors_add_im(errs, rc, pg_pattern->sc_parent,
+ pg_pattern, pg, pattern, prop, NULL, &einfo);
+ return (rc);
+ }
+
+ /* Set up error info before entering loop. */
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_RANGE;
+ einfo.ei_u.ei_range.ei_rtype = type;
+
+ /* Compare numeric values of the property to the range. */
+ for (v = uu_list_first(prop->sc_property_values);
+ v != NULL;
+ v = uu_list_next(prop->sc_property_values, v)) {
+ if (value_in_range(v, type, ranges, count) == 1)
+ continue;
+ if (type == SCF_TYPE_COUNT) {
+ einfo.ei_u.ei_range.ei_uvalue = v->sc_u.sc_count;
+ } else {
+ einfo.ei_u.ei_range.ei_ivalue = v->sc_u.sc_integer;
+ }
+ rc = TVS_VALIDATION;
+ if (add_scf_error(errs, SCF_TERR_RANGE_VIOLATION, pg_pattern,
+ pg, pattern, prop, v, &einfo) != 0) {
+ return (rc);
+ }
+ }
+
+ return (rc);
+}
+
+/*
+ * If the prop_pattern has value constraints, verify that all the values
+ * for the property at prop are legal values.
+ *
+ * pg is the property group that holds prop, and pg_pattern is the
+ * pg_pattern for the property group. pg and pg_pattern are only used for
+ * error reporting.
+ */
+static tmpl_validate_status_t
+tmpl_validate_values(pgroup_t *prop_pattern, property_t *prop, pgroup_t *pg,
+ pgroup_t *pg_pattern, tmpl_errors_t *errs)
+{
+ int found;
+ uint_t i;
+ avalues_t *legal;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ value_t *v;
+
+ /* Get list of legal values. */
+ r = av_get_values(prop_pattern, SCF_PROPERTY_TM_CONSTRAINT_NAME,
+ prop->sc_value_type, &legal);
+ switch (r) {
+ case TVS_BAD_CONVERSION:
+ (void) tmpl_errors_add_im(errs, r, pg->sc_parent, pg_pattern,
+ pg, prop_pattern, prop, NULL, NULL);
+ return (r);
+ case TVS_NOMATCH:
+ /* No constraints in template. */
+ return (TVS_SUCCESS);
+ case TVS_SUCCESS:
+ /* process the constraints. */
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ /* Check the property values against the legal values. */
+ for (v = uu_list_first(prop->sc_property_values);
+ v != NULL;
+ v = uu_list_next(prop->sc_property_values, v)) {
+ /* Check this property value against the legal values. */
+ found = 0;
+ for (i = 0; (i < legal->av_count) && (found == 0); i++) {
+ switch (v->sc_type) {
+ case SCF_TYPE_BOOLEAN:
+ case SCF_TYPE_COUNT:
+ if (av_get_unsigned(legal, i) ==
+ v->sc_u.sc_count) {
+ found = 1;
+ }
+ break;
+ case SCF_TYPE_INTEGER:
+ if (av_get_integer(legal, i) ==
+ v->sc_u.sc_integer) {
+ found = 1;
+ }
+ break;
+ default:
+ if (strcmp(av_get_string(legal, i),
+ v->sc_u.sc_string) == 0) {
+ found = 1;
+ }
+ break;
+ }
+ }
+ if (found == 0) {
+ rc = TVS_VALIDATION;
+ if (add_scf_error(errs,
+ SCF_TERR_VALUE_CONSTRAINT_VIOLATED, pg_pattern, pg,
+ prop_pattern, prop, v, NULL) != 0) {
+ /*
+ * Exit loop if no longer able to report
+ * errors.
+ */
+ break;
+ }
+ }
+ }
+
+out:
+ av_destroy(legal);
+ return (rc);
+}
+
+/*
+ * Verify the following items about the values of property, prop.
+ *
+ * - The values all have the type specified by the prop_pattern at
+ * pattern.
+ * - Check numeric values against range constraints.
+ * - If the prop_pattern has one or more value constraints, validate
+ * the property's values against the constraints.
+ *
+ * pg is the property group that holds prop, and pg_pattern is the
+ * pg_pattern for the property group. pg and pg_pattern are only used for
+ * error reporting.
+ */
+static tmpl_validate_status_t
+tmpl_validate_value_constraints(pgroup_t *pattern, property_t *prop,
+ pgroup_t *pg, pgroup_t *pg_pattern, tmpl_errors_t *errs)
+{
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc;
+
+ rc = tmpl_validate_value_range(pattern, prop, pg, pg_pattern, errs);
+ r = tmpl_validate_values(pattern, prop, pg, pg_pattern, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ return (rc);
+}
+
+/*
+ * Perform the following validations on the property, prop.
+ *
+ * - Verify that the property's type agrees with the type specified in
+ * the prop_pattern template, tmpl.
+ * - Verify the cardinality.
+ * - Verify that the property values satisfy the constraints specified
+ * by the template.
+ *
+ * pg is the property group that holds prop, and pg_pattern is the
+ * pg_pattern for the property group. pg and pg_pattern are only used for
+ * error reporting.
+ */
+static tmpl_validate_status_t
+tmpl_validate_prop(property_t *prop, pgroup_t *tmpl, pgroup_t *pg,
+ pgroup_t *pg_pattern, tmpl_errors_t *errs)
+{
+ scf_tmpl_error_type_t ec;
+ error_info_t einfo;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ int status;
+ scf_type_t type;
+
+ r = prop_pattern_type(tmpl, &type);
+ switch (r) {
+ case TVS_SUCCESS:
+ if (type == SCF_TYPE_INVALID) {
+ rc = TVS_INVALID_TYPE_SPECIFICATION;
+ r = tmpl_errors_add_im(errs, rc, pg->sc_parent, NULL,
+ pg, tmpl, NULL, NULL, NULL);
+ if (r != TVS_SUCCESS) {
+ /*
+ * Give up if we can no longer accumulate
+ * errors.
+ */
+ return (rc);
+ }
+ } else {
+ if (property_is_type(prop, type) == 0) {
+ CLEAR_ERROR_INFO(&einfo);
+ rc = TVS_VALIDATION;
+ ec = SCF_TERR_WRONG_PROP_TYPE;
+ einfo.ei_type = EIT_PROP_TYPE;
+ einfo.ei_u.ei_prop_type.ei_specified = type;
+ einfo.ei_u.ei_prop_type.ei_actual =
+ prop->sc_value_type;
+ status = add_scf_error(errs, ec,
+ pg_pattern, pg, tmpl, prop, NULL, &einfo);
+ if (status != 0) {
+ /*
+ * Give up if we can no longer
+ * accumulate errors.
+ */
+ return (rc);
+ }
+ }
+ }
+ break;
+ case TVS_MISSING_TYPE_SPECIFICATION:
+ /*
+ * A null type specification means that we do not need to
+ * check the property's type.
+ */
+ break;
+ default:
+ rc = r;
+ }
+
+ /* Validate the cardinality */
+ r = tmpl_validate_cardinality(tmpl, prop, pg, pg_pattern, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ /* Validate that property values satisfy constraints. */
+ r = tmpl_validate_value_constraints(tmpl, prop, pg, pg_pattern, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ return (rc);
+}
+
+/*
+ * Validate the property group at pg by performing the following checks:
+ *
+ * - Verify that the types of the pg and the pg_pattern are
+ * compatible.
+ * - Verify the properties in the pg.
+ * - Verify that required properties are present.
+ */
+static tmpl_validate_status_t
+tmpl_validate_pg(entity_t *e, pgroup_t *pg, tmpl_errors_t *errs)
+{
+ error_info_t einfo;
+ const char *pg_pattern_type; /* Type declared by pg_pattern. */
+ pgroup_t *pg_pattern; /* Prop. group for pg_pattern */
+ property_t *prop;
+ pgroup_t *prop_pattern;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ int stat;
+
+ /*
+ * See if there is a pg_pattern for this property group. If it
+ * exists, use it to validate the property group. If there is no
+ * pg_pattern, then there is no validation to do.
+ */
+ rc = tmpl_find_pg_pattern(e, pg, &pg_pattern);
+ switch (rc) {
+ case TVS_SUCCESS:
+ break;
+ case TVS_BAD_TEMPLATE:
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_BAD_TEMPLATE;
+ einfo.ei_u.ei_bad_template.ei_reason = gettext("Property "
+ "group name too long");
+ (void) tmpl_errors_add_im(errs, rc, e, NULL, pg, NULL, NULL,
+ NULL, &einfo);
+ return (rc);
+ default:
+ assert(0);
+ abort();
+ }
+ if (pg_pattern == NULL)
+ return (TVS_SUCCESS);
+
+ /*
+ * If the pg_pattern declares a type, verify that the PG has the
+ * correct type.
+ */
+ pg_pattern_type = find_type_specification(pg_pattern);
+ if ((pg_pattern_type != NULL) &&
+ (*pg_pattern_type != 0)) {
+ if ((pg->sc_pgroup_type != NULL) &&
+ (*(pg->sc_pgroup_type) != 0)) {
+ if (strcmp(pg_pattern_type,
+ pg->sc_pgroup_type) != 0) {
+ rc = TVS_VALIDATION;
+ stat = add_scf_error(errs,
+ SCF_TERR_WRONG_PG_TYPE, pg_pattern, pg,
+ NULL, NULL, NULL, NULL);
+ if (stat != 0) {
+ /*
+ * If we can no longer accumulate
+ * errors, return without trying to
+ * do further validation.
+ */
+ return (rc);
+ }
+ }
+ } else {
+ rc = TVS_MISSING_PG_TYPE;
+ r = tmpl_errors_add_im(errs, rc, e, pg_pattern, pg,
+ NULL, NULL, NULL, NULL);
+ if (r != TVS_SUCCESS) {
+ /*
+ * If we can no longer accumulate errors,
+ * return without trying to do further
+ * validation.
+ */
+ return (rc);
+ }
+ }
+ }
+
+ /* Verify the properties in the property group. */
+ prop = NULL;
+ while ((prop = next_property(pg, prop)) != NULL) {
+ r = tmpl_find_prop_pattern(e, pg_pattern, prop, &prop_pattern);
+ switch (r) {
+ case TVS_SUCCESS:
+ /* Found match. Validate property. */
+ break;
+ case TVS_NOMATCH:
+ /* No prop_patern. Go on to next property. */
+ continue;
+ case TVS_BAD_TEMPLATE:
+ CLEAR_ERROR_INFO(&einfo);
+ einfo.ei_type = EIT_BAD_TEMPLATE;
+ einfo.ei_u.ei_bad_template.ei_reason =
+ gettext("prop_pattern name too long");
+ (void) tmpl_errors_add_im(errs, r, e, NULL, pg, NULL,
+ NULL, NULL, &einfo);
+ continue;
+ default:
+ assert(0);
+ abort();
+ }
+ r = tmpl_validate_prop(prop, prop_pattern, pg, pg_pattern,
+ errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+ }
+
+ /*
+ * Confirm required properties are present.
+ */
+ r = tmpl_required_props_present(e, pg, pg_pattern, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ return (rc);
+}
+
+/*
+ * Validate that the property groups in the entity conform to the template
+ * specifications. Specifically, this means do the following:
+ *
+ * - Loop through the property groups in the entity skipping the ones
+ * that are of type "template".
+ *
+ * - For the PG search for the corresponding template_pg_pattern
+ * property group. It is possible that one may not exist.
+ *
+ * - Verify that the PG is in conformance with the pg_pattern
+ * specification if it exists.
+ */
+static tmpl_validate_status_t
+tmpl_validate_entity_pgs(entity_t *e, tmpl_errors_t *errs)
+{
+ composed_pg_t *cpg;
+ uu_avl_t *pgroups;
+ pgroup_t *pg;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+
+ assert(e->sc_etype == SVCCFG_INSTANCE_OBJECT);
+
+ pgroups = e->sc_u.sc_instance.sc_composed;
+ for (cpg = uu_avl_first(pgroups);
+ cpg != NULL;
+ cpg = uu_avl_next(pgroups, cpg)) {
+ if (strcmp(cpg->cpg_type, SCF_GROUP_TEMPLATE) == 0)
+ continue;
+ pg = CPG2PG(cpg);
+ if ((r = tmpl_validate_pg(e, pg, errs)) != TVS_SUCCESS)
+ rc = r;
+ }
+
+ return (rc);
+}
+
+/*
+ * Validate the instance, e, by performing the following checks:
+ *
+ * - Verify template consistency.
+ *
+ * - Validate each property group in the entity is in conformance
+ * with the template specifications.
+ *
+ * - Verify that all required property groups are present in the
+ * entity.
+ */
+static tmpl_validate_status_t
+tmpl_validate_instance(entity_t *e, tmpl_errors_t *errs)
+{
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+ int status;
+ tv_errors_t *ste;
+
+ /* Prepare to collect errors for this instance. */
+ ste = tv_errors_create(e->sc_fmri);
+ status = uu_list_insert_after(errs->te_scf, errs->te_cur_scf, ste);
+ assert(status == 0);
+ errs->te_cur_scf = ste;
+
+ /* Verify template consistency */
+ rc = tmpl_consistency(e, errs);
+
+ /* Validate the property groups in the entity. */
+ r = tmpl_validate_entity_pgs(e, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ /* Verify that all required property groups are present. */
+ r = tmpl_required_pg_present(e, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+
+ return (rc);
+}
+
+/*
+ * First validate the instances of the service.
+ */
+static tmpl_validate_status_t
+tmpl_validate_service(entity_t *svc, tmpl_errors_t *errs)
+{
+ entity_t *inst;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+
+ assert(svc->sc_etype == SVCCFG_SERVICE_OBJECT);
+
+ load_general_templates(svc);
+
+ /* Validate the service's instances. */
+ for (inst = uu_list_first(svc->sc_u.sc_service.sc_service_instances);
+ inst != NULL;
+ inst = uu_list_next(svc->sc_u.sc_service.sc_service_instances,
+ inst)) {
+ load_instance_restarter(inst);
+ build_composed_instance(inst);
+ r = tmpl_validate_instance(inst, errs);
+ if (r != TVS_SUCCESS)
+ rc = r;
+ demolish_composed_instance(inst);
+ }
+
+ return (rc);
+}
+
+/*
+ * Validate all services and instances in the bundle against their
+ * templates. If err_list is not NULL, a tmpl_errors structure will be
+ * allocated and its address will be returned to err_list. This structure
+ * can be used to generate error messages.
+ */
+tmpl_validate_status_t
+tmpl_validate_bundle(bundle_t *bndl, tmpl_errors_t **err_list)
+{
+ tmpl_errors_t *errs = NULL;
+ entity_t *svc;
+ tmpl_validate_status_t r;
+ tmpl_validate_status_t rc = TVS_SUCCESS;
+
+ if (err_list != NULL)
+ *err_list = NULL;
+ if (bndl->sc_bundle_type != SVCCFG_MANIFEST) {
+ semerr(gettext("Bundle is not a manifest. Unable to validate "
+ "against templates.\n"));
+ return (TVS_FATAL);
+ }
+
+ errs = tmpl_errors_create();
+ if (errs == NULL)
+ uu_die(emesg_nomem);
+
+ lscf_prep_hndl(); /* Initialize g_hndl */
+ if (load_init() != 0)
+ uu_die(emesg_nomem);
+
+ /*
+ * We will process all services in the bundle, unless we get a
+ * fatal error. That way we can report all errors on all services
+ * on a single run of svccfg.
+ */
+ for (svc = uu_list_first(bndl->sc_bundle_services);
+ svc != NULL;
+ svc = uu_list_next(bndl->sc_bundle_services, svc)) {
+ if (svc->sc_etype != SVCCFG_SERVICE_OBJECT) {
+ semerr(gettext("Manifest for %s contains an object "
+ "named \"%s\" that is not a service.\n"),
+ bndl->sc_bundle_name, svc->sc_name);
+ tmpl_errors_destroy(errs);
+ load_fini();
+ return (TVS_FATAL);
+ }
+ if ((r = tmpl_validate_service(svc, errs)) != TVS_SUCCESS)
+ rc = r;
+ if (r == TVS_FATAL)
+ break;
+ }
+
+ if (err_list == NULL) {
+ tmpl_errors_destroy(errs);
+ } else {
+ *err_list = errs;
+ }
+
+ load_fini();
+
+ return (rc);
+}
diff --git a/usr/src/cmd/svc/svccfg/svccfg_xml.c b/usr/src/cmd/svc/svccfg/svccfg_xml.c
index 930a61bdd4..2b04a0a1df 100644
--- a/usr/src/cmd/svc/svccfg/svccfg_xml.c
+++ b/usr/src/cmd/svc/svccfg/svccfg_xml.c
@@ -19,11 +19,18 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * XML document manipulation routines
+ *
+ * These routines provide translation to and from the internal representation to
+ * XML. Directionally-oriented verbs are with respect to the external source,
+ * so lxml_get_service() fetches a service from the XML file into the
+ * internal representation.
+ */
#include <libxml/parser.h>
#include <libxml/xinclude.h>
@@ -32,7 +39,10 @@
#include <ctype.h>
#include <errno.h>
#include <libintl.h>
+#include <libscf.h>
+#include <libscf_priv.h>
#include <libuutil.h>
+#include <sasl/saslutil.h>
#include <stdlib.h>
#include <string.h>
@@ -43,20 +53,46 @@
#include "svccfg.h"
/*
- * XML document manipulation routines
+ * snprintf(3C) format strings for constructing property names that include
+ * the locale designation. Use %s to indicate where the locale should go.
*
- * These routines provide translation to and from the internal representation to
- * XML. Directionally-oriented verbs are with respect to the external source,
- * so lxml_get_service() fetches a service from the XML file into the
- * internal representation.
+ * The VALUE_* symbols are an exception. The firs %s will be replaced with
+ * "value_". The second %s will be replaced by the name of the value and
+ * %%s will be replaced by the locale designation. These formats are
+ * processed twice by snprintf(3C). The first time captures the value name
+ * and the second time captures the locale.
*/
-
+#define LOCALE_ONLY_FMT ("%s")
+#define COMMON_NAME_FMT ("common_name_%s")
+#define DESCRIPTION_FMT ("description_%s")
+#define UNITS_FMT ("units_%s")
+#define VALUE_COMMON_NAME_FMT ("%s%s_common_name_%%s")
+#define VALUE_DESCRIPTION_FMT ("%s%s_description_%%s")
+
+/* Attribute names */
const char * const delete_attr = "delete";
const char * const enabled_attr = "enabled";
+const char * const lang_attr = "lang";
+const char * const manpath_attr = "manpath";
+const char * const max_attr = "max";
+const char * const min_attr = "min";
const char * const name_attr = "name";
const char * const override_attr = "override";
+const char * const required_attr = "required";
+const char * const section_attr = "section";
+const char * const set_attr = "set";
+const char * const target_attr = "target";
+const char * const timeout_seconds_attr = "timeout_seconds";
+const char * const title_attr = "title";
const char * const type_attr = "type";
+const char * const uri_attr = "uri";
const char * const value_attr = "value";
+const char * const version_attr = "version";
+const char * const xml_lang_attr = "xml:lang";
+
+/* Attribute values */
+const char * const all_value = "all";
+
const char * const true = "true";
const char * const false = "false";
@@ -67,7 +103,10 @@ const char * const false = "false";
static const char *lxml_elements[] = {
"astring_list", /* SC_ASTRING */
"boolean_list", /* SC_BOOLEAN */
+ "cardinality", /* SC_CARDINALITY */
+ "choices", /* SC_CHOICES */
"common_name", /* SC_COMMON_NAME */
+ "constraints", /* SC_CONSTRAINTS */
"count_list", /* SC_COUNT */
"create_default_instance", /* SC_INSTANCE_CREATE_DEFAULT */
"dependency", /* SC_DEPENDENCY */
@@ -80,8 +119,10 @@ static const char *lxml_elements[] = {
"fmri_list", /* SC_FMRI */
"host_list", /* SC_HOST */
"hostname_list", /* SC_HOSTNAME */
+ "include_values", /* SC_INCLUDE_VALUES */
"instance", /* SC_INSTANCE */
"integer_list", /* SC_INTEGER */
+ "internal_separators", /* SC_INTERNAL_SEPARATORS */
"loctext", /* SC_LOCTEXT */
"manpage", /* SC_MANPAGE */
"method_context", /* SC_METHOD_CONTEXT */
@@ -92,9 +133,12 @@ static const char *lxml_elements[] = {
"net_address_v4_list", /* SC_NET_ADDR_V4 */
"net_address_v6_list", /* SC_NET_ADDR_V6 */
"opaque_list", /* SC_OPAQUE */
+ "pg_pattern", /* SC_PG_PATTERN */
+ "prop_pattern", /* SC_PROP_PATTERN */
"property", /* SC_PROPERTY */
"property_group", /* SC_PROPERTY_GROUP */
"propval", /* SC_PROPVAL */
+ "range", /* SC_RANGE */
"restarter", /* SC_RESTARTER */
"service", /* SC_SERVICE */
"service_bundle", /* SC_SERVICE_BUNDLE */
@@ -103,9 +147,13 @@ static const char *lxml_elements[] = {
"stability", /* SC_STABILITY */
"template", /* SC_TEMPLATE */
"time_list", /* SC_TIME */
+ "units", /* SC_UNITS */
"uri_list", /* SC_URI */
"ustring_list", /* SC_USTRING */
+ "value", /* SC_VALUE */
"value_node", /* SC_VALUE_NODE */
+ "values", /* SC_VALUES */
+ "visibility", /* SC_VISIBILITY */
"xi:fallback", /* SC_XI_FALLBACK */
"xi:include" /* SC_XI_INCLUDE */
};
@@ -117,7 +165,10 @@ static const char *lxml_elements[] = {
static const char *lxml_prop_types[] = {
"astring", /* SC_ASTRING */
"boolean", /* SC_BOOLEAN */
+ "", /* SC_CARDINALITY */
+ "", /* SC_CHOICES */
"", /* SC_COMMON_NAME */
+ "", /* SC_CONSTRAINTS */
"count", /* SC_COUNT */
"", /* SC_INSTANCE_CREATE_DEFAULT */
"", /* SC_DEPENDENCY */
@@ -130,8 +181,10 @@ static const char *lxml_prop_types[] = {
"fmri", /* SC_FMRI */
"host", /* SC_HOST */
"hostname", /* SC_HOSTNAME */
+ "", /* SC_INCLUDE_VALUES */
"", /* SC_INSTANCE */
"integer", /* SC_INTEGER */
+ "", /* SC_INTERNAL_SEPARATORS */
"", /* SC_LOCTEXT */
"", /* SC_MANPAGE */
"", /* SC_METHOD_CONTEXT */
@@ -142,9 +195,12 @@ static const char *lxml_prop_types[] = {
"net_address_v4", /* SC_NET_ADDR_V4 */
"net_address_v6", /* SC_NET_ADDR_V6 */
"opaque", /* SC_OPAQUE */
+ "", /* SC_PG_PATTERN */
+ "", /* SC_PROP_PATTERN */
"", /* SC_PROPERTY */
"", /* SC_PROPERTY_GROUP */
"", /* SC_PROPVAL */
+ "", /* SC_RANGE */
"", /* SC_RESTARTER */
"", /* SC_SERVICE */
"", /* SC_SERVICE_BUNDLE */
@@ -153,10 +209,14 @@ static const char *lxml_prop_types[] = {
"", /* SC_STABILITY */
"", /* SC_TEMPLATE */
"time", /* SC_TIME */
+ "", /* SC_UNITS */
"uri", /* SC_URI */
"ustring", /* SC_USTRING */
- "" /* SC_VALUE_NODE */
- "" /* SC_XI_FALLBACK */
+ "", /* SC_VALUE */
+ "", /* SC_VALUE_NODE */
+ "", /* SC_VALUES */
+ "", /* SC_VISIBILITY */
+ "", /* SC_XI_FALLBACK */
"" /* SC_XI_INCLUDE */
};
@@ -280,6 +340,49 @@ lxml_element_to_scf_type(element_t type)
/* NOTREACHED */
}
+/*
+ * Create a SCF_TYPE_BOOLEAN property name pname and attach it to the
+ * property group at pgrp. The value of the property will be set from the
+ * attribute named attr. attr must have a value of 0, 1, true or false.
+ *
+ * Zero is returned on success. An error is indicated by -1. It indicates
+ * that either the attribute had an invalid value or that we could not
+ * attach the property to pgrp. The attribute should not have an invalid
+ * value if the DTD is correctly written.
+ */
+static int
+new_bool_prop_from_attr(pgroup_t *pgrp, const char *pname, xmlNodePtr n,
+ const char *attr)
+{
+ uint64_t bool;
+ xmlChar *val;
+ property_t *p;
+ int r;
+
+ val = xmlGetProp(n, (xmlChar *)attr);
+ if (val == NULL)
+ return (0);
+
+ if ((xmlStrcmp(val, (xmlChar *)"0") == 0) ||
+ (xmlStrcmp(val, (xmlChar *)"false") == 0)) {
+ bool = 0;
+ } else if ((xmlStrcmp(val, (xmlChar *)"1") == 0) ||
+ (xmlStrcmp(val, (xmlChar *)"true") == 0)) {
+ bool = 1;
+ } else {
+ xmlFree(val);
+ return (-1);
+ }
+ xmlFree(val);
+ p = internal_property_create(pname, SCF_TYPE_BOOLEAN, 1, bool);
+ r = internal_attach_property(pgrp, p);
+
+ if (r != 0)
+ internal_property_free(p);
+
+ return (r);
+}
+
static int
new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
xmlNodePtr n, const char *attr)
@@ -300,6 +403,39 @@ new_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
}
static int
+new_opt_str_prop_from_attr(pgroup_t *pgrp, const char *pname, scf_type_t ty,
+ xmlNodePtr n, const char *attr, const char *dflt)
+{
+ xmlChar *val;
+ property_t *p;
+ int r;
+
+ val = xmlGetProp(n, (xmlChar *)attr);
+ if (val == NULL) {
+ if (dflt == NULL) {
+ /*
+ * A missing attribute is considered to be a
+ * success in this function, because many of the
+ * attributes are optional. Missing non-optional
+ * attributes will be detected later when template
+ * validation is done.
+ */
+ return (0);
+ } else {
+ val = (xmlChar *)dflt;
+ }
+ }
+
+ p = internal_property_create(pname, ty, 1, val);
+ r = internal_attach_property(pgrp, p);
+
+ if (r != 0)
+ internal_property_free(p);
+
+ return (r);
+}
+
+static int
lxml_ignorable_block(xmlNodePtr n)
{
return ((xmlStrcmp(n->name, (xmlChar *)"text") == 0 ||
@@ -446,12 +582,12 @@ lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
p = internal_property_new();
p->sc_property_name = (char *)xmlGetProp(propval, (xmlChar *)name_attr);
- if (p->sc_property_name == NULL)
+ if ((p->sc_property_name == NULL) || (*p->sc_property_name == 0))
uu_die(gettext("property name missing in group '%s'\n"),
pgrp->sc_pgroup_name);
type = xmlGetProp(propval, (xmlChar *)type_attr);
- if (type == NULL)
+ if ((type == NULL) || (*type == 0))
uu_die(gettext("property type missing for property '%s/%s'\n"),
pgrp->sc_pgroup_name, p->sc_property_name);
@@ -459,6 +595,7 @@ lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
break;
}
+ xmlFree(type);
if (r >= sizeof (lxml_prop_types) / sizeof (char *))
uu_die(gettext("property type invalid for property '%s/%s'\n"),
pgrp->sc_pgroup_name, p->sc_property_name);
@@ -471,6 +608,7 @@ lxml_get_propval(pgroup_t *pgrp, xmlNodePtr propval)
pgrp->sc_pgroup_name, p->sc_property_name);
v = lxml_make_value(r, val);
+ xmlFree(val);
internal_attach_value(p, v);
override = xmlGetProp(propval, (xmlChar *)override_attr);
@@ -490,15 +628,17 @@ lxml_get_property(pgroup_t *pgrp, xmlNodePtr property)
p = internal_property_new();
- if ((p->sc_property_name = (char *)xmlGetProp(property,
- (xmlChar *)name_attr)) == NULL)
+ if (((p->sc_property_name = (char *)xmlGetProp(property,
+ (xmlChar *)name_attr)) == NULL) || (*p->sc_property_name == 0))
uu_die(gettext("property name missing in group \'%s\'\n"),
pgrp->sc_pgroup_name);
- if ((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL)
+ if (((type = xmlGetProp(property, (xmlChar *)type_attr)) == NULL) ||
+ (*type == 0)) {
uu_die(gettext("property type missing for "
"property \'%s/%s\'\n"), pgrp->sc_pgroup_name,
p->sc_property_name);
+ }
for (r = 0; r < sizeof (lxml_prop_types) / sizeof (char *); r++) {
if (xmlStrcmp(type, (const xmlChar *)lxml_prop_types[r]) == 0)
@@ -674,8 +814,8 @@ lxml_get_envvar(xmlNodePtr envvar)
char *value;
char *ret;
- name = (char *)xmlGetProp(envvar, (xmlChar *)"name");
- value = (char *)xmlGetProp(envvar, (xmlChar *)"value");
+ name = (char *)xmlGetProp(envvar, (xmlChar *)name_attr);
+ value = (char *)xmlGetProp(envvar, (xmlChar *)value_attr);
if (strlen(name) == 0 || strchr(name, '=') != NULL)
uu_die(gettext("Invalid environment variable "
@@ -803,7 +943,7 @@ lxml_get_exec_method(entity_t *entity, xmlNodePtr emeth)
emeth, "exec") != 0)
return (-1);
- timeout = xmlGetProp(emeth, (xmlChar *)"timeout_seconds");
+ timeout = xmlGetProp(emeth, (xmlChar *)timeout_seconds_attr);
if (timeout != NULL) {
uint64_t u_timeout;
char *endptr;
@@ -1093,7 +1233,8 @@ lxml_get_entity_stability(entity_t *entity, xmlNodePtr rstr)
property_t *p;
xmlChar *stabval;
- if ((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) {
+ if (((stabval = xmlGetProp(rstr, (xmlChar *)value_attr)) == NULL) ||
+ (*stabval == 0)) {
uu_warn(gettext("no stability value found\n"));
stabval = (xmlChar *)strdup("External");
}
@@ -1156,28 +1297,45 @@ lxml_get_restarter(entity_t *entity, xmlNodePtr rstr)
return (0);
}
-static void
-sanitize_locale(uchar_t *locale)
-{
- for (; *locale != '\0'; locale++)
- if (!isalnum(*locale) && *locale != '_')
- *locale = '_';
-}
-
+/*
+ * Add a property containing the localized text from the manifest. The
+ * property is added to the property group at pg. The name of the created
+ * property is based on the format at pn_format. This is an snprintf(3C)
+ * format containing a single %s conversion specification. At conversion
+ * time, the %s is replaced by the locale designation.
+ *
+ * source is the source element and it is only used for error messages.
+ */
static int
-lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext)
+lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext,
+ const char *pn_format, const char *source)
{
+ int extra;
xmlNodePtr cursor;
xmlChar *val;
char *stripped, *cp;
property_t *p;
+ char *prop_name;
int r;
- if ((val = xmlGetProp(loctext, (xmlChar *)"xml:lang")) == NULL)
- if ((val = xmlGetProp(loctext, (xmlChar *)"lang")) == NULL)
+ if (((val = xmlGetProp(loctext, (xmlChar *)xml_lang_attr)) == NULL) ||
+ (*val == 0)) {
+ if (((val = xmlGetProp(loctext,
+ (xmlChar *)lang_attr)) == NULL) || (*val == 0)) {
val = (xmlChar *)"unknown";
+ }
+ }
- sanitize_locale(val);
+ _scf_sanitize_locale((char *)val);
+ prop_name = safe_malloc(max_scf_name_len + 1);
+ if ((extra = snprintf(prop_name, max_scf_name_len + 1, pn_format,
+ val)) >= max_scf_name_len + 1) {
+ extra -= max_scf_name_len;
+ uu_die(gettext("%s attribute is %d characters too long for "
+ "%s in %s\n"),
+ xml_lang_attr, extra, source, service->sc_name);
+ }
+ xmlFree(val);
for (cursor = loctext->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
@@ -1207,45 +1365,54 @@ lxml_get_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr loctext)
;
*(cp + 1) = '\0';
- p = internal_property_create((const char *)val, SCF_TYPE_USTRING, 1,
+ p = internal_property_create(prop_name, SCF_TYPE_USTRING, 1,
stripped);
r = internal_attach_property(pg, p);
- if (r != 0)
+ if (r != 0) {
internal_property_free(p);
+ free(prop_name);
+ }
return (r);
}
+/*
+ * This function processes all loctext elements in the current XML element
+ * designated by container. A property is created for each loctext element
+ * and added to the property group at pg. The name of the property is
+ * derived from the loctext language designation using the format at
+ * pn_format. pn_format should be an snprintf format string containing one
+ * %s which is replaced by the language designation.
+ *
+ * The function returns 0 on success and -1 if it is unable to attach the
+ * newly created property to pg.
+ */
static int
-lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
+lxml_get_all_loctext(entity_t *service, pgroup_t *pg, xmlNodePtr container,
+ const char *pn_format, const char *source)
{
xmlNodePtr cursor;
- pgroup_t *pg;
-
- /*
- * Create the property group, if absent.
- */
- pg = internal_pgroup_find_or_create(service,
- (char *)SCF_PG_TM_COMMON_NAME, (char *)SCF_GROUP_TEMPLATE);
/*
- * Iterate through one or more loctext elements. The locale is the
- * property name; the contents are the ustring value for the property.
+ * Iterate through one or more loctext elements. The locale is
+ * used to generate the property name; the contents are the ustring
+ * value for the property.
*/
- for (cursor = common_name->xmlChildrenNode; cursor != NULL;
+ for (cursor = container->xmlChildrenNode; cursor != NULL;
cursor = cursor->next) {
if (lxml_ignorable_block(cursor))
continue;
switch (lxml_xlate_element(cursor->name)) {
case SC_LOCTEXT:
- if (lxml_get_loctext(service, pg, cursor))
+ if (lxml_get_loctext(service, pg, cursor, pn_format,
+ source))
return (-1);
break;
default:
- uu_die(gettext("illegal element \"%s\" on common_name "
- "element for \"%s\"\n"), cursor->name,
+ uu_die(gettext("illegal element \"%s\" on %s element "
+ "for \"%s\"\n"), cursor->name, container->name,
service->sc_name);
break;
}
@@ -1254,41 +1421,170 @@ lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
return (0);
}
+/*
+ * Obtain the specified cardinality attribute and place it in a property
+ * named prop_name. The converted attribute is placed at *value, and the
+ * newly created property is returned to propp. NULL is returned to propp
+ * if the attribute is not provided in the manifest.
+ *
+ * 0 is returned upon success, and -1 indicates that the manifest contained
+ * an invalid cardinality value.
+ */
static int
-lxml_get_tm_description(entity_t *service, xmlNodePtr description)
+lxml_get_cardinality_attribute(entity_t *service, xmlNodePtr cursor,
+ const char *attr_name, const char *prop_name, uint64_t *value,
+ property_t **propp)
{
- xmlNodePtr cursor;
- pgroup_t *pg;
+ char *c;
+ property_t *p;
+ xmlChar *val;
+ uint64_t count;
+ char *endptr;
- /*
- * Create the property group, if absent.
- */
- pg = internal_pgroup_find_or_create(service,
- (char *)SCF_PG_TM_DESCRIPTION, (char *)SCF_GROUP_TEMPLATE);
+ *propp = NULL;
+ val = xmlGetProp(cursor, (xmlChar *)attr_name);
+ if (val == NULL)
+ return (0);
+ if (*val == 0) {
+ xmlFree(val);
+ return (0);
+ }
/*
- * Iterate through one or more loctext elements. The locale is the
- * property name; the contents are the ustring value for the property.
+ * Make sure that the string at val doesn't have a leading minus
+ * sign. The strtoull() call below does not catch this problem.
*/
- for (cursor = description->xmlChildrenNode; cursor != NULL;
- cursor = cursor->next) {
- if (lxml_ignorable_block(cursor))
+ for (c = (char *)val; *c != 0; c++) {
+ if (isspace(*c))
continue;
-
- switch (lxml_xlate_element(cursor->name)) {
- case SC_LOCTEXT:
- if (lxml_get_loctext(service, pg, cursor))
- return (-1);
- break;
- default:
- uu_die(gettext("illegal element \"%s\" on description "
- "element for \"%s\"\n"), cursor->name,
- service->sc_name);
+ if (isdigit(*c))
break;
+ semerr(gettext("\"%c\" is not a legal character in the %s "
+ "attribute of the %s element in %s.\n"), *c,
+ attr_name, prop_name, service->sc_name);
+ xmlFree(val);
+ return (-1);
+ }
+ errno = 0;
+ count = strtoull((char *)val, &endptr, 10);
+ if (errno != 0 || endptr == (char *)val || *endptr) {
+ semerr(gettext("\"%s\" is not a legal number for the %s "
+ "attribute of the %s element in %s.\n"), (char *)val,
+ attr_name, prop_name, service->sc_name);
+ xmlFree(val);
+ return (-1);
+ }
+
+ xmlFree(val);
+
+ /* Value is valid. Create the property. */
+ p = internal_property_create(prop_name, SCF_TYPE_COUNT, 1, count);
+ *value = count;
+ *propp = p;
+ return (0);
+}
+
+/*
+ * The cardinality is specified by two attributes max and min at cursor.
+ * Both are optional, but if present they must be unsigned integers.
+ */
+static int
+lxml_get_tm_cardinality(entity_t *service, pgroup_t *pg, xmlNodePtr cursor)
+{
+ int min_attached = 0;
+ int compare = 1;
+ property_t *min_prop;
+ property_t *max_prop;
+ uint64_t max;
+ uint64_t min;
+ int r;
+
+ r = lxml_get_cardinality_attribute(service, cursor, min_attr,
+ SCF_PROPERTY_TM_CARDINALITY_MIN, &min, &min_prop);
+ if (r != 0)
+ return (r);
+ if (min_prop == NULL)
+ compare = 0;
+ r = lxml_get_cardinality_attribute(service, cursor, max_attr,
+ SCF_PROPERTY_TM_CARDINALITY_MAX, &max, &max_prop);
+ if (r != 0)
+ goto errout;
+ if ((max_prop != NULL) && (compare == 1)) {
+ if (max < min) {
+ semerr(gettext("Cardinality max is less than min for "
+ "the %s element in %s.\n"), pg->sc_pgroup_name,
+ service->sc_fmri);
+ goto errout;
}
}
+ /* Attach the properties to the property group. */
+ if (min_prop) {
+ if (internal_attach_property(pg, min_prop) == 0) {
+ min_attached = 1;
+ } else {
+ goto errout;
+ }
+ }
+ if (max_prop) {
+ if (internal_attach_property(pg, max_prop) != 0) {
+ if (min_attached)
+ internal_detach_property(pg, min_prop);
+ goto errout;
+ }
+ }
return (0);
+
+errout:
+ if (min_prop)
+ internal_property_free(min_prop);
+ if (max_prop)
+ internal_property_free(max_prop);
+ return (-1);
+}
+
+/*
+ * Get the common_name which is present as localized text at common_name in
+ * the manifest. The common_name is stored as the value of a property in
+ * the property group whose name is SCF_PG_TM_COMMON_NAME and type is
+ * SCF_GROUP_TEMPLATE. This property group will be created in service if
+ * it is not already there.
+ */
+static int
+lxml_get_tm_common_name(entity_t *service, xmlNodePtr common_name)
+{
+ pgroup_t *pg;
+
+ /*
+ * Create the property group, if absent.
+ */
+ pg = internal_pgroup_find_or_create(service, SCF_PG_TM_COMMON_NAME,
+ SCF_GROUP_TEMPLATE);
+
+ return (lxml_get_all_loctext(service, pg, common_name, LOCALE_ONLY_FMT,
+ "common_name"));
+}
+
+/*
+ * Get the description which is present as localized text at description in
+ * the manifest. The description is stored as the value of a property in
+ * the property group whose name is SCF_PG_TM_DESCRIPTION and type is
+ * SCF_GROUP_TEMPLATE. This property group will be created in service if
+ * it is not already there.
+ */
+static int
+lxml_get_tm_description(entity_t *service, xmlNodePtr description)
+{
+ pgroup_t *pg;
+
+ /*
+ * Create the property group, if absent.
+ */
+ pg = internal_pgroup_find_or_create(service, SCF_PG_TM_DESCRIPTION,
+ SCF_GROUP_TEMPLATE);
+
+ return (lxml_get_all_loctext(service, pg, description,
+ LOCALE_ONLY_FMT, "description"));
}
static char *
@@ -1329,6 +1625,108 @@ lxml_label_to_groupname(const char *prefix, const char *in)
return (out);
}
+/*
+ * If *p is NULL, astring_prop_value() first creates a property with the
+ * name specified in prop_name. The address of the newly created property
+ * is placed in *p.
+ *
+ * In either case, newly created property or existing property, a new
+ * SCF_TYPE_ASTRING value will created and attached to the property at *p.
+ * The value of the newly created property is prop_value.
+ *
+ * free_flag is used to indicate whether or not the memory at prop_value
+ * should be freed when the property is freed by a call to
+ * internal_property_free().
+ */
+static void
+astring_prop_value(property_t **p, const char *prop_name, char *prop_value,
+ boolean_t free_flag)
+{
+ value_t *v;
+
+ if (*p == NULL) {
+ /* Create the property */
+ *p = internal_property_new();
+ (*p)->sc_property_name = (char *)prop_name;
+ (*p)->sc_value_type = SCF_TYPE_ASTRING;
+ }
+
+ /* Add the property value to the property's list of values. */
+ v = internal_value_new();
+ v->sc_type = SCF_TYPE_ASTRING;
+ if (free_flag == B_TRUE)
+ v->sc_free = lxml_free_str;
+ v->sc_u.sc_string = prop_value;
+ internal_attach_value(*p, v);
+}
+
+/*
+ * If p points to a null pointer, create an internal_separators property
+ * saving the address at p. For each character at seps create a property
+ * value and attach it to the property at p.
+ */
+static void
+seps_to_prop_values(property_t **p, xmlChar *seps)
+{
+ value_t *v;
+ char val_str[2];
+
+ if (*p == NULL) {
+ *p = internal_property_new();
+ (*p)->sc_property_name =
+ (char *)SCF_PROPERTY_INTERNAL_SEPARATORS;
+ (*p)->sc_value_type = SCF_TYPE_ASTRING;
+ }
+
+ /* Add the values to the property's list. */
+ val_str[1] = 0; /* Terminate the string. */
+ for (; *seps != 0; seps++) {
+ v = internal_value_new();
+ v->sc_type = (*p)->sc_value_type;
+ v->sc_free = lxml_free_str;
+ val_str[0] = *seps;
+ v->sc_u.sc_string = strdup(val_str);
+ if (v->sc_u.sc_string == NULL)
+ uu_die(gettext("Out of memory\n"));
+ internal_attach_value(*p, v);
+ }
+}
+
+/*
+ * Create an internal_separators property and attach it to the property
+ * group at pg. The separator characters are provided in the text nodes
+ * that are the children of seps. Each separator character is stored as a
+ * property value in the internal_separators property.
+ */
+static int
+lxml_get_tm_internal_seps(entity_t *service, pgroup_t *pg, xmlNodePtr seps)
+{
+ xmlNodePtr cursor;
+ property_t *prop = NULL;
+ int r;
+
+ for (cursor = seps->xmlChildrenNode; cursor != NULL;
+ cursor = cursor->next) {
+ if (strcmp("text", (const char *)cursor->name) == 0) {
+ seps_to_prop_values(&prop, cursor->content);
+ } else if (strcmp("comment", (const char *)cursor->name) != 0) {
+ uu_die(gettext("illegal element \"%s\" on %s element "
+ "for \"%s\"\n"), cursor->name, seps->name,
+ service->sc_name);
+ }
+ }
+ if (prop == NULL) {
+ semerr(gettext("The %s element in %s had an empty list of "
+ "separators.\n"), (const char *)seps->name,
+ service->sc_name);
+ return (-1);
+ }
+ r = internal_attach_property(pg, prop);
+ if (r != 0)
+ internal_property_free(prop);
+ return (r);
+}
+
static int
lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
{
@@ -1340,9 +1738,10 @@ lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
* Fetch title attribute, convert to something sanitized, and create
* property group.
*/
- title = xmlGetProp(manpage, (xmlChar *)"title");
+ title = xmlGetProp(manpage, (xmlChar *)title_attr);
pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_MAN_PREFIX,
(const char *)title);
+ xmlFree(title);
pg = internal_pgroup_find_or_create(service, pgname,
(char *)SCF_GROUP_TEMPLATE);
@@ -1350,12 +1749,12 @@ lxml_get_tm_manpage(entity_t *service, xmlNodePtr manpage)
/*
* Each attribute is an astring property within the group.
*/
- if (new_str_prop_from_attr(pg, "title", SCF_TYPE_ASTRING, manpage,
- "title") != 0 ||
- new_str_prop_from_attr(pg, "section", SCF_TYPE_ASTRING, manpage,
- "section") != 0 ||
- new_str_prop_from_attr(pg, "manpath", SCF_TYPE_ASTRING, manpage,
- "manpath") != 0)
+ if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_TITLE,
+ SCF_TYPE_ASTRING, manpage, title_attr) != 0 ||
+ new_str_prop_from_attr(pg, SCF_PROPERTY_TM_SECTION,
+ SCF_TYPE_ASTRING, manpage, section_attr) != 0 ||
+ new_str_prop_from_attr(pg, SCF_PROPERTY_TM_MANPATH,
+ SCF_TYPE_ASTRING, manpage, manpath_attr) != 0)
return (-1);
return (0);
@@ -1372,21 +1771,22 @@ lxml_get_tm_doclink(entity_t *service, xmlNodePtr doc_link)
* Fetch name attribute, convert name to something sanitized, and create
* property group.
*/
- name = xmlGetProp(doc_link, (xmlChar *)"name");
+ name = xmlGetProp(doc_link, (xmlChar *)name_attr);
pgname = (char *)lxml_label_to_groupname(SCF_PG_TM_DOC_PREFIX,
(const char *)name);
pg = internal_pgroup_find_or_create(service, pgname,
(char *)SCF_GROUP_TEMPLATE);
+ xmlFree(name);
/*
* Each attribute is an astring property within the group.
*/
- if (new_str_prop_from_attr(pg, "name", SCF_TYPE_ASTRING, doc_link,
- "name") != 0 ||
- new_str_prop_from_attr(pg, "uri", SCF_TYPE_ASTRING, doc_link,
- "uri") != 0)
+ if (new_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME, SCF_TYPE_ASTRING,
+ doc_link, name_attr) != 0 ||
+ new_str_prop_from_attr(pg, SCF_PROPERTY_TM_URI, SCF_TYPE_ASTRING,
+ doc_link, uri_attr) != 0)
return (-1);
return (0);
@@ -1420,6 +1820,919 @@ lxml_get_tm_documentation(entity_t *service, xmlNodePtr documentation)
}
static int
+lxml_get_prop_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
+{
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
+ SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
+ SCF_TYPE_ASTRING, cursor, type_attr, "") != 0) {
+ return (-1);
+ }
+ if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
+ required_attr) != 0)
+ return (-1);
+ return (0);
+}
+
+static int
+lxml_get_tm_include_values(entity_t *service, pgroup_t *pg,
+ xmlNodePtr include_values, const char *prop_name)
+{
+ boolean_t attach_to_pg = B_FALSE;
+ property_t *p;
+ int r = 0;
+ char *type;
+
+ /* Get the type attribute of the include_values element. */
+ type = (char *)xmlGetProp(include_values, (const xmlChar *)type_attr);
+ if ((type == NULL) || (*type == 0)) {
+ uu_die(gettext("%s element requires a %s attribute in the %s "
+ "service.\n"), include_values->name, type_attr,
+ service->sc_name);
+ }
+
+ /* Add the type to the values of the prop_name property. */
+ p = internal_property_find(pg, prop_name);
+ if (p == NULL)
+ attach_to_pg = B_TRUE;
+ astring_prop_value(&p, prop_name, type, B_FALSE);
+ if (attach_to_pg == B_TRUE) {
+ r = internal_attach_property(pg, p);
+ if (r != 0)
+ internal_property_free(p);
+ }
+ return (r);
+}
+
+#define RC_MIN 0
+#define RC_MAX 1
+#define RC_COUNT 2
+
+/*
+ * Verify that the strings at min and max are valid numeric strings. Also
+ * verify that max is numerically >= min.
+ *
+ * 0 is returned if the range is valid, and -1 is returned if it is not.
+ */
+static int
+verify_range(entity_t *service, xmlNodePtr range, char *min, char *max)
+{
+ char *c;
+ int i;
+ int is_signed = 0;
+ int inverted = 0;
+ const char *limit[RC_COUNT];
+ char *strings[RC_COUNT];
+ uint64_t urange[RC_COUNT]; /* unsigned range. */
+ int64_t srange[RC_COUNT]; /* signed range. */
+
+ strings[RC_MIN] = min;
+ strings[RC_MAX] = max;
+ limit[RC_MIN] = min_attr;
+ limit[RC_MAX] = max_attr;
+
+ /* See if the range is signed. */
+ for (i = 0; (i < RC_COUNT) && (is_signed == 0); i++) {
+ c = strings[i];
+ while (isspace(*c)) {
+ c++;
+ }
+ if (*c == '-')
+ is_signed = 1;
+ }
+
+ /* Attempt to convert the strings. */
+ for (i = 0; i < RC_COUNT; i++) {
+ errno = 0;
+ if (is_signed) {
+ srange[i] = strtoll(strings[i], &c, 0);
+ } else {
+ urange[i] = strtoull(strings[i], &c, 0);
+ }
+ if ((errno != 0) || (c == strings[i]) || (*c != 0)) {
+ /* Conversion failed. */
+ uu_die(gettext("Unable to convert %s for the %s "
+ "element in service %s.\n"), limit[i],
+ (char *)range->name, service->sc_name);
+ }
+ }
+
+ /* Make sure that min is <= max */
+ if (is_signed) {
+ if (srange[RC_MAX] < srange[RC_MIN])
+ inverted = 1;
+ } else {
+ if (urange[RC_MAX] < urange[RC_MIN])
+ inverted = 1;
+ }
+ if (inverted != 0) {
+ semerr(gettext("Maximum less than minimum for the %s element "
+ "in service %s.\n"), (char *)range->name,
+ service->sc_name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * This, function creates a property named prop_name. The range element
+ * should have two attributes -- min and max. The property value then
+ * becomes the concatenation of their value separated by a comma. The
+ * property is then attached to the property group at pg.
+ *
+ * If pg already contains a property with a name of prop_name, it is only
+ * necessary to create a new value and attach it to the existing property.
+ */
+static int
+lxml_get_tm_range(entity_t *service, pgroup_t *pg, xmlNodePtr range,
+ const char *prop_name)
+{
+ boolean_t attach_to_pg = B_FALSE;
+ char *max;
+ char *min;
+ property_t *p;
+ char *prop_value;
+ int r = 0;
+
+ /* Get max and min from the XML description. */
+ max = (char *)xmlGetProp(range, (xmlChar *)max_attr);
+ if ((max == NULL) || (*max == 0)) {
+ uu_die(gettext("%s element is missing the %s attribute in "
+ "service %s.\n"), (char *)range->name, max_attr,
+ service->sc_name);
+ }
+ min = (char *)xmlGetProp(range, (xmlChar *)min_attr);
+ if ((min == NULL) || (*min == 0)) {
+ uu_die(gettext("%s element is missing the %s attribute in "
+ "service %s.\n"), (char *)range->name, min_attr,
+ service->sc_name);
+ }
+ if (verify_range(service, range, min, max) != 0) {
+ xmlFree(min);
+ xmlFree(max);
+ return (-1);
+ }
+
+ /* Property value is concatenation of min and max. */
+ prop_value = safe_malloc(max_scf_value_len + 1);
+ if (snprintf(prop_value, max_scf_value_len + 1, "%s,%s", min, max) >=
+ max_scf_value_len + 1) {
+ uu_die(gettext("min and max are too long for the %s element "
+ "of %s.\n"), (char *)range->name, service->sc_name);
+ }
+ xmlFree(min);
+ xmlFree(max);
+
+ /*
+ * If necessary create the property and attach it to the property
+ * group.
+ */
+ p = internal_property_find(pg, prop_name);
+ if (p == NULL)
+ attach_to_pg = B_TRUE;
+ astring_prop_value(&p, prop_name, prop_value, B_TRUE);
+ if (attach_to_pg == B_TRUE) {
+ r = internal_attach_property(pg, p);
+ if (r != 0) {
+ internal_property_free(p);
+ }
+ }
+ return (r);
+}
+
+/*
+ * Determine how many plain characters are represented by count Base32
+ * encoded characters. 5 plain text characters are converted to 8 Base32
+ * characters.
+ */
+static size_t
+encoded_count_to_plain(size_t count)
+{
+ return (5 * ((count + 7) / 8));
+}
+
+/*
+ * The value element contains 0 or 1 common_name element followed by 0 or 1
+ * description element. It also has a required attribute called "name".
+ * The common_name and description are stored as property values in pg.
+ * The property names are:
+ * value_<name>_common_name_<lang>
+ * value_<name>_description_<lang>
+ *
+ * The <name> portion of the preceeding proper names requires more
+ * explanation. Ideally it would just the name attribute of this value
+ * element. Unfortunately, the name attribute can contain characters that
+ * are not legal in a property name. Thus, we base 32 encode the name
+ * attribute and use that for <name>.
+ *
+ * There are cases where the caller needs to know the name, so it is
+ * returned through the name_value pointer if it is not NULL.
+ *
+ * Parameters:
+ * service - Information about the service that is being
+ * processed. This function only uses this parameter
+ * for producing error messages.
+ *
+ * pg - The property group to receive the newly created
+ * properties.
+ *
+ * value - Pointer to the value element in the XML tree.
+ *
+ * name_value - Address to receive the value of the name attribute.
+ * The caller must free the memory.
+ */
+static int
+lxml_get_tm_value_element(entity_t *service, pgroup_t *pg, xmlNodePtr value,
+ char **name_value)
+{
+ char *common_name_fmt;
+ xmlNodePtr cursor;
+ char *description_fmt;
+ char *encoded_value = NULL;
+ size_t extra;
+ char *value_name;
+ int r = 0;
+
+ common_name_fmt = safe_malloc(max_scf_name_len + 1);
+ description_fmt = safe_malloc(max_scf_name_len + 1);
+
+ /*
+ * Get the value of our name attribute, so that we can use it to
+ * construct property names.
+ */
+ value_name = (char *)xmlGetProp(value, (xmlChar *)name_attr);
+ /* The value name must be present, but it can be empty. */
+ if (value_name == NULL) {
+ uu_die(gettext("%s element requires a %s attribute in the %s "
+ "service.\n"), (char *)value->name, name_attr,
+ service->sc_name);
+ }
+
+ /*
+ * The value_name may contain characters that are not valid in in a
+ * property name. So we will encode value_name and then use the
+ * encoded value in the property name.
+ */
+ encoded_value = safe_malloc(max_scf_name_len + 1);
+ if (scf_encode32(value_name, strlen(value_name), encoded_value,
+ max_scf_name_len + 1, &extra, SCF_ENCODE32_PAD) != 0) {
+ extra = encoded_count_to_plain(extra - max_scf_name_len);
+ uu_die(gettext("Constructed property name is %u characters "
+ "too long for value \"%s\" in the %s service.\n"),
+ extra, value_name, service->sc_name);
+ }
+ if ((extra = snprintf(common_name_fmt, max_scf_name_len + 1,
+ VALUE_COMMON_NAME_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
+ encoded_value)) >= max_scf_name_len + 1) {
+ extra = encoded_count_to_plain(extra - max_scf_name_len);
+ uu_die(gettext("Name attribute is "
+ "%u characters too long for %s in service %s\n"),
+ extra, (char *)value->name, service->sc_name);
+ }
+ if ((extra = snprintf(description_fmt, max_scf_name_len + 1,
+ VALUE_DESCRIPTION_FMT, SCF_PROPERTY_TM_VALUE_PREFIX,
+ encoded_value)) >= max_scf_name_len + 1) {
+ extra = encoded_count_to_plain(extra - max_scf_name_len);
+ uu_die(gettext("Name attribute is "
+ "%u characters too long for %s in service %s\n"),
+ extra, (char *)value->name, service->sc_name);
+ }
+
+ for (cursor = value->xmlChildrenNode;
+ cursor != NULL;
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_COMMON_NAME:
+ r = lxml_get_all_loctext(service, pg, cursor,
+ common_name_fmt, (const char *)cursor->name);
+ break;
+ case SC_DESCRIPTION:
+ r = lxml_get_all_loctext(service, pg, cursor,
+ description_fmt, (const char *)cursor->name);
+ break;
+ default:
+ uu_die(gettext("\"%s\" is an illegal element in %s "
+ "of service %s\n"), (char *)cursor->name,
+ (char *)value->name, service->sc_name);
+ }
+ if (r != 0)
+ break;
+ }
+
+ free(description_fmt);
+ free(common_name_fmt);
+ if (r == 0) {
+ *name_value = safe_strdup(value_name);
+ }
+ xmlFree(value_name);
+ free(encoded_value);
+ return (r);
+}
+
+static int
+lxml_get_tm_choices(entity_t *service, pgroup_t *pg, xmlNodePtr choices)
+{
+ xmlNodePtr cursor;
+ char *name_value;
+ property_t *name_prop = NULL;
+ int r = 0;
+
+ for (cursor = choices->xmlChildrenNode;
+ (cursor != NULL) && (r == 0);
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_INCLUDE_VALUES:
+ (void) lxml_get_tm_include_values(service, pg, cursor,
+ SCF_PROPERTY_TM_CHOICES_INCLUDE_VALUES);
+ break;
+ case SC_RANGE:
+ r = lxml_get_tm_range(service, pg, cursor,
+ SCF_PROPERTY_TM_CHOICES_RANGE);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_VALUE:
+ r = lxml_get_tm_value_element(service, pg, cursor,
+ &name_value);
+ if (r == 0) {
+ /*
+ * There is no need to free the memory
+ * associated with name_value, because the
+ * property value will end up pointing to
+ * the memory.
+ */
+ astring_prop_value(&name_prop,
+ SCF_PROPERTY_TM_CHOICES_NAME, name_value,
+ B_TRUE);
+ } else {
+ goto out;
+ }
+ break;
+ default:
+ uu_die(gettext("%s is an invalid element of "
+ "choices for service %s.\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ /* Attach the name property if we created one. */
+ if ((r == 0) && (name_prop != NULL)) {
+ r = internal_attach_property(pg, name_prop);
+ }
+ if ((r != 0) && (name_prop != NULL)) {
+ internal_property_free(name_prop);
+ }
+
+ return (r);
+}
+
+static int
+lxml_get_tm_constraints(entity_t *service, pgroup_t *pg, xmlNodePtr constraints)
+{
+ xmlNodePtr cursor;
+ char *name_value;
+ property_t *name_prop = NULL;
+ int r = 0;
+
+ for (cursor = constraints->xmlChildrenNode;
+ (cursor != NULL) && (r == 0);
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_RANGE:
+ r = lxml_get_tm_range(service, pg, cursor,
+ SCF_PROPERTY_TM_CONSTRAINT_RANGE);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_VALUE:
+ r = lxml_get_tm_value_element(service, pg, cursor,
+ &name_value);
+ if (r == 0) {
+ /*
+ * There is no need to free the memory
+ * associated with name_value, because the
+ * property value will end up pointing to
+ * the memory.
+ */
+ astring_prop_value(&name_prop,
+ SCF_PROPERTY_TM_CONSTRAINT_NAME, name_value,
+ B_TRUE);
+ } else {
+ goto out;
+ }
+ break;
+ default:
+ uu_die(gettext("%s is an invalid element of "
+ "constraints for service %s.\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ /* Attach the name property if we created one. */
+ if ((r == 0) && (name_prop != NULL)) {
+ r = internal_attach_property(pg, name_prop);
+ }
+ if ((r != 0) && (name_prop != NULL)) {
+ internal_property_free(name_prop);
+ }
+
+ return (r);
+}
+
+/*
+ * The values element contains one or more value elements.
+ */
+static int
+lxml_get_tm_values(entity_t *service, pgroup_t *pg, xmlNodePtr values)
+{
+ xmlNodePtr cursor;
+ char *name_value;
+ property_t *name_prop = NULL;
+ int r = 0;
+
+ for (cursor = values->xmlChildrenNode;
+ (cursor != NULL) && (r == 0);
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+ if (lxml_xlate_element(cursor->name) != SC_VALUE) {
+ uu_die(gettext("\"%s\" is an illegal element in the "
+ "%s element of %s\n"), (char *)cursor->name,
+ (char *)values->name, service->sc_name);
+ }
+ r = lxml_get_tm_value_element(service, pg, cursor, &name_value);
+ if (r == 0) {
+ /*
+ * There is no need to free the memory
+ * associated with name_value, because the
+ * property value will end up pointing to
+ * the memory.
+ */
+ astring_prop_value(&name_prop,
+ SCF_PROPERTY_TM_VALUES_NAME, name_value,
+ B_TRUE);
+ }
+ }
+
+ /* Attach the name property if we created one. */
+ if ((r == 0) && (name_prop != NULL)) {
+ r = internal_attach_property(pg, name_prop);
+ }
+ if ((r != 0) && (name_prop != NULL)) {
+ internal_property_free(name_prop);
+ }
+
+ return (r);
+}
+
+/*
+ * This function processes a prop_pattern element within a pg_pattern XML
+ * element. First it creates a property group to hold the prop_pattern
+ * information. The name of this property group is the concatenation of:
+ * - SCF_PG_TM_PROP_PATTERN_PREFIX
+ * - The unique part of the property group name of the enclosing
+ * pg_pattern. The property group name of the enclosing pg_pattern
+ * is passed to us in pgpat_name. The unique part, is the part
+ * following SCF_PG_TM_PG_PATTERN_PREFIX.
+ * - The name of this prop_pattern element.
+ *
+ * After creating the property group, the prop_pattern attributes are saved
+ * as properties in the PG. Finally, the prop_pattern elements are
+ * processed and added to the PG.
+ */
+static int
+lxml_get_tm_prop_pattern(entity_t *service, xmlNodePtr prop_pattern,
+ const char *pgpat_name)
+{
+ xmlNodePtr cursor;
+ int extra;
+ pgroup_t *pg;
+ property_t *p;
+ char *pg_name;
+ size_t prefix_len;
+ xmlChar *prop_pattern_name;
+ int r;
+ const char *unique;
+ value_t *v;
+
+ /* Find the unique part of the pg_pattern property group name. */
+ prefix_len = strlen(SCF_PG_TM_PG_PAT_BASE);
+ assert(strncmp(pgpat_name, SCF_PG_TM_PG_PAT_BASE, prefix_len) == 0);
+ unique = pgpat_name + prefix_len;
+
+ /*
+ * We need to get the value of the name attribute first. The
+ * prop_pattern name as well as the name of the enclosing
+ * pg_pattern both constitute part of the name of the property
+ * group that we will create.
+ */
+ prop_pattern_name = xmlGetProp(prop_pattern, (xmlChar *)name_attr);
+ if ((prop_pattern_name == NULL) || (*prop_pattern_name == 0)) {
+ semerr(gettext("prop_pattern name is missing for %s\n"),
+ service->sc_name);
+ return (-1);
+ }
+ if (uu_check_name((const char *)prop_pattern_name,
+ UU_NAME_DOMAIN) != 0) {
+ semerr(gettext("prop_pattern name, \"%s\", for %s is not "
+ "valid.\n"), prop_pattern_name, service->sc_name);
+ xmlFree(prop_pattern_name);
+ return (-1);
+ }
+ pg_name = safe_malloc(max_scf_name_len + 1);
+ if ((extra = snprintf(pg_name, max_scf_name_len + 1, "%s%s_%s",
+ SCF_PG_TM_PROP_PATTERN_PREFIX, unique,
+ (char *)prop_pattern_name)) >= max_scf_name_len + 1) {
+ uu_die(gettext("prop_pattern name, \"%s\", for %s is %d "
+ "characters too long\n"), (char *)prop_pattern_name,
+ service->sc_name, extra - max_scf_name_len);
+ }
+
+ /*
+ * Create the property group, the property referencing the pg_pattern
+ * name, and add the prop_pattern attributes to the property group.
+ */
+ pg = internal_pgroup_create_strict(service, pg_name,
+ SCF_GROUP_TEMPLATE_PROP_PATTERN);
+ if (pg == NULL) {
+ uu_die(gettext("Property group for prop_pattern, \"%s\", "
+ "already exists in %s\n"), prop_pattern_name,
+ service->sc_name);
+ }
+
+ p = internal_property_create(SCF_PROPERTY_TM_PG_PATTERN,
+ SCF_TYPE_ASTRING, 1, safe_strdup(pgpat_name));
+ /*
+ * Unfortunately, internal_property_create() does not set the free
+ * function for the value, so we'll set it now.
+ */
+ v = uu_list_first(p->sc_property_values);
+ v->sc_free = lxml_free_str;
+ if (internal_attach_property(pg, p) != 0)
+ internal_property_free(p);
+
+
+ r = lxml_get_prop_pattern_attributes(pg, prop_pattern);
+ if (r != 0)
+ goto out;
+
+ /*
+ * Now process the elements of prop_pattern
+ */
+ for (cursor = prop_pattern->xmlChildrenNode;
+ cursor != NULL;
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_CARDINALITY:
+ r = lxml_get_tm_cardinality(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_CHOICES:
+ r = lxml_get_tm_choices(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_COMMON_NAME:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ COMMON_NAME_FMT, (const char *)cursor->name);
+ break;
+ case SC_CONSTRAINTS:
+ r = lxml_get_tm_constraints(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_DESCRIPTION:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ DESCRIPTION_FMT, (const char *)cursor->name);
+ break;
+ case SC_INTERNAL_SEPARATORS:
+ r = lxml_get_tm_internal_seps(service, pg, cursor);
+ if (r != 0)
+ goto out;
+ break;
+ case SC_UNITS:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ UNITS_FMT, "units");
+ break;
+ case SC_VALUES:
+ (void) lxml_get_tm_values(service, pg, cursor);
+ break;
+ case SC_VISIBILITY:
+ /*
+ * The visibility element is empty, so we only need
+ * to proccess the value attribute.
+ */
+ (void) new_str_prop_from_attr(pg,
+ SCF_PROPERTY_TM_VISIBILITY, SCF_TYPE_ASTRING,
+ cursor, value_attr);
+ break;
+ default:
+ uu_die(gettext("illegal element \"%s\" in prop_pattern "
+ "for service \"%s\"\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ xmlFree(prop_pattern_name);
+ free(pg_name);
+ return (r);
+}
+
+/*
+ * Get the pg_pattern attributes and save them as properties in the
+ * property group at pg. The pg_pattern element accepts four attributes --
+ * name, type, required and target.
+ */
+static int
+lxml_get_pg_pattern_attributes(pgroup_t *pg, xmlNodePtr cursor)
+{
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_NAME,
+ SCF_TYPE_ASTRING, cursor, name_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TYPE,
+ SCF_TYPE_ASTRING, cursor, type_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_opt_str_prop_from_attr(pg, SCF_PROPERTY_TM_TARGET,
+ SCF_TYPE_ASTRING, cursor, target_attr, NULL) != 0) {
+ return (-1);
+ }
+ if (new_bool_prop_from_attr(pg, SCF_PROPERTY_TM_REQUIRED, cursor,
+ required_attr) != 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * There are several restrictions on the pg_pattern attributes that cannot
+ * be specifed in the service bundle DTD. This function verifies that
+ * those restrictions have been satisfied. The restrictions are:
+ *
+ * - The target attribute may have a value of "instance" only when the
+ * template block is in a service declaration.
+ *
+ * - The target attribute may have a value of "delegate" only when the
+ * template block applies to a restarter.
+ *
+ * - The target attribute may have a value of "all" only when the
+ * template block applies to the master restarter.
+ *
+ * The function returns 0 on success and -1 on failure.
+ */
+static int
+verify_pg_pattern_attributes(entity_t *s, pgroup_t *pg)
+{
+ int is_restarter;
+ property_t *target;
+ value_t *v;
+
+ /* Find the value of the target property. */
+ target = internal_property_find(pg, SCF_PROPERTY_TM_TARGET);
+ if (target == NULL) {
+ uu_die(gettext("pg_pattern is missing the %s attribute "
+ "in %s\n"), target_attr, s->sc_name);
+ return (-1);
+ }
+ v = uu_list_first(target->sc_property_values);
+ assert(v != NULL);
+ assert(v->sc_type == SCF_TYPE_ASTRING);
+
+ /*
+ * If target has a value of instance, the template must be in a
+ * service object.
+ */
+ if (strcmp(v->sc_u.sc_string, "instance") == 0) {
+ if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
+ uu_warn(gettext("pg_pattern %s attribute may only "
+ "have a value of \"instance\" when it is in a "
+ "service declaration.\n"), target_attr);
+ return (-1);
+ }
+ }
+
+ /*
+ * If target has a value of "delegate", the template must be in a
+ * restarter.
+ */
+ if (strcmp(v->sc_u.sc_string, "delegate") == 0) {
+ is_restarter = 0;
+ if ((s->sc_etype == SVCCFG_SERVICE_OBJECT) &&
+ (s->sc_u.sc_service.sc_service_type == SVCCFG_RESTARTER)) {
+ is_restarter = 1;
+ }
+ if ((s->sc_etype == SVCCFG_INSTANCE_OBJECT) &&
+ (s->sc_parent->sc_u.sc_service.sc_service_type ==
+ SVCCFG_RESTARTER)) {
+ is_restarter = 1;
+ }
+ if (is_restarter == 0) {
+ uu_warn(gettext("pg_pattern %s attribute has a "
+ "value of \"delegate\" but is not in a "
+ "restarter service\n"), target_attr);
+ return (-1);
+ }
+ }
+
+ /*
+ * If target has a value of "all", the template must be in the
+ * global (SCF_SERVICE_GLOBAL) service.
+ */
+ if (strcmp(v->sc_u.sc_string, all_value) == 0) {
+ if (s->sc_etype != SVCCFG_SERVICE_OBJECT) {
+ uu_warn(gettext("pg_pattern %s attribute has a "
+ "value of \"%s\" but is not in a "
+ "service entity.\n"), target_attr, all_value);
+ return (-1);
+ }
+ if (strcmp(s->sc_fmri, SCF_SERVICE_GLOBAL) != 0) {
+ uu_warn(gettext("pg_pattern %s attribute has a "
+ "value of \"%s\" but is in the \"%s\" service. "
+ "pg_patterns with target \"%s\" are only allowed "
+ "in the global service.\n"),
+ target_attr, all_value, s->sc_fmri, all_value);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+lxml_get_tm_pg_pattern(entity_t *service, xmlNodePtr pg_pattern)
+{
+ xmlNodePtr cursor;
+ int out_len;
+ xmlChar *name;
+ pgroup_t *pg = NULL;
+ char *pg_name;
+ int r = -1;
+ xmlChar *type;
+
+ pg_name = safe_malloc(max_scf_name_len + 1);
+
+ /*
+ * Get the name and type attributes. Their presence or absence
+ * determines whcih prefix we will use for the property group name.
+ * There are four cases -- neither attribute is present, both are
+ * present, only name is present or only type is present.
+ */
+ name = xmlGetProp(pg_pattern, (xmlChar *)name_attr);
+ type = xmlGetProp(pg_pattern, (xmlChar *)type_attr);
+ if ((name == NULL) || (*name == 0)) {
+ if ((type == NULL) || (*type == 0)) {
+ /* PG name contains only the prefix in this case */
+ if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
+ max_scf_name_len + 1) >= max_scf_name_len + 1) {
+ uu_die(gettext("Unable to create pg_pattern "
+ "property for %s\n"), service->sc_name);
+ }
+ } else {
+ /*
+ * If we have a type and no name, the type becomes
+ * part of the pg_pattern property group name.
+ */
+ if ((out_len = snprintf(pg_name, max_scf_name_len + 1,
+ "%s%s", SCF_PG_TM_PG_PATTERN_T_PREFIX, type)) >=
+ max_scf_name_len + 1) {
+ uu_die(gettext("pg_pattern type is for %s is "
+ "%d bytes too long\n"), service->sc_name,
+ out_len - max_scf_name_len);
+ }
+ }
+ } else {
+ const char *prefix;
+
+ /* Make sure that the name is valid. */
+ if (uu_check_name((const char *)name, UU_NAME_DOMAIN) != 0) {
+ semerr(gettext("pg_pattern name attribute, \"%s\", "
+ "for %s is invalid\n"), name, service->sc_name);
+ goto out;
+ }
+
+ /*
+ * As long as the pg_pattern has a name, it becomes part of
+ * the name of the pg_pattern property group name. We
+ * merely need to pick the appropriate prefix.
+ */
+ if ((type == NULL) || (*type == 0)) {
+ prefix = SCF_PG_TM_PG_PATTERN_N_PREFIX;
+ } else {
+ prefix = SCF_PG_TM_PG_PATTERN_NT_PREFIX;
+ }
+ if ((out_len = snprintf(pg_name, max_scf_name_len + 1, "%s%s",
+ prefix, name)) >= max_scf_name_len + 1) {
+ uu_die(gettext("pg_pattern property group name "
+ "for %s is %d bytes too long\n"), service->sc_name,
+ out_len - max_scf_name_len);
+ }
+ }
+
+ /*
+ * Create the property group for holding this pg_pattern
+ * information, and capture the pg_pattern attributes.
+ */
+ pg = internal_pgroup_create_strict(service, pg_name,
+ SCF_GROUP_TEMPLATE_PG_PATTERN);
+ if (pg == NULL) {
+ if ((name == NULL) || (*name == 0)) {
+ if ((type == NULL) ||(*type == 0)) {
+ semerr(gettext("pg_pattern with empty name and "
+ "type is not unique in %s\n"),
+ service->sc_name);
+ } else {
+ semerr(gettext("pg_pattern with empty name and "
+ "type \"%s\" is not unique in %s\n"),
+ type, service->sc_name);
+ }
+ } else {
+ if ((type == NULL) || (*type == 0)) {
+ semerr(gettext("pg_pattern with name \"%s\" "
+ "and empty type is not unique in %s\n"),
+ name, service->sc_name);
+ } else {
+ semerr(gettext("pg_pattern with name \"%s\" "
+ "and type \"%s\" is not unique in %s\n"),
+ name, type, service->sc_name);
+ }
+ }
+ goto out;
+ }
+
+ /*
+ * Get the pg_pattern attributes from the manifest and verify
+ * that they satisfy our restrictions.
+ */
+ r = lxml_get_pg_pattern_attributes(pg, pg_pattern);
+ if (r != 0)
+ goto out;
+ if (verify_pg_pattern_attributes(service, pg) != 0) {
+ semerr(gettext("Invalid pg_pattern attributes in %s\n"),
+ service->sc_name);
+ r = -1;
+ goto out;
+ }
+
+ /*
+ * Now process all of the elements of pg_pattern.
+ */
+ for (cursor = pg_pattern->xmlChildrenNode;
+ cursor != NULL;
+ cursor = cursor->next) {
+ if (lxml_ignorable_block(cursor))
+ continue;
+
+ switch (lxml_xlate_element(cursor->name)) {
+ case SC_COMMON_NAME:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ COMMON_NAME_FMT, (const char *)cursor->name);
+ break;
+ case SC_DESCRIPTION:
+ (void) lxml_get_all_loctext(service, pg, cursor,
+ DESCRIPTION_FMT, (const char *)cursor->name);
+ break;
+ case SC_PROP_PATTERN:
+ r = lxml_get_tm_prop_pattern(service, cursor,
+ pg_name);
+ if (r != 0)
+ goto out;
+ break;
+ default:
+ uu_die(gettext("illegal element \"%s\" in pg_pattern "
+ "for service \"%s\"\n"), cursor->name,
+ service->sc_name);
+ }
+ }
+
+out:
+ if ((r != 0) && (pg != NULL)) {
+ internal_detach_pgroup(service, pg);
+ internal_pgroup_free(pg);
+ }
+ free(pg_name);
+ xmlFree(name);
+ xmlFree(type);
+
+ return (r);
+}
+
+static int
lxml_get_template(entity_t *service, xmlNodePtr templ)
{
xmlNodePtr cursor;
@@ -1439,6 +2752,10 @@ lxml_get_template(entity_t *service, xmlNodePtr templ)
case SC_DOCUMENTATION:
(void) lxml_get_tm_documentation(service, cursor);
break;
+ case SC_PG_PATTERN:
+ if (lxml_get_tm_pg_pattern(service, cursor) != 0)
+ return (-1);
+ break;
default:
uu_die(gettext("illegal element \"%s\" on template "
"for service \"%s\"\n"),
@@ -1581,7 +2898,8 @@ lxml_get_instance(entity_t *service, xmlNodePtr inst, svccfg_op_t op)
(void) lxml_get_pgroup(i, cursor);
break;
case SC_TEMPLATE:
- (void) lxml_get_template(i, cursor);
+ if (lxml_get_template(i, cursor) != 0)
+ return (-1);
break;
default:
uu_die(gettext(
@@ -1636,7 +2954,7 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
s = internal_service_new((char *)xmlGetProp(svc,
(xmlChar *)name_attr));
- version = xmlGetProp(svc, (xmlChar *)"version");
+ version = xmlGetProp(svc, (xmlChar *)version_attr);
s->sc_u.sc_service.sc_service_version = atol((const char *)version);
xmlFree(version);
@@ -1664,10 +2982,12 @@ lxml_get_service(bundle_t *bundle, xmlNodePtr svc, svccfg_op_t op)
switch (e) {
case SC_INSTANCE:
- (void) lxml_get_instance(s, cursor, op);
+ if (lxml_get_instance(s, cursor, op) != 0)
+ return (-1);
break;
case SC_TEMPLATE:
- (void) lxml_get_template(s, cursor);
+ if (lxml_get_template(s, cursor) != 0)
+ return (-1);
break;
case SC_STABILITY:
(void) lxml_get_entity_stability(s, cursor);
@@ -1741,7 +3061,7 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
/*
* 1. Get bundle attributes.
*/
- type = xmlGetProp(subbundle, (xmlChar *)"type");
+ type = xmlGetProp(subbundle, (xmlChar *)type_attr);
bundle->sc_bundle_type = lxml_xlate_bundle_type(type);
if (bundle->sc_bundle_type != bundle_type &&
bundle_type != SVCCFG_UNKNOWN_BUNDLE) {
@@ -1772,8 +3092,8 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
break;
}
- if ((bundle->sc_bundle_name = xmlGetProp(subbundle,
- (xmlChar *)"name")) == NULL) {
+ if (((bundle->sc_bundle_name = xmlGetProp(subbundle,
+ (xmlChar *)name_attr)) == NULL) || (*bundle->sc_bundle_name == 0)) {
semerr(gettext("service bundle lacks name attribute\n"));
return (-1);
}
@@ -1797,7 +3117,8 @@ lxml_get_bundle(bundle_t *bundle, bundle_type_t bundle_type,
return (-1);
break;
case SC_SERVICE:
- (void) lxml_get_service(bundle, cursor, op);
+ if (lxml_get_service(bundle, cursor, op) != 0)
+ return (-1);
break;
}
}