libscf.c revision 5b6c54434ca92caf3a22e592d20b481ada117cb2
/*
* 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
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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)
{
int r;
assert(r == 0);
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 *
{
void *o;
o = f(h);
if (o != NULL)
return (o);
return (NULL);
msecs = ALLOC_DELAY;
o = f(h);
if (o != NULL)
return (o);
return (NULL);
}
uu_die("Insufficient memory.\n");
/* NOTREACHED */
}
{
scf_handle_t *h;
h = scf_instance_handle(inst);
if (h == NULL)
return (NULL);
snap = scf_snapshot_create(h);
return (NULL);
return (snap);
return (NULL);
}
/*
* Make sure a service has a "running" snapshot. If it doesn't, make one from
* the editing configuration.
*/
{
scf_handle_t *h;
h = scf_instance_handle(inst);
snap = scf_snapshot_create(h);
goto err;
return (snap);
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
return (NULL);
default:
err:
"Could not check for running snapshot of %s (%s).\n", fmri,
scf_strerror(scf_error()));
return (NULL);
}
fmri);
} else {
else
"Could not create running snapshot for %s "
}
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
{
int ret = 0, r;
switch (r) {
case 0:
break;
case ENOTSUP:
case ECONNABORTED:
case ENOENT:
return (r);
case EINVAL:
default:
assert(0);
abort();
}
ret = 0;
goto out;
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_EXISTS:
goto lookup;
case SCF_ERROR_NO_RESOURCES:
uu_die("Repository server out of "
"resources.\n");
/* NOTREACHED */
goto readonly;
uu_die("Insufficient privileges.\n");
/* NOTREACHED */
case SCF_ERROR_BACKEND_ACCESS:
goto out;
case SCF_ERROR_NOT_SET:
bad_error("_scf_snapshot_take_new",
scf_error());
}
}
break;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_SET:
}
}
fmri);
} else {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto again;
case SCF_ERROR_NO_RESOURCES:
uu_die("Repository server out of resources.\n");
/* NOTREACHED */
uu_die("Insufficient privileges.\n");
/* NOTREACHED */
case SCF_ERROR_BACKEND_ACCESS:
goto out;
if (retake)
break;
case SCF_ERROR_NOT_SET:
}
}
out:
return (ret);
}
/*
* Before a refresh, update the "running" snapshot from the editing
* configuration.
*
* Returns 0 on success and -1 on failure.
*/
int
{
scf_handle_t *h;
h = scf_instance_handle(inst);
if (h == NULL)
goto out;
snap = scf_snapshot_create(h);
goto out;
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;
}
"Service %s has no %s snapshot; creating one.\n", fmri,
snap) == 0)
err = 0;
}
out:
if (!err)
return (0);
"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
{
int r = 0;
if (scf_error() == SCF_ERROR_NOT_FOUND)
else
goto read_single_astring_fail;
}
goto read_single_astring_fail;
}
return (r);
}
static int
{
scf_handle_t *h;
int ret = 0;
h = scf_pg_handle(pg);
prop = safe_scf_property_create(h);
if (scf_error() == SCF_ERROR_NOT_FOUND)
else
} else {
if (ret != 0) {
if (ret != LIBSCF_PROPERTY_ABSENT)
} else {
ret = 0;
}
}
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
{
if (state_ret == LIBSCF_PROPERTY_ERROR ||
} else if (state_ret == 0 && next_state_ret == 0) {
ret = 0;
} else if (state_ret == LIBSCF_PROPERTY_ABSENT &&
ret = 0;
} else if (state_ret == LIBSCF_PROPERTY_ABSENT ||
"Only one repository state exists, setting "
"restarter states to MAINTENANCE and NONE\n");
ret = 0;
} else {
}
return (ret);
}
/*
* depgroup_empty()
*
* Returns 0 if not empty.
* Returns 1 if empty.
* Returns -1 on error (check scf_error()).
*/
int
{
int empty = 1;
int ret;
iter = safe_scf_iter_create(h);
prop = safe_scf_property_create(h);
return (-1);
}
if (ret < 0) {
return (-1);
}
if (ret == 1)
empty = 0;
return (empty);
}
{
prop = safe_scf_property_create(h);
return (GVT_UNSUPPORTED);
}
else
return (ret);
}
{
return (DEPGRP_UNSUPPORTED);
}
else {
}
return (ret);
}
{
return (RERR_UNSUPPORTED);
}
ret = RERR_FAULT;
ret = RERR_RESTART;
ret = RERR_REFRESH;
else
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
*/
static int
{
scf_handle_t *h;
int ret = 0, r;
h = scf_pg_handle(pg);
prop = safe_scf_property_create(h);
val = safe_scf_value_create(h);
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_FOUND:
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_SET:
}
}
if (type != SCF_TYPE_BOOLEAN) {
goto out;
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
assert(r == 0);
out:
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
*/
static int
{
scf_handle_t *h;
int ret = 0, r;
h = scf_pg_handle(pg);
prop = safe_scf_property_create(h);
val = safe_scf_value_create(h);
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_FOUND:
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_TYPE_MISMATCH:
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_FOUND:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
assert(r == 0);
out:
return (ret);
}
static void
{
*restarter[0] = '\0';
}
/*
* 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
{
inst_fmri[0] = 0;
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
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
{
int r;
inst = safe_scf_instance_create(h);
if (r == 0)
else
return (r);
}
int
{
switch (scf_error()) {
return (EINVAL);
return (ENOTSUP);
return (ECONNABORTED);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
default:
}
}
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.
*
* or invalid, *enabledp will be -1 and a debug message is logged.
*/
int
{
int r;
pg = safe_scf_pg_create(h);
if (enabled_ovrp == NULL)
goto enabled;
0) {
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
*enabled_ovrp = -1;
break;
case SCF_ERROR_NOT_SET:
}
} else {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
return (r);
case ENOENT:
case EINVAL:
*enabled_ovrp = -1;
break;
default:
bad_error("get_boolean", r);
}
}
/*
* 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.
*/
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_NOT_SET:
}
}
case 0:
break;
case ECONNABORTED:
case ECANCELED:
return (r);
case ENOENT:
/*
* DEBUG because this happens when svccfg import creates
* a temporary service.
*/
*enabledp = -1;
break;
case EINVAL:
*enabledp = -1;
break;
default:
bad_error("get_boolean", r);
}
if (restarter_fmri != NULL)
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
{
uint32_t f;
if (scf_pg_get_flags(pg, &f) != 0) {
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
goto add;
case SCF_ERROR_NOT_SET:
}
}
if (f == flags)
return (0);
if (scf_pg_delete(pg) != 0) {
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
break;
return (EPERM);
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
return (EROFS);
case SCF_ERROR_NOT_SET:
}
}
} else {
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_SET:
}
}
add:
return (0);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
goto again;
return (EPERM);
case SCF_ERROR_BACKEND_ACCESS:
return (EACCES);
return (EROFS);
case SCF_ERROR_NOT_SET:
/* NOTREACHED */
}
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED
*/
static scf_error_t
{
for (;;) {
ty) == 0)
return (0);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
bad_error("scf_transaction_property_change_type",
scf_error());
}
return (0);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
/* NOTREACHED */
}
}
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - pg was deleted
* EPERM
* EACCES
* EROFS
*/
static int
{
scf_handle_t *h;
int ret, r;
h = scf_pg_handle(pg);
e = safe_scf_entry_create(h);
ty = scf_value_type(v);
for (;;) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
}
r = scf_entry_add_value(e, v);
assert(r == 0);
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
switch (scfe) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_SET:
}
}
}
ret = 0;
out:
return (ret);
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
{
scf_handle_t *h;
scf_value_t *v;
int ret = 0;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
v = safe_scf_value_create(h);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
}
scf_value_set_boolean(v, val);
switch (ret) {
case 0:
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
break;
default:
}
out:
return (ret);
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
{
scf_handle_t *h;
scf_value_t *v;
int ret = 0;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
v = safe_scf_value_create(h);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
}
scf_value_set_count(v, count);
switch (ret) {
case 0:
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
break;
default:
}
out:
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
{
}
/*
* Returns
* 0 - success
* ECONNABORTED - repository connection broken
* ECANCELED - inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
const char *pname)
{
scf_handle_t *h;
int ret = 0, r;
h = scf_instance_handle(inst);
pg = safe_scf_pg_create(h);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
return (0);
case SCF_ERROR_NOT_SET:
}
}
e = safe_scf_entry_create(h);
for (;;) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = 0;
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
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_NOT_BOUND:
bad_error("scf_transaction_property_delete",
scf_error());
}
}
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
switch (serr) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = 0;
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
ret = 0;
goto out;
case SCF_ERROR_NOT_SET:
case SCF_ERROR_NOT_BOUND:
}
}
}
out:
(void) scf_entry_destroy(e);
return (ret);
}
/*
* Returns 0, ECONNABORTED, ECANCELED, or EPERM.
*/
int
{
}
/*
* 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
{
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
return (EINVAL);
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
case SCF_ERROR_TYPE_MISMATCH:
return (EINVAL);
case SCF_ERROR_NOT_SET:
default:
}
}
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
{
int r;
case 0:
case ECONNABORTED:
case EINVAL:
goto out;
case ECANCELED:
case ENOENT:
break;
default:
bad_error("pg_get_milestone", r);
}
} else {
switch (scf_error()) {
default:
r = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
r = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_SET:
}
}
} else {
switch (scf_error()) {
default:
r = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
r = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
r = ENOENT;
break;
case SCF_ERROR_NOT_SET:
}
}
out:
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
{
char buf[2];
switch (scf_error()) {
return (ECONNABORTED);
case SCF_ERROR_NOT_SET:
return (ENOENT);
case SCF_ERROR_DELETED:
return (ECANCELED);
return (EINVAL);
case SCF_ERROR_NOT_FOUND:
return (ENOENT);
case SCF_ERROR_NOT_BOUND:
default:
}
}
if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
return (EINVAL);
}
return (EINVAL);
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
{
scf_handle_t *h;
int ret = 0, r;
h = scf_pg_handle(pg);
e_rl = safe_scf_entry_create(h);
e_ms = safe_scf_entry_create(h);
val = safe_scf_value_create(h);
if (milestone) {
assert(r == 0);
}
for (;;) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
"runlevel") == 0) {
} else {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_BOUND:
bad_error("scf_transaction_property_delete",
scf_error());
}
}
if (milestone) {
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
}
assert(r == 0);
}
if (isempty)
goto out;
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
switch (serr) {
ret = ECONNABORTED;
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
default:
}
}
switch (scf_error()) {
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_SET:
goto out;
default:
assert(0);
abort();
}
}
}
out:
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
char **common_name, char **c_common_name)
{
scf_handle_t *h;
int ret = 0, r;
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.
*/
== -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
goto template_values_out;
case SCF_ERROR_NOT_FOUND:
goto template_values_out;
default:
ret = ECONNABORTED;
goto template_values_out;
case SCF_ERROR_NOT_SET:
}
}
/*
* The name we wish uses the current locale name as the property name.
*/
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
break;
default:
ret = ECONNABORTED;
goto template_values_out;
case SCF_ERROR_NOT_SET:
}
} else {
0) {
if (r != LIBSCF_PROPERTY_ABSENT)
goto template_values_out;
}
*common_name = cname;
}
}
/*
* Also pull out the C locale name, as a fallback for the case where
* service offers no localized name.
*/
switch (scf_error()) {
case SCF_ERROR_DELETED:
goto template_values_out;
case SCF_ERROR_NOT_FOUND:
break;
default:
ret = ECONNABORTED;
goto template_values_out;
case SCF_ERROR_NOT_SET:
}
} else {
if (r != LIBSCF_PROPERTY_ABSENT)
goto template_values_out;
}
*c_common_name = c_cname;
}
if (common_name_initialized == B_FALSE)
if (c_common_name_initialized == B_FALSE)
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
{
scf_handle_t *h;
int style = RINST_CONTRACT;
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.
*/
switch (scf_error()) {
case SCF_ERROR_DELETED:
goto instance_flags_out;
case SCF_ERROR_NOT_FOUND:
goto instance_flags_out;
default:
ret = ECONNABORTED;
goto instance_flags_out;
case SCF_ERROR_NOT_SET:
}
}
/*
* 1. Duration property.
*/
switch (scf_error()) {
case SCF_ERROR_DELETED:
goto instance_flags_out;
case SCF_ERROR_NOT_FOUND:
break;
default:
ret = ECONNABORTED;
goto instance_flags_out;
case SCF_ERROR_NOT_SET:
}
} else {
errno = 0;
!= 0) {
if (r != LIBSCF_PROPERTY_ABSENT)
goto instance_flags_out;
}
style = RINST_WAIT;
}
/*
* 2. utmpx prefix property.
*/
errno = 0;
if (r != LIBSCF_PROPERTY_ABSENT)
goto instance_flags_out;
}
} else {
switch (scf_error()) {
case SCF_ERROR_DELETED:
goto instance_flags_out;
case SCF_ERROR_NOT_FOUND:
goto instance_flags_out;
default:
ret = ECONNABORTED;
goto instance_flags_out;
case SCF_ERROR_NOT_SET:
}
}
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
{
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);
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto read_id_err;
case SCF_ERROR_DELETED:
goto read_id_err;
case SCF_ERROR_NOT_FOUND:
goto read_id_err;
case SCF_ERROR_NOT_SET:
}
}
switch (ret) {
case 0:
break;
case EINVAL:
"%s: Ignoring %s/%s: multivalued or not of type count\n",
/* FALLTHROUGH */
case ENOENT:
ret = 0;
goto read_trans;
case ECONNABORTED:
case ECANCELED:
goto read_id_err;
default:
}
*primary = p;
switch (ret) {
case 0:
break;
case EINVAL:
"%s: Ignoring %s/%s: multivalued or not of type count\n",
/* FALLTHROUGH */
case ENOENT:
ret = 0;
goto read_pid_only;
case ECONNABORTED:
case ECANCELED:
goto read_id_err;
default:
}
*transient = t;
switch (ret) {
case 0:
break;
case EINVAL:
"%s: Ignoring %s/%s: multivalued or not of type count\n",
/* FALLTHROUGH */
case ENOENT:
ret = 0;
goto read_id_err;
case ECONNABORTED:
case ECANCELED:
goto read_id_err;
default:
}
*start_pid = p;
return (ret);
}
/*
* Returns with
* 0 - success
* ECONNABORTED - repository connection broken
* - unknown libscf error
* ECANCELED - s_inst was deleted
* EPERM
* EACCES
* EROFS
*/
int
{
scf_handle_t *h;
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);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto write_start_err;
default:
}
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto get_pg;
default:
}
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
*
* Returns
* 0 - success
* ECONNABORTED
* ECANCELED
* EPERM
* EACCES
* EROFS
* EAGAIN
*/
int
{
scf_handle_t *h;
scf_value_t *v;
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);
goto out;
}
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
}
(void) scf_value_set_astring(v, logname);
else
switch (ret) {
case 0:
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
break;
default:
}
out:
return (ret);
}
/*
* Returns
* 0 - success
* ENAMETOOLONG - name is too long
* ECONNABORTED
* ECANCELED
* EPERM
* EACCES
* EROFS
*/
int
int status)
{
scf_handle_t *h;
int ret = 0, r;
char pname[30];
return (ENAMETOOLONG);
h = scf_instance_handle(s_inst);
pg = safe_scf_pg_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);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
case EPERM:
case EACCES:
case EROFS:
goto out;
default:
}
assert(r == 0);
for (;;) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
name);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
}
assert(r == 0);
name);
switch (ret) {
case 0:
break;
case ECONNABORTED:
case ECANCELED:
goto out;
default:
}
if (r != 0)
r = scf_transaction_commit(tx);
if (r == 1)
break;
if (r != 0) {
switch (scfe) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
goto out;
case SCF_ERROR_BACKEND_ACCESS:
goto out;
goto out;
case SCF_ERROR_NOT_SET:
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_DELETED:
goto out;
case SCF_ERROR_NOT_SET:
}
}
}
out:
return (ret);
}
/*
* Call dgraph_add_instance() for each instance in the repository.
*/
void
{
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);
uu_die("walking local scope's services failed\n");
uu_die("unable to walk service's instances");
char *fmri;
int err;
"Failed to add %s (%s).\n", fmri,
}
}
}
}
/*
* 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 *
{
const char *name;
char *ret;
int error = 0, r;
pg = safe_scf_pg_create(h);
pg_startd = safe_scf_pg_create(h);
prop = safe_scf_property_create(h);
switch (type) {
case METHOD_START:
name = "start";
break;
case METHOD_STOP:
name = "stop";
break;
case METHOD_REFRESH:
name = "refresh";
break;
default:
goto get_method_cleanup;
}
"%s: get_method decode instance FMRI failed: %s\n",
goto get_method_cleanup;
}
if (scf_error() == SCF_ERROR_NOT_FOUND)
else
goto get_method_cleanup;
}
if (scf_error() == SCF_ERROR_NOT_FOUND)
else
goto get_method_cleanup;
}
if (error != 0) {
"%s: get_method failed: can't get a single astring "
goto get_method_cleanup;
}
if (error != 0) {
goto get_method_cleanup;
}
switch (r) {
case 0:
break;
case ECONNABORTED:
goto get_method_cleanup;
case EINVAL:
"type count. Using infinite timeout.", name,
/* FALLTHROUGH */
case ECANCELED:
case ENOENT:
break;
default:
bad_error("get_count", r);
}
/* Both 0 and -1 (ugh) are considered infinite timeouts. */
pg_startd) == -1) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
goto get_method_cleanup;
case SCF_ERROR_NOT_FOUND:
*cte_mask = 0;
break;
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_NOT_SET:
}
} else {
prop) == -1) {
if (scf_error() == SCF_ERROR_NOT_FOUND)
*cte_mask = 0;
else {
goto get_method_cleanup;
}
} else {
if (error != 0) {
"%s: get_method failed: can't get a single "
goto get_method_cleanup;
}
else
*cte_mask = 0;
}
switch (r) {
case 0:
break;
case ECONNABORTED:
goto get_method_cleanup;
case ECANCELED:
case ENOENT:
case EINVAL:
*need_sessionp = 0;
break;
default:
bad_error("get_boolean", r);
}
/*
* Determine whether service has overriden retry after
* method timeout. Default to retry if no value is
* specified.
*/
switch (r) {
case 0:
break;
case ECONNABORTED:
goto get_method_cleanup;
case ECANCELED:
case ENOENT:
case EINVAL:
*timeout_retry = 1;
break;
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_error() == SCF_ERROR_NOT_FOUND)
else
goto get_method_cleanup;
}
if (error != 0) {
"%s: get_method failed: can't get a single astring "
goto get_method_cleanup;
}
}
return (ret);
}
/*
* Returns 1 if we've reached the fault threshold
*/
int
{
if (type == FAULT_COUNT_INCR) {
}
if (type == FAULT_COUNT_RESET)
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
{
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 (;;) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
goto unset_action_cleanup;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
goto unset_action_cleanup;
goto unset_action_cleanup;
case SCF_ERROR_BACKEND_ACCESS:
goto unset_action_cleanup;
case SCF_ERROR_IN_USE:
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
/* Return failure only if the property hasn't been deleted. */
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
goto unset_action_cleanup;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
goto unset_action_cleanup;
/*
* More than one value was associated with
* this property -- this is incorrect. Take
* the opportunity to clean up and clear the
* entire property.
*/
break;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
rep_ts = 0;
}
/* Repository ts is more current. Don't clear the action. */
goto unset_action_cleanup;
r = scf_transaction_property_change_type(t, e,
assert(r == 0);
r = scf_transaction_commit(t);
if (r == 1)
break;
if (r != 0) {
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto unset_action_cleanup;
case SCF_ERROR_DELETED:
break;
goto unset_action_cleanup;
case SCF_ERROR_BACKEND_ACCESS:
goto unset_action_cleanup;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
}
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
{
/*
* Decorate if alternate door path set.
*/
if (st->st_door_path) {
0)
uu_die("$STARTD_ALT_DOOR is too long.\n");
}
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_IN_USE:
default:
/* NOTREACHED */
}
}
{
return (hndl);
if (handle_decorate_and_bind(hndl) == 0)
return (hndl);
return (NULL);
}
void
{
(void) scf_handle_unbind(h);
/*
* Try to rebind the handle before sleeping in case the server isn't
* really dead.
*/
while (handle_decorate_and_bind(h) != 0)
}
/*
* Create a handle and try to bind it until it succeeds. Always returns
* a bound handle.
*/
{
scf_handle_t *h;
/* This should have been caught earlier. */
(void) sleep(2);
}
if (handle_decorate_and_bind(h) != 0)
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
{
scf_handle_t *h;
int r;
h = scf_instance_handle(inst);
iter = safe_scf_iter_create(h);
pg = safe_scf_pg_create(h);
SCF_GROUP_DEPENDENCY) != 0) {
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
for (;;) {
if (r == 0)
break;
if (r == -1) {
return (ECONNABORTED);
}
if (r != 0)
break;
}
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
{
scf_handle_t *h;
char *buf;
int r;
switch (scf_error()) {
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);
switch (scf_error()) {
default:
return (ECONNABORTED);
case SCF_ERROR_DELETED:
return (ECANCELED);
case SCF_ERROR_NOT_SET:
assert(0);
abort();
}
}
for (;;) {
if (r < 0) {
return (ECONNABORTED);
}
if (r == 0)
break;
if (r != 0)
break;
}
return (r == 0 ? 0 : EINTR);
}
/*
* Returns 0 or ECONNABORTED.
*/
int
{
int ret = 0, r;
const char * const startd_svc = "system/svc/restarter";
const char * const startd_inst = "default";
/* If SCF_SERVICE_STARTD changes, our strings must change, too. */
scope = safe_scf_scope_create(h);
svc = safe_scf_service_create(h);
inst = safe_scf_instance_create(h);
ret = ECONNABORTED;
goto out;
}
switch (scf_error()) {
case SCF_ERROR_DELETED:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_NOT_SET:
}
switch (scf_error()) {
case SCF_ERROR_DELETED:
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_EXISTS:
goto get_svc;
case SCF_ERROR_BACKEND_ACCESS:
uu_warn("Could not create %s: %s\n",
scf_strerror(scf_error()));
goto out;
case SCF_ERROR_NOT_SET:
}
}
}
goto out;
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
goto add_svc;
case SCF_ERROR_NOT_SET:
}
switch (scf_error()) {
default:
ret = ECONNABORTED;
goto out;
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_BACKEND_ACCESS:
scf_strerror(scf_error()));
/* NOTREACHED */
"Could not create %s: backend readonly.\n",
goto out;
case SCF_ERROR_DELETED:
goto add_svc;
case SCF_ERROR_NOT_SET:
}
}
/* Set start time. */
RESTARTER_STATE_NONE, NULL)) {
case 0:
break;
case ENOMEM:
++count;
if (count < ALLOC_RETRY) {
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:
strerror(r));
break;
case EINVAL:
default:
bad_error("_restarter_commit_states", r);
}
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto add_inst;
default:
}
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto add_inst;
default:
}
ctid = proc_get_ctid();
if (ctid > 0) {
switch (ret) {
case 0:
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
break;
case ECANCELED:
goto add_inst;
default:
}
}
if (ret == 0) {
}
switch (ret) {
case ECONNABORTED:
case EPERM:
case EACCES:
case EROFS:
case EAGAIN:
break;
case ECANCELED:
goto add_inst;
default:
}
out:
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;
int ret = 0;
inst = safe_scf_instance_create(h);
pg = safe_scf_pg_create(h);
switch (scf_error()) {
default:
goto again;
case SCF_ERROR_NOT_FOUND:
goto reconfig_out;
}
}
switch (ret) {
case 0:
case EPERM:
case EACCES:
case EROFS:
break;
case ECONNABORTED:
goto again;
case ECANCELED:
break;
default:
}
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
{
scf_handle_t *h;
int r;
switch (r) {
case 0:
case ENOENT:
return;
case ECONNABORTED:
goto again;
case EINVAL:
case ENOTSUP:
default:
bad_error("libscf_lookup_instance", r);
}
}