libscf.c revision 5ca87c7f602e6b7673ed42068d10305c6c2dbe2b
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/contract/process.h>
#include <assert.h>
#include <errno.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "startd.h"
#define SMF_SNAPSHOT_RUNNING "running"
char *
inst_fmri_to_svc_fmri(const char *fmri)
{
char *buf, *sfmri;
const char *scope, *svc;
int r;
boolean_t local;
buf = startd_alloc(max_scf_fmri_size);
sfmri = startd_alloc(max_scf_fmri_size);
(void) strcpy(buf, fmri);
r = scf_parse_svc_fmri(buf, &scope, &svc, NULL, NULL, NULL);
assert(r == 0);
local = strcmp(scope, SCF_SCOPE_LOCAL) == 0;
(void) snprintf(sfmri, max_scf_fmri_size, "svc:%s%s/%s",
local ? "" : "//", local ? "" : scope, svc);
startd_free(buf, max_scf_fmri_size);
return (sfmri);
}
/*
* Wrapper for the scf_*_create() functions. On SCF_ERROR_NO_MEMORY and
* SCF_ERROR_NO_RESOURCES, retries or dies. So this can only fail with
* SCF_ERROR_INVALID_ARGUMENT, if h is NULL.
*/
void *
libscf_object_create(void *f(scf_handle_t *), scf_handle_t *h)
{
void *o;
uint_t try, msecs;
scf_error_t err;
o = f(h);
if (o != NULL)
return (o);
err = scf_error();
if (err != SCF_ERROR_NO_MEMORY && err != SCF_ERROR_NO_RESOURCES)
return (NULL);
msecs = ALLOC_DELAY;
for (try = 0; try < ALLOC_RETRY; ++try) {
(void) poll(NULL, 0, msecs);
msecs *= ALLOC_DELAY_MULT;
o = f(h);
if (o != NULL)
return (o);
err = scf_error();
if (err != SCF_ERROR_NO_MEMORY && err != SCF_ERROR_NO_RESOURCES)
return (NULL);
}
uu_die("Insufficient memory.\n");
/* NOTREACHED */
}
scf_snapshot_t *
libscf_get_running_snapshot(scf_instance_t *inst)
{
scf_handle_t *h;
scf_snapshot_t *snap;
h = scf_instance_handle(inst);
if (h == NULL)
return (NULL);
snap = scf_snapshot_create(h);
if (snap == NULL)
return (NULL);
if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0)
return (snap);
scf_snapshot_destroy(snap);
return (NULL);
}
/*
* Make sure a service has a "running" snapshot. If it doesn't, make one from
* the editing configuration.
*/
scf_snapshot_t *
libscf_get_or_make_running_snapshot(scf_instance_t *inst, const char *fmri,
boolean_t retake)
{
scf_handle_t *h;
scf_snapshot_t *snap;
h = scf_instance_handle(inst);
snap = scf_snapshot_create(h);
if (snap == NULL)
goto err;
if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0)
return (snap);
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
scf_snapshot_destroy(snap);
return (NULL);
default:
err:
log_error(LOG_NOTICE,
"Could not check for running snapshot of %s (%s).\n", fmri,
scf_strerror(scf_error()));
scf_snapshot_destroy(snap);
return (NULL);
}
if (_scf_snapshot_take_new(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) {
log_framework(LOG_DEBUG, "Took running snapshot for %s.\n",
fmri);
} else {
if (retake && scf_error() == SCF_ERROR_BACKEND_READONLY)
restarter_mark_pending_snapshot(fmri,
RINST_RETAKE_RUNNING);
else
log_error(LOG_DEBUG,
"Could not create running snapshot for %s "
"(%s).\n", fmri, scf_strerror(scf_error()));
scf_snapshot_destroy(snap);
snap = NULL;
}
return (snap);
}
/*
* When a service comes up, point the "start" snapshot at the "running"
* snapshot. Returns 0 on success, ENOTSUP if fmri designates something other
* than an instance, ECONNABORTED, ENOENT if the instance does not exist, or
* EACCES.
*/
int
libscf_snapshots_poststart(scf_handle_t *h, const char *fmri, boolean_t retake)
{
scf_instance_t *inst = NULL;
scf_snapshot_t *running, *start = NULL;
int ret = 0, r;
r = libscf_fmri_get_instance(h, fmri, &inst);
switch (r) {
case 0:
break;
case ENOTSUP:
case ECONNABORTED:
case ENOENT:
return (r);
case EINVAL:
default:
assert(0);
abort();
}
start = safe_scf_snapshot_create(h);
again:
running = libscf_get_or_make_running_snapshot(inst, fmri, retake);
if (running == NULL) {
ret = 0;
goto out;
}
lookup:
if (scf_instance_get_snapshot(inst, "start", start) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
if (_scf_snapshot_take_new(inst, "start", start) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ENOENT;
goto out;
case SCF_ERROR_EXISTS:
goto lookup;
case SCF_ERROR_NO_RESOURCES:
uu_die("Repository server out of "
"resources.\n");
/* NOTREACHED */
case SCF_ERROR_BACKEND_READONLY:
goto readonly;
case SCF_ERROR_PERMISSION_DENIED:
uu_die("Insufficient privileges.\n");
/* NOTREACHED */
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("_scf_snapshot_take_new",
scf_error());
}
}
break;
case SCF_ERROR_DELETED:
ret = ENOENT;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
bad_error("scf_instance_get_snapshot", scf_error());
}
}
if (_scf_snapshot_attach(running, start) == 0) {
log_framework(LOG_DEBUG, "Updated \"start\" snapshot for %s.\n",
fmri);
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
scf_snapshot_destroy(running);
goto again;
case SCF_ERROR_NO_RESOURCES:
uu_die("Repository server out of resources.\n");
/* NOTREACHED */
case SCF_ERROR_PERMISSION_DENIED:
uu_die("Insufficient privileges.\n");
/* NOTREACHED */
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
readonly:
if (retake)
restarter_mark_pending_snapshot(fmri,
RINST_RETAKE_START);
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("_scf_snapshot_attach", scf_error());
}
}
out:
scf_snapshot_destroy(start);
scf_snapshot_destroy(running);
scf_instance_destroy(inst);
return (ret);
}
/*
* Before a refresh, update the "running" snapshot from the editing
* configuration.
*
* Returns 0 on success and -1 on failure.
*/
int
libscf_snapshots_refresh(scf_instance_t *inst, const char *fmri)
{
scf_handle_t *h;
scf_snapshot_t *snap;
boolean_t err = 1;
h = scf_instance_handle(inst);
if (h == NULL)
goto out;
snap = scf_snapshot_create(h);
if (snap == NULL)
goto out;
if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) {
if (_scf_snapshot_take_attach(inst, snap) == 0)
err = 0;
} else {
switch (scf_error()) {
case SCF_ERROR_DELETED:
err = 0;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
/* NOTREACHED */
default:
goto out;
}
log_error(LOG_DEBUG,
"Service %s has no %s snapshot; creating one.\n", fmri,
SMF_SNAPSHOT_RUNNING);
if (_scf_snapshot_take_new(inst, SMF_SNAPSHOT_RUNNING,
snap) == 0)
err = 0;
}
out:
scf_snapshot_destroy(snap);
if (!err)
return (0);
log_error(LOG_WARNING,
"Could not update \"running\" snapshot for refresh of %s.\n", fmri);
return (-1);
}
/*
* int libscf_read_single_astring()
* Reads a single astring value of the requested property into the
* pre-allocated buffer (conventionally of size max_scf_value_size).
* Multiple values constitute an error.
*
* Returns 0 on success or LIBSCF_PROPERTY_ABSENT or LIBSCF_PROPERTY_ERROR.
*/
static int
libscf_read_single_astring(scf_handle_t *h, scf_property_t *prop, char **ret)
{
scf_value_t *val = safe_scf_value_create(h);
int r = 0;
if (scf_property_get_value(prop, val) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
r = LIBSCF_PROPERTY_ABSENT;
else
r = LIBSCF_PROPERTY_ERROR;
goto read_single_astring_fail;
}
if (scf_value_get_astring(val, *ret, max_scf_value_size) <= 0) {
r = LIBSCF_PROPERTY_ERROR;
goto read_single_astring_fail;
}
read_single_astring_fail:
scf_value_destroy(val);
return (r);
}
static int
libscf_read_state(const scf_propertygroup_t *pg, const char *prop_name,
restarter_instance_state_t *state)
{
scf_handle_t *h;
scf_property_t *prop;
char *char_state = startd_alloc(max_scf_value_size);
int ret = 0;
h = scf_pg_handle(pg);
prop = safe_scf_property_create(h);
if (scf_pg_get_property(pg, prop_name, prop) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
ret = LIBSCF_PROPERTY_ABSENT;
else
ret = LIBSCF_PROPERTY_ERROR;
} else {
ret = libscf_read_single_astring(h, prop, &char_state);
if (ret != 0) {
if (ret != LIBSCF_PROPERTY_ABSENT)
ret = LIBSCF_PROPERTY_ERROR;
} else {
*state = restarter_string_to_state(char_state);
ret = 0;
}
}
startd_free(char_state, max_scf_value_size);
scf_property_destroy(prop);
return (ret);
}
/*
* int libscf_read_states(const scf_propertygroup_t *,
* restarter_instance_state_t *, restarter_instance_state_t *)
*
* Set the current state and next_state values for the given service instance.
* Returns 0 on success, or a libscf error code on failure.
*/
int
libscf_read_states(const scf_propertygroup_t *pg,
restarter_instance_state_t *state, restarter_instance_state_t *next_state)
{
int state_ret, next_state_ret, ret;
state_ret = libscf_read_state(pg, SCF_PROPERTY_STATE, state);
next_state_ret = libscf_read_state(pg, SCF_PROPERTY_NEXT_STATE,
next_state);
if (state_ret == LIBSCF_PROPERTY_ERROR ||
next_state_ret == LIBSCF_PROPERTY_ERROR) {
ret = LIBSCF_PROPERTY_ERROR;
} else if (state_ret == 0 && next_state_ret == 0) {
ret = 0;
} else if (state_ret == LIBSCF_PROPERTY_ABSENT &&
next_state_ret == LIBSCF_PROPERTY_ABSENT) {
*state = RESTARTER_STATE_UNINIT;
*next_state = RESTARTER_STATE_NONE;
ret = 0;
} else if (state_ret == LIBSCF_PROPERTY_ABSENT ||
next_state_ret == LIBSCF_PROPERTY_ABSENT) {
log_framework(LOG_DEBUG,
"Only one repository state exists, setting "
"restarter states to MAINTENANCE and NONE\n");
*state = RESTARTER_STATE_MAINT;
*next_state = RESTARTER_STATE_NONE;
ret = 0;
} else {
ret = LIBSCF_PROPERTY_ERROR;
}
read_states_out:
return (ret);
}
/*
* depgroup_empty()
*
* Returns 0 if not empty.
* Returns 1 if empty.
* Returns -1 on error (check scf_error()).
*/
int
depgroup_empty(scf_handle_t *h, scf_propertygroup_t *pg)
{
int empty = 1;
scf_iter_t *iter;
scf_property_t *prop;
int ret;
iter = safe_scf_iter_create(h);
prop = safe_scf_property_create(h);
if (scf_iter_pg_properties(iter, pg) != SCF_SUCCESS) {
scf_property_destroy(prop);
scf_iter_destroy(iter);
return (-1);
}
ret = scf_iter_next_property(iter, prop);
if (ret < 0) {
scf_property_destroy(prop);
scf_iter_destroy(iter);
return (-1);
}
if (ret == 1)
empty = 0;
scf_property_destroy(prop);
scf_iter_destroy(iter);
return (empty);
}
gv_type_t
depgroup_read_scheme(scf_handle_t *h, scf_propertygroup_t *pg)
{
scf_property_t *prop;
char *scheme = startd_alloc(max_scf_value_size);
gv_type_t ret;
prop = safe_scf_property_create(h);
if (scf_pg_get_property(pg, SCF_PROPERTY_TYPE, prop) == -1 ||
libscf_read_single_astring(h, prop, &scheme) != 0) {
scf_property_destroy(prop);
startd_free(scheme, max_scf_value_size);
return (GVT_UNSUPPORTED);
}
if (strcmp(scheme, "service") == 0)
ret = GVT_INST;
else if (strcmp(scheme, "path") == 0)
ret = GVT_FILE;
else
ret = GVT_UNSUPPORTED;
startd_free(scheme, max_scf_value_size);
scf_property_destroy(prop);
return (ret);
}
depgroup_type_t
depgroup_read_grouping(scf_handle_t *h, scf_propertygroup_t *pg)
{
char *grouping = startd_alloc(max_scf_value_size);
depgroup_type_t ret;
scf_property_t *prop = safe_scf_property_create(h);
if (scf_pg_get_property(pg, SCF_PROPERTY_GROUPING, prop) == -1 ||
libscf_read_single_astring(h, prop, &grouping) != 0) {
scf_property_destroy(prop);
startd_free(grouping, max_scf_value_size);
return (DEPGRP_UNSUPPORTED);
}
if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0)
ret = DEPGRP_REQUIRE_ANY;
else if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0)
ret = DEPGRP_REQUIRE_ALL;
else if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0)
ret = DEPGRP_OPTIONAL_ALL;
else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0)
ret = DEPGRP_EXCLUDE_ALL;
else {
ret = DEPGRP_UNSUPPORTED;
}
startd_free(grouping, max_scf_value_size);
scf_property_destroy(prop);
return (ret);
}
restarter_error_t
depgroup_read_restart(scf_handle_t *h, scf_propertygroup_t *pg)
{
scf_property_t *prop = safe_scf_property_create(h);
char *restart_on = startd_alloc(max_scf_value_size);
restarter_error_t ret;
if (scf_pg_get_property(pg, SCF_PROPERTY_RESTART_ON, prop) == -1 ||
libscf_read_single_astring(h, prop, &restart_on) != 0) {
startd_free(restart_on, max_scf_value_size);
scf_property_destroy(prop);
return (RERR_UNSUPPORTED);
}
if (strcmp(restart_on, SCF_DEP_RESET_ON_ERROR) == 0)
ret = RERR_FAULT;
else if (strcmp(restart_on, SCF_DEP_RESET_ON_RESTART) == 0)
ret = RERR_RESTART;
else if (strcmp(restart_on, SCF_DEP_RESET_ON_REFRESH) == 0)
ret = RERR_REFRESH;
else if (strcmp(restart_on, SCF_DEP_RESET_ON_NONE) == 0)
ret = RERR_NONE;
else
ret = RERR_UNSUPPORTED;
startd_free(restart_on, max_scf_value_size);
scf_property_destroy(prop);
return (ret);
}
/*
* int get_boolean()
* Fetches the value of a boolean property of the given property group.
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ECANCELED - pg was deleted
* ENOENT - the property doesn't exist or has no values
* EINVAL - the property has the wrong type
* the property is not single-valued
* EACCES - the current user does not have permission to read the value
*/
static int
get_boolean(scf_propertygroup_t *pg, const char *propname, uint8_t *valuep)
{
scf_handle_t *h;
scf_property_t *prop;
scf_value_t *val;
int ret = 0, r;
scf_type_t type;
h = scf_pg_handle(pg);
prop = safe_scf_property_create(h);
val = safe_scf_value_create(h);
if (scf_pg_get_property(pg, propname, prop) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
}
if (scf_property_type(prop, &type) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ENOENT;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_property_type", scf_error());
}
}
if (type != SCF_TYPE_BOOLEAN) {
ret = EINVAL;
goto out;
}
if (scf_property_get_value(prop, val) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto out;
case SCF_ERROR_CONSTRAINT_VIOLATED:
ret = EINVAL;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EACCES;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_property_get_value", scf_error());
}
}
r = scf_value_get_boolean(val, valuep);
assert(r == 0);
out:
scf_value_destroy(val);
scf_property_destroy(prop);
return (ret);
}
/*
* int get_count()
* Fetches the value of a count property of the given property group.
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* unknown libscf error
* ECANCELED - pg was deleted
* ENOENT - the property doesn't exist or has no values
* EINVAL - the property has the wrong type
* the property is not single-valued
* EACCES - the current user does not have permission to read the value
*/
static int
get_count(scf_propertygroup_t *pg, const char *propname, uint64_t *valuep)
{
scf_handle_t *h;
scf_property_t *prop;
scf_value_t *val;
int ret = 0, r;
h = scf_pg_handle(pg);
prop = safe_scf_property_create(h);
val = safe_scf_value_create(h);
if (scf_pg_get_property(pg, propname, prop) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
}
if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_TYPE_MISMATCH:
ret = EINVAL;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
bad_error("scf_property_is_type", scf_error());
}
}
if (scf_property_get_value(prop, val) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto out;
case SCF_ERROR_CONSTRAINT_VIOLATED:
ret = EINVAL;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EACCES;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_property_get_value", scf_error());
}
}
r = scf_value_get_count(val, valuep);
assert(r == 0);
out:
scf_value_destroy(val);
scf_property_destroy(prop);
return (ret);
}
static void
get_restarter(scf_handle_t *h, scf_propertygroup_t *pg, char **restarter)
{
scf_property_t *prop = safe_scf_property_create(h);
if (scf_pg_get_property(pg, SCF_PROPERTY_RESTARTER, prop) == -1 ||
libscf_read_single_astring(h, prop, restarter) != 0)
*restarter[0] = '\0';
scf_property_destroy(prop);
}
/*
* int libscf_instance_get_fmri(scf_instance_t *, char **)
* Give a valid SCF instance, return its FMRI. Returns 0 on success,
* ECONNABORTED, or ECANCELED if inst is deleted.
*/
int
libscf_instance_get_fmri(scf_instance_t *inst, char **retp)
{
char *inst_fmri = startd_alloc(max_scf_fmri_size);
inst_fmri[0] = 0;
if (scf_instance_to_fmri(inst, inst_fmri, max_scf_fmri_size) <= 0) {
startd_free(inst_fmri, max_scf_fmri_size);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
*retp = inst_fmri;
return (0);
}
/*
* int libscf_fmri_get_instance(scf_handle_t *, const char *,
* scf_instance_t **)
* Given a valid SCF handle and an FMRI, return the SCF instance that matches
* exactly. The instance must be released using scf_instance_destroy().
* Returns 0 on success, EINVAL if the FMRI is invalid, ENOTSUP if the FMRI
* is valid but designates something other than an instance, ECONNABORTED if
* the repository connection is broken, or ENOENT if the instance does not
* exist.
*/
int
libscf_fmri_get_instance(scf_handle_t *h, const char *fmri,
scf_instance_t **instp)
{
scf_instance_t *inst;
int r;
inst = safe_scf_instance_create(h);
r = libscf_lookup_instance(fmri, inst);
if (r == 0)
*instp = inst;
else
scf_instance_destroy(inst);
return (r);
}
int
libscf_lookup_instance(const char *fmri, scf_instance_t *inst)
{
if (scf_handle_decode_fmri(scf_instance_handle(inst), fmri, NULL, NULL,
inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_INVALID_ARGUMENT:
return (EINVAL);
case SCF_ERROR_CONSTRAINT_VIOLATED:
return (ENOTSUP);
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_handle_decode_fmri", scf_error());
}
}
return (0);
}
/*
* void libscf_get_basic_instance_data()
* Read enabled, enabled_ovr, and restarter_fmri (into an allocated
* buffer) for inst. Returns 0, ECONNABORTED if the connection to the
* repository is broken, ECANCELED if inst is deleted, or ENOENT if inst
* has no general property group.
*
* On success, restarter_fmri may be NULL. If general/enabled was missing
* or invalid, *enabledp will be -1 and a debug message is logged.
*/
int
libscf_get_basic_instance_data(scf_handle_t *h, scf_instance_t *inst,
const char *fmri, int *enabledp, int *enabled_ovrp, char **restarter_fmri)
{
scf_propertygroup_t *pg;
int r;
uint8_t enabled_8;
pg = safe_scf_pg_create(h);
if (enabled_ovrp == NULL)
goto enabled;
if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL_OVR, pg) !=
0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
scf_pg_destroy(pg);
return (ECONNABORTED);
case SCF_ERROR_DELETED:
scf_pg_destroy(pg);
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
*enabled_ovrp = -1;
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg_composed", scf_error());
}
} else {
switch (r = get_boolean(pg, SCF_PROPERTY_ENABLED, &enabled_8)) {
case 0:
*enabled_ovrp = enabled_8;
break;
case ECONNABORTED:
case ECANCELED:
scf_pg_destroy(pg);
return (r);
case ENOENT:
case EINVAL:
*enabled_ovrp = -1;
break;
case EACCES:
default:
bad_error("get_boolean", r);
}
}
enabled:
/*
* Since general/restarter can be at the service level, we must do
* a composed lookup. These properties are immediate, though, so we
* must use the "editing" snapshot. Technically enabled shouldn't be
* at the service level, but looking it up composed, too, doesn't
* hurt.
*/
if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, pg) != 0) {
scf_pg_destroy(pg);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg_composed", scf_error());
}
}
switch (r = get_boolean(pg, SCF_PROPERTY_ENABLED, &enabled_8)) {
case 0:
*enabledp = enabled_8;
break;
case ECONNABORTED:
case ECANCELED:
scf_pg_destroy(pg);
return (r);
case ENOENT:
/*
* DEBUG because this happens when svccfg import creates
* a temporary service.
*/
log_framework(LOG_DEBUG,
"general/enabled property of %s is missing.\n", fmri);
*enabledp = -1;
break;
case EINVAL:
log_framework(LOG_ERR,
"general/enabled property of %s is invalid.\n", fmri);
*enabledp = -1;
break;
case EACCES:
default:
bad_error("get_boolean", r);
}
if (restarter_fmri != NULL)
get_restarter(h, pg, restarter_fmri);
scf_pg_destroy(pg);
return (0);
}
/*
* Sets pg to the name property group of s_inst. If it doesn't exist, it is
* added.
*
* Fails with
* ECONNABORTED - repository disconnection or unknown libscf error
* ECANCELED - inst is deleted
* EPERM - permission is denied
* EACCES - backend denied access
* EROFS - backend readonly
*/
int
libscf_inst_get_or_add_pg(scf_instance_t *inst, const char *name,
const char *type, uint32_t flags, scf_propertygroup_t *pg)
{
uint32_t f;
again:
if (scf_instance_get_pg(inst, name, pg) == 0) {
if (scf_pg_get_flags(pg, &f) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_flags", scf_error());
}
}
if (f == flags)
return (0);
if (scf_pg_delete(pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_PERMISSION_DENIED:
return (EPERM);
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
case SCF_ERROR_BACKEND_READONLY:
return (EROFS);
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_delete", scf_error());
}
}
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg", scf_error());
}
}
add:
if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
goto again;
case SCF_ERROR_PERMISSION_DENIED:
return (EPERM);
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
case SCF_ERROR_BACKEND_READONLY:
return (EROFS);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_add_pg", scf_error());
/* NOTREACHED */
}
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED
*/
static scf_error_t
transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent,
const char *pname, scf_type_t ty)
{
for (;;) {
if (scf_transaction_property_change_type(tx, ent, pname,
ty) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_property_change_type",
scf_error());
}
if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_property_new", scf_error());
/* NOTREACHED */
}
}
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - pg was deleted
* EPERM
* EACCES
* EROFS
*/
static int
pg_set_prop_value(scf_propertygroup_t *pg, const char *pname, scf_value_t *v)
{
scf_handle_t *h;
scf_transaction_t *tx;
scf_transaction_entry_t *e;
scf_type_t ty;
scf_error_t scfe;
int ret, r;
h = scf_pg_handle(pg);
tx = safe_scf_transaction_create(h);
e = safe_scf_entry_create(h);
ty = scf_value_type(v);
assert(ty != SCF_TYPE_INVALID);
for (;;) {
if (scf_transaction_start(tx, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_start", ret);
}
}
ret = transaction_add_set(tx, e, pname, ty);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
bad_error("transaction_add_set", ret);
}
r = scf_entry_add_value(e, v);
assert(r == 0);
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
scfe = scf_error();
scf_transaction_reset(tx);
switch (scfe) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_commit", scfe);
}
}
scf_transaction_reset(tx);
if (scf_pg_update(pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_update", scf_error());
}
}
}
ret = 0;
out:
scf_transaction_destroy(tx);
scf_entry_destroy(e);
return (ret);
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
libscf_inst_set_boolean_prop(scf_instance_t *inst, const char *pgname,
const char *pgtype, uint32_t pgflags, const char *pname, int val)
{
scf_handle_t *h;
scf_propertygroup_t *pg = NULL;
scf_value_t *v;
int ret = 0;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
v = safe_scf_value_create(h);
ret = libscf_inst_get_or_add_pg(inst, pgname, pgtype, pgflags, pg);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
bad_error("libscf_inst_get_or_add_pg", ret);
}
scf_value_set_boolean(v, val);
ret = pg_set_prop_value(pg, pname, v);
switch (ret) {
case 0:
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
break;
default:
bad_error("pg_set_prop_value", ret);
}
out:
scf_pg_destroy(pg);
scf_value_destroy(v);
return (ret);
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
libscf_inst_set_count_prop(scf_instance_t *inst, const char *pgname,
const char *pgtype, uint32_t pgflags, const char *pname, uint64_t count)
{
scf_handle_t *h;
scf_propertygroup_t *pg = NULL;
scf_value_t *v;
int ret = 0;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
v = safe_scf_value_create(h);
ret = libscf_inst_get_or_add_pg(inst, pgname, pgtype, pgflags, pg);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
bad_error("libscf_inst_get_or_add_pg", ret);
}
scf_value_set_count(v, count);
ret = pg_set_prop_value(pg, pname, v);
switch (ret) {
case 0:
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
break;
default:
bad_error("pg_set_prop_value", ret);
}
out:
scf_pg_destroy(pg);
scf_value_destroy(v);
return (ret);
}
/*
* Returns 0 on success, ECONNABORTED if the repository connection is broken,
* ECANCELED if inst is deleted, EROFS if the backend is readonly, or EPERM if
* permission was denied.
*/
int
libscf_set_enable_ovr(scf_instance_t *inst, int enable)
{
return (libscf_inst_set_boolean_prop(inst, SCF_PG_GENERAL_OVR,
SCF_PG_GENERAL_OVR_TYPE, SCF_PG_GENERAL_OVR_FLAGS,
SCF_PROPERTY_ENABLED, enable));
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ECANCELED - inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
libscf_inst_delete_prop(scf_instance_t *inst, const char *pgname,
const char *pname)
{
scf_handle_t *h;
scf_propertygroup_t *pg;
scf_transaction_t *tx;
scf_transaction_entry_t *e;
scf_error_t serr;
int ret = 0, r;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
if (scf_instance_get_pg(inst, pgname, pg) != 0) {
scf_pg_destroy(pg);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
return (0);
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg", scf_error());
}
}
tx = safe_scf_transaction_create(h);
e = safe_scf_entry_create(h);
for (;;) {
if (scf_transaction_start(tx, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = 0;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_start", scf_error());
}
}
if (scf_transaction_property_delete(tx, e, pname) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
ret = 0;
goto out;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
bad_error("scf_transaction_property_delete",
scf_error());
}
}
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
serr = scf_error();
scf_transaction_reset(tx);
switch (serr) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = 0;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
bad_error("scf_transaction_commit", serr);
}
}
scf_transaction_reset(tx);
if (scf_pg_update(pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = 0;
goto out;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
bad_error("scf_pg_update", scf_error());
}
}
}
out:
scf_transaction_destroy(tx);
(void) scf_entry_destroy(e);
scf_pg_destroy(pg);
return (ret);
}
/*
* Returns 0, ECONNABORTED, ECANCELED, or EPERM.
*/
int
libscf_delete_enable_ovr(scf_instance_t *inst)
{
return (libscf_inst_delete_prop(inst, SCF_PG_GENERAL_OVR,
SCF_PROPERTY_ENABLED));
}
/*
* Fails with
* ECONNABORTED - repository connection was broken
* ECANCELED - pg was deleted
* ENOENT - pg has no milestone property
* EINVAL - the milestone property is misconfigured
*/
static int
pg_get_milestone(scf_propertygroup_t *pg, scf_property_t *prop,
scf_value_t *val, char *buf, size_t buf_sz)
{
if (scf_pg_get_property(pg, SCF_PROPERTY_MILESTONE, prop) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
}
if (scf_property_get_value(prop, val) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_NOT_FOUND:
return (EINVAL);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_PERMISSION_DENIED:
bad_error("scf_property_get_value", scf_error());
}
}
if (scf_value_get_astring(val, buf, buf_sz) < 0) {
switch (scf_error()) {
case SCF_ERROR_TYPE_MISMATCH:
return (EINVAL);
case SCF_ERROR_NOT_SET:
default:
bad_error("scf_value_get_astring", scf_error());
}
}
return (0);
}
/*
* Fails with
* ECONNABORTED - repository connection was broken
* ECANCELED - inst was deleted
* ENOENT - inst has no milestone property
* EINVAL - the milestone property is misconfigured
*/
int
libscf_get_milestone(scf_instance_t *inst, scf_property_t *prop,
scf_value_t *val, char *buf, size_t buf_sz)
{
scf_propertygroup_t *pg;
int r;
pg = safe_scf_pg_create(scf_instance_handle(inst));
if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) == 0) {
switch (r = pg_get_milestone(pg, prop, val, buf, buf_sz)) {
case 0:
case ECONNABORTED:
case EINVAL:
goto out;
case ECANCELED:
case ENOENT:
break;
default:
bad_error("pg_get_milestone", r);
}
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
r = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
r = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg", scf_error());
}
}
if (scf_instance_get_pg(inst, SCF_PG_OPTIONS, pg) == 0) {
r = pg_get_milestone(pg, prop, val, buf, buf_sz);
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
r = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
r = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
r = ENOENT;
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg", scf_error());
}
}
out:
scf_pg_destroy(pg);
return (r);
}
/*
* Get the runlevel character from the runlevel property of the given property
* group. Fails with
* ECONNABORTED - repository connection was broken
* ECANCELED - prop's property group was deleted
* ENOENT - the property has no values
* EINVAL - the property has more than one value
* the property is of the wrong type
* the property value is malformed
*/
int
libscf_extract_runlevel(scf_property_t *prop, char *rlp)
{
scf_value_t *val;
char buf[2];
val = safe_scf_value_create(scf_property_handle(prop));
if (scf_property_get_value(prop, val) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
return (ENOENT);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_CONSTRAINT_VIOLATED:
return (EINVAL);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_property_get_value", scf_error());
}
}
if (scf_value_get_astring(val, buf, sizeof (buf)) < 0) {
if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
bad_error("scf_value_get_astring", scf_error());
return (EINVAL);
}
if (buf[0] == '\0' || buf[1] != '\0')
return (EINVAL);
*rlp = buf[0];
return (0);
}
/*
* Delete the "runlevel" property from the given property group. Also set the
* "milestone" property to the given string. Fails with ECONNABORTED,
* ECANCELED, EPERM, EACCES, or EROFS.
*/
int
libscf_clear_runlevel(scf_propertygroup_t *pg, const char *milestone)
{
scf_handle_t *h;
scf_transaction_t *tx;
scf_transaction_entry_t *e_rl, *e_ms;
scf_value_t *val;
scf_error_t serr;
boolean_t isempty = B_TRUE;
int ret = 0, r;
h = scf_pg_handle(pg);
tx = safe_scf_transaction_create(h);
e_rl = safe_scf_entry_create(h);
e_ms = safe_scf_entry_create(h);
val = safe_scf_value_create(h);
if (milestone) {
r = scf_value_set_astring(val, milestone);
assert(r == 0);
}
for (;;) {
if (scf_transaction_start(tx, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_start", scf_error());
}
}
if (scf_transaction_property_delete(tx, e_rl,
"runlevel") == 0) {
isempty = B_FALSE;
} else {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_INVALID_ARGUMENT:
bad_error("scf_transaction_property_delete",
scf_error());
}
}
if (milestone) {
ret = transaction_add_set(tx, e_ms,
SCF_PROPERTY_MILESTONE, SCF_TYPE_ASTRING);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
bad_error("transaction_add_set", ret);
}
isempty = B_FALSE;
r = scf_entry_add_value(e_ms, val);
assert(r == 0);
}
if (isempty)
goto out;
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
serr = scf_error();
scf_transaction_reset(tx);
switch (serr) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
default:
bad_error("scf_transaction_commit", serr);
}
}
scf_transaction_reset(tx);
if (scf_pg_update(pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_SET:
ret = ECANCELED;
goto out;
default:
assert(0);
abort();
}
}
}
out:
scf_transaction_destroy(tx);
scf_entry_destroy(e_rl);
scf_entry_destroy(e_ms);
scf_value_destroy(val);
return (ret);
}
/*
* int libscf_get_template_values(scf_instance_t *, scf_snapshot_t *,
* char **)
*
* Return template values for inst in *common_name suitable for use in
* restarter_inst_t->ri_common_name. Called by restarter_insert_inst().
*
* Returns 0 on success, ECANCELED if the instance is deleted, ECHILD if
* a value fetch failed for a property, ENOENT if the instance has no
* tm_common_name property group or the property group is deleted, and
* ECONNABORTED if the repository connection is broken.
*/
int
libscf_get_template_values(scf_instance_t *inst, scf_snapshot_t *snap,
char **common_name, char **c_common_name)
{
scf_handle_t *h;
scf_propertygroup_t *pg = NULL;
scf_property_t *prop = NULL;
int ret = 0, r;
char *cname = startd_alloc(max_scf_value_size);
char *c_cname = startd_alloc(max_scf_value_size);
int common_name_initialized = B_FALSE;
int c_common_name_initialized = B_FALSE;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
prop = safe_scf_property_create(h);
/*
* The tm_common_name property group, as with all template property
* groups, is optional.
*/
if (scf_instance_get_pg_composed(inst, snap, SCF_PG_TM_COMMON_NAME, pg)
== -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto template_values_out;
case SCF_ERROR_NOT_FOUND:
goto template_values_out;
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto template_values_out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg_composed", scf_error());
}
}
/*
* The name we wish uses the current locale name as the property name.
*/
if (st->st_locale != NULL) {
if (scf_pg_get_property(pg, st->st_locale, prop) == -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto template_values_out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
} else {
if ((r = libscf_read_single_astring(h, prop, &cname)) !=
0) {
if (r != LIBSCF_PROPERTY_ABSENT)
ret = ECHILD;
goto template_values_out;
}
*common_name = cname;
common_name_initialized = B_TRUE;
}
}
/*
* Also pull out the C locale name, as a fallback for the case where
* service offers no localized name.
*/
if (scf_pg_get_property(pg, "C", prop) == -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ENOENT;
goto template_values_out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto template_values_out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
} else {
if ((r = libscf_read_single_astring(h, prop, &c_cname)) != 0) {
if (r != LIBSCF_PROPERTY_ABSENT)
ret = ECHILD;
goto template_values_out;
}
*c_common_name = c_cname;
c_common_name_initialized = B_TRUE;
}
template_values_out:
if (common_name_initialized == B_FALSE)
startd_free(cname, max_scf_value_size);
if (c_common_name_initialized == B_FALSE)
startd_free(c_cname, max_scf_value_size);
scf_property_destroy(prop);
scf_pg_destroy(pg);
return (ret);
}
/*
* int libscf_get_startd_properties(scf_handle_t *, scf_instance_t *,
* scf_snapshot_t *, uint_t *, char **)
*
* Return startd settings for inst in *flags suitable for use in
* restarter_inst_t->ri_flags. Called by restarter_insert_inst().
*
* Returns 0 on success, ECANCELED if the instance is deleted, ECHILD if
* a value fetch failed for a property, ENOENT if the instance has no
* general property group or the property group is deleted, and
* ECONNABORTED if the repository connection is broken.
*/
int
libscf_get_startd_properties(scf_instance_t *inst,
scf_snapshot_t *snap, uint_t *flags, char **prefixp)
{
scf_handle_t *h;
scf_propertygroup_t *pg = NULL;
scf_property_t *prop = NULL;
int style = RINST_CONTRACT;
char *style_str = startd_alloc(max_scf_value_size);
int ret = 0, r;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
prop = safe_scf_property_create(h);
/*
* The startd property group is optional.
*/
if (scf_instance_get_pg_composed(inst, snap, SCF_PG_STARTD, pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto instance_flags_out;
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto instance_flags_out;
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto instance_flags_out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg_composed", scf_error());
}
}
/*
* 1. Duration property.
*/
if (scf_pg_get_property(pg, SCF_PROPERTY_DURATION, prop) == -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ENOENT;
goto instance_flags_out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto instance_flags_out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
} else {
errno = 0;
if ((r = libscf_read_single_astring(h, prop, &style_str))
!= 0) {
if (r != LIBSCF_PROPERTY_ABSENT)
ret = ECHILD;
goto instance_flags_out;
}
if (strcmp(style_str, "child") == 0)
style = RINST_WAIT;
else if (strcmp(style_str, "transient") == 0)
style = RINST_TRANSIENT;
}
/*
* 2. utmpx prefix property.
*/
if (scf_pg_get_property(pg, SCF_PROPERTY_UTMPX_PREFIX, prop) == 0) {
errno = 0;
if ((r = libscf_read_single_astring(h, prop, prefixp)) != 0) {
if (r != LIBSCF_PROPERTY_ABSENT)
ret = ECHILD;
goto instance_flags_out;
}
} else {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ENOENT;
goto instance_flags_out;
case SCF_ERROR_NOT_FOUND:
goto instance_flags_out;
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto instance_flags_out;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_get_property", scf_error());
}
}
instance_flags_out:
startd_free(style_str, max_scf_value_size);
*flags = (*flags & ~RINST_STYLE_MASK) | style;
scf_property_destroy(prop);
scf_pg_destroy(pg);
return (ret);
}
/*
* int libscf_read_method_ids(scf_handle_t *, scf_instance_t *, ctid_t *,
* ctid_t *, pid_t *)
*
* Sets given id_t variables to primary and transient contract IDs and start
* PID. Returns 0, ECONNABORTED, and ECANCELED.
*/
int
libscf_read_method_ids(scf_handle_t *h, scf_instance_t *inst, const char *fmri,
ctid_t *primary, ctid_t *transient, pid_t *start_pid)
{
scf_propertygroup_t *pg = NULL;
scf_property_t *prop = NULL;
scf_value_t *val = NULL;
uint64_t p, t;
int ret = 0;
*primary = 0;
*transient = 0;
*start_pid = -1;
pg = safe_scf_pg_create(h);
prop = safe_scf_property_create(h);
val = safe_scf_value_create(h);
if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto read_id_err;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto read_id_err;
case SCF_ERROR_NOT_FOUND:
goto read_id_err;
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg", scf_error());
}
}
ret = get_count(pg, SCF_PROPERTY_CONTRACT, &p);
switch (ret) {
case 0:
break;
case EINVAL:
log_error(LOG_NOTICE,
"%s: Ignoring %s/%s: multivalued or not of type count\n",
fmri, SCF_PG_RESTARTER, SCF_PROPERTY_CONTRACT);
/* FALLTHROUGH */
case ENOENT:
ret = 0;
goto read_trans;
case ECONNABORTED:
case ECANCELED:
goto read_id_err;
case EACCES:
default:
bad_error("get_count", ret);
}
*primary = p;
read_trans:
ret = get_count(pg, SCF_PROPERTY_TRANSIENT_CONTRACT, &t);
switch (ret) {
case 0:
break;
case EINVAL:
log_error(LOG_NOTICE,
"%s: Ignoring %s/%s: multivalued or not of type count\n",
fmri, SCF_PG_RESTARTER, SCF_PROPERTY_TRANSIENT_CONTRACT);
/* FALLTHROUGH */
case ENOENT:
ret = 0;
goto read_pid_only;
case ECONNABORTED:
case ECANCELED:
goto read_id_err;
case EACCES:
default:
bad_error("get_count", ret);
}
*transient = t;
read_pid_only:
ret = get_count(pg, SCF_PROPERTY_START_PID, &p);
switch (ret) {
case 0:
break;
case EINVAL:
log_error(LOG_NOTICE,
"%s: Ignoring %s/%s: multivalued or not of type count\n",
fmri, SCF_PG_RESTARTER, SCF_PROPERTY_START_PID);
/* FALLTHROUGH */
case ENOENT:
ret = 0;
goto read_id_err;
case ECONNABORTED:
case ECANCELED:
goto read_id_err;
case EACCES:
default:
bad_error("get_count", ret);
}
*start_pid = p;
read_id_err:
scf_value_destroy(val);
scf_property_destroy(prop);
scf_pg_destroy(pg);
return (ret);
}
/*
* Returns with
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - s_inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
libscf_write_start_pid(scf_instance_t *s_inst, pid_t pid)
{
scf_handle_t *h;
scf_transaction_entry_t *t_pid;
scf_value_t *v_pid;
scf_propertygroup_t *pg;
int ret = 0;
h = scf_instance_handle(s_inst);
pg = safe_scf_pg_create(h);
t_pid = safe_scf_entry_create(h);
v_pid = safe_scf_value_create(h);
get_pg:
ret = libscf_inst_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto write_start_err;
default:
bad_error("libscf_inst_get_or_add_pg", ret);
}
scf_value_set_count(v_pid, pid);
ret = pg_set_prop_value(pg, SCF_PROPERTY_START_PID, v_pid);
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto get_pg;
default:
bad_error("pg_set_prop_value", ret);
}
write_start_err:
scf_entry_destroy(t_pid);
scf_value_destroy(v_pid);
scf_pg_destroy(pg);
return (ret);
}
/*
* Add a property indicating the instance log file. If the dir is
* equal to LOG_PREFIX_EARLY, then the property restarter/alt_logfile
* of the instance is used; otherwise, restarter/logfile is used.
*
* Returns
* 0 - success
* ECONNABORTED
* ECANCELED
* EPERM
* EACCES
* EROFS
* EAGAIN
*/
int
libscf_note_method_log(scf_instance_t *inst, const char *dir, const char *file)
{
scf_handle_t *h;
scf_value_t *v;
scf_propertygroup_t *pg;
int ret = 0;
char *logname;
const char *propname;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
v = safe_scf_value_create(h);
logname = uu_msprintf("%s%s", dir, file);
if (logname == NULL) {
ret = errno;
goto out;
}
ret = libscf_inst_get_or_add_pg(inst, SCF_PG_RESTARTER,
SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
bad_error("libscf_inst_get_or_add_pg", ret);
}
(void) scf_value_set_astring(v, logname);
if (strcmp(LOG_PREFIX_EARLY, dir) == 0)
propname = SCF_PROPERTY_ALT_LOGFILE;
else
propname = SCF_PROPERTY_LOGFILE;
ret = pg_set_prop_value(pg, propname, v);
switch (ret) {
case 0:
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
break;
default:
bad_error("pg_set_prop_value", ret);
}
out:
scf_pg_destroy(pg);
scf_value_destroy(v);
uu_free(logname);
return (ret);
}
/*
* Returns
* 0 - success
* ENAMETOOLONG - name is too long
* ECONNABORTED
* ECANCELED
* EPERM
* EACCES
* EROFS
*/
int
libscf_write_method_status(scf_instance_t *s_inst, const char *name,
int status)
{
scf_handle_t *h;
scf_transaction_t *tx;
scf_transaction_entry_t *e_time, *e_stat;
scf_value_t *v_time, *v_stat;
scf_propertygroup_t *pg;
int ret = 0, r;
char pname[30];
struct timeval tv;
scf_error_t scfe;
if (strlen(name) + sizeof ("_method_waitstatus") > sizeof (pname))
return (ENAMETOOLONG);
h = scf_instance_handle(s_inst);
pg = safe_scf_pg_create(h);
tx = safe_scf_transaction_create(h);
e_time = safe_scf_entry_create(h);
v_time = safe_scf_value_create(h);
e_stat = safe_scf_entry_create(h);
v_stat = safe_scf_value_create(h);
get_pg:
ret = libscf_inst_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
bad_error("libscf_inst_get_or_add_pg", ret);
}
(void) gettimeofday(&tv, NULL);
r = scf_value_set_time(v_time, tv.tv_sec, tv.tv_usec * 1000);
assert(r == 0);
scf_value_set_integer(v_stat, status);
for (;;) {
if (scf_transaction_start(tx, pg) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_start", ret);
}
}
(void) snprintf(pname, sizeof (pname), "%s_method_timestamp",
name);
ret = transaction_add_set(tx, e_time, pname, SCF_TYPE_TIME);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
bad_error("transaction_add_set", ret);
}
r = scf_entry_add_value(e_time, v_time);
assert(r == 0);
(void) snprintf(pname, sizeof (pname), "%s_method_waitstatus",
name);
ret = transaction_add_set(tx, e_stat, pname, SCF_TYPE_INTEGER);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
bad_error("transaction_add_set", ret);
}
r = scf_entry_add_value(e_stat, v_stat);
if (r != 0)
bad_error("scf_entry_add_value", scf_error());
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
scfe = scf_error();
scf_transaction_reset_all(tx);
switch (scfe) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
ret = EACCES;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_commit", scfe);
}
}
scf_transaction_reset_all(tx);
if (scf_pg_update(pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_SET:
bad_error("scf_pg_update", scf_error());
}
}
}
out:
scf_transaction_destroy(tx);
scf_entry_destroy(e_time);
scf_value_destroy(v_time);
scf_entry_destroy(e_stat);
scf_value_destroy(v_stat);
scf_pg_destroy(pg);
return (ret);
}
/*
* Call dgraph_add_instance() for each instance in the repository.
*/
void
libscf_populate_graph(scf_handle_t *h)
{
scf_scope_t *scope;
scf_service_t *svc;
scf_instance_t *inst;
scf_iter_t *svc_iter;
scf_iter_t *inst_iter;
int ret;
scope = safe_scf_scope_create(h);
svc = safe_scf_service_create(h);
inst = safe_scf_instance_create(h);
svc_iter = safe_scf_iter_create(h);
inst_iter = safe_scf_iter_create(h);
if ((ret = scf_handle_get_local_scope(h, scope)) !=
SCF_SUCCESS)
uu_die("retrieving local scope failed: %d\n", ret);
if (scf_iter_scope_services(svc_iter, scope) == -1)
uu_die("walking local scope's services failed\n");
while (scf_iter_next_service(svc_iter, svc) > 0) {
if (scf_iter_service_instances(inst_iter, svc) == -1)
uu_die("unable to walk service's instances");
while (scf_iter_next_instance(inst_iter, inst) > 0) {
char *fmri;
if (libscf_instance_get_fmri(inst, &fmri) == 0) {
int err;
err = dgraph_add_instance(fmri, inst, B_TRUE);
if (err != 0 && err != EEXIST)
log_error(LOG_WARNING,
"Failed to add %s (%s).\n", fmri,
strerror(err));
startd_free(fmri, max_scf_fmri_size);
}
}
}
scf_iter_destroy(inst_iter);
scf_iter_destroy(svc_iter);
scf_instance_destroy(inst);
scf_service_destroy(svc);
scf_scope_destroy(scope);
}
/*
* Monitors get handled differently since there can be multiple of them.
*
* Returns exec string on success. If method not defined, returns
* LIBSCF_PGROUP_ABSENT; if exec property missing, returns
* LIBSCF_PROPERTY_ABSENT. Returns LIBSCF_PROPERTY_ERROR on other failures.
*/
char *
libscf_get_method(scf_handle_t *h, int type, restarter_inst_t *inst,
scf_snapshot_t *snap, method_restart_t *restart_on, uint_t *cte_mask,
uint8_t *need_sessionp, uint64_t *timeout, uint8_t *timeout_retry)
{
scf_instance_t *scf_inst = NULL;
scf_propertygroup_t *pg = NULL, *pg_startd = NULL;
scf_property_t *prop = NULL;
const char *name;
char *method = startd_alloc(max_scf_value_size);
char *ig = startd_alloc(max_scf_value_size);
char *restart = startd_alloc(max_scf_value_size);
char *ret;
int error = 0, r;
scf_inst = safe_scf_instance_create(h);
pg = safe_scf_pg_create(h);
pg_startd = safe_scf_pg_create(h);
prop = safe_scf_property_create(h);
ret = NULL;
*restart_on = METHOD_RESTART_UNKNOWN;
switch (type) {
case METHOD_START:
name = "start";
break;
case METHOD_STOP:
name = "stop";
break;
case METHOD_REFRESH:
name = "refresh";
break;
default:
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
if (scf_handle_decode_fmri(h, inst->ri_i.i_fmri, NULL, NULL, scf_inst,
NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
log_error(LOG_WARNING,
"%s: get_method decode instance FMRI failed: %s\n",
inst->ri_i.i_fmri, scf_strerror(scf_error()));
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
if (scf_instance_get_pg_composed(scf_inst, snap, name, pg) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
error = LIBSCF_PGROUP_ABSENT;
else
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
if (scf_pg_get_property(pg, SCF_PROPERTY_EXEC, prop) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
error = LIBSCF_PROPERTY_ABSENT;
else
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
error = libscf_read_single_astring(h, prop, &method);
if (error != 0) {
log_error(LOG_WARNING,
"%s: get_method failed: can't get a single astring "
"from %s/%s\n", inst->ri_i.i_fmri, name, SCF_PROPERTY_EXEC);
goto get_method_cleanup;
}
error = expand_method_tokens(method, scf_inst, snap, type, &ret);
if (error != 0) {
log_instance(inst, B_TRUE, "Could not expand method tokens "
"in \"%s\": %s.", method, ret);
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
r = get_count(pg, SCF_PROPERTY_TIMEOUT, timeout);
switch (r) {
case 0:
break;
case ECONNABORTED:
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
case EINVAL:
log_instance(inst, B_TRUE, "%s/%s is multi-valued or not of "
"type count. Using infinite timeout.", name,
SCF_PROPERTY_TIMEOUT);
/* FALLTHROUGH */
case ECANCELED:
case ENOENT:
*timeout = METHOD_TIMEOUT_INFINITE;
break;
case EACCES:
default:
bad_error("get_count", r);
}
/* Both 0 and -1 (ugh) are considered infinite timeouts. */
if (*timeout == -1 || *timeout == 0)
*timeout = METHOD_TIMEOUT_INFINITE;
if (scf_instance_get_pg_composed(scf_inst, snap, SCF_PG_STARTD,
pg_startd) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
case SCF_ERROR_NOT_FOUND:
*cte_mask = 0;
break;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
bad_error("scf_instance_get_pg_composed", scf_error());
}
} else {
if (scf_pg_get_property(pg_startd, SCF_PROPERTY_IGNORE,
prop) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
*cte_mask = 0;
else {
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
} else {
error = libscf_read_single_astring(h, prop, &ig);
if (error != 0) {
log_error(LOG_WARNING,
"%s: get_method failed: can't get a single "
"astring from %s/%s\n", inst->ri_i.i_fmri,
name, SCF_PROPERTY_IGNORE);
goto get_method_cleanup;
}
if (strcmp(ig, "core") == 0)
*cte_mask = CT_PR_EV_CORE;
else if (strcmp(ig, "signal") == 0)
*cte_mask = CT_PR_EV_SIGNAL;
else if (strcmp(ig, "core,signal") == 0 ||
strcmp(ig, "signal,core") == 0)
*cte_mask = CT_PR_EV_CORE | CT_PR_EV_SIGNAL;
else
*cte_mask = 0;
}
r = get_boolean(pg_startd, SCF_PROPERTY_NEED_SESSION,
need_sessionp);
switch (r) {
case 0:
break;
case ECONNABORTED:
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
case ECANCELED:
case ENOENT:
case EINVAL:
*need_sessionp = 0;
break;
case EACCES:
default:
bad_error("get_boolean", r);
}
/*
* Determine whether service has overriden retry after
* method timeout. Default to retry if no value is
* specified.
*/
r = get_boolean(pg_startd, SCF_PROPERTY_TIMEOUT_RETRY,
timeout_retry);
switch (r) {
case 0:
break;
case ECONNABORTED:
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
case ECANCELED:
case ENOENT:
case EINVAL:
*timeout_retry = 1;
break;
case EACCES:
default:
bad_error("get_boolean", r);
}
}
if (type != METHOD_START)
goto get_method_cleanup;
/* Only start methods need to honor the restart_on property. */
if (scf_pg_get_property(pg, SCF_PROPERTY_RESTART_ON, prop) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
*restart_on = METHOD_RESTART_ALL;
else
error = LIBSCF_PROPERTY_ERROR;
goto get_method_cleanup;
}
error = libscf_read_single_astring(h, prop, &restart);
if (error != 0) {
log_error(LOG_WARNING,
"%s: get_method failed: can't get a single astring "
"from %s/%s\n", inst->ri_i.i_fmri, name,
SCF_PROPERTY_RESTART_ON);
goto get_method_cleanup;
}
if (strcmp(restart, "all") == 0)
*restart_on = METHOD_RESTART_ALL;
else if (strcmp(restart, "external_fault") == 0)
*restart_on = METHOD_RESTART_EXTERNAL_FAULT;
else if (strcmp(restart, "any_fault") == 0)
*restart_on = METHOD_RESTART_ANY_FAULT;
get_method_cleanup:
startd_free(ig, max_scf_value_size);
startd_free(method, max_scf_value_size);
startd_free(restart, max_scf_value_size);
scf_instance_destroy(scf_inst);
scf_pg_destroy(pg);
scf_pg_destroy(pg_startd);
scf_property_destroy(prop);
if (error != 0 && ret != NULL) {
free(ret);
ret = NULL;
}
errno = error;
return (ret);
}
/*
* Returns 1 if we've reached the fault threshold
*/
int
update_fault_count(restarter_inst_t *inst, int type)
{
assert(type == FAULT_COUNT_INCR || type == FAULT_COUNT_RESET);
if (type == FAULT_COUNT_INCR) {
inst->ri_i.i_fault_count++;
log_framework(LOG_INFO, "%s: Increasing fault count to %d\n",
inst->ri_i.i_fmri, inst->ri_i.i_fault_count);
}
if (type == FAULT_COUNT_RESET)
inst->ri_i.i_fault_count = 0;
if (inst->ri_i.i_fault_count >= FAULT_THRESHOLD)
return (1);
return (0);
}
/*
* int libscf_unset_action()
* Delete any pending timestamps for the specified action which is
* older than the supplied ts.
*
* Returns 0 on success, ECONNABORTED, EACCES, or EPERM on failure.
*/
int
libscf_unset_action(scf_handle_t *h, scf_propertygroup_t *pg,
admin_action_t a, hrtime_t ts)
{
scf_transaction_t *t;
scf_transaction_entry_t *e;
scf_property_t *prop;
scf_value_t *val;
hrtime_t rep_ts;
int ret = 0, r;
t = safe_scf_transaction_create(h);
e = safe_scf_entry_create(h);
prop = safe_scf_property_create(h);
val = safe_scf_value_create(h);
for (;;) {
if (scf_pg_update(pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
goto unset_action_cleanup;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
if (scf_transaction_start(t, pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
goto unset_action_cleanup;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto unset_action_cleanup;
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
ret = EACCES;
goto unset_action_cleanup;
case SCF_ERROR_IN_USE:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
/* Return failure only if the property hasn't been deleted. */
if (scf_pg_get_property(pg, admin_actions[a], prop) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
goto unset_action_cleanup;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
if (scf_property_get_value(prop, val) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
goto unset_action_cleanup;
case SCF_ERROR_CONSTRAINT_VIOLATED:
/*
* More than one value was associated with
* this property -- this is incorrect. Take
* the opportunity to clean up and clear the
* entire property.
*/
rep_ts = ts;
break;
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
} else if (scf_value_get_integer(val, &rep_ts) == -1) {
assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
rep_ts = 0;
}
/* Repository ts is more current. Don't clear the action. */
if (rep_ts > ts)
goto unset_action_cleanup;
r = scf_transaction_property_change_type(t, e,
admin_actions[a], SCF_TYPE_INTEGER);
assert(r == 0);
r = scf_transaction_commit(t);
if (r == 1)
break;
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
break;
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto unset_action_cleanup;
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
ret = EACCES;
goto unset_action_cleanup;
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
scf_transaction_reset(t);
}
unset_action_cleanup:
scf_transaction_destroy(t);
scf_entry_destroy(e);
scf_property_destroy(prop);
scf_value_destroy(val);
return (ret);
}
/*
* Decorates & binds hndl. hndl must be unbound. Returns
* 0 - success
* -1 - repository server is not running
* -1 - repository server is out of resources
*/
static int
handle_decorate_and_bind(scf_handle_t *hndl)
{
scf_value_t *door_dec_value;
door_dec_value = safe_scf_value_create(hndl);
/*
* Decorate if alternate door path set.
*/
if (st->st_door_path) {
if (scf_value_set_astring(door_dec_value, st->st_door_path) !=
0)
uu_die("$STARTD_ALT_DOOR is too long.\n");
if (scf_handle_decorate(hndl, "door_path", door_dec_value) != 0)
bad_error("scf_handle_decorate", scf_error());
}
scf_value_destroy(door_dec_value);
if (scf_handle_bind(hndl) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_NO_SERVER:
case SCF_ERROR_NO_RESOURCES:
return (-1);
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_IN_USE:
default:
bad_error("scf_handle_bind", scf_error());
/* NOTREACHED */
}
}
scf_handle_t *
libscf_handle_create_bound(scf_version_t v)
{
scf_handle_t *hndl = scf_handle_create(v);
if (hndl == NULL)
return (hndl);
if (handle_decorate_and_bind(hndl) == 0)
return (hndl);
scf_handle_destroy(hndl);
return (NULL);
}
void
libscf_handle_rebind(scf_handle_t *h)
{
(void) scf_handle_unbind(h);
MUTEX_LOCK(&st->st_configd_live_lock);
/*
* Try to rebind the handle before sleeping in case the server isn't
* really dead.
*/
while (handle_decorate_and_bind(h) != 0)
(void) pthread_cond_wait(&st->st_configd_live_cv,
&st->st_configd_live_lock);
MUTEX_UNLOCK(&st->st_configd_live_lock);
}
/*
* Create a handle and try to bind it until it succeeds. Always returns
* a bound handle.
*/
scf_handle_t *
libscf_handle_create_bound_loop()
{
scf_handle_t *h;
while ((h = scf_handle_create(SCF_VERSION)) == NULL) {
/* This should have been caught earlier. */
assert(scf_error() != SCF_ERROR_VERSION_MISMATCH);
(void) sleep(2);
}
if (handle_decorate_and_bind(h) != 0)
libscf_handle_rebind(h);
return (h);
}
/*
* Call cb for each dependency property group of inst. cb is invoked with
* a pointer to the scf_propertygroup_t and arg. If the repository connection
* is broken, returns ECONNABORTED. If inst is deleted, returns ECANCELED.
* If cb returns non-zero, the walk is stopped and EINTR is returned.
* Otherwise returns 0.
*/
int
walk_dependency_pgs(scf_instance_t *inst, callback_t cb, void *arg)
{
scf_handle_t *h;
scf_snapshot_t *snap;
scf_iter_t *iter;
scf_propertygroup_t *pg;
int r;
h = scf_instance_handle(inst);
iter = safe_scf_iter_create(h);
pg = safe_scf_pg_create(h);
snap = libscf_get_running_snapshot(inst);
if (scf_iter_instance_pgs_typed_composed(iter, inst, snap,
SCF_GROUP_DEPENDENCY) != 0) {
scf_snapshot_destroy(snap);
scf_pg_destroy(pg);
scf_iter_destroy(iter);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
for (;;) {
r = scf_iter_next_pg(iter, pg);
if (r == 0)
break;
if (r == -1) {
scf_snapshot_destroy(snap);
scf_pg_destroy(pg);
scf_iter_destroy(iter);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
default:
bad_error("scf_iter_next_pg", scf_error());
}
}
r = cb(pg, arg);
if (r != 0)
break;
}
scf_snapshot_destroy(snap);
scf_pg_destroy(pg);
scf_iter_destroy(iter);
return (r == 0 ? 0 : EINTR);
}
/*
* Call cb for each of the string values of prop. cb is invoked with
* a pointer to the string and arg. If the connection to the repository is
* broken, ECONNABORTED is returned. If the property is deleted, ECANCELED is
* returned. If the property does not have astring type, EINVAL is returned.
* If cb returns non-zero, the walk is stopped and EINTR is returned.
* Otherwise 0 is returned.
*/
int
walk_property_astrings(scf_property_t *prop, callback_t cb, void *arg)
{
scf_handle_t *h;
scf_value_t *val;
scf_iter_t *iter;
char *buf;
int r;
ssize_t sz;
if (scf_property_is_type(prop, SCF_TYPE_ASTRING) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_TYPE_MISMATCH:
return (EINVAL);
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
h = scf_property_handle(prop);
val = safe_scf_value_create(h);
iter = safe_scf_iter_create(h);
if (scf_iter_property_values(iter, prop) != 0) {
scf_iter_destroy(iter);
scf_value_destroy(val);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
buf = startd_alloc(max_scf_value_size);
for (;;) {
r = scf_iter_next_value(iter, val);
if (r < 0) {
startd_free(buf, max_scf_value_size);
scf_iter_destroy(iter);
scf_value_destroy(val);
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_SET:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_PERMISSION_DENIED:
default:
bad_error("scf_iter_next_value", scf_error());
}
}
if (r == 0)
break;
sz = scf_value_get_astring(val, buf, max_scf_value_size);
assert(sz >= 0);
r = cb(buf, arg);
if (r != 0)
break;
}
startd_free(buf, max_scf_value_size);
scf_value_destroy(val);
scf_iter_destroy(iter);
return (r == 0 ? 0 : EINTR);
}
/*
* Returns 0 or ECONNABORTED.
*/
int
libscf_create_self(scf_handle_t *h)
{
scf_scope_t *scope;
scf_service_t *svc;
scf_instance_t *inst;
instance_data_t idata;
int ret = 0, r;
ctid_t ctid;
uint64_t uint64;
uint_t count = 0, msecs = ALLOC_DELAY;
const char * const startd_svc = "system/svc/restarter";
const char * const startd_inst = "default";
/* If SCF_SERVICE_STARTD changes, our strings must change, too. */
assert(strcmp(SCF_SERVICE_STARTD,
"svc:/system/svc/restarter:default") == 0);
scope = safe_scf_scope_create(h);
svc = safe_scf_service_create(h);
inst = safe_scf_instance_create(h);
if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) {
assert(scf_error() == SCF_ERROR_CONNECTION_BROKEN);
ret = ECONNABORTED;
goto out;
}
get_svc:
if (scf_scope_get_service(scope, startd_svc, svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_scope_get_service", scf_error());
}
add_svc:
if (scf_scope_add_service(scope, startd_svc, svc) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_DELETED:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_EXISTS:
goto get_svc;
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_ACCESS:
case SCF_ERROR_BACKEND_READONLY:
uu_warn("Could not create %s: %s\n",
SCF_SERVICE_STARTD,
scf_strerror(scf_error()));
goto out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_scope_add_service", scf_error());
}
}
}
if (scf_service_get_instance(svc, startd_inst, NULL) == 0)
goto out;
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
goto add_svc;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_service_get_instance", scf_error());
}
add_inst:
if (scf_service_add_instance(svc, startd_inst, inst) != 0) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_BACKEND_ACCESS:
uu_die("Could not create %s: %s\n", SCF_SERVICE_STARTD,
scf_strerror(scf_error()));
/* NOTREACHED */
case SCF_ERROR_BACKEND_READONLY:
log_error(LOG_NOTICE,
"Could not create %s: backend readonly.\n",
SCF_SERVICE_STARTD);
goto out;
case SCF_ERROR_DELETED:
goto add_svc;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_NOT_SET:
bad_error("scf_service_add_instance", scf_error());
}
}
/* Set start time. */
idata.i_fmri = SCF_SERVICE_STARTD;
idata.i_state = RESTARTER_STATE_NONE;
idata.i_next_state = RESTARTER_STATE_NONE;
set_state:
switch (r = _restarter_commit_states(h, &idata, RESTARTER_STATE_ONLINE,
RESTARTER_STATE_NONE, NULL)) {
case 0:
break;
case ENOMEM:
++count;
if (count < ALLOC_RETRY) {
(void) poll(NULL, 0, msecs);
msecs *= ALLOC_DELAY_MULT;
goto set_state;
}
uu_die("Insufficient memory.\n");
/* NOTREACHED */
case ECONNABORTED:
ret = ECONNABORTED;
goto out;
case ENOENT:
goto add_inst;
case EPERM:
case EACCES:
case EROFS:
uu_warn("Could not timestamp %s: %s\n", idata.i_fmri,
strerror(r));
break;
case EINVAL:
default:
bad_error("_restarter_commit_states", r);
}
/* Set general/enabled. */
ret = libscf_inst_set_boolean_prop(inst, SCF_PG_GENERAL,
SCF_PG_GENERAL_TYPE, SCF_PG_GENERAL_FLAGS, SCF_PROPERTY_ENABLED, 1);
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto add_inst;
default:
bad_error("libscf_inst_set_boolean_prop", ret);
}
ret = libscf_write_start_pid(inst, getpid());
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto add_inst;
default:
bad_error("libscf_write_start_pid", ret);
}
ctid = proc_get_ctid();
if (ctid > 0) {
uint64 = (uint64_t)ctid;
ret = libscf_inst_set_count_prop(inst,
SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE,
SCF_PG_RESTARTER_FLAGS, SCF_PROPERTY_CONTRACT, uint64);
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto add_inst;
default:
bad_error("libscf_inst_set_count_prop", ret);
}
}
ret = libscf_note_method_log(inst, LOG_PREFIX_EARLY,
STARTD_DEFAULT_LOG);
if (ret == 0) {
ret = libscf_note_method_log(inst, LOG_PREFIX_NORMAL,
STARTD_DEFAULT_LOG);
}
switch (ret) {
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
case EAGAIN:
break;
case ECANCELED:
goto add_inst;
default:
bad_error("libscf_note_method_log", ret);
}
out:
scf_instance_destroy(inst);
scf_service_destroy(svc);
scf_scope_destroy(scope);
return (ret);
}
/*
* Returns
* 0 - success
* ENOENT - SCF_SERVICE_STARTD does not exist in repository
* EPERM
* EACCES
* EROFS
*/
int
libscf_set_reconfig(int set)
{
scf_handle_t *h;
scf_instance_t *inst;
scf_propertygroup_t *pg;
int ret = 0;
h = libscf_handle_create_bound_loop();
inst = safe_scf_instance_create(h);
pg = safe_scf_pg_create(h);
again:
if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL,
inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
default:
libscf_handle_rebind(h);
goto again;
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto reconfig_out;
case SCF_ERROR_HANDLE_MISMATCH:
case SCF_ERROR_INVALID_ARGUMENT:
case SCF_ERROR_CONSTRAINT_VIOLATED:
bad_error("scf_handle_decode_fmri", scf_error());
}
}
ret = libscf_inst_set_boolean_prop(inst, "system", SCF_GROUP_FRAMEWORK,
SCF_PG_FLAG_NONPERSISTENT, "reconfigure", set);
switch (ret) {
case 0:
case EPERM:
case EACCES:
case EROFS:
break;
case ECONNABORTED:
libscf_handle_rebind(h);
goto again;
case ECANCELED:
ret = ENOENT;
break;
default:
bad_error("libscf_inst_set_boolean_prop", ret);
}
reconfig_out:
scf_pg_destroy(pg);
scf_instance_destroy(inst);
scf_handle_destroy(h);
return (ret);
}
/*
* Set inst->ri_m_inst to the scf instance for inst. If it has been deleted,
* set inst->ri_mi_deleted to true. If the repository connection is broken, it
* is rebound with libscf_handle_rebound().
*/
void
libscf_reget_instance(restarter_inst_t *inst)
{
scf_handle_t *h;
int r;
h = scf_instance_handle(inst->ri_m_inst);
again:
r = libscf_lookup_instance(inst->ri_i.i_fmri, inst->ri_m_inst);
switch (r) {
case 0:
case ENOENT:
inst->ri_mi_deleted = (r == ENOENT);
return;
case ECONNABORTED:
libscf_handle_rebind(h);
goto again;
case EINVAL:
case ENOTSUP:
default:
bad_error("libscf_lookup_instance", r);
}
}