explain.c revision 3eae19d9cf3390cf5b75e10c9c1945fd36ad856a
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Service state explanation. For select services, display a description, the
* state, and possibly why the service is in that state, what's causing it to
* be in that state, and what other services it is keeping offline (impact).
*
* Explaining states other than offline is easy. For maintenance and
* degraded, we just use the auxiliary state. For offline, we must determine
* which dependencies are unsatisfied and recurse. If a causal service is not
* offline, then a svcptr to it is added to the offline service's causes list.
* If a causal service is offline, then we recurse to determine its causes and
* merge them into the causes list of the service in question (see
* add_causes()). Note that by adding a self-pointing svcptr to the causes
* lists of services which are not offline or are offline for unknown reasons,
* we can always merge the unsatisfied dependency's causes into the
* dependent's list.
*
* Computing an impact list is more involved because the dependencies in the
* repository are unidirectional; it requires determining the causes of all
* offline services. For each unsatisfied dependency of an offline service,
* a svcptr to the dependent is added to the dependency's impact_dependents
* list (see add_causes()). determine_impact() uses the lists to build an
* impact list. The direct dependency is used so that a path from the
* affected service to the causal service can be constructed (see
* print_dependency_reasons()).
*
* Because we always need at least impact counts, we always run
* determine_causes() on all services.
*
* If no arguments are given, we must select the services which are causing
* other services to be offline. We do so by adding services which are not
* running for any reason other than another service to the g_causes list in
* determine_causes().
*
* Since all services must be examined, and their states may be consulted
* a lot, it is important that we only read volatile data (like states) from
* the repository once. add_instance() reads data for an instance from the
* repository into an inst_t and puts it into the "services" cache, which is
* organized as a hash table of svc_t's, each of which has a list of inst_t's.
*/
#include "svcs.h"
#include <assert.h>
#include <errno.h>
#include <libintl.h>
#include <libuutil.h>
#include <libscf.h>
#include <libscf_priv.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define DC_DISABLED "SMF-8000-05"
#define DC_TEMPDISABLED "SMF-8000-1S"
#define DC_RSTRINVALID "SMF-8000-2A"
#define DC_RSTRABSENT "SMF-8000-3P"
#define DC_UNINIT "SMF-8000-4D"
#define DC_RSTRDEAD "SMF-8000-5H"
#define DC_ADMINMAINT "SMF-8000-63"
#define DC_REPTFAIL "SMF-8000-7Y"
#define DC_METHFAIL "SMF-8000-8Q"
#define DC_NONE "SMF-8000-9C"
#define DC_UNKNOWN "SMF-8000-AR"
#define DC_STARTING "SMF-8000-C4"
#define DC_ADMINDEGR "SMF-8000-DX"
#define DC_DEPABSENT "SMF-8000-E2"
#define DC_DEPRUNNING "SMF-8000-FJ"
#define DC_DEPOTHER "SMF-8000-GE"
#define DC_DEPCYCLE "SMF-8000-HP"
#define DC_INVALIDDEP "SMF-8000-JA"
#define DC_STARTFAIL "SMF-8000-KS"
#define DC_TOOQUICKLY "SMF-8000-L5"
#define DC_INVALIDSTATE "SMF-8000-N3"
#define DC_TRANSITION "SMF-8000-PH"
#define DEFAULT_MAN_PATH "/usr/share/man"
#ifdef NDEBUG
#else
abort();
#endif
typedef struct {
const char *svcname;
const char *instname;
/* restarter pg properties */
char state[MAX_SCF_STATE_STRING_SZ];
const char *aux_state;
int enabled;
int temporary;
const char *restarter;
int active; /* In use? (cycle detection) */
int restarter_bad;
const char *summary;
} inst_t;
typedef struct service {
const char *svcname;
} svc_t;
struct svcptr {
};
struct dependency_group {
const char *type;
};
struct dependency {
const char *fmri;
};
/* Hash table of service names -> svc_t's */
#define SVC_HASH_NBUCKETS 256
static scf_scope_t *g_local_scope;
static scf_service_t *g_svc;
static scf_instance_t *g_inst;
static scf_snapshot_t *g_snap;
static scf_propertygroup_t *g_pg;
static scf_property_t *g_prop;
static scf_value_t *g_val;
static const char *g_msgbase = "http://sun.com/msg/";
static char *emsg_nomem;
static char *emsg_invalid_dep;
extern scf_handle_t *h;
/* ARGSUSED */
static int
{
}
static uint32_t
{
uint32_t h = 0, g;
const char *p;
for (p = name; *p != '\0'; ++p) {
h = (h << 4) + *p;
if ((g = (h & 0xf0000000)) != 0) {
h ^= (g >> 24);
h ^= g;
}
}
return (h);
}
static void
x_init(void)
{
gettext("svc:/%s:%s has invalid dependency \"%s\".\n");
sizeof (struct dependency_group),
scfdie();
scfdie();
}
/*
* Repository loading routines.
*/
/*
* Returns
* 0 - success
* ECANCELED - inst was deleted
* EINVAL - inst is invalid
*/
static int
{
struct dependency_group *dg;
struct dependency *d;
int r;
} else {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
}
SCF_GROUP_DEPENDENCY) != 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
return (ECANCELED);
}
for (;;) {
if (r == 0)
break;
if (r != 1) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
return (ECANCELED);
}
return (EINVAL);
else {
"dependency with unknown type \"%s\".\n"),
return (EINVAL);
}
g_value, g_value_sz, 0) != 0)
return (EINVAL);
0) {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
"dependency without an entities "
return (EINVAL);
case SCF_ERROR_DELETED:
return (ECANCELED);
default:
scfdie();
}
}
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
return (ECANCELED);
}
for (;;) {
if (r == 0)
break;
if (r != 1) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
return (ECANCELED);
}
d = safe_malloc(sizeof (*d));
max_scf_fmri_length + 1) < 0)
scfdie();
}
assert(r == 0);
}
return (0);
}
static void
{
int have_enabled = 0;
int i;
uint32_t h;
break;
}
}
switch (scf_error()) {
case SCF_ERROR_DELETED:
return;
case SCF_ERROR_NOT_FOUND:
"\"%s\" property group; ignoring.\n"),
return;
default:
scfdie();
}
}
return;
return;
return;
return;
have_enabled = 1;
} else {
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
return;
default:
scfdie();
}
}
0) {
switch (scf_error()) {
case SCF_ERROR_DELETED:
case SCF_ERROR_NOT_FOUND:
return;
default:
scfdie();
}
}
&i, 0, 0) != 0)
return;
if (!have_enabled) {
} else {
}
else
return;
assert(i == 0);
}
static void
load_services(void)
{
int r;
scfdie();
scfdie();
for (;;) {
if (r == 0)
break;
if (r != 1)
scfdie();
max_scf_name_length + 1) < 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
continue;
}
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
continue;
}
for (;;) {
if (r == 0)
break;
if (r != 1) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
break;
}
max_scf_name_length + 1) < 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
continue;
}
}
}
}
/*
* Dependency analysis routines.
*/
static void
{
int r;
return;
}
assert(r == 0);
}
static int determine_causes(inst_t *, void *);
/*
* Determine the causes of src and add them to the causes list of dst.
* Returns ELOOP if src is active, and 0 otherwise.
*/
static int
{
/* Dependency cycle. */
return (ELOOP);
}
continue;
}
return (0);
}
static int
{
}
static int
{
return (inst_running(ip) ||
}
static svc_t *
{
uint32_t h;
break;
}
return (svcp);
}
/* ARGSUSED */
static inst_t *
{
return (instp);
}
return (NULL);
}
static int
{
return (EINVAL);
return (EINVAL);
return (EINVAL);
return (ENOENT);
return (ENOENT);
}
return (0);
}
static int
{
struct dependency *d;
int r, svcrunning;
switch (r) {
case EINVAL:
/* LINTED */
continue;
case ENOENT:
assert(r == 0);
continue;
case 0:
break;
default:
bad_error("get_fmri", r);
}
if (inst_running(ip))
continue;
if (r != 0) {
return (r);
}
continue;
}
svcrunning = 0;
if (inst_running(ip))
svcrunning = 1;
}
if (!svcrunning) {
if (r != 0) {
return (r);
}
}
}
}
return (0);
}
static int
{
struct dependency *d;
int r;
d != NULL;
switch (r) {
case 0:
break;
case EINVAL:
/* LINTED */
continue;
case ENOENT:
continue;
default:
bad_error("eval_svc_dep", r);
}
if (inst_running(ip))
return (0);
continue;
}
if (inst_running(ip))
return (0);
}
}
/*
* The dependency group is not satisfied. Add all unsatisfied members
* to the cause list.
*/
switch (r) {
case 0:
break;
case ENOENT:
assert(r == 0);
continue;
case EINVAL:
/* Should have caught above. */
default:
bad_error("eval_svc_dep", r);
}
if (inst_running(ip))
continue;
if (r != 0) {
return (r);
}
continue;
}
if (inst_running(ip))
continue;
if (r != 0) {
return (r);
}
}
}
return (0);
}
static int
{
struct dependency *d;
int r;
switch (r) {
case 0:
break;
case EINVAL:
/* LINTED */
continue;
case ENOENT:
continue;
default:
bad_error("get_fmri", r);
}
if (r != 0) {
return (r);
}
}
continue;
}
if (r != 0) {
return (r);
}
}
}
}
return (0);
}
static int
{
struct dependency *d;
int r;
d != NULL;
switch (r) {
case 0:
break;
case EINVAL:
/* LINTED */
continue;
case ENOENT:
continue;
default:
bad_error("eval_svc_dep", r);
}
if (inst_running(ip)) {
if (r != 0) {
return (r);
}
}
continue;
}
if (inst_running(ip)) {
if (r != 0) {
return (r);
}
}
}
}
return (0);
}
static int
{
case DGG_REQALL:
case DGG_REQANY:
case DGG_OPTALL:
case DGG_EXCALL:
default:
#ifndef NDEBUG
"%s:%d: Unknown dependency grouping %d.\n", __FILE__,
#endif
abort();
/* NOTREACHED */
}
}
/*
* Returns
* EINVAL - fmri is not a valid FMRI
* 0 - the file indicated by fmri is missing
* 1 - the file indicated by fmri is present
*/
static int
eval_file_dep(const char *fmri)
{
const char *path;
return (EINVAL);
if (path[0] != '/')
return (EINVAL);
path += 2;
else if (path[0] != '/')
return (EINVAL);
}
}
static void
{
struct dependency *d, **deps;
int r, i = 0, any_satisfied = 0;
}
r = eval_file_dep(d->fmri);
if (r == EINVAL) {
/* LINTED */
continue;
}
assert(r == 0 || r == 1);
case DGG_REQALL:
case DGG_OPTALL:
if (r == 0) {
assert(r == 0);
}
break;
case DGG_REQANY:
if (r == 1)
any_satisfied = 1;
else
deps[i++] = d;
break;
case DGG_EXCALL:
if (r == 1) {
assert(r == 0);
}
break;
default:
#ifndef NDEBUG
#endif
abort();
}
}
return;
if (!any_satisfied) {
while (--i >= 0) {
assert(r == 0);
}
}
}
/*
* Populate the causes list of svcp. This function should not return with
* causes empty.
*/
static int
{
struct dependency_group *dg;
int r;
}
return (UU_WALK_NEXT);
if (inst_running(svcp) ||
/*
* If we're running, add a self-pointer in case we're
* excluding another service.
*/
return (UU_WALK_NEXT);
}
return (UU_WALK_NEXT);
}
return (UU_WALK_NEXT);
}
gettext("svc:/%s:%s has invalid state \"%s\".\n"),
return (UU_WALK_NEXT);
}
return (UU_WALK_NEXT);
}
/*
* Dependency analysis can add elements to our baddeps list (absent
* dependency, unsatisfied file dependency), or to our cause list
* (unsatisfied dependency).
*/
int r;
if (r != 0) {
return ((int)canfailp != 0 ?
}
} else {
"dependency group with invalid type \"%s\".\n"),
}
}
} else {
if (r == 0 && !inst_running(restarter)) {
if (r != 0) {
return ((int)canfailp != 0 ?
}
} else {
svcp->restarter_bad = r;
}
}
}
return (UU_WALK_NEXT);
}
static void
determine_all_causes(void)
{
int i;
for (i = 0; i < SVC_HASH_NBUCKETS; ++i) {
(uu_walk_fn_t *)determine_causes, 0, 0);
}
}
/*
* Returns
* 0 - success
* ELOOP - dependency cycle detected
*/
static int
{
return (ELOOP);
}
return (0);
return (ELOOP);
}
continue;
}
}
return (0);
}
/*
* Printing routines.
*/
static void
check_msgbase(void)
{
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
return;
}
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
return;
default:
scfdie();
}
}
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
case SCF_ERROR_DELETED:
return;
default:
scfdie();
}
}
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
return;
case SCF_ERROR_DELETED:
return;
default:
scfdie();
}
}
if (scf_error() != SCF_ERROR_TYPE_MISMATCH)
scfdie();
return;
}
}
static void
{
return;
if (inst_running(ip)) {
return;
}
else
else
"by an administrator.");
0) {
"a method failed repeatedly.");
"a method failed.");
gettext("is not running for an unknown reason.");
0) {
} else {
}
} else {
}
}
static void
{
char buf[50];
if (stat != 0) {
"exited with $SMF_EXIT_ERR_CONFIG"),
sizeof (buf));
"exited with $SMF_EXIT_ERR_FATAL"),
sizeof (buf));
} else {
gettext("exited with status %d"),
WEXITSTATUS(stat));
}
} else if (WIFSIGNALED(stat)) {
gettext("dumped core on %s (%d)"),
else
gettext("dumped core signal %d"),
} else {
gettext("died on %s (%d)"),
} else {
gettext("died on signal %d"),
}
}
} else {
goto fail;
}
buf);
else
"Start method failed repeatedly, last %s.\n"), buf);
*dcp = DC_STARTFAIL;
} else {
fail:
"Reason: Method failed repeatedly."));
else
*dcp = DC_METHFAIL;
}
}
static void
{
struct dependency *d;
const char *dc;
/*
* If we couldn't determine why the service is offline, then baddeps
* will be empty and causes will have a pointer to self.
*/
switch (svcp->restarter_bad) {
case 0:
dc = DC_UNKNOWN;
break;
case EINVAL:
"Restarter \"%s\" is invalid.\n"),
dc = DC_RSTRINVALID;
break;
case ENOENT:
"Restarter \"%s\" does not exist.\n"),
dc = DC_RSTRABSENT;
break;
default:
#ifndef NDEBUG
"restarter_bad value %d. Aborting.\n",
#endif
abort();
}
if (g_msgbase)
return;
}
}
d != NULL;
d->fmri);
if (g_msgbase)
}
"Service svc:/%s:%s is running.\n"),
dc = DC_DEPRUNNING;
} else {
gettext("Reason: Service svc:/%s:%s %s"),
"Reason: Service svc:/%s:%s %s\n"),
} else {
"Reason: Service svc:/%s:%s\n"
}
dc = DC_DEPOTHER;
}
if (verbose) {
int indent;
indent = 1;
break;
/* set pp to next_hop of cause with same svcp */
}
}
}
}
static void
{
int r;
return;
switch (r) {
case 0:
break;
/* FALLTHROUGH */
case EINVAL:
dc = DC_RSTRINVALID;
goto diagcode;
case ENOENT:
"Restarter \"%s\" does not exist.\n"),
dc = DC_RSTRABSENT;
goto diagcode;
default:
bad_error("get_fmri", r);
}
if (inst_running(rsp)) {
"has not initialized service state.\n"),
} else {
"Reason: Restarter %s is not running.\n"),
dc = DC_RSTRDEAD;
}
"Reason: Disabled by an administrator."));
dc = DC_DISABLED;
} else {
"Temporarily disabled by an administrator."));
}
"Maintenance requested by an administrator."));
dc = DC_ADMINMAINT;
"Reason: Completes a dependency cycle."));
dc = DC_DEPCYCLE;
0) {
dc = DC_INVALIDDEP;
dc = DC_RSTRINVALID;
0) {
dc = DC_TOOQUICKLY;
"Reason: Restarter %s gave no explanation.\n"),
} else {
dc = DC_UNKNOWN;
}
"Reason: Start method is running."));
dc = DC_STARTING;
0) {
/* Function prints diagcodes. */
return;
} else {
"Reason: Transitioning to state %s.\n"),
svcp->next_state);
dc = DC_TRANSITION;
}
dc = DC_ADMINDEGR;
} else {
}
}
static void
print_manpage(int verbose)
{
}
(void *)title, g_value_sz, 0) != 0)
return;
return;
if (!verbose) {
return;
}
(void *)g_value, g_value_sz, 0) != 0)
return;
}
}
static void
{
}
(void *)uri, g_value_sz, 0) != 0)
return;
}
/*
* Returns
* 0 - success
* 1 - inst was deleted
*/
static int
{
int r;
switch (scf_error()) {
case SCF_ERROR_NOT_FOUND:
break;
case SCF_ERROR_DELETED:
return (1);
default:
scfdie();
}
} else {
}
SCF_GROUP_TEMPLATE) != 0) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
return (1);
}
for (;;) {
if (r == 0)
break;
if (r != 1) {
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
return (1);
}
if (scf_error() != SCF_ERROR_DELETED)
scfdie();
continue;
}
strlen(SCF_PG_TM_MAN_PREFIX)) == 0) {
continue;
}
strlen(SCF_PG_TM_DOC_PREFIX)) == 0) {
continue;
}
}
return (0);
}
static void
{
return;
}
static int first = 1;
/*
* Explain why the given service is in the state it's in.
*/
static void
{
char *timebuf;
int deleted = 0;
if (first)
first = 0;
else
(void) putchar('\n');
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
deleted = 1;
0) {
if (scf_error() != SCF_ERROR_NOT_FOUND)
scfdie();
deleted = 1;
}
if (!deleted) {
/* EMPTY */;
} else {
(void) putchar('\n');
}
break;
}
/* Reasons */
if (!deleted)
if (!deleted)
(void) determine_impact(svcp);
case 0:
if (inst_running(svcp))
else
"Impact: This service is not running."));
break;
case 1:
if (!verbose)
"is not running. (Use -v for list.)"));
else
"Impact: 1 dependent service is not running:"));
break;
default:
if (!verbose)
"are not running. (Use -v for list.)\n"),
else
"Impact: %d dependent services are not running:\n"),
}
if (verbose) {
}
}
/*
* Top level routine.
*/
static int
impact_compar(const void *a, const void *b)
{
int n, m;
return (m - n);
}
static int
{
int r;
if (r == ENOENT)
return (0);
assert(r == 0);
return (0);
}
void
{
/* Initialize globals. */
x_init();
/* Walk the graph and populate services with inst_t's */
/* Populate causes for services. */
if (argc > 0) {
/* Call print_service() for each operand. */
if (err != 0) {
"failed to iterate over instances: %s\n"),
scf_strerror(err));
}
} else {
int n, i;
/* Sort g_causes. */
n = uu_list_numnodes(g_causes);
if (n == 0)
return;
i = 0;
}
/* Call print_service() for each service. */
for (i = 0; i < n; ++i)
}
}