#include <locale.h>
#include <libintl.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <libcontract.h>
#include <libcontract_priv.h>
#include <sys/contract/process.h>
#include <libuutil.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <procfs.h>
#include <assert.h>
#include <errno.h>
#include <zone.h>
#ifndef TEXT_DOMAIN
#define TEXT_DOMAIN "SUNW_OST_OSCMD"
#endif
#define HT_BUCKETS 64
#define EXIT_SVC_FAILURE 3
#define EXIT_DEP_FAILURE 4
#define WALK_FLAGS (SCF_WALK_UNIPARTIAL | SCF_WALK_MULTIPLE)
#define WAIT_INTERVAL 3
#define bad_error(func, err) \
uu_panic("%s:%d: %s() failed with unexpected error %d.\n", \
__FILE__, __LINE__, (func), (err));
struct ht_elt {
struct ht_elt *next;
boolean_t active;
char str[1];
};
#define SET_ENABLED 0x1
#define SET_TEMPORARY 0x2
#define SET_RECURSIVE 0x4
typedef struct {
char ed_comment[SCF_COMMENT_MAX_LENGTH];
int ed_flags;
} enable_data_t;
#define MARK_IMMEDIATE 0x1
#define MARK_TEMPORARY 0x2
typedef struct {
int md_flags;
} mark_data_t;
scf_handle_t *h;
ssize_t max_scf_fmri_sz;
static const char *emsg_permission_denied;
static const char *emsg_nomem;
static const char *emsg_create_pg_perm_denied;
static const char *emsg_pg_perm_denied;
static const char *emsg_prop_perm_denied;
static const char *emsg_no_service;
static int exit_status = 0;
static int verbose = 0;
static char *scratch_fmri;
static char *g_zonename = NULL;
static char svcstate[80];
static boolean_t svcsearch = B_FALSE;
static struct ht_elt **visited;
void do_scfdie(int lineno) __NORETURN;
static void usage_milestone(void) __NORETURN;
static void set_astring_prop(const char *, const char *, const char *,
uint32_t, const char *, const char *);
static void pr_warn(const char *format, ...);
extern int is_enabled(scf_instance_t *);
extern int has_potential(scf_instance_t *, int);
void
do_scfdie(int lineno)
{
scf_error_t err;
switch (err = scf_error()) {
case SCF_ERROR_CONNECTION_BROKEN:
uu_die(gettext("Connection to repository server broken. "
"Exiting.\n"));
case SCF_ERROR_BACKEND_READONLY:
uu_die(gettext("Repository is read-only. Exiting.\n"));
default:
#ifdef NDEBUG
uu_die(gettext("Unexpected libscf error: %s. Exiting.\n"),
scf_strerror(err));
#else
uu_die("Unexpected libscf error on line %d: %s.\n", lineno,
scf_strerror(err));
#endif
}
}
#define scfdie() do_scfdie(__LINE__)
static void
usage()
{
(void) fprintf(stderr, gettext(
"Usage: %1$s [-S <state>] [-v] [-Z | -z zone] [cmd [args ... ]]\n\n"
"\t%1$s enable [-rst] [<service> ...]\t- enable and online service(s)\n"
"\t%1$s disable [-c comment] [-st] [<service> ...] - disable "
"service(s)\n"
"\t%1$s restart [-d] [<service> ...]\t- restart specified service(s)\n"
"\t%1$s refresh [<service> ...]\t\t- re-read service configuration\n"
"\t%1$s mark [-It] <state> [<service> ...] - set maintenance state\n"
"\t%1$s clear [<service> ...]\t\t- clear maintenance state\n"
"\t%1$s milestone [-d] <milestone>\t- advance to a service milestone\n"
"\n\t"
"Services can be specified using an FMRI, abbreviation, or fnmatch(7)\n"
"\tpattern, as shown in these examples for svc:/network/smtp:sendmail\n"
"\n"
"\t%1$s <cmd> svc:/network/smtp:sendmail\n"
"\t%1$s <cmd> network/smtp:sendmail\n"
"\t%1$s <cmd> network/*mail\n"
"\t%1$s <cmd> network/smtp\n"
"\t%1$s <cmd> smtp:sendmail\n"
"\t%1$s <cmd> smtp\n"
"\t%1$s <cmd> sendmail\n"), uu_getpname());
exit(UU_EXIT_USAGE);
}
static uint32_t
hash_fmri(const char *str)
{
uint32_t h = 0, g;
const char *p;
for (p = str; *p != '\0'; ++p) {
h = (h << 4) + *p;
if ((g = (h & 0xf0000000)) != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h);
}
static int
visited_find_or_add(const char *str, struct ht_elt **hep)
{
uint32_t h;
uint_t i;
struct ht_elt *he;
h = hash_fmri(str);
i = h & (HT_BUCKETS - 1);
for (he = visited[i]; he != NULL; he = he->next) {
if (strcmp(he->str, str) == 0) {
if (hep)
*hep = he;
return (1);
}
}
he = malloc(offsetof(struct ht_elt, str) + strlen(str) + 1);
if (he == NULL)
return (-1);
(void) strcpy(he->str, str);
he->next = visited[i];
visited[i] = he;
if (hep)
*hep = he;
return (0);
}
int
get_bool_prop(scf_propertygroup_t *pg, const char *propname, uint8_t *bp)
{
scf_property_t *prop;
scf_value_t *val;
int ret;
if ((prop = scf_property_create(h)) == NULL ||
(val = scf_value_create(h)) == NULL)
scfdie();
if (scf_pg_get_property(pg, propname, prop) != 0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ECANCELED;
goto out;
case SCF_ERROR_NOT_FOUND:
ret = ENOENT;
goto out;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
default:
scfdie();
}
}
if (scf_property_get_value(prop, val) == 0) {
ret = 0;
} else {
switch (scf_error()) {
case SCF_ERROR_DELETED:
ret = ENOENT;
goto out;
case SCF_ERROR_NOT_FOUND:
ret = EINVAL;
goto out;
case SCF_ERROR_CONSTRAINT_VIOLATED:
ret = E2BIG;
break;
case SCF_ERROR_NOT_SET:
assert(0);
abort();
default:
scfdie();
}
}
if (scf_value_get_boolean(val, bp) != 0) {
if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
scfdie();
ret = EINVAL;
goto out;
}
out:
scf_value_destroy(val);
scf_property_destroy(prop);
return (ret);
}
static int
set_bool_prop(scf_propertygroup_t *pg, const char *propname, boolean_t b)
{
scf_value_t *v;
scf_transaction_t *tx;
scf_transaction_entry_t *ent;
int ret = 0, r;
if ((tx = scf_transaction_create(h)) == NULL ||
(ent = scf_entry_create(h)) == NULL ||
(v = scf_value_create(h)) == NULL)
scfdie();
scf_value_set_boolean(v, b);
for (;;) {
if (scf_transaction_start(tx, pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
default:
scfdie();
}
}
if (scf_transaction_property_change_type(tx, ent, propname,
SCF_TYPE_BOOLEAN) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_transaction_property_new(tx, ent, propname,
SCF_TYPE_BOOLEAN) != 0)
scfdie();
}
r = scf_entry_add_value(ent, v);
assert(r == 0);
r = scf_transaction_commit(tx);
if (r == 1)
break;
scf_transaction_reset(tx);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
default:
scfdie();
}
}
if (scf_pg_update(pg) == -1)
scfdie();
}
out:
scf_transaction_destroy(tx);
scf_entry_destroy(ent);
scf_value_destroy(v);
return (ret);
}
ssize_t
get_astring_prop(const scf_propertygroup_t *pg, const char *propname,
scf_property_t *prop, scf_value_t *v, char *buf, size_t bufsz)
{
ssize_t sz;
if (scf_pg_get_property(pg, propname, prop) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
return (-ENOENT);
}
if (scf_property_get_value(prop, v) != 0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_CONSTRAINT_VIOLATED:
return (-E2BIG);
default:
scfdie();
}
}
sz = scf_value_get_astring(v, buf, bufsz);
if (sz < 0) {
if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
scfdie();
return (-EINVAL);
}
return (sz);
}
static int
pg_get_or_add(const scf_instance_t *inst, const char *pgname,
const char *pgtype, uint32_t pgflags, scf_propertygroup_t *pg)
{
again:
if (scf_instance_get_pg(inst, pgname, pg) == 0)
return (0);
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) == 0)
return (0);
switch (scf_error()) {
case SCF_ERROR_EXISTS:
goto again;
case SCF_ERROR_PERMISSION_DENIED:
return (EPERM);
default:
scfdie();
}
}
static int
my_ct_name(char *out, size_t len)
{
ct_stathdl_t st;
char *ct_fmri;
ctid_t ct;
int fd, errno, ret;
if ((ct = getctid()) == -1)
uu_die(gettext("Could not get contract id for process"));
fd = contract_open(ct, "process", "status", O_RDONLY);
if ((errno = ct_status_read(fd, CTD_ALL, &st)) != 0)
uu_warn(gettext("Could not read status of contract "
"%ld: %s.\n"), ct, strerror(errno));
if ((errno = ct_pr_status_get_svc_fmri(st, &ct_fmri)) != 0)
uu_warn(gettext("Could not get svc_fmri for contract "
"%ld: %s.\n"), ct, strerror(errno));
ret = strlcpy(out, ct_fmri, len);
ct_status_free(st);
(void) close(fd);
return (ret);
}
static int
restarter_setup(const char *fmri, const scf_instance_t *inst)
{
boolean_t b = B_FALSE;
scf_propertygroup_t *pg = NULL;
int ret = 0;
if ((pg = scf_pg_create(h)) == NULL)
scfdie();
if (pg_get_or_add(inst, SCF_PG_RESTARTER_ACTIONS,
SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
pg) == EPERM) {
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_create_pg_perm_denied, fmri,
SCF_PG_RESTARTER_ACTIONS);
ret = EPERM;
goto out;
}
if (isatty(STDIN_FILENO))
b = B_TRUE;
switch (set_bool_prop(pg, SCF_PROPERTY_AUX_TTY, b)) {
case 0:
break;
case EPERM:
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_prop_perm_denied, fmri,
SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
ret = EPERM;
goto out;
case EROFS:
if (!verbose)
uu_warn(gettext("%s: Repository read-only.\n"), fmri);
else
uu_warn(gettext("%s: Could not set %s/%s "
"(repository read-only).\n"), fmri,
SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_TTY);
ret = EROFS;
goto out;
default:
scfdie();
}
if (my_ct_name(scratch_fmri, max_scf_fmri_sz) > 0) {
set_astring_prop(fmri, SCF_PG_RESTARTER_ACTIONS,
SCF_PG_RESTARTER_ACTIONS_TYPE,
SCF_PG_RESTARTER_ACTIONS_FLAGS,
SCF_PROPERTY_AUX_FMRI, scratch_fmri);
} else {
uu_warn(gettext("%s: Could not set %s/%s: "
"my_ct_name failed.\n"), fmri,
SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI);
}
out:
scf_pg_destroy(pg);
return (ret);
}
static int
delete_prop(const char *fmri, scf_instance_t *inst, const char *pgname,
const char *propname)
{
int r = scf_instance_delete_prop(inst, pgname, propname);
switch (r) {
case 0:
break;
case ECANCELED:
uu_warn(emsg_no_service, fmri);
break;
case EACCES:
uu_warn(gettext("Could not delete %s/%s "
"property of %s: backend access denied.\n"),
pgname, propname, fmri);
break;
case EROFS:
uu_warn(gettext("Could not delete %s/%s "
"property of %s: backend is read-only.\n"),
pgname, propname, fmri);
break;
default:
bad_error("scf_instance_delete_prop", r);
}
return (r);
}
static int
set_enabled_props(scf_propertygroup_t *pg, enable_data_t *ed)
{
scf_transaction_entry_t *ent1;
scf_transaction_entry_t *ent2;
scf_transaction_t *tx;
scf_value_t *v2;
scf_value_t *v1;
int ret = 0, r;
if ((tx = scf_transaction_create(h)) == NULL ||
(ent1 = scf_entry_create(h)) == NULL ||
(ent2 = scf_entry_create(h)) == NULL ||
(v1 = scf_value_create(h)) == NULL ||
(v2 = scf_value_create(h)) == NULL)
scfdie();
for (;;) {
if (scf_transaction_start(tx, pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
default:
scfdie();
}
}
if (scf_transaction_property_change_type(tx, ent1,
SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_transaction_property_new(tx, ent1,
SCF_PROPERTY_ENABLED, SCF_TYPE_BOOLEAN) != 0)
scfdie();
}
scf_value_set_boolean(v1, !!(ed->ed_flags & SET_ENABLED));
r = scf_entry_add_value(ent1, v1);
assert(r == 0);
if (scf_transaction_property_change_type(tx, ent2,
SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_transaction_property_new(tx, ent2,
SCF_PROPERTY_COMMENT, SCF_TYPE_ASTRING) != 0)
scfdie();
}
if (scf_value_set_astring(v2, ed->ed_comment) != SCF_SUCCESS)
scfdie();
if (scf_entry_add_value(ent2, v2) != SCF_SUCCESS)
scfdie();
r = scf_transaction_commit(tx);
if (r == 1)
break;
scf_transaction_reset(tx);
if (r != 0) {
switch (scf_error()) {
case SCF_ERROR_PERMISSION_DENIED:
ret = EPERM;
goto out;
case SCF_ERROR_BACKEND_READONLY:
ret = EROFS;
goto out;
default:
scfdie();
}
}
if (scf_pg_update(pg) == -1)
scfdie();
}
out:
scf_transaction_destroy(tx);
scf_entry_destroy(ent1);
scf_entry_destroy(ent2);
scf_value_destroy(v1);
scf_value_destroy(v2);
return (ret);
}
static void
set_inst_enabled(const char *fmri, scf_instance_t *inst, enable_data_t *ed)
{
scf_propertygroup_t *pg;
uint8_t b;
const char *pgname = NULL;
pg = scf_pg_create(h);
if (pg == NULL)
scfdie();
if (restarter_setup(fmri, inst))
goto out;
pgname = SCF_PG_GENERAL;
if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
SCF_PG_GENERAL_FLAGS, pg) != 0)
goto eperm;
if (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b) != 0) {
switch (set_bool_prop(pg, SCF_PROPERTY_ENABLED, B_FALSE)) {
case 0:
break;
case EPERM:
goto eperm;
case EROFS:
if (!verbose)
uu_warn(gettext("%s: Repository read-only.\n"),
fmri);
else
uu_warn(gettext("%s: Could not set %s/%s "
"(repository read-only).\n"), fmri,
SCF_PG_GENERAL, SCF_PROPERTY_ENABLED);
goto out;
default:
assert(0);
abort();
}
}
if (ed->ed_flags & SET_TEMPORARY) {
pgname = SCF_PG_GENERAL_OVR;
if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_OVR_TYPE,
SCF_PG_GENERAL_OVR_FLAGS, pg) != 0)
goto eperm;
switch (set_enabled_props(pg, ed)) {
case 0:
break;
case EPERM:
goto eperm;
case EROFS:
if (!verbose)
uu_warn(gettext("%s: Repository read-only.\n"),
fmri);
else
uu_warn(gettext("%s: Could not set %s/%s "
"(repository read-only).\n"), fmri,
SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED);
goto out;
default:
assert(0);
abort();
}
if (verbose)
(void) printf((ed->ed_flags & SET_ENABLED) ?
gettext("%s temporarily enabled.\n") :
gettext("%s temporarily disabled.\n"), fmri);
} else {
again:
if (pg_get_or_add(inst, pgname, SCF_PG_GENERAL_TYPE,
SCF_PG_GENERAL_FLAGS, pg) != 0)
goto eperm;
switch (set_enabled_props(pg, ed)) {
case 0:
break;
case EPERM:
goto eperm;
case EROFS:
switch (get_bool_prop(pg, SCF_PROPERTY_ENABLED, &b)) {
case 0:
if (!(b) == !(ed->ed_flags & SET_ENABLED))
break;
case ENOENT:
case EINVAL:
case E2BIG:
if (!verbose)
uu_warn(gettext("%s: Repository "
"read-only.\n"), fmri);
else
uu_warn(gettext("%s: Could not set "
"%s/%s (repository read-only).\n"),
fmri, SCF_PG_GENERAL,
SCF_PROPERTY_ENABLED);
goto out;
case ECANCELED:
goto again;
default:
assert(0);
abort();
}
break;
default:
assert(0);
abort();
}
switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
SCF_PROPERTY_ENABLED)) {
case 0:
break;
case EPERM:
goto eperm;
default:
goto out;
}
switch (delete_prop(fmri, inst, SCF_PG_GENERAL_OVR,
SCF_PROPERTY_COMMENT)) {
case 0:
break;
case EPERM:
goto eperm;
default:
goto out;
}
if (verbose) {
(void) printf((ed->ed_flags & SET_ENABLED) ?
gettext("%s enabled.\n") :
gettext("%s disabled.\n"), fmri);
}
}
scf_pg_destroy(pg);
return;
eperm:
assert(pgname != NULL);
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_pg_perm_denied, fmri, pgname);
out:
scf_pg_destroy(pg);
exit_status = 1;
}
static int
get_inst_mult(const char *fmri, scf_instance_t *inst)
{
char *cfmri;
const char *svc_name, *inst_name, *pg_name;
scf_service_t *svc;
scf_instance_t *inst2;
scf_iter_t *iter;
int ret;
if (strncmp(fmri, "lrc:", sizeof ("lrc:") - 1) == 0) {
uu_warn(gettext("FMRI \"%s\" is a legacy service.\n"), fmri);
exit_status = 1;
return (ENOTSUP);
}
cfmri = strdup(fmri);
if (cfmri == NULL)
uu_die(emsg_nomem);
if (scf_parse_svc_fmri(cfmri, NULL, &svc_name, &inst_name, &pg_name,
NULL) != SCF_SUCCESS) {
free(cfmri);
uu_warn(gettext("FMRI \"%s\" is invalid.\n"), fmri);
exit_status = 1;
return (EINVAL);
}
free(cfmri);
if (svc_name == NULL || pg_name != NULL) {
uu_warn(gettext(
"FMRI \"%s\" does not designate a service or instance.\n"),
fmri);
exit_status = 1;
return (ENOTDIR);
}
if (inst_name != NULL) {
if (scf_handle_decode_fmri(h, fmri, NULL, NULL, inst, NULL,
NULL, SCF_DECODE_FMRI_EXACT) == 0)
return (0);
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
uu_warn(gettext("No such instance \"%s\".\n"), fmri);
exit_status = 1;
return (ENOENT);
}
if ((svc = scf_service_create(h)) == NULL ||
(inst2 = scf_instance_create(h)) == NULL ||
(iter = scf_iter_create(h)) == NULL)
scfdie();
if (scf_handle_decode_fmri(h, fmri, NULL, svc, NULL, NULL, NULL,
SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
uu_warn(emsg_no_service, fmri);
exit_status = 1;
ret = ENOENT;
goto out;
}
if (scf_iter_service_instances(iter, svc) != SCF_SUCCESS)
scfdie();
ret = scf_iter_next_instance(iter, inst);
if (ret < 0)
scfdie();
if (ret != 1) {
uu_warn(gettext("Service \"%s\" has no instances.\n"),
fmri);
exit_status = 1;
ret = ENOENT;
goto out;
}
ret = scf_iter_next_instance(iter, inst2);
if (ret < 0)
scfdie();
if (ret != 0) {
ret = E2BIG;
goto out;
}
ret = 0;
out:
scf_iter_destroy(iter);
scf_instance_destroy(inst2);
scf_service_destroy(svc);
return (ret);
}
static int
get_inst(const char *fmri, scf_instance_t *inst)
{
int r;
r = get_inst_mult(fmri, inst);
if (r != E2BIG)
return (r);
uu_warn(gettext("operation on service %s is ambiguous; "
"instance specification needed.\n"), fmri);
return (ENOENT);
}
static char *
inst_get_fmri(const scf_instance_t *inst)
{
ssize_t sz;
sz = scf_instance_to_fmri(inst, scratch_fmri, max_scf_fmri_sz);
if (sz < 0)
scfdie();
if (sz >= max_scf_fmri_sz)
uu_die(gettext("scf_instance_to_fmri() returned unexpectedly "
"long value.\n"));
return (scratch_fmri);
}
static ssize_t
dep_get_astring(const char *fmri, const char *pgname,
const scf_propertygroup_t *pg, const char *propname, scf_property_t *prop,
scf_value_t *v, char *buf, size_t bufsz)
{
ssize_t sz;
sz = get_astring_prop(pg, propname, prop, v, buf, bufsz);
if (sz >= 0)
return (sz);
switch (-sz) {
case ENOENT:
uu_warn(gettext("\"%s\" is misconfigured (\"%s\" dependency "
"lacks \"%s\" property.)\n"), fmri, pgname, propname);
return (-1);
case E2BIG:
uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
"is not single-valued.)\n"), fmri, pgname, propname);
return (-1);
case EINVAL:
uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" property "
"is not of astring type.)\n"), fmri, pgname, propname);
return (-1);
default:
assert(0);
abort();
}
}
static boolean_t
multiple_instances(scf_iter_t *iter, scf_value_t *v, char *buf)
{
int count = 0, r;
boolean_t ret;
scf_instance_t *inst;
inst = scf_instance_create(h);
if (inst == NULL)
scfdie();
for (;;) {
r = scf_iter_next_value(iter, v);
if (r == 0) {
ret = B_FALSE;
goto out;
}
if (r != 1)
scfdie();
if (scf_value_get_astring(v, buf, max_scf_fmri_sz) < 0)
scfdie();
switch (get_inst_mult(buf, inst)) {
case 0:
++count;
if (count > 1) {
ret = B_TRUE;
goto out;
}
break;
case ENOTSUP:
case EINVAL:
case ENOTDIR:
case ENOENT:
continue;
case E2BIG:
ret = B_TRUE;
goto out;
default:
assert(0);
abort();
}
}
out:
scf_instance_destroy(inst);
return (ret);
}
static int
enable_fmri_rec(char *fmri, enable_data_t *ed)
{
scf_instance_t *inst;
scf_snapshot_t *snap;
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *v;
scf_iter_t *pg_iter, *val_iter;
scf_type_t ty;
char *buf, *pgname;
ssize_t name_sz, len, sz;
int ret;
struct ht_elt *he;
len = scf_canonify_fmri(fmri, fmri, max_scf_fmri_sz);
if (len < 0) {
assert(scf_error() == SCF_ERROR_INVALID_ARGUMENT);
return (EINVAL);
}
assert(len < max_scf_fmri_sz);
switch (visited_find_or_add(fmri, &he)) {
case 0:
he->active = B_TRUE;
break;
case 1:
return (he->active ? ELOOP : 0);
case -1:
uu_die(emsg_nomem);
default:
assert(0);
abort();
}
inst = scf_instance_create(h);
if (inst == NULL)
scfdie();
switch (get_inst_mult(fmri, inst)) {
case 0:
break;
case E2BIG:
he->active = B_FALSE;
return (E2BIG);
default:
he->active = B_FALSE;
return (0);
}
set_inst_enabled(fmri, inst, ed);
if ((snap = scf_snapshot_create(h)) == NULL ||
(pg = scf_pg_create(h)) == NULL ||
(prop = scf_property_create(h)) == NULL ||
(v = scf_value_create(h)) == NULL ||
(pg_iter = scf_iter_create(h)) == NULL ||
(val_iter = scf_iter_create(h)) == NULL)
scfdie();
buf = malloc(max_scf_fmri_sz);
if (buf == NULL)
uu_die(emsg_nomem);
name_sz = scf_limit(SCF_LIMIT_MAX_NAME_LENGTH);
if (name_sz < 0)
scfdie();
++name_sz;
pgname = malloc(name_sz);
if (pgname == NULL)
uu_die(emsg_nomem);
if (scf_instance_get_snapshot(inst, "running", snap) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
scf_snapshot_destroy(snap);
snap = NULL;
}
if (scf_instance_get_pg_composed(inst, snap, SCF_PG_GENERAL, pg) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
uu_warn(gettext("\"%s\" is misconfigured (lacks \"%s\" "
"property group).\n"), fmri, SCF_PG_GENERAL);
ret = 0;
goto out;
}
sz = get_astring_prop(pg, SCF_PROPERTY_RESTARTER, prop, v, buf,
max_scf_fmri_sz);
if (sz > max_scf_fmri_sz) {
uu_warn(gettext("\"%s\" is misconfigured (the value of "
"\"%s/%s\" is too long).\n"), fmri, SCF_PG_GENERAL,
SCF_PROPERTY_RESTARTER);
ret = 0;
goto out;
} else if (sz >= 0) {
switch (enable_fmri_rec(buf, ed)) {
case 0:
break;
case EINVAL:
uu_warn(gettext("Restarter FMRI for \"%s\" is "
"invalid.\n"), fmri);
break;
case E2BIG:
uu_warn(gettext("Restarter FMRI for \"%s\" identifies "
"a service with multiple instances.\n"), fmri);
break;
case ELOOP:
ret = ELOOP;
goto out;
default:
assert(0);
abort();
}
} else if (sz < 0) {
switch (-sz) {
case ENOENT:
break;
case E2BIG:
uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
"property is not single-valued).\n"), fmri,
SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
ret = 0;
goto out;
case EINVAL:
uu_warn(gettext("\"%s\" is misconfigured (\"%s/%s\" "
"property is not of astring type).\n"), fmri,
SCF_PG_GENERAL, SCF_PROPERTY_RESTARTER);
ret = 0;
goto out;
default:
assert(0);
abort();
}
}
if (scf_iter_instance_pgs_typed_composed(pg_iter, inst, snap,
SCF_GROUP_DEPENDENCY) == -1)
scfdie();
while (scf_iter_next_pg(pg_iter, pg) > 0) {
len = scf_pg_get_name(pg, pgname, name_sz);
if (len < 0)
scfdie();
assert(len < name_sz);
if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_TYPE, prop,
v, buf, max_scf_fmri_sz) < 0)
continue;
if (strcmp(buf, "service") != 0)
continue;
if (dep_get_astring(fmri, pgname, pg, SCF_PROPERTY_GROUPING,
prop, v, buf, max_scf_fmri_sz) < 0)
continue;
if (strcmp(buf, SCF_DEP_EXCLUDE_ALL) == 0 ||
strcmp(buf, SCF_DEP_OPTIONAL_ALL) == 0)
continue;
if (strcmp(buf, SCF_DEP_REQUIRE_ALL) != 0 &&
strcmp(buf, SCF_DEP_REQUIRE_ANY) != 0) {
uu_warn(gettext("Dependency \"%s\" of \"%s\" has "
"unknown type \"%s\".\n"), pgname, fmri, buf);
continue;
}
if (scf_pg_get_property(pg, SCF_PROPERTY_ENTITIES, prop) ==
-1) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
uu_warn(gettext("\"%s\" is misconfigured (\"%s\" "
"dependency lacks \"%s\" property.)\n"), fmri,
pgname, SCF_PROPERTY_ENTITIES);
continue;
}
if (scf_property_type(prop, &ty) != SCF_SUCCESS)
scfdie();
if (ty != SCF_TYPE_FMRI) {
uu_warn(gettext("\"%s\" is misconfigured (property "
"\"%s/%s\" is not of fmri type).\n"), fmri, pgname,
SCF_PROPERTY_ENTITIES);
continue;
}
if (scf_iter_property_values(val_iter, prop) == -1)
scfdie();
if (strcmp(buf, SCF_DEP_REQUIRE_ANY) == 0) {
if (multiple_instances(val_iter, v, buf)) {
(void) printf(gettext("%s requires one of:\n"),
fmri);
if (scf_iter_property_values(val_iter, prop) !=
0)
scfdie();
for (;;) {
int r;
r = scf_iter_next_value(val_iter, v);
if (r == 0)
break;
if (r != 1)
scfdie();
if (scf_value_get_astring(v, buf,
max_scf_fmri_sz) < 0)
scfdie();
(void) fputs(" ", stdout);
(void) puts(buf);
}
continue;
}
if (scf_iter_property_values(val_iter, prop) != 0)
scfdie();
}
for (;;) {
ret = scf_iter_next_value(val_iter, v);
if (ret == 0)
break;
if (ret != 1)
scfdie();
if (scf_value_get_astring(v, buf, max_scf_fmri_sz) ==
-1)
scfdie();
switch (enable_fmri_rec(buf, ed)) {
case 0:
break;
case EINVAL:
uu_warn(gettext("\"%s\" dependency of \"%s\" "
"has invalid FMRI \"%s\".\n"), pgname,
fmri, buf);
break;
case E2BIG:
uu_warn(gettext("%s depends on %s, which has "
"multiple instances.\n"), fmri, buf);
break;
case ELOOP:
ret = ELOOP;
goto out;
default:
assert(0);
abort();
}
}
}
ret = 0;
out:
he->active = B_FALSE;
free(buf);
free(pgname);
(void) scf_value_destroy(v);
scf_property_destroy(prop);
scf_pg_destroy(pg);
scf_snapshot_destroy(snap);
scf_iter_destroy(pg_iter);
scf_iter_destroy(val_iter);
return (ret);
}
static void
set_inst_action(const char *fmri, const scf_instance_t *inst,
const char *action)
{
scf_transaction_t *tx;
scf_transaction_entry_t *ent;
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *v;
int ret;
int64_t t;
hrtime_t timestamp;
const char * const scf_pg_restarter_actions = SCF_PG_RESTARTER_ACTIONS;
if ((pg = scf_pg_create(h)) == NULL ||
(prop = scf_property_create(h)) == NULL ||
(v = scf_value_create(h)) == NULL ||
(tx = scf_transaction_create(h)) == NULL ||
(ent = scf_entry_create(h)) == NULL)
scfdie();
if (restarter_setup(fmri, inst)) {
exit_status = 1;
goto out;
}
if (scf_instance_get_pg(inst, scf_pg_restarter_actions, pg) == -1) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_instance_add_pg(inst, scf_pg_restarter_actions,
SCF_PG_RESTARTER_ACTIONS_TYPE,
SCF_PG_RESTARTER_ACTIONS_FLAGS, pg) == -1) {
switch (scf_error()) {
case SCF_ERROR_EXISTS:
break;
case SCF_ERROR_PERMISSION_DENIED:
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_create_pg_perm_denied,
fmri, scf_pg_restarter_actions);
goto out;
default:
scfdie();
}
}
}
do {
timestamp = gethrtime();
if (scf_transaction_start(tx, pg) == -1) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_pg_perm_denied, fmri,
scf_pg_restarter_actions);
goto out;
}
if (scf_pg_get_property(pg, action, prop) == -1) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_transaction_property_new(tx, ent,
action, SCF_TYPE_INTEGER) == -1)
scfdie();
goto action_set;
} else {
if (scf_transaction_property_change_type(tx, ent,
action, SCF_TYPE_INTEGER) == -1)
scfdie();
}
if (scf_property_get_value(prop, v) == -1) {
switch (scf_error()) {
case SCF_ERROR_CONSTRAINT_VIOLATED:
case SCF_ERROR_NOT_FOUND:
goto action_set;
default:
scfdie();
}
} else {
if (scf_value_get_integer(v, &t) == -1) {
assert(scf_error() == SCF_ERROR_TYPE_MISMATCH);
goto action_set;
}
if (t > timestamp)
break;
}
action_set:
scf_value_set_integer(v, timestamp);
if (scf_entry_add_value(ent, v) == -1)
scfdie();
ret = scf_transaction_commit(tx);
if (ret == -1) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_prop_perm_denied, fmri,
scf_pg_restarter_actions, action);
scf_transaction_reset(tx);
goto out;
}
scf_transaction_reset(tx);
if (ret == 0) {
if (scf_pg_update(pg) == -1)
scfdie();
}
} while (ret == 0);
if (verbose)
(void) printf(gettext("Action %s set for %s.\n"), action, fmri);
out:
scf_value_destroy(v);
scf_entry_destroy(ent);
scf_transaction_destroy(tx);
scf_property_destroy(prop);
scf_pg_destroy(pg);
}
int
inst_get_state(scf_instance_t *inst, char *state, const char *fmri,
scf_propertygroup_t **pgp)
{
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *val;
int ret = -1;
ssize_t szret;
if ((pg = scf_pg_create(h)) == NULL ||
(prop = scf_property_create(h)) == NULL ||
(val = scf_value_create(h)) == NULL)
scfdie();
if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
uu_warn(gettext("%s is misconfigured (lacks \"%s\" property "
"group).\n"), fmri ? fmri : inst_get_fmri(inst),
SCF_PG_RESTARTER);
goto out;
}
szret = get_astring_prop(pg, SCF_PROPERTY_STATE, prop, val, state,
MAX_SCF_STATE_STRING_SZ);
if (szret < 0) {
switch (-szret) {
case ENOENT:
uu_warn(gettext("%s is misconfigured (\"%s\" property "
"group lacks \"%s\" property).\n"),
fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
SCF_PROPERTY_STATE);
goto out;
case E2BIG:
uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
"property is not single-valued).\n"),
fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
SCF_PROPERTY_STATE);
goto out;
case EINVAL:
uu_warn(gettext("%s is misconfigured (\"%s/%s\" "
"property is not of type astring).\n"),
fmri ? fmri : inst_get_fmri(inst), SCF_PG_RESTARTER,
SCF_PROPERTY_STATE);
goto out;
default:
assert(0);
abort();
}
}
if (szret >= MAX_SCF_STATE_STRING_SZ) {
uu_warn(gettext("%s is misconfigured (\"%s/%s\" property value "
"is too long).\n"), fmri ? fmri : inst_get_fmri(inst),
SCF_PG_RESTARTER, SCF_PROPERTY_STATE);
goto out;
}
ret = 0;
if (pgp)
*pgp = pg;
out:
(void) scf_value_destroy(val);
scf_property_destroy(prop);
if (ret || pgp == NULL)
scf_pg_destroy(pg);
return (ret);
}
static void
set_astring_prop(const char *fmri, const char *pgname, const char *pgtype,
uint32_t pgflags, const char *propname, const char *str)
{
scf_instance_t *inst;
scf_propertygroup_t *pg;
scf_property_t *prop;
scf_value_t *val;
scf_transaction_t *tx;
scf_transaction_entry_t *txent;
int ret;
inst = scf_instance_create(h);
if (inst == NULL)
scfdie();
if (get_inst(fmri, inst) != 0)
return;
if ((pg = scf_pg_create(h)) == NULL ||
(prop = scf_property_create(h)) == NULL ||
(val = scf_value_create(h)) == NULL ||
(tx = scf_transaction_create(h)) == NULL ||
(txent = scf_entry_create(h)) == NULL)
scfdie();
if (scf_instance_get_pg(inst, pgname, pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_instance_add_pg(inst, pgname, pgtype, pgflags, pg) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_EXISTS:
if (scf_instance_get_pg(inst, pgname, pg) !=
SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
uu_warn(gettext("Repository write "
"contention.\n"));
goto out;
}
break;
case SCF_ERROR_PERMISSION_DENIED:
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_create_pg_perm_denied,
fmri, pgname);
goto out;
default:
scfdie();
}
}
}
do {
if (scf_transaction_start(tx, pg) != SCF_SUCCESS) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_pg_perm_denied, fmri, pgname);
goto out;
}
if (scf_transaction_property_change_type(tx, txent, propname,
SCF_TYPE_ASTRING) != 0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
if (scf_transaction_property_new(tx, txent, propname,
SCF_TYPE_ASTRING) != 0)
scfdie();
}
if (scf_value_set_astring(val, str) != SCF_SUCCESS)
scfdie();
if (scf_entry_add_value(txent, val) != SCF_SUCCESS)
scfdie();
ret = scf_transaction_commit(tx);
if (ret == -1) {
if (scf_error() != SCF_ERROR_PERMISSION_DENIED)
scfdie();
if (!verbose)
uu_warn(emsg_permission_denied, fmri);
else
uu_warn(emsg_prop_perm_denied, fmri, pgname,
propname);
goto out;
}
if (ret == 0) {
scf_transaction_reset(tx);
if (scf_pg_update(pg) == -1)
scfdie();
}
} while (ret == 0);
out:
scf_transaction_destroy(tx);
scf_entry_destroy(txent);
scf_value_destroy(val);
scf_property_destroy(prop);
scf_pg_destroy(pg);
scf_instance_destroy(inst);
}
static int
set_fmri_enabled(void *data, scf_walkinfo_t *wip)
{
enable_data_t *ed = data;
assert(wip->inst != NULL);
assert(wip->pg == NULL);
if (svcsearch) {
char state[MAX_SCF_STATE_STRING_SZ];
if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
return (0);
if (strcmp(state, svcstate) != 0)
return (0);
}
if (ed->ed_flags & SET_RECURSIVE) {
char *fmri_buf = malloc(max_scf_fmri_sz);
if (fmri_buf == NULL)
uu_die(emsg_nomem);
visited = calloc(HT_BUCKETS, sizeof (*visited));
if (visited == NULL)
uu_die(emsg_nomem);
assert(strlen(wip->fmri) <= max_scf_fmri_sz);
(void) strlcpy(fmri_buf, wip->fmri, max_scf_fmri_sz);
switch (enable_fmri_rec(fmri_buf, ed)) {
case E2BIG:
uu_warn(gettext("operation on service %s is ambiguous; "
"instance specification needed.\n"), fmri_buf);
break;
case ELOOP:
uu_warn(gettext("%s: Dependency cycle detected.\n"),
fmri_buf);
}
free(visited);
free(fmri_buf);
} else {
set_inst_enabled(wip->fmri, wip->inst, ed);
}
return (0);
}
static int
wait_fmri_enabled(void *data, scf_walkinfo_t *wip)
{
scf_propertygroup_t *pg = NULL;
char state[MAX_SCF_STATE_STRING_SZ];
assert(wip->inst != NULL);
assert(wip->pg == NULL);
do {
if (pg)
scf_pg_destroy(pg);
if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
exit_status = EXIT_SVC_FAILURE;
return (0);
}
if (strcmp(state, SCF_STATE_STRING_ONLINE) == 0 ||
strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
goto out;
}
if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
uu_warn(gettext("Instance \"%s\" is in maintenance"
" state.\n"), wip->fmri);
exit_status = EXIT_SVC_FAILURE;
goto out;
}
if (!is_enabled(wip->inst)) {
uu_warn(gettext("Instance \"%s\" has been disabled"
" by another entity.\n"), wip->fmri);
exit_status = EXIT_SVC_FAILURE;
goto out;
}
if (!has_potential(wip->inst, B_FALSE)) {
uu_warn(gettext("Instance \"%s\" has unsatisfied"
" dependencies.\n"), wip->fmri);
if (exit_status == 0)
exit_status = EXIT_DEP_FAILURE;
goto out;
}
} while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
scfdie();
out:
scf_pg_destroy(pg);
return (0);
}
static int
wait_fmri_disabled(void *data, scf_walkinfo_t *wip)
{
scf_propertygroup_t *pg = NULL;
char state[MAX_SCF_STATE_STRING_SZ];
assert(wip->inst != NULL);
assert(wip->pg == NULL);
do {
if (pg)
scf_pg_destroy(pg);
if (inst_get_state(wip->inst, state, wip->fmri, &pg) != 0) {
exit_status = EXIT_SVC_FAILURE;
return (0);
}
if (strcmp(state, SCF_STATE_STRING_DISABLED) == 0) {
goto out;
}
if (is_enabled(wip->inst)) {
uu_warn(gettext("Instance \"%s\" has been enabled"
" by another entity.\n"), wip->fmri);
exit_status = EXIT_SVC_FAILURE;
goto out;
}
if (!has_potential(wip->inst, B_TRUE)) {
uu_warn(gettext("Restarter for instance \"%s\" is"
" unavailable.\n"), wip->fmri);
if (exit_status == 0)
exit_status = EXIT_DEP_FAILURE;
goto out;
}
} while (_scf_pg_wait(pg, WAIT_INTERVAL) >= 0);
scfdie();
out:
scf_pg_destroy(pg);
return (0);
}
static int
clear_instance(void *data, scf_walkinfo_t *wip)
{
char state[MAX_SCF_STATE_STRING_SZ];
assert(wip->inst != NULL);
assert(wip->pg == NULL);
if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
return (0);
if (svcsearch && strcmp(state, svcstate) != 0)
return (0);
if (strcmp(state, SCF_STATE_STRING_MAINT) == 0) {
set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_MAINT_OFF);
} else if (strcmp(state, SCF_STATE_STRING_DEGRADED) == 0) {
set_inst_action(wip->fmri, wip->inst, SCF_PROPERTY_RESTORE);
} else {
uu_warn(gettext("Instance \"%s\" is not in a "
"maintenance or degraded state.\n"), wip->fmri);
exit_status = 1;
}
return (0);
}
static int
set_fmri_action(void *action, scf_walkinfo_t *wip)
{
assert(wip->inst != NULL && wip->pg == NULL);
if (svcsearch) {
char state[MAX_SCF_STATE_STRING_SZ];
if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
return (0);
if (strcmp(state, svcstate) != 0)
return (0);
}
set_inst_action(wip->fmri, wip->inst, action);
return (0);
}
static int
force_degraded(void *data, scf_walkinfo_t *wip)
{
mark_data_t *md = data;
char state[MAX_SCF_STATE_STRING_SZ];
if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0) {
exit_status = 1;
return (0);
}
if (svcsearch && strcmp(state, svcstate) != 0)
return (0);
if (strcmp(state, SCF_STATE_STRING_ONLINE) != 0) {
uu_warn(gettext("Instance \"%s\" is not online.\n"), wip->fmri);
exit_status = 1;
return (0);
}
set_inst_action(wip->fmri, wip->inst, (md->md_flags & MARK_IMMEDIATE) ?
SCF_PROPERTY_DEGRADE_IMMEDIATE : SCF_PROPERTY_DEGRADED);
return (0);
}
static int
force_maintenance(void *data, scf_walkinfo_t *wip)
{
mark_data_t *md = data;
const char *prop;
if (svcsearch) {
char state[MAX_SCF_STATE_STRING_SZ];
if (inst_get_state(wip->inst, state, wip->fmri, NULL) != 0)
return (0);
if (strcmp(state, svcstate) != 0)
return (0);
}
if (md->md_flags & MARK_IMMEDIATE) {
prop = (md->md_flags & MARK_TEMPORARY) ?
SCF_PROPERTY_MAINT_ON_IMMTEMP :
SCF_PROPERTY_MAINT_ON_IMMEDIATE;
} else {
prop = (md->md_flags & MARK_TEMPORARY) ?
SCF_PROPERTY_MAINT_ON_TEMPORARY :
SCF_PROPERTY_MAINT_ON;
}
set_inst_action(wip->fmri, wip->inst, prop);
return (0);
}
static void
set_milestone(const char *fmri, boolean_t temporary)
{
scf_instance_t *inst;
scf_propertygroup_t *pg;
if (temporary) {
set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS_OVR,
SCF_PG_OPTIONS_OVR_TYPE, SCF_PG_OPTIONS_OVR_FLAGS,
SCF_PROPERTY_MILESTONE, fmri);
return;
}
if ((inst = scf_instance_create(h)) == NULL ||
(pg = scf_pg_create(h)) == NULL)
scfdie();
if (get_inst(SCF_SERVICE_STARTD, inst) != 0) {
scf_instance_destroy(inst);
return;
}
set_astring_prop(SCF_SERVICE_STARTD, SCF_PG_OPTIONS,
SCF_PG_OPTIONS_TYPE, SCF_PG_OPTIONS_FLAGS, SCF_PROPERTY_MILESTONE,
fmri);
if (delete_prop(SCF_SERVICE_STARTD, inst, SCF_PG_OPTIONS_OVR,
SCF_PROPERTY_MILESTONE) != 0)
exit_status = 1;
scf_pg_destroy(pg);
scf_instance_destroy(inst);
}
static char const *milestones[] = {
SCF_MILESTONE_SINGLE_USER,
SCF_MILESTONE_MULTI_USER,
SCF_MILESTONE_MULTI_USER_SERVER,
NULL
};
static void
usage_milestone(void)
{
const char **ms;
(void) fprintf(stderr, gettext(
"Usage: svcadm milestone [-d] <milestone>\n\n"
"\t-d\tmake the specified milestone the default for system boot\n\n"
"\tMilestones can be specified using an FMRI or abbreviation.\n"
"\tThe major milestones are as follows:\n\n"
"\tall\n"
"\tnone\n"));
for (ms = milestones; *ms != NULL; ms++)
(void) fprintf(stderr, "\t%s\n", *ms);
exit(UU_EXIT_USAGE);
}
static const char *
validate_milestone(const char *milestone)
{
const char **ms;
const char *tmp;
size_t len;
if (strcmp(milestone, "all") == 0)
return (milestone);
if (strcmp(milestone, "none") == 0)
return (milestone);
for (ms = milestones; *ms != NULL; ms++) {
if ((tmp = strstr(*ms, milestone)) != NULL) {
len = strlen(milestone);
if ((tmp == *ms || tmp[-1] == '/') &&
(tmp[len] == '\0' || tmp[len] == ':'))
return (*ms);
}
}
(void) fprintf(stderr,
gettext("\"%s\" is not a valid major milestone.\n"), milestone);
usage_milestone();
}
static void
pr_warn(const char *format, ...)
{
const char *pname = uu_getpname();
va_list alist;
va_start(alist, format);
if (pname != NULL)
(void) fprintf(stderr, "%s", pname);
if (g_zonename != NULL)
(void) fprintf(stderr, " (%s)", g_zonename);
(void) fprintf(stderr, ": ");
(void) vfprintf(stderr, format, alist);
if (strrchr(format, '\n') == NULL)
(void) fprintf(stderr, ": %s\n", strerror(errno));
va_end(alist);
}
static void
quiet(const char *fmt, ...)
{
}
int
main(int argc, char *argv[])
{
int o;
int err;
int sw_back;
boolean_t do_zones = B_FALSE;
boolean_t do_a_zone = B_FALSE;
char zonename[ZONENAME_MAX];
uint_t nzents = 0, zent = 0;
zoneid_t *zids = NULL;
int orig_optind, orig_argc;
char **orig_argv;
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
(void) uu_setpname(argv[0]);
if (argc < 2)
usage();
max_scf_fmri_sz = scf_limit(SCF_LIMIT_MAX_FMRI_LENGTH);
if (max_scf_fmri_sz < 0)
scfdie();
++max_scf_fmri_sz;
scratch_fmri = malloc(max_scf_fmri_sz);
if (scratch_fmri == NULL)
uu_die(emsg_nomem);
while ((o = getopt(argc, argv, "S:vZz:")) != -1) {
switch (o) {
case 'S':
(void) strlcpy(svcstate, optarg, sizeof (svcstate));
svcsearch = B_TRUE;
break;
case 'v':
verbose = 1;
break;
case 'z':
if (getzoneid() != GLOBAL_ZONEID)
uu_die(gettext("svcadm -z may only be used "
"from the global zone\n"));
if (do_zones)
usage();
(void) strlcpy(zonename, optarg, sizeof (zonename));
do_a_zone = B_TRUE;
break;
case 'Z':
if (getzoneid() != GLOBAL_ZONEID)
uu_die(gettext("svcadm -Z may only be used "
"from the global zone\n"));
if (do_a_zone)
usage();
do_zones = B_TRUE;
break;
default:
usage();
}
}
while (do_zones) {
uint_t found;
if (zone_list(NULL, &nzents) != 0)
uu_die(gettext("could not get number of zones"));
if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
uu_die(gettext("could not allocate array for "
"%d zone IDs"), nzents);
}
found = nzents;
if (zone_list(zids, &found) != 0)
uu_die(gettext("could not get zone list"));
if (found == nzents)
break;
free(zids);
}
emsg_permission_denied = gettext("%s: Permission denied.\n");
emsg_nomem = gettext("Out of memory.\n");
emsg_create_pg_perm_denied = gettext("%s: Couldn't create \"%s\" "
"property group (permission denied).\n");
emsg_pg_perm_denied = gettext("%s: Couldn't modify \"%s\" property "
"group (permission denied).\n");
emsg_prop_perm_denied = gettext("%s: Couldn't modify \"%s/%s\" "
"property (permission denied).\n");
emsg_no_service = gettext("No such service \"%s\".\n");
orig_optind = optind;
orig_argc = argc;
orig_argv = argv;
again:
h = scf_handle_create(SCF_VERSION);
if (h == NULL)
scfdie();
if (do_zones) {
zone_status_t status;
if (zone_getattr(zids[zent], ZONE_ATTR_STATUS, &status,
sizeof (status)) < 0 || status != ZONE_IS_RUNNING) {
zent++;
goto nextzone;
}
if (getzonenamebyid(zids[zent++], zonename,
sizeof (zonename)) < 0) {
uu_warn(gettext("could not get name for "
"zone %d; ignoring"), zids[zent - 1]);
goto nextzone;
}
g_zonename = zonename;
}
if (do_a_zone || do_zones) {
scf_value_t *zone;
if ((zone = scf_value_create(h)) == NULL)
scfdie();
if (scf_value_set_astring(zone, zonename) != SCF_SUCCESS)
scfdie();
if (scf_handle_decorate(h, "zone", zone) != SCF_SUCCESS) {
if (do_a_zone) {
uu_die(gettext("zone '%s': %s\n"),
zonename, scf_strerror(scf_error()));
} else {
scf_value_destroy(zone);
goto nextzone;
}
}
scf_value_destroy(zone);
}
if (scf_handle_bind(h) == -1) {
if (do_zones)
goto nextzone;
uu_die(gettext("Couldn't bind to configuration repository: "
"%s.\n"), scf_strerror(scf_error()));
}
optind = orig_optind;
argc = orig_argc;
argv = orig_argv;
if (optind >= argc)
usage();
if (strcmp(argv[optind], "enable") == 0) {
enable_data_t ed = {
.ed_flags = SET_ENABLED,
.ed_comment = ""
};
int wait = 0;
int error = 0;
++optind;
while ((o = getopt(argc, argv, "rst")) != -1) {
if (o == 'r')
ed.ed_flags |= SET_RECURSIVE;
else if (o == 't')
ed.ed_flags |= SET_TEMPORARY;
else if (o == 's')
wait = 1;
else if (o == '?')
usage();
else {
assert(0);
abort();
}
}
argc -= optind;
argv += optind;
if (argc == 0 && !svcsearch)
usage();
if (argc > 0 && svcsearch)
usage();
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
set_fmri_enabled, &ed, &error, pr_warn)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
} else if (wait && exit_status == 0 &&
(err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
wait_fmri_enabled, NULL, &error, quiet)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
}
if (error > 0)
exit_status = error;
} else if (strcmp(argv[optind], "disable") == 0) {
enable_data_t ed = {
.ed_flags = 0,
.ed_comment = ""
};
int wait = 0;
int error = 0;
++optind;
while ((o = getopt(argc, argv, "c:st")) != -1) {
if (o == 'c') {
if (strlcpy(ed.ed_comment, optarg,
sizeof (ed.ed_comment)) >=
sizeof (ed.ed_comment)) {
uu_die(gettext("disable -c comment "
"too long.\n"));
}
} else if (o == 't')
ed.ed_flags |= SET_TEMPORARY;
else if (o == 's')
wait = 1;
else if (o == '?')
usage();
else {
assert(0);
abort();
}
}
argc -= optind;
argv += optind;
if (argc == 0 && !svcsearch)
usage();
if (argc > 0 && svcsearch)
usage();
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
set_fmri_enabled, &ed, &exit_status,
pr_warn)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
} else if (wait && exit_status == 0 &&
(err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
wait_fmri_disabled, NULL, &error, quiet)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
}
if (error > 0)
exit_status = error;
} else if (strcmp(argv[optind], "restart") == 0) {
boolean_t do_dump = B_FALSE;
++optind;
while ((o = getopt(argc, argv, "d")) != -1) {
if (o == 'd')
do_dump = B_TRUE;
else if (o == '?')
usage();
else {
assert(0);
abort();
}
}
argc -= optind;
argv += optind;
if (argc == 0 && !svcsearch)
usage();
if (argc > 0 && svcsearch)
usage();
if (do_dump) {
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
set_fmri_action, (void *)SCF_PROPERTY_DODUMP,
&exit_status, pr_warn)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
}
}
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
set_fmri_action, (void *)SCF_PROPERTY_RESTART, &exit_status,
pr_warn)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
}
} else if (strcmp(argv[optind], "refresh") == 0) {
++optind;
argc -= optind;
argv += optind;
if (argc == 0 && !svcsearch)
usage();
if (argc > 0 && svcsearch)
usage();
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
set_fmri_action, (void *)SCF_PROPERTY_REFRESH, &exit_status,
pr_warn)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(scf_error()));
exit_status = UU_EXIT_FATAL;
}
} else if (strcmp(argv[optind], "mark") == 0) {
scf_walk_callback callback;
mark_data_t md = {
.md_flags = 0
};
++optind;
while ((o = getopt(argc, argv, "It")) != -1) {
if (o == 'I')
md.md_flags |= MARK_IMMEDIATE;
else if (o == 't')
md.md_flags |= MARK_TEMPORARY;
else if (o == '?')
usage();
else {
assert(0);
abort();
}
}
if (argc - optind < 2)
usage();
if (strcmp(argv[optind], "degraded") == 0) {
if ((md.md_flags & MARK_TEMPORARY) != 0) {
uu_xdie(UU_EXIT_USAGE, gettext(
"-t may not be used with degraded.\n"));
}
callback = force_degraded;
} else if (strcmp(argv[optind], "maintenance") == 0) {
callback = force_maintenance;
} else {
usage();
}
optind++;
argc -= optind;
argv += optind;
if (argc == 0 && !svcsearch)
usage();
if (argc > 0 && svcsearch)
usage();
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS, callback,
(void *)&md, &exit_status, pr_warn)) != 0) {
pr_warn(gettext(
"failed to iterate over instances: %s\n"),
scf_strerror(err));
exit_status = UU_EXIT_FATAL;
}
} else if (strcmp(argv[optind], "clear") == 0) {
++optind;
argc -= optind;
argv += optind;
if (argc == 0 && !svcsearch)
usage();
if (svcsearch) {
if (argc > 0)
usage();
if (strcmp(svcstate, SCF_STATE_STRING_MAINT) != 0 &&
strcmp(svcstate, SCF_STATE_STRING_DEGRADED) != 0)
uu_die(gettext("State must be '%s' or '%s'\n"),
SCF_STATE_STRING_MAINT,
SCF_STATE_STRING_DEGRADED);
}
if ((err = scf_walk_fmri(h, argc, argv, WALK_FLAGS,
clear_instance, NULL, &exit_status, pr_warn)) != 0) {
pr_warn(gettext("failed to iterate over "
"instances: %s\n"), scf_strerror(err));
exit_status = UU_EXIT_FATAL;
}
} else if (strcmp(argv[optind], "milestone") == 0) {
boolean_t temporary = B_TRUE;
const char *milestone;
++optind;
while ((o = getopt(argc, argv, "d")) != -1) {
if (o == 'd')
temporary = B_FALSE;
else if (o == '?')
usage_milestone();
else {
assert(0);
abort();
}
}
if (optind >= argc)
usage_milestone();
milestone = validate_milestone(argv[optind]);
set_milestone(milestone, temporary);
} else if (strcmp(argv[optind], "_smf_backup") == 0) {
const char *reason = NULL;
++optind;
if (optind != argc - 1)
usage();
if ((err = _scf_request_backup(h, argv[optind])) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_NOT_BOUND:
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
scfdie();
break;
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_INVALID_ARGUMENT:
reason = scf_strerror(scf_error());
break;
case SCF_ERROR_INTERNAL:
reason =
"unknown error (see console for details)";
break;
}
pr_warn("failed to backup repository: %s\n", reason);
exit_status = UU_EXIT_FATAL;
}
} else if (strcmp(argv[optind], "_smf_repository_switch") == 0) {
const char *reason = NULL;
++optind;
if (optind != argc - 1)
exit(1);
if (strcmp(argv[optind], "fast") == 0) {
sw_back = 0;
} else if (strcmp(argv[optind], "perm") == 0) {
sw_back = 1;
} else {
exit(UU_EXIT_USAGE);
}
if ((err = _scf_repository_switch(h, sw_back)) !=
SCF_SUCCESS) {
switch (scf_error()) {
case SCF_ERROR_NOT_BOUND:
abort();
case SCF_ERROR_CONNECTION_BROKEN:
case SCF_ERROR_BACKEND_READONLY:
scfdie();
case SCF_ERROR_PERMISSION_DENIED:
case SCF_ERROR_INVALID_ARGUMENT:
reason = scf_strerror(scf_error());
break;
case SCF_ERROR_INTERNAL:
reason = "File operation error: (see console)";
break;
default:
abort();
}
pr_warn("failed to switch repository: %s\n", reason);
exit_status = UU_EXIT_FATAL;
}
} else {
usage();
}
if (scf_handle_unbind(h) == -1)
scfdie();
nextzone:
scf_handle_destroy(h);
if (do_zones && zent < nzents)
goto again;
return (exit_status);
}