#include "libscf_impl.h"
#include <assert.h>
#include <strings.h>
static const scf_error_t errs_1[] = {
SCF_ERROR_BACKEND_ACCESS,
SCF_ERROR_BACKEND_READONLY,
SCF_ERROR_CONNECTION_BROKEN,
SCF_ERROR_DELETED,
SCF_ERROR_INTERNAL,
SCF_ERROR_INVALID_ARGUMENT,
SCF_ERROR_NO_MEMORY,
SCF_ERROR_NO_RESOURCES,
SCF_ERROR_NOT_FOUND,
SCF_ERROR_PERMISSION_DENIED,
0
};
static const scf_error_t errs_2[] = {
SCF_ERROR_BACKEND_ACCESS,
SCF_ERROR_BACKEND_READONLY,
SCF_ERROR_CONNECTION_BROKEN,
SCF_ERROR_DELETED,
SCF_ERROR_INTERNAL,
SCF_ERROR_NO_MEMORY,
SCF_ERROR_NO_RESOURCES,
SCF_ERROR_NOT_FOUND,
SCF_ERROR_PERMISSION_DENIED,
0
};
static int
check_scf_error(scf_error_t e, const scf_error_t *errs)
{
if (ismember(e, errs))
return (1);
assert(0);
abort();
}
static struct st_pgname {
const char *st_pgname;
int32_t st_state;
} st_pgnames[] = {
{ "to-uninitialized", SCF_TRANS(0, SCF_STATE_UNINIT) },
{ "from-uninitialized", SCF_TRANS(SCF_STATE_UNINIT, 0) },
{ "to-maintenance", SCF_TRANS(0, SCF_STATE_MAINT) },
{ "from-maintenance", SCF_TRANS(SCF_STATE_MAINT, 0) },
{ "to-offline", SCF_TRANS(0, SCF_STATE_OFFLINE) },
{ "from-offline", SCF_TRANS(SCF_STATE_OFFLINE, 0) },
{ "to-disabled", SCF_TRANS(0, SCF_STATE_DISABLED) },
{ "from-disabled", SCF_TRANS(SCF_STATE_DISABLED, 0) },
{ "to-online", SCF_TRANS(0, SCF_STATE_ONLINE) },
{ "from-online", SCF_TRANS(SCF_STATE_ONLINE, 0) },
{ "to-degraded", SCF_TRANS(0, SCF_STATE_DEGRADED) },
{ "from-degraded", SCF_TRANS(SCF_STATE_DEGRADED, 0) },
{ NULL, 0 }
};
static boolean_t
is_svc_stn(const char *class)
{
int n = strlen(SCF_SVC_TRANSITION_CLASS);
if (class && strncmp(class, SCF_SVC_TRANSITION_CLASS, n) == 0)
if (class[n] == '\0' || class[n] == '.')
return (1);
return (0);
}
static size_t
base_class_len(const char *c)
{
const char *p;
size_t n;
if ((n = strlen(c)) == 0)
return (0);
p = c + n;
if (*--p == '*')
n--;
while (p >= c && *--p == '.')
n--;
return (n);
}
static char *
class_to_pgname(const char *class)
{
size_t n;
ssize_t sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
char *pgname = NULL;
n = base_class_len(class);
if (n == 0) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
return (NULL);
}
if ((pgname = malloc(sz)) == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto error;
}
if (snprintf(pgname, sz, "%.*s,%s", (int)n, class,
SCF_NOTIFY_PG_POSTFIX) >= sz) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto error;
}
return (pgname);
error:
free(pgname);
pgname = NULL;
return (pgname);
}
static int
get_pg(scf_service_t *s, scf_instance_t *i, const char *n,
scf_propertygroup_t *pg, int composed)
{
scf_handle_t *h = scf_instance_handle(i);
scf_error_t scf_e = scf_error();
scf_snapshot_t *snap = scf_snapshot_create(h);
scf_snaplevel_t *slvl = scf_snaplevel_create(h);
int r = -1;
if (h == NULL) {
(void) scf_set_error(scf_e);
goto out;
}
if (s == NULL) {
if (snap == NULL || slvl == NULL)
goto out;
if (scf_instance_get_snapshot(i, "running", snap) != 0)
goto out;
if (composed) {
if (scf_instance_get_pg_composed(i, snap, n, pg) != 0)
goto out;
} else {
if (scf_snapshot_get_base_snaplevel(snap, slvl) != 0 ||
scf_snaplevel_get_pg(slvl, n, pg) != 0)
goto out;
}
} else {
if (scf_service_get_pg(s, n, pg) != 0)
goto out;
}
r = 0;
out:
scf_snaplevel_destroy(slvl);
scf_snapshot_destroy(snap);
return (r);
}
static int
get_or_add_pg(scf_service_t *s, scf_instance_t *i, const char *n, const char *t,
uint32_t flags, scf_propertygroup_t *pg)
{
int r;
if (s == NULL)
r = scf_instance_add_pg(i, n, t, flags, pg);
else
r = scf_service_add_pg(s, n, t, flags, pg);
if (r == 0)
return (0);
else if (scf_error() != SCF_ERROR_EXISTS)
return (-1);
if (s == NULL)
r = scf_instance_get_pg(i, n, pg);
else
r = scf_service_get_pg(s, n, pg);
return (r);
}
static int
del_pg(scf_service_t *s, scf_instance_t *i, const char *n,
scf_propertygroup_t *pg)
{
if ((s == NULL ? scf_instance_get_pg(i, n, pg) :
scf_service_get_pg(s, n, pg)) != SCF_SUCCESS)
if (scf_error() == SCF_ERROR_NOT_FOUND)
return (SCF_SUCCESS);
else
return (SCF_FAILED);
if (scf_pg_delete(pg) != SCF_SUCCESS)
if (scf_error() == SCF_ERROR_DELETED)
return (SCF_SUCCESS);
else
return (SCF_FAILED);
return (SCF_SUCCESS);
}
static scf_type_t
get_scf_type(nvpair_t *p)
{
switch (nvpair_type(p)) {
case DATA_TYPE_BOOLEAN:
case DATA_TYPE_BOOLEAN_VALUE:
case DATA_TYPE_BOOLEAN_ARRAY:
return (SCF_TYPE_BOOLEAN);
case DATA_TYPE_BYTE:
case DATA_TYPE_UINT8:
case DATA_TYPE_UINT16:
case DATA_TYPE_UINT32:
case DATA_TYPE_UINT64:
case DATA_TYPE_BYTE_ARRAY:
case DATA_TYPE_UINT8_ARRAY:
case DATA_TYPE_UINT16_ARRAY:
case DATA_TYPE_UINT32_ARRAY:
case DATA_TYPE_UINT64_ARRAY:
return (SCF_TYPE_COUNT);
case DATA_TYPE_INT8:
case DATA_TYPE_INT16:
case DATA_TYPE_INT32:
case DATA_TYPE_INT64:
case DATA_TYPE_INT8_ARRAY:
case DATA_TYPE_INT16_ARRAY:
case DATA_TYPE_INT32_ARRAY:
case DATA_TYPE_INT64_ARRAY:
return (SCF_TYPE_INTEGER);
case DATA_TYPE_STRING:
case DATA_TYPE_STRING_ARRAY:
return (SCF_TYPE_ASTRING);
default:
return (SCF_TYPE_INVALID);
}
}
static int
add_entry(scf_transaction_entry_t *te, scf_value_t *val)
{
if (scf_entry_add_value(te, val) != 0) {
scf_value_destroy(val);
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
static int
add_boolean_entry(scf_handle_t *h, scf_transaction_entry_t *te, uint8_t v)
{
scf_value_t *val = scf_value_create(h);
if (val == NULL)
return (SCF_FAILED);
scf_value_set_boolean(val, v);
return (add_entry(te, val));
}
static int
add_count_entry(scf_handle_t *h, scf_transaction_entry_t *te, uint64_t v)
{
scf_value_t *val = scf_value_create(h);
if (val == NULL)
return (SCF_FAILED);
scf_value_set_count(val, v);
return (add_entry(te, val));
}
static int
add_integer_entry(scf_handle_t *h, scf_transaction_entry_t *te, int64_t v)
{
scf_value_t *val = scf_value_create(h);
if (val == NULL)
return (SCF_FAILED);
scf_value_set_integer(val, v);
return (add_entry(te, val));
}
static int
add_astring_entry(scf_handle_t *h, scf_transaction_entry_t *te, char *s)
{
scf_value_t *val = scf_value_create(h);
if (val == NULL)
return (SCF_FAILED);
if (scf_value_set_astring(val, s) != 0) {
scf_value_destroy(val);
return (SCF_FAILED);
}
return (add_entry(te, val));
}
static int
get_nvpair_vals(scf_handle_t *h, scf_transaction_entry_t *te, nvpair_t *p)
{
scf_value_t *val = scf_value_create(h);
uint_t n = 1;
int i;
if (val == NULL)
return (SCF_FAILED);
switch (nvpair_type(p)) {
case DATA_TYPE_BOOLEAN:
return (add_boolean_entry(h, te, 1));
case DATA_TYPE_BOOLEAN_VALUE:
{
boolean_t v;
(void) nvpair_value_boolean_value(p, &v);
return (add_boolean_entry(h, te, (uint8_t)v));
}
case DATA_TYPE_BOOLEAN_ARRAY:
{
boolean_t *v;
(void) nvpair_value_boolean_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_boolean_entry(h, te, (uint8_t)v[i]) !=
SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_BYTE:
{
uchar_t v;
(void) nvpair_value_byte(p, &v);
return (add_count_entry(h, te, v));
}
case DATA_TYPE_UINT8:
{
uint8_t v;
(void) nvpair_value_uint8(p, &v);
return (add_count_entry(h, te, v));
}
case DATA_TYPE_UINT16:
{
uint16_t v;
(void) nvpair_value_uint16(p, &v);
return (add_count_entry(h, te, v));
}
case DATA_TYPE_UINT32:
{
uint32_t v;
(void) nvpair_value_uint32(p, &v);
return (add_count_entry(h, te, v));
}
case DATA_TYPE_UINT64:
{
uint64_t v;
(void) nvpair_value_uint64(p, &v);
return (add_count_entry(h, te, v));
}
case DATA_TYPE_BYTE_ARRAY:
{
uchar_t *v;
(void) nvpair_value_byte_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_UINT8_ARRAY:
{
uint8_t *v;
(void) nvpair_value_uint8_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_UINT16_ARRAY:
{
uint16_t *v;
(void) nvpair_value_uint16_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_UINT32_ARRAY:
{
uint32_t *v;
(void) nvpair_value_uint32_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_UINT64_ARRAY:
{
uint64_t *v;
(void) nvpair_value_uint64_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_count_entry(h, te, v[i]) != SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_INT8:
{
int8_t v;
(void) nvpair_value_int8(p, &v);
return (add_integer_entry(h, te, v));
}
case DATA_TYPE_INT16:
{
int16_t v;
(void) nvpair_value_int16(p, &v);
return (add_integer_entry(h, te, v));
}
case DATA_TYPE_INT32:
{
int32_t v;
(void) nvpair_value_int32(p, &v);
return (add_integer_entry(h, te, v));
}
case DATA_TYPE_INT64:
{
int64_t v;
(void) nvpair_value_int64(p, &v);
return (add_integer_entry(h, te, v));
}
case DATA_TYPE_INT8_ARRAY:
{
int8_t *v;
(void) nvpair_value_int8_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_integer_entry(h, te, v[i]) !=
SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_INT16_ARRAY:
{
int16_t *v;
(void) nvpair_value_int16_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_integer_entry(h, te, v[i]) !=
SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_INT32_ARRAY:
{
int32_t *v;
(void) nvpair_value_int32_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_integer_entry(h, te, v[i]) !=
SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_INT64_ARRAY:
{
int64_t *v;
(void) nvpair_value_int64_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_integer_entry(h, te, v[i]) !=
SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
case DATA_TYPE_STRING:
{
char *str;
(void) nvpair_value_string(p, &str);
return (add_astring_entry(h, te, str));
}
case DATA_TYPE_STRING_ARRAY:
{
char **v;
(void) nvpair_value_string_array(p, &v, &n);
for (i = 0; i < n; ++i) {
if (add_astring_entry(h, te, v[i]) !=
SCF_SUCCESS)
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
default:
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
return (SCF_FAILED);
}
}
static int
prep_transaction(scf_transaction_t *tx, scf_transaction_entry_t *te,
const char *prop, scf_type_t type)
{
if (scf_transaction_property_new(tx, te, prop, type) != SCF_SUCCESS &&
(scf_error() != SCF_ERROR_EXISTS ||
scf_transaction_property_change(tx, te, prop, type) !=
SCF_SUCCESS)) {
if (check_scf_error(scf_error(), errs_2)) {
return (SCF_FAILED);
}
}
return (SCF_SUCCESS);
}
static int
notify_set_params(scf_propertygroup_t *pg, nvlist_t *params)
{
scf_handle_t *h = scf_pg_handle(pg);
scf_error_t scf_e = scf_error();
scf_transaction_t *tx = scf_transaction_create(h);
int bufsz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
char *propname = malloc(bufsz);
int r = -1;
int err;
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (tx == NULL)
goto cleanup;
if (propname == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
do {
nvpair_t *nvp;
if (scf_pg_update(pg) == SCF_FAILED ||
scf_transaction_start(tx, pg) != SCF_SUCCESS) {
if (check_scf_error(scf_error(), errs_2)) {
goto cleanup;
}
}
for (nvp = nvlist_next_nvpair(params, NULL); nvp != NULL;
nvp = nvlist_next_nvpair(params, nvp)) {
nvlist_t *m;
nvpair_t *p;
if (nvpair_type(nvp) != DATA_TYPE_NVLIST) {
char *name = nvpair_name(nvp);
if (strcmp(name, SCF_NOTIFY_NAME_TSET) == 0)
continue;
(void) scf_set_error(
SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
if (nvpair_value_nvlist(nvp, &m) != 0) {
(void) scf_set_error(
SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
for (p = nvlist_next_nvpair(m, NULL); p != NULL;
p = nvlist_next_nvpair(m, p)) {
scf_transaction_entry_t *te =
scf_entry_create(h);
scf_type_t type = get_scf_type(p);
if (te == NULL) {
if (scf_error() !=
SCF_ERROR_INVALID_ARGUMENT) {
scf_entry_destroy(te);
goto cleanup;
} else {
assert(0);
abort();
}
}
if (type == SCF_TYPE_INVALID) {
(void) scf_set_error(
SCF_ERROR_INVALID_ARGUMENT);
scf_entry_destroy(te);
goto cleanup;
}
if (snprintf(propname, bufsz, "%s,%s",
nvpair_name(nvp), nvpair_name(p)) >=
bufsz) {
(void) scf_set_error(
SCF_ERROR_INVALID_ARGUMENT);
scf_entry_destroy(te);
goto cleanup;
}
if (prep_transaction(tx, te, propname, type) !=
SCF_SUCCESS) {
scf_entry_destroy(te);
goto cleanup;
}
if (get_nvpair_vals(h, te, p) != SCF_SUCCESS) {
if (check_scf_error(scf_error(),
errs_2)) {
goto cleanup;
}
}
}
}
err = scf_transaction_commit(tx);
scf_transaction_destroy_children(tx);
} while (err == 0);
if (err == -1) {
if (check_scf_error(scf_error(), errs_2)) {
goto cleanup;
}
}
r = 0;
cleanup:
scf_transaction_destroy_children(tx);
scf_transaction_destroy(tx);
free(propname);
return (r);
}
static int
decode_fmri(const char *fmri, scf_handle_t *h, scf_service_t **s,
scf_instance_t **i)
{
if (scf_handle_decode_fmri(h, fmri, NULL, *s, NULL, NULL, NULL,
SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
scf_service_destroy(*s);
*s = NULL;
} else {
return (SCF_FAILED);
}
}
if (*s == NULL)
if (scf_handle_decode_fmri(h, fmri, NULL, NULL, *i,
NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
return (SCF_FAILED);
}
return (SCF_SUCCESS);
}
static int
get_type_size(scf_type_t t)
{
switch (t) {
case SCF_TYPE_BOOLEAN:
return (sizeof (uint8_t));
case SCF_TYPE_COUNT:
return (sizeof (uint64_t));
case SCF_TYPE_INTEGER:
return (sizeof (int64_t));
case SCF_TYPE_ASTRING:
case SCF_TYPE_USTRING:
return (sizeof (void *));
default:
return (-1);
}
}
static void **
get_v_pointer(scf_values_t *v)
{
switch (v->value_type) {
case SCF_TYPE_BOOLEAN:
return ((void **)&v->values.v_boolean);
case SCF_TYPE_COUNT:
return ((void **)&v->values.v_count);
case SCF_TYPE_INTEGER:
return ((void **)&v->values.v_integer);
case SCF_TYPE_ASTRING:
return ((void **)&v->values.v_astring);
case SCF_TYPE_USTRING:
return ((void **)&v->values.v_ustring);
default:
return (NULL);
}
}
static int
get_value(scf_value_t *val, scf_values_t *v, int c, char *buf, int sz)
{
switch (v->value_type) {
case SCF_TYPE_BOOLEAN:
return (scf_value_get_boolean(val, v->values.v_boolean + c));
case SCF_TYPE_COUNT:
return (scf_value_get_count(val, v->values.v_count + c));
case SCF_TYPE_INTEGER:
return (scf_value_get_integer(val, v->values.v_integer + c));
case SCF_TYPE_ASTRING:
if (scf_value_get_astring(val, buf, sz) < 0 ||
(v->values.v_astring[c] = strdup(buf)) == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
return (-1);
}
return (0);
case SCF_TYPE_USTRING:
if (scf_value_get_ustring(val, buf, sz) < 0 ||
(v->values.v_ustring[c] = strdup(buf)) == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
return (-1);
}
return (0);
default:
return (-1);
}
}
static int
values_get(scf_property_t *prop, scf_values_t *v)
{
scf_handle_t *h = scf_property_handle(prop);
scf_error_t scf_e = scf_error();
scf_value_t *val = scf_value_create(h);
scf_iter_t *it = scf_iter_create(h);
scf_type_t type = SCF_TYPE_INVALID;
ssize_t sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH) + 1;
char *buf = malloc(sz);
void **p;
int err, elem_sz, count, cursz;
int r = SCF_FAILED;
assert(v != NULL);
assert(v->reserved == NULL);
if (buf == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (val == NULL || it == NULL)
goto cleanup;
if (scf_property_type(prop, &type) != SCF_SUCCESS)
goto cleanup;
if (scf_property_is_type(prop, v->value_type) != SCF_SUCCESS)
goto error;
elem_sz = get_type_size(type);
assert(elem_sz > 0);
p = get_v_pointer(v);
assert(p != NULL);
cursz = count = v->value_count;
if (scf_iter_property_values(it, prop) != 0) {
goto error;
}
while ((err = scf_iter_next_value(it, val)) == 1) {
if (count + 1 >= cursz) {
void *tmp;
cursz = cursz ? 2 * cursz : 8;
if ((tmp = realloc(*p, cursz * elem_sz)) == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto error;
}
*p = tmp;
}
if (get_value(val, v, count, buf, sz) != 0)
goto error;
count++;
}
v->value_count = count;
if (err != 0)
goto error;
r = SCF_SUCCESS;
goto cleanup;
error:
v->value_count = count;
scf_values_destroy(v);
cleanup:
free(buf);
scf_iter_destroy(it);
scf_value_destroy(val);
return (r);
}
static int
add_prop_to_nvlist(scf_property_t *p, const char *pname, nvlist_t *nvl,
int array)
{
scf_values_t vals = { 0 };
scf_type_t type, base_type;
int r = SCF_FAILED;
int err = 0;
if (p == NULL || pname == NULL || *pname == '\0' || nvl == NULL) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
return (r);
}
if (scf_property_type(p, &type) != 0)
goto cleanup;
base_type = scf_true_base_type(type);
if (base_type == SCF_TYPE_ASTRING && type != SCF_TYPE_ASTRING)
type = SCF_TYPE_USTRING;
vals.value_type = type;
if (values_get(p, &vals) != SCF_SUCCESS) {
if (scf_error() == SCF_ERROR_INVALID_ARGUMENT) {
assert(0);
abort();
}
goto cleanup;
}
switch (vals.value_type) {
case SCF_TYPE_BOOLEAN:
{
boolean_t *v;
int i;
int n = vals.value_count;
v = calloc(n, sizeof (boolean_t));
if (v == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
for (i = 0; i < n; ++i)
v[i] = (boolean_t)vals.values.v_boolean[i];
if (n == 1 && !array)
err = nvlist_add_boolean_value(nvl, pname, *v);
else
err = nvlist_add_boolean_array(nvl, pname,
v, n);
if (err != 0) {
free(v);
goto cleanup;
}
free(v);
}
break;
case SCF_TYPE_COUNT:
if (vals.value_count == 1 && !array)
err = nvlist_add_uint64(nvl, pname,
*vals.values.v_count);
else
err = nvlist_add_uint64_array(nvl, pname,
vals.values.v_count, vals.value_count);
if (err != 0)
goto cleanup;
break;
case SCF_TYPE_INTEGER:
if (vals.value_count == 1 && !array)
err = nvlist_add_int64(nvl, pname,
*vals.values.v_integer);
else
err = nvlist_add_int64_array(nvl, pname,
vals.values.v_integer, vals.value_count);
if (err != 0)
goto cleanup;
break;
case SCF_TYPE_ASTRING:
if (vals.value_count == 1 && !array)
err = nvlist_add_string(nvl, pname,
*vals.values.v_astring);
else
err = nvlist_add_string_array(nvl, pname,
vals.values.v_astring, vals.value_count);
if (err != 0)
goto cleanup;
break;
default:
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
r = SCF_SUCCESS;
cleanup:
scf_values_destroy(&vals);
switch (err) {
case 0:
break;
case EINVAL:
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
break;
case ENOMEM:
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
break;
default:
abort();
}
return (r);
}
static int
get_mech_name(const char *name, char **mech, char **val)
{
char *p;
char *m;
if ((m = strdup(name)) == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
return (SCF_FAILED);
}
if ((p = strchr(m, ',')) == NULL) {
free(m);
(void) scf_set_error(SCF_ERROR_NOT_FOUND);
return (SCF_FAILED);
}
*p = '\0';
*val = p + 1;
*mech = m;
return (SCF_SUCCESS);
}
static uint_t
num_of_transitions(int32_t t)
{
int i;
int n = 0;
if (SCF_TRANS_VALID(t)) {
for (i = 0x1; i < SCF_STATE_ALL; i <<= 1) {
if (i & t)
++n;
if (SCF_TRANS_INITIAL_STATE(t) & i)
++n;
}
}
return (n);
}
static int32_t
class_to_transition(const char *c)
{
const char *p;
int r = 0;
size_t n;
if (!is_svc_stn(c)) {
return (0);
}
p = c + strlen(SCF_SVC_TRANSITION_CLASS);
if (*p == '.')
++p;
else
return (0);
if ((n = base_class_len(p)) == 0)
return (0);
if ((r = state_from_string(p, n)) == -1)
r = 0;
return (r);
}
int
smf_notify_set_params(const char *class, nvlist_t *attr)
{
uint32_t ver;
int32_t tset;
scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
scf_error_t scf_e = scf_error();
scf_service_t *s = scf_service_create(h);
scf_instance_t *i = scf_instance_create(h);
scf_propertygroup_t *pg = scf_pg_create(h);
nvlist_t *params = NULL;
char *fmri = (char *)SCF_NOTIFY_PARAMS_INST;
char *pgname = NULL;
int r = SCF_FAILED;
boolean_t is_stn;
int j;
assert(class != NULL);
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (i == NULL || s == NULL || pg == NULL)
goto cleanup;
if (nvlist_lookup_uint32(attr, SCF_NOTIFY_NAME_VERSION, &ver) != 0 ||
ver != SCF_NOTIFY_PARAMS_VERSION) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
if (nvlist_lookup_nvlist(attr, SCF_NOTIFY_PARAMS, ¶ms) != 0) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
is_stn = is_svc_stn(class);
if (is_stn &&
(nvlist_lookup_string(attr, SCF_NOTIFY_NAME_FMRI, &fmri) != 0 ||
nvlist_lookup_int32(attr, SCF_NOTIFY_NAME_TSET, &tset) != 0 ||
!SCF_TRANS_VALID(tset))) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS)
if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
} else if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
if (is_stn) {
tset |= class_to_transition(class);
if (!SCF_TRANS_VALID(tset)) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
for (j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
if (!(tset & st_pgnames[j].st_state))
continue;
if (get_or_add_pg(s, i, st_pgnames[j].st_pgname,
SCF_NOTIFY_PARAMS_PG_TYPE, 0, pg) != 0 &&
check_scf_error(scf_error(), errs_2))
goto cleanup;
if (notify_set_params(pg, params) != 0)
goto cleanup;
}
if (s == NULL) {
if (_smf_refresh_instance_i(i) != 0 &&
check_scf_error(scf_error(), errs_1))
goto cleanup;
} else {
if (_smf_refresh_all_instances(s) != 0 &&
check_scf_error(scf_error(), errs_1))
goto cleanup;
}
} else {
if ((pgname = class_to_pgname(class)) == NULL)
goto cleanup;
if (get_or_add_pg(s, i, pgname, SCF_GROUP_APPLICATION, 0, pg) !=
0) {
if (check_scf_error(scf_error(), errs_2)) {
goto cleanup;
}
}
if (notify_set_params(pg, params) != 0) {
goto cleanup;
}
if (_smf_refresh_instance_i(i) != 0 &&
check_scf_error(scf_error(), errs_1))
goto cleanup;
}
r = SCF_SUCCESS;
cleanup:
scf_instance_destroy(i);
scf_service_destroy(s);
scf_pg_destroy(pg);
scf_handle_destroy(h);
free(pgname);
return (r);
}
int
_scf_notify_get_params(scf_propertygroup_t *pg, nvlist_t *params)
{
scf_handle_t *h = scf_pg_handle(pg);
scf_error_t scf_e = scf_error();
scf_property_t *p = scf_property_create(h);
scf_iter_t *it = scf_iter_create(h);
int sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH) + 1;
char *name = malloc(sz);
int r = SCF_FAILED;
int err;
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (it == NULL || p == NULL)
goto cleanup;
if (name == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
if (scf_iter_pg_properties(it, pg) != SCF_SUCCESS) {
if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
while ((err = scf_iter_next_property(it, p)) == 1) {
nvlist_t *nvl;
int nvl_new = 0;
char *mech;
char *val;
if (scf_property_get_name(p, name, sz) == SCF_FAILED) {
if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
if (get_mech_name(name, &mech, &val) != SCF_SUCCESS) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
continue;
goto cleanup;
}
if (nvlist_lookup_nvlist(params, mech, &nvl) != 0) {
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
free(mech);
goto cleanup;
}
nvl_new = 1;
}
if (add_prop_to_nvlist(p, val, nvl, 1) != SCF_SUCCESS) {
if (check_scf_error(scf_error(), errs_2)) {
free(mech);
nvlist_free(nvl);
goto cleanup;
}
}
if (nvl_new) {
if (nvlist_add_nvlist(params, mech, nvl) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
free(mech);
nvlist_free(nvl);
goto cleanup;
}
nvlist_free(nvl);
}
free(mech);
}
if (err == 0) {
r = SCF_SUCCESS;
} else if (check_scf_error(scf_error(), errs_2)) {
goto cleanup;
}
cleanup:
scf_iter_destroy(it);
scf_property_destroy(p);
free(name);
return (r);
}
static int
get_stn_pg(scf_service_t *s, scf_instance_t *i, scf_instance_t *g,
const char *pgname, scf_propertygroup_t *pg)
{
if (get_pg(s, i, pgname, pg, 1) == 0 ||
scf_error() == SCF_ERROR_NOT_FOUND &&
get_pg(NULL, g, pgname, pg, 0) == 0)
return (SCF_SUCCESS);
return (SCF_FAILED);
}
static int
get_pg_source(scf_propertygroup_t *pg, nvlist_t *params)
{
size_t sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH) + 1;
char *fmri = malloc(sz);
char *p;
int r = SCF_FAILED;
if (fmri == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto out;
}
if (scf_pg_to_fmri(pg, fmri, sz) == -1) {
if (check_scf_error(scf_error(), errs_1)) {
goto out;
}
}
if ((p = strrchr(fmri, ':')) != NULL && p > fmri)
*(p - 1) = '\0';
if (nvlist_add_string(params, SCF_NOTIFY_PARAMS_SOURCE_NAME, fmri) !=
0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto out;
}
r = SCF_SUCCESS;
out:
free(fmri);
return (r);
}
int
_scf_get_svc_notify_params(const char *fmri, nvlist_t *nvl, int32_t tset,
int getsource, int getglobal)
{
scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
scf_error_t scf_e = scf_error();
scf_service_t *s = scf_service_create(h);
scf_instance_t *i = scf_instance_create(h);
scf_instance_t *g = scf_instance_create(h);
scf_propertygroup_t *pg = scf_pg_create(h);
int r = SCF_FAILED;
nvlist_t **params = NULL;
uint_t c, nvl_num = 0;
int not_found = 1;
int j;
const char *pgname;
assert(fmri != NULL && nvl != NULL);
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (s == NULL || i == NULL || g == NULL || pg == NULL)
goto cleanup;
if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS ||
scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, g, NULL,
NULL, SCF_DECODE_FMRI_EXACT) != 0) {
if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
} else if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
nvl_num = num_of_transitions(tset);
if ((params = calloc(nvl_num, sizeof (nvlist_t *))) == NULL) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
for (c = 0; c < nvl_num; ++c)
if (nvlist_alloc(params + c, NV_UNIQUE_NAME, 0) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
for (c = 0, j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
if (!(tset & st_pgnames[j].st_state))
continue;
assert(c < nvl_num);
pgname = st_pgnames[j].st_pgname;
if (nvlist_add_int32(params[c], SCF_NOTIFY_NAME_TSET,
st_pgnames[j].st_state) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
if ((getglobal ? get_stn_pg(s, i, g, pgname, pg) :
get_pg(s, i, pgname, pg, 1)) == SCF_SUCCESS) {
not_found = 0;
if (_scf_notify_get_params(pg, params[c]) !=
SCF_SUCCESS)
goto cleanup;
if (getsource && get_pg_source(pg, params[c]) !=
SCF_SUCCESS)
goto cleanup;
} else if (scf_error() == SCF_ERROR_NOT_FOUND ||
scf_error() == SCF_ERROR_DELETED) {
} else if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
++c;
}
if (not_found) {
(void) scf_set_error(SCF_ERROR_NOT_FOUND);
goto cleanup;
}
assert(c == nvl_num);
if (nvlist_add_nvlist_array(nvl, SCF_NOTIFY_PARAMS, params, nvl_num) !=
0 || nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
SCF_NOTIFY_PARAMS_VERSION) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
r = SCF_SUCCESS;
cleanup:
scf_pg_destroy(pg);
scf_instance_destroy(i);
scf_instance_destroy(g);
scf_service_destroy(s);
scf_handle_destroy(h);
if (params != NULL)
for (c = 0; c < nvl_num; ++c)
nvlist_free(params[c]);
free(params);
return (r);
}
int
_scf_get_fma_notify_params(const char *class, nvlist_t *nvl, int getsource)
{
scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
scf_error_t scf_e = scf_error();
scf_instance_t *i = scf_instance_create(h);
scf_propertygroup_t *pg = scf_pg_create(h);
int r = SCF_FAILED;
nvlist_t *params = NULL;
char *pgname = NULL;
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (i == NULL || pg == NULL)
goto cleanup;
if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL, NULL, i,
NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
if ((pgname = class_to_pgname(class)) == NULL)
goto cleanup;
while (get_pg(NULL, i, pgname, pg, 0) != 0) {
if (scf_error() == SCF_ERROR_NOT_FOUND) {
char *p = strrchr(pgname, '.');
if (p != NULL) {
*p = ',';
(void) strcpy(p + 1, SCF_NOTIFY_PG_POSTFIX);
continue;
}
}
if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
if (nvlist_alloc(¶ms, NV_UNIQUE_NAME, 0) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
if (_scf_notify_get_params(pg, params) != SCF_SUCCESS)
goto cleanup;
if (getsource && get_pg_source(pg, params) != SCF_SUCCESS)
goto cleanup;
if (nvlist_add_nvlist_array(nvl, SCF_NOTIFY_PARAMS, ¶ms, 1) != 0 ||
nvlist_add_uint32(nvl, SCF_NOTIFY_NAME_VERSION,
SCF_NOTIFY_PARAMS_VERSION) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
goto cleanup;
}
r = SCF_SUCCESS;
cleanup:
nvlist_free(params);
scf_pg_destroy(pg);
scf_instance_destroy(i);
scf_handle_destroy(h);
free(pgname);
return (r);
}
int
smf_notify_get_params(nvlist_t **params, nvlist_t *nvl)
{
char *class;
char *from;
char *to;
nvlist_t *attr;
char *fmri;
int32_t tset = 0;
int r = SCF_FAILED;
if (params == NULL || nvlist_lookup_string(nvl, "class", &class) != 0) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
return (r);
}
if (nvlist_alloc(params, NV_UNIQUE_NAME, 0) != 0) {
(void) scf_set_error(SCF_ERROR_NO_MEMORY);
return (r);
}
if (is_svc_stn(class)) {
if (nvlist_lookup_nvlist(nvl, "attr", &attr) != 0 ||
nvlist_lookup_string(attr, "svc-string", &fmri) != 0 ||
nvlist_lookup_string(attr, "from-state", &from) != 0 ||
nvlist_lookup_string(attr, "to-state", &to) != 0) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
tset = SCF_TRANS(smf_state_from_string(from),
smf_state_from_string(to));
if (!SCF_TRANS_VALID(tset)) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
tset |= class_to_transition(class);
r = _scf_get_svc_notify_params(fmri, *params, tset, 0, 1);
} else {
r = _scf_get_fma_notify_params(class, *params, 0);
}
cleanup:
if (r == SCF_FAILED) {
nvlist_free(*params);
*params = NULL;
}
return (r);
}
int
smf_notify_del_params(const char *class, const char *fmri, int32_t tset)
{
scf_handle_t *h = _scf_handle_create_and_bind(SCF_VERSION);
scf_error_t scf_e = scf_error();
scf_service_t *s = scf_service_create(h);
scf_instance_t *i = scf_instance_create(h);
scf_propertygroup_t *pg = scf_pg_create(h);
int r = SCF_FAILED;
char *pgname = NULL;
int j;
if (class == NULL) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
if (h == NULL) {
(void) scf_set_error(scf_e);
goto cleanup;
}
if (s == NULL || i == NULL || pg == NULL)
goto cleanup;
if (is_svc_stn(class)) {
tset |= class_to_transition(class);
if (!SCF_TRANS_VALID(tset) || fmri == NULL) {
(void) scf_set_error(SCF_ERROR_INVALID_ARGUMENT);
goto cleanup;
}
if (decode_fmri(fmri, h, &s, &i) != SCF_SUCCESS) {
if (scf_error() == SCF_ERROR_CONSTRAINT_VIOLATED)
(void) scf_set_error(
SCF_ERROR_INVALID_ARGUMENT);
if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
for (j = 0; st_pgnames[j].st_pgname != NULL; ++j) {
if (!(tset & st_pgnames[j].st_state))
continue;
if (del_pg(s, i, st_pgnames[j].st_pgname, pg) !=
SCF_SUCCESS &&
scf_error() != SCF_ERROR_DELETED &&
scf_error() != SCF_ERROR_NOT_FOUND) {
if (check_scf_error(scf_error(),
errs_1)) {
goto cleanup;
}
}
}
if (s == NULL) {
if (_smf_refresh_instance_i(i) != 0 &&
check_scf_error(scf_error(), errs_1))
goto cleanup;
} else {
if (_smf_refresh_all_instances(s) != 0 &&
check_scf_error(scf_error(), errs_1))
goto cleanup;
}
} else {
if ((pgname = class_to_pgname(class)) == NULL)
goto cleanup;
if (scf_handle_decode_fmri(h, SCF_NOTIFY_PARAMS_INST, NULL,
NULL, i, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS)
goto cleanup;
if (del_pg(NULL, i, pgname, pg) != SCF_SUCCESS &&
scf_error() != SCF_ERROR_DELETED &&
scf_error() != SCF_ERROR_NOT_FOUND) {
if (check_scf_error(scf_error(), errs_1)) {
goto cleanup;
}
}
if (_smf_refresh_instance_i(i) != 0 &&
check_scf_error(scf_error(), errs_1))
goto cleanup;
}
r = SCF_SUCCESS;
cleanup:
scf_pg_destroy(pg);
scf_instance_destroy(i);
scf_service_destroy(s);
scf_handle_destroy(h);
free(pgname);
return (r);
}