#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"
#define CLEAR_ERROR_INFO(ei) ((void) memset((ei), 0, sizeof (error_info_t)))
#define CPG2PG(cpg) (cpg->cpg_instance_pg ? cpg->cpg_instance_pg : \
cpg->cpg_service_pg)
#define EMPTY_TO_NULL(p) (((p) && (*(p) == 0)) ? NULL : (p))
#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
typedef enum err_info_type {
EIT_NONE,
EIT_BAD_TEMPLATE,
EIT_CARDINALITY,
EIT_INCLUDE_VALUES,
EIT_MISSING_PG,
EIT_MISSING_PROP,
EIT_PATTERN_CONFLICT,
EIT_PROP_TYPE,
EIT_RANGE
} err_info_type_t;
typedef struct error_info {
err_info_type_t ei_type;
union {
struct {
const char *ei_reason;
} ei_bad_template;
struct {
uint64_t ei_min;
uint64_t ei_max;
uint64_t ei_count;
} ei_cardinality;
struct {
const char *ei_type;
} ei_inc_values;
struct {
const char *ei_pg_name;
const char *ei_pg_type;
} ei_missing_pg;
struct {
const char *ei_prop_name;
} ei_missing_prop;
struct {
pgroup_t *ei_pattern;
} ei_pattern_conflict;
struct {
scf_type_t ei_actual;
scf_type_t ei_specified;
} ei_prop_type;
struct {
scf_type_t ei_rtype;
int64_t ei_ivalue;
uint64_t ei_uvalue;
} ei_range;
} ei_u;
} error_info_t;
typedef struct im_tmpl_error {
tmpl_validate_status_t ite_type;
entity_t *ite_entity;
pgroup_t *ite_pg;
pgroup_t *ite_pg_pattern;
property_t *ite_prop;
pgroup_t *ite_prop_pattern;
value_t *ite_value;
error_info_t ite_einfo;
uu_list_node_t ite_node;
} im_tmpl_error_t;
typedef struct tv_errors {
scf_tmpl_errors_t *tve_errors;
uu_list_node_t tve_node;
} tv_errors_t;
struct tmpl_errors {
uu_list_t *te_list;
im_tmpl_error_t *te_next;
uu_list_t *te_scf;
tv_errors_t *te_cur_scf;
};
typedef enum pg_type {
NORMAL_PG,
PG_PATTERN_PG,
PROP_PATTERN_PG
} pg_type_t;
typedef struct avalues {
uint_t av_count;
scf_type_t av_type;
union {
uint64_t *av_unsigned;
int64_t *av_integer;
const char **av_string;
} av_v;
} avalues_t;
struct composed_pg {
const char *cpg_name;
const char *cpg_type;
pgroup_t *cpg_instance_pg;
pgroup_t *cpg_service_pg;
uu_avl_t *cpg_composed_props;
uu_avl_node_t cpg_node;
};
typedef struct prop_prefix {
const char *pp_prefix;
size_t pp_size;
} prop_prefix_t;
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;
typedef enum tmpl_level {
TL_NOLEVEL = 0,
TL_INSTANCE,
TL_COMPOSED,
TL_SERVICE,
TL_RESTARTER,
TL_GLOBAL
} tmpl_level_t;
typedef struct pg_iter {
entity_t *pgi_entity;
const char *pgi_restrict;
tmpl_level_t pgi_level;
entity_t *pgi_service;
union {
pgroup_t *pgi_pg;
composed_pg_t *pgi_cpg;
} pgi_current;
} pg_iter_t;
typedef enum ptrn_type {
PG_PATTERN,
PROP_PATTERN
} ptrn_type_t;
typedef struct ptrn_info {
ptrn_type_t pi_ptrn_type;
pgroup_t *pi_ptrnpg;
const char *pi_name;
const char *pi_type;
const char *pi_target;
const char *pi_pgp_name;
pgroup_t *pi_enc_pgp;
uu_avl_node_t pi_link;
} ptrn_info_t;
static const char *emesg_nomem;
static uu_avl_pool_t *composed_pg_pool;
static uu_avl_pool_t *composed_prop_pool;
static uu_list_pool_t *inmem_errors_pool;
static uu_avl_pool_t *ptrn_info_pool;
static uu_list_pool_t *tv_errors_pool;
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
};
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);
}
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;
if ((cpg->cpg_composed_props != NULL)) {
while (uu_avl_teardown(cpg->cpg_composed_props, &marker) !=
NULL) {
}
uu_avl_destroy(cpg->cpg_composed_props);
}
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);
}
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) {
uu_avl_insert(tree, prop, marker);
}
}
}
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;
assert(cpg->cpg_instance_pg != NULL);
grow_props_tree(cpg->cpg_instance_pg, tree);
assert(cpg->cpg_service_pg != NULL);
grow_props_tree(cpg->cpg_service_pg, tree);
}
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;
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);
assert(match == NULL);
uu_avl_insert(tree, cpg, marker);
pg->sc_pgroup_composed = cpg;
}
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);
cpg = NULL;
} else {
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;
}
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);
}
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)) {
return (internal_property_find(pg, name));
}
look.sc_property_name = (char *)name;
return (uu_avl_find(cpg->cpg_composed_props, &look, NULL, NULL));
}
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:
uu_free(av->av_v.av_string);
break;
}
uu_free(av);
}
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);
}
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));
}
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));
}
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));
}
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);
}
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);
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);
}
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);
}
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);
}
static const char *
find_name_specification(pgroup_t *pattern)
{
return (find_astring_value_in_pg(pattern, SCF_PROPERTY_TM_NAME));
}
static const char *
find_type_specification(pgroup_t *pattern)
{
return (find_astring_value_in_pg(pattern, SCF_PROPERTY_TM_TYPE));
}
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);
}
}
return (NULL);
}
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);
}
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);
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;
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);
}
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);
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;
*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);
}
#define IPI_NOT_FIRST 0x1
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;
if (item == NULL)
return;
assert(type != SCF_TYPE_INVALID);
if (est->sc_cmd_flags & SC_CMD_IACTIVE) {
first_sep = ":\n\t";
subsequent_sep = "\n\t";
} else {
first_sep = ": ";
subsequent_sep = "; ";
}
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:
(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;
}
}
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);
}
}
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);
}
}
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);
}
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);
}
}
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);
}
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);
}
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);
}
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);
}
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:
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);
}
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));
}
#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
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));
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);
}
for (i = 0; i < ED_COUNT; i++) {
if (ed[i] == NULL)
continue;
ed[i] = safe_strdup(ed[i]);
}
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:
if (pg_pattern != NULL) {
ev1 = find_type_specification(pg_pattern);
if (ev1 != NULL) {
ev1 = safe_strdup(ev1);
}
}
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);
}
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);
}
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);
if (required == NULL)
return (0);
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);
return (0);
}
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 ((strcmp(svc->sc_fmri, SCF_INSTANCE_GLOBAL) == 0) ||
(strcmp(svc->sc_fmri, SCF_SERVICE_GLOBAL) == 0)) {
is_global = 1;
}
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) {
if (r == EINVAL)
warn(gettext("WARNING: restarter FMRI %s is invalid\n"),
restarter);
if (r == ENOTSUP)
warn(gettext("WARNING: restarter FMRI %s is not valid; "
"instance fmri required.\n"), restarter);
if (r == ENOMEM)
uu_die(emesg_nomem);
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) {
if (r == ENOMEM)
uu_die(emesg_nomem);
else
svc->sc_u.sc_service.sc_global = NULL;
}
}
}
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) {
return;
}
r = load_instance(restarter, "instance_restarter",
&i->sc_u.sc_instance.sc_instance_restarter);
if (r != 0) {
if (r == EINVAL)
warn(gettext("WARNING: restarter FMRI %s is invalid\n"),
restarter);
if (r == ENOTSUP)
warn(gettext("WARNING: restarter FMRI %s is not valid; "
"instance fmri required.\n"), restarter);
if (r == ENOMEM)
uu_die(emesg_nomem);
}
}
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)) {
if (current) {
prop = uu_avl_next(cpg->cpg_composed_props, current);
} else {
prop = uu_avl_first(cpg->cpg_composed_props);
}
} else {
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));
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);
}
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) {
ptrn_info_destroy(info);
}
info = ptrn_info_create(pg);
match = uu_avl_find(tree, info, NULL, &marker);
if (match == NULL) {
uu_avl_insert(tree, info, marker);
info = NULL;
continue;
}
if ((info->pi_name == NULL) ||
(info->pi_type == NULL) ||
(match->pi_name == NULL) ||
(match->pi_type == NULL)) {
continue;
}
if (strcmp(info->pi_type, match->pi_type) == 0) {
continue;
}
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) {
break;
}
} else {
if ((info->pi_pgp_name == NULL) ||
(match->pi_pgp_name == NULL)) {
continue;
}
if (strcmp(info->pi_pgp_name, match->pi_pgp_name) != 0)
continue;
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) {
break;
}
}
}
ptrn_info_destroy(info);
return (rc);
}
static void
pg_iter_destroy(pg_iter_t *i)
{
if (i == NULL)
return;
uu_free(i);
}
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);
}
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) {
(void) memset(&i->pgi_current, 0,
sizeof (i->pgi_current));
break;
}
if (i->pgi_restrict) {
if (strcmp(pg->sc_pgroup_type, i->pgi_restrict) != 0) {
continue;
}
}
return (pg);
}
switch (i->pgi_level) {
case TL_COMPOSED:
e = i->pgi_entity;
if (e->sc_u.sc_instance.sc_instance_restarter == NULL) {
i->pgi_entity =
i->pgi_service->sc_u.sc_service.sc_restarter;
} else {
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();
}
return (next_pattern_pg(i));
}
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)) {
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 (l->pi_type == NULL)
return (-1);
return (1);
}
if (l->pi_name == NULL)
return (-1);
return (1);
}
static int
target_check(const char *target, tmpl_level_t level)
{
if ((target == NULL) || (*target == 0)) {
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) {
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));
}
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);
}
static int
property_is_type(property_t *prop, scf_type_t type)
{
return (scf_is_compatible_type(type, prop->sc_value_type) ==
SCF_SUCCESS);
}
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;
if ((name == NULL) || (*name == 0)) {
if ((type == NULL) || (*type == 0)) {
if (strlcpy(pg_name, SCF_PG_TM_PG_PATTERN_PREFIX,
name_size) >= name_size) {
rv = NULL;
}
} else {
if (snprintf(pg_name, name_size, "%s%s",
SCF_PG_TM_PG_PATTERN_T_PREFIX, type) >=
name_size) {
rv = NULL;
}
}
} else {
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) {
free(pg_name);
}
return (rv);
}
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);
}
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);
}
}
}
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);
}
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);
}
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;
rc = gather_pattern(inst, type, tree, errs);
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) {
continue;
}
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) {
break;
}
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 (rv != 0)
break;
}
}
return (rc);
}
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;
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) {
continue;
}
if (ginfo != NULL)
ptrn_info_destroy(ginfo);
ginfo = ptrn_info_create(pg);
match = uu_avl_find(tree, ginfo, NULL, NULL);
if (match != NULL) {
if (target_check(ginfo->pi_target, level) == 0)
continue;
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;
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) {
break;
}
}
}
if (ginfo != NULL)
ptrn_info_destroy(ginfo);
return (rc);
}
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) {
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);
}
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;
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);
r = tmpl_level_redefine(inst, tree, errs);
if (r != TVS_SUCCESS) {
if (r == TVS_WARN) {
if (rc == TVS_SUCCESS)
rc = r;
} else {
rc = r;
}
}
r = tmpl_required_attr_present(tree, errs);
if (r != TVS_SUCCESS)
rc = r;
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;
r = tmpl_required_attr_present(tree, errs);
if (r != TVS_SUCCESS)
rc = r;
r = tmpl_include_values_check(tree, errs);
if (r != TVS_SUCCESS)
rc = r;
marker = NULL;
while ((info = uu_avl_teardown(tree, &marker)) != NULL) {
ptrn_info_destroy(info);
}
uu_avl_destroy(tree);
return (rc);
}
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);
}
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);
}
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);
}
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;
rc = gen_prop_pattern_pg_name(pg_pattern,
prop->sc_property_name, &prop_pattern_name);
if (rc != TVS_SUCCESS)
goto out;
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);
}
#define PGN_BOTH 0
#define PGN_NAME 1
#define PGN_TYPE 2
#define PGN_NEITHER 3
#define PGN_MAX 4
static tmpl_validate_status_t
tmpl_find_pg_pattern(entity_t *e, pgroup_t *pg, pgroup_t **pgp)
{
pgroup_t *cpg;
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;
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;
}
}
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;
for (i = 0; i < PGN_MAX; i++) {
if (strcmp(cpg->sc_pgroup_name, pg_names[i]) == 0) {
if (pg_patterns[i] == NULL)
pg_patterns[i] = cpg;
break;
}
}
}
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);
}
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()));
}
}
void
tmpl_property_fini(property_t *p)
{
uu_avl_node_fini(p, &p->sc_composed_node, composed_prop_pool);
}
void
tmpl_property_init(property_t *p)
{
uu_avl_node_init(p, &p->sc_composed_node, composed_prop_pool);
}
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:
return (TVS_SUCCESS);
case TVS_SUCCESS:
break;
default:
return (rc);
}
if ((min == 0) && (max == ULLONG_MAX)) {
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);
}
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) {
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);
}
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;
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)) {
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 (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) {
break;
}
}
}
pg_iter_destroy(iter);
return (rc);
}
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);
}
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;
if ((range_prop = property_find(pattern,
SCF_PROPERTY_TM_CONSTRAINT_RANGE)) == NULL) {
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);
}
CLEAR_ERROR_INFO(&einfo);
einfo.ei_type = EIT_RANGE;
einfo.ei_u.ei_range.ei_rtype = type;
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);
}
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;
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:
return (TVS_SUCCESS);
case TVS_SUCCESS:
break;
default:
assert(0);
abort();
}
for (v = uu_list_first(prop->sc_property_values);
v != NULL;
v = uu_list_next(prop->sc_property_values, v)) {
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) {
break;
}
}
}
av_destroy(legal);
return (rc);
}
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);
}
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) {
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) {
return (rc);
}
}
}
break;
case TVS_MISSING_TYPE_SPECIFICATION:
break;
default:
rc = r;
}
r = tmpl_validate_cardinality(tmpl, prop, pg, pg_pattern, errs);
if (r != TVS_SUCCESS)
rc = r;
r = tmpl_validate_value_constraints(tmpl, prop, pg, pg_pattern, errs);
if (r != TVS_SUCCESS)
rc = r;
return (rc);
}
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;
pgroup_t *pg_pattern;
property_t *prop;
pgroup_t *prop_pattern;
tmpl_validate_status_t r;
tmpl_validate_status_t rc = TVS_SUCCESS;
int stat;
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);
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) {
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) {
return (rc);
}
}
}
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:
break;
case TVS_NOMATCH:
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;
}
r = tmpl_required_props_present(e, pg, pg_pattern, errs);
if (r != TVS_SUCCESS)
rc = r;
return (rc);
}
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);
}
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;
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;
rc = tmpl_consistency(e, errs);
r = tmpl_validate_entity_pgs(e, errs);
if (r != TVS_SUCCESS)
rc = r;
r = tmpl_required_pg_present(e, errs);
if (r != TVS_SUCCESS)
rc = r;
return (rc);
}
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);
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);
}
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();
if (load_init() != 0)
uu_die(emesg_nomem);
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);
}