fmd_asru.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/fm/protocol.h>
#include <uuid/uuid.h>
#include <dirent.h>
#include <limits.h>
#include <unistd.h>
#include <alloca.h>
#include <fmd_alloc.h>
#include <fmd_string.h>
#include <fmd_error.h>
#include <fmd_subr.h>
#include <fmd_protocol.h>
#include <fmd_event.h>
#include <fmd_conf.h>
#include <fmd_fmri.h>
#include <fmd_dispq.h>
#include <fmd_asru.h>
#include <fmd.h>
static const char *const _fmd_asru_events[] = {
"resource.sunos.fmd.asru.ok", /* UNUSABLE=0 FAULTED=0 */
"resource.sunos.fmd.asru.degraded", /* UNUSABLE=0 FAULTED=1 */
"resource.sunos.fmd.asru.unknown", /* UNUSABLE=1 FAULTED=0 */
"resource.sunos.fmd.asru.faulted" /* UNUSABLE=1 FAULTED=1 */
};
static const char *const _fmd_asru_snames[] = {
"uf", "uF", "Uf", "UF" /* same order as above */
};
static fmd_asru_t *
fmd_asru_create(fmd_asru_hash_t *ahp, const char *uuid,
const char *name, nvlist_t *fmri)
{
fmd_asru_t *ap = fmd_alloc(sizeof (fmd_asru_t), FMD_SLEEP);
char *s;
(void) pthread_mutex_init(&ap->asru_lock, NULL);
(void) pthread_cond_init(&ap->asru_cv, NULL);
ap->asru_next = NULL;
ap->asru_name = fmd_strdup(name, FMD_SLEEP);
(void) nvlist_xdup(fmri, &ap->asru_fmri, &fmd.d_nva);
ap->asru_root = fmd_strdup(ahp->ah_dirpath, FMD_SLEEP);
ap->asru_uuid = fmd_strdup(uuid, FMD_SLEEP);
ap->asru_uuidlen = ap->asru_uuid ? strlen(ap->asru_uuid) : 0;
ap->asru_log = NULL;
ap->asru_refs = 1;
ap->asru_flags = 0;
ap->asru_case = NULL;
if (nvlist_lookup_string(ap->asru_fmri, FM_FMRI_SCHEME, &s) == 0 &&
strcmp(s, FM_FMRI_SCHEME_FMD) == 0)
ap->asru_flags |= FMD_ASRU_INTERNAL;
return (ap);
}
static void
fmd_asru_destroy(fmd_asru_t *ap)
{
ASSERT(MUTEX_HELD(&ap->asru_lock));
ASSERT(ap->asru_refs == 0);
if (ap->asru_log != NULL)
fmd_log_rele(ap->asru_log);
fmd_strfree(ap->asru_case);
fmd_strfree(ap->asru_name);
nvlist_free(ap->asru_fmri);
fmd_strfree(ap->asru_root);
fmd_free(ap->asru_uuid, ap->asru_uuidlen + 1);
fmd_free(ap, sizeof (fmd_asru_t));
}
static void
fmd_asru_hash_insert(fmd_asru_hash_t *ahp, fmd_asru_t *ap)
{
uint_t h = fmd_strhash(ap->asru_name) % ahp->ah_hashlen;
ASSERT(RW_WRITE_HELD(&ahp->ah_lock));
ap->asru_next = ahp->ah_hash[h];
ahp->ah_hash[h] = ap;
ahp->ah_count++;
}
static fmd_asru_t *
fmd_asru_hold(fmd_asru_t *ap)
{
(void) pthread_mutex_lock(&ap->asru_lock);
ap->asru_refs++;
ASSERT(ap->asru_refs != 0);
(void) pthread_mutex_unlock(&ap->asru_lock);
return (ap);
}
/*
* Lookup an asru in the hash by name and place a hold on it. If the asru is
* not found, no entry is created and NULL is returned. This internal function
* is for callers who have the ah_lock held and is used by lookup_name below.
*/
fmd_asru_t *
fmd_asru_hash_lookup(fmd_asru_hash_t *ahp, const char *name)
{
fmd_asru_t *ap;
uint_t h;
ASSERT(RW_LOCK_HELD(&ahp->ah_lock));
h = fmd_strhash(name) % ahp->ah_hashlen;
for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) {
if (strcmp(ap->asru_name, name) == 0)
break;
}
if (ap != NULL)
(void) fmd_asru_hold(ap);
else
(void) fmd_set_errno(EFMD_ASRU_NOENT);
return (ap);
}
static void
fmd_asru_hash_recreate(fmd_log_t *lp, fmd_event_t *ep, fmd_asru_hash_t *ahp)
{
nvlist_t *nvl = ((fmd_event_impl_t *)ep)->ev_nvl;
char *case_uuid = NULL;
char *name = NULL;
ssize_t namelen;
nvlist_t *fmri, *fsrc, *flt;
fmd_event_t *e;
char *class;
boolean_t f, u, m;
fmd_asru_t *ap;
int ps, us;
/*
* Extract the resource FMRI and most recent values of 'faulty' and
* 'unusable' from the event log. If the event is malformed, return.
*/
if (nvlist_lookup_nvlist(nvl, FM_RSRC_RESOURCE, &fmri) != 0 ||
nvlist_lookup_string(nvl, FM_RSRC_ASRU_UUID, &case_uuid) != 0 ||
nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_FAULTY, &f) != 0 ||
nvlist_lookup_boolean_value(nvl, FM_RSRC_ASRU_UNUSABLE, &u) != 0) {
fmd_error(EFMD_ASRU_EVENT, "failed to reload asru %s: "
"invalid event log record\n", lp->log_name);
ahp->ah_error = EFMD_ASRU_EVENT;
return;
}
/*
* Check to see if the resource is still present in the system. If
* so, then update the value of the unusable bit based on the current
* system configuration. If not, then either keep the entry in our
* cache if it is recent, or return and discard it if it is too old.
*/
if ((ps = fmd_fmri_present(fmri)) == -1) {
fmd_error(EFMD_ASRU_FMRI, "failed to locate %s", lp->log_name);
ahp->ah_error = EFMD_ASRU_FMRI;
return;
}
if (ps) {
if ((us = fmd_fmri_unusable(fmri)) == -1) {
fmd_error(EFMD_ASRU_FMRI, "failed to update "
"status of asru %s", lp->log_name);
u = FMD_B_FALSE;
} else
u = us != 0;
} else if ((hrtime_t)(lp->log_stat.st_atime -
lp->log_stat.st_mtime) * NANOSEC < ahp->ah_lifetime) {
u = FMD_B_TRUE; /* not present; set unusable */
} else
return; /* too old; discard this log */
/*
* In order to insert the ASRU into our hash, convert the FMRI from
* nvlist form into a string form and assign this name to the ASRU.
*/
if ((namelen = fmd_fmri_nvl2str(fmri, NULL, 0)) == -1 ||
(name = fmd_alloc(namelen + 1, FMD_NOSLEEP)) == NULL ||
fmd_fmri_nvl2str(fmri, name, namelen + 1) == -1) {
fmd_error(EFMD_ASRU_FMRI,
"failed to reload asru %s", lp->log_name);
if (name != NULL)
fmd_free(name, namelen + 1);
ahp->ah_error = EFMD_ASRU_FMRI;
return;
}
/*
* Look to see if the ASRU already exists in the hash: if it does and
* the existing ASRU entry is unusable but the duplicate is not, then
* delete the existing entry and continue on using the new entry; if
* the new entry is no "better", return an error and ignore it.
*/
if ((ap = fmd_asru_hash_lookup(ahp, name)) != NULL) {
if (!u && (ap->asru_flags & FMD_ASRU_UNUSABLE)) {
(void) fmd_asru_hash_delete_name(ahp, name);
fmd_asru_hash_release(ahp, ap);
} else {
fmd_error(EFMD_ASRU_DUP, "removing duplicate asru "
"log %s for %s\n", lp->log_name, name);
fmd_asru_hash_release(ahp, ap);
ahp->ah_error = EFMD_ASRU_DUP;
return;
}
}
ap = fmd_asru_create(ahp, fmd_strbasename(lp->log_name), name, fmri);
fmd_free(name, namelen + 1);
if (f)
ap->asru_flags |= FMD_ASRU_FAULTY;
if (u)
ap->asru_flags |= FMD_ASRU_UNUSABLE;
if (nvlist_lookup_boolean_value(nvl,
FM_SUSPECT_MESSAGE, &m) == 0 && m == B_FALSE)
ap->asru_flags |= FMD_ASRU_INVISIBLE;
if (case_uuid != NULL)
ap->asru_case = fmd_strdup(case_uuid, FMD_SLEEP);
ap->asru_flags |= FMD_ASRU_VALID;
fmd_asru_hash_insert(ahp, ap);
TRACE((FMD_DBG_ASRU, "asru %s recreated as %p (%s)", ap->asru_uuid,
(void *)ap, _fmd_asru_snames[ap->asru_flags & FMD_ASRU_STATE]));
/*
* If the resource is present and faulty but not unusable, replay the
* fault event that caused it be marked faulty. This will cause the
* agent subscribing to this fault class to again disable the resource.
*/
if (ps > 0 && !(ap->asru_flags & FMD_ASRU_INTERNAL) &&
(ap->asru_flags & FMD_ASRU_STATE) == FMD_ASRU_FAULTY &&
nvlist_lookup_nvlist(nvl, FM_RSRC_ASRU_EVENT, &fsrc) == 0) {
fmd_dprintf(FMD_DBG_ASRU,
"replaying fault event for %s", ap->asru_name);
(void) nvlist_xdup(fsrc, &flt, &fmd.d_nva);
(void) nvlist_lookup_string(flt, FM_CLASS, &class);
e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, flt, class);
fmd_dispq_dispatch(fmd.d_disp, e, class);
}
}
static void
fmd_asru_hash_discard(fmd_asru_hash_t *ahp, const char *uuid, int err)
{
char src[PATH_MAX], dst[PATH_MAX];
(void) snprintf(src, PATH_MAX, "%s/%s", ahp->ah_dirpath, uuid);
(void) snprintf(dst, PATH_MAX, "%s/%s-", ahp->ah_dirpath, uuid);
if (err != 0)
err = rename(src, dst);
else
err = unlink(src);
if (err != 0 && errno != ENOENT)
fmd_error(EFMD_ASRU_EVENT, "failed to rename log %s", src);
}
/*
* Open a saved log file and restore it into the ASRU hash. If we can't even
* open the log, rename the log file to <uuid>- to indicate it is corrupt. If
* fmd_log_replay() fails, we either delete the file (if it has reached the
* upper limit on cache age) or rename it for debugging if it was corrupted.
*/
static void
fmd_asru_hash_logopen(fmd_asru_hash_t *ahp, const char *uuid)
{
fmd_log_t *lp = fmd_log_tryopen(ahp->ah_dirpath, uuid, FMD_LOG_ASRU);
uint_t n;
if (lp == NULL) {
fmd_asru_hash_discard(ahp, uuid, errno);
return;
}
ahp->ah_error = 0;
n = ahp->ah_count;
fmd_log_replay(lp, (fmd_log_f *)fmd_asru_hash_recreate, ahp);
fmd_log_rele(lp);
if (ahp->ah_count == n)
fmd_asru_hash_discard(ahp, uuid, ahp->ah_error);
}
void
fmd_asru_hash_refresh(fmd_asru_hash_t *ahp)
{
struct dirent *dp, *ep;
DIR *dirp;
int zero;
if ((dirp = opendir(ahp->ah_dirpath)) == NULL) {
fmd_error(EFMD_ASRU_NODIR,
"failed to open asru cache directory %s", ahp->ah_dirpath);
return;
}
(void) fmd_conf_getprop(fmd.d_conf, "rsrc.zero", &zero);
ep = alloca(sizeof (struct dirent) + PATH_MAX + 1);
bzero(ep, sizeof (struct dirent) + PATH_MAX + 1);
(void) pthread_rwlock_wrlock(&ahp->ah_lock);
while (readdir_r(dirp, ep, &dp) == 0 && dp != NULL) {
if (dp->d_name[0] == '.')
continue; /* skip "." and ".." */
if (zero)
fmd_asru_hash_discard(ahp, dp->d_name, 0);
else if (!fmd_strmatch(dp->d_name, "*-"))
fmd_asru_hash_logopen(ahp, dp->d_name);
}
(void) pthread_rwlock_unlock(&ahp->ah_lock);
(void) closedir(dirp);
}
fmd_asru_hash_t *
fmd_asru_hash_create(const char *root, const char *dir)
{
fmd_asru_hash_t *ahp;
char path[PATH_MAX];
ahp = fmd_alloc(sizeof (fmd_asru_hash_t), FMD_SLEEP);
(void) pthread_rwlock_init(&ahp->ah_lock, NULL);
ahp->ah_hashlen = fmd.d_str_buckets;
ahp->ah_hash = fmd_zalloc(sizeof (void *) * ahp->ah_hashlen, FMD_SLEEP);
(void) snprintf(path, sizeof (path), "%s/%s", root, dir);
ahp->ah_dirpath = fmd_strdup(path, FMD_SLEEP);
(void) fmd_conf_getprop(fmd.d_conf, "rsrc.age", &ahp->ah_lifetime);
ahp->ah_count = 0;
ahp->ah_error = 0;
return (ahp);
}
void
fmd_asru_hash_destroy(fmd_asru_hash_t *ahp)
{
fmd_asru_t *ap, *np;
uint_t i;
for (i = 0; i < ahp->ah_hashlen; i++) {
for (ap = ahp->ah_hash[i]; ap != NULL; ap = np) {
np = ap->asru_next;
ap->asru_next = NULL;
fmd_asru_hash_release(ahp, ap);
}
}
fmd_strfree(ahp->ah_dirpath);
fmd_free(ahp->ah_hash, sizeof (void *) * ahp->ah_hashlen);
fmd_free(ahp, sizeof (fmd_asru_hash_t));
}
/*
* Take a snapshot of the ASRU database by placing an additional hold on each
* member in an auxiliary array, and then call 'func' for each ASRU.
*/
void
fmd_asru_hash_apply(fmd_asru_hash_t *ahp,
void (*func)(fmd_asru_t *, void *), void *arg)
{
fmd_asru_t *ap, **aps, **app;
uint_t apc, i;
(void) pthread_rwlock_rdlock(&ahp->ah_lock);
aps = app = fmd_alloc(ahp->ah_count * sizeof (fmd_asru_t *), FMD_SLEEP);
apc = ahp->ah_count;
for (i = 0; i < ahp->ah_hashlen; i++) {
for (ap = ahp->ah_hash[i]; ap != NULL; ap = ap->asru_next)
*app++ = fmd_asru_hold(ap);
}
ASSERT(app == aps + apc);
(void) pthread_rwlock_unlock(&ahp->ah_lock);
for (i = 0; i < apc; i++) {
func(aps[i], arg);
fmd_asru_hash_release(ahp, aps[i]);
}
fmd_free(aps, apc * sizeof (fmd_asru_t *));
}
/*
* Lookup an asru in the hash by name and place a hold on it. If the asru is
* not found, no entry is created and NULL is returned.
*/
fmd_asru_t *
fmd_asru_hash_lookup_name(fmd_asru_hash_t *ahp, const char *name)
{
fmd_asru_t *ap;
(void) pthread_rwlock_rdlock(&ahp->ah_lock);
ap = fmd_asru_hash_lookup(ahp, name);
(void) pthread_rwlock_unlock(&ahp->ah_lock);
return (ap);
}
/*
* Lookup an asru in the hash and place a hold on it. If 'create' is true, an
* absent entry will be created for the caller; otherwise NULL is returned.
*/
fmd_asru_t *
fmd_asru_hash_lookup_nvl(fmd_asru_hash_t *ahp, nvlist_t *fmri, int create)
{
fmd_asru_t *ap;
char *name = NULL;
ssize_t namelen;
uint_t h;
/*
* In order to lookup the ASRU in our hash, convert the FMRI from
* nvlist form into a string form using the scheme module.
*/
if ((namelen = fmd_fmri_nvl2str(fmri, NULL, 0)) == -1 ||
(name = fmd_alloc(namelen + 1, FMD_NOSLEEP)) == NULL ||
fmd_fmri_nvl2str(fmri, name, namelen + 1) == -1) {
if (name != NULL)
fmd_free(name, namelen + 1);
return (NULL);
}
/*
* If we must create the asru, grab the rwlock as a writer; otherwise
* reader is sufficient. Then search the hash for the given asru name.
* If we didn't find the asru in the hash and we need to create it,
* create and insert the asru with ahp->ah_lock held and hash it in.
* We'll then drop the rwlock and proceed to initializing the asru.
*/
if (create)
(void) pthread_rwlock_wrlock(&ahp->ah_lock);
else
(void) pthread_rwlock_rdlock(&ahp->ah_lock);
h = fmd_strhash(name) % ahp->ah_hashlen;
for (ap = ahp->ah_hash[h]; ap != NULL; ap = ap->asru_next) {
if (strcmp(ap->asru_name, name) == 0)
break;
}
if (ap == NULL && create == FMD_B_TRUE) {
ap = fmd_asru_create(ahp, NULL, name, fmri);
fmd_asru_hash_insert(ahp, ap);
(void) pthread_mutex_lock(&ap->asru_lock);
} else
create = FMD_B_FALSE;
(void) pthread_rwlock_unlock(&ahp->ah_lock);
fmd_free(name, namelen + 1);
/*
* If 'create' is still true, then we need to initialize the asru log;
* If 'create' is false and an asru was found, we must cond_wait for
* the FMD_ASRU_VALID bit to be set before returning. In both cases,
* we increment asru_refs for the caller.
*/
if (create == FMD_B_TRUE) {
uuid_t uuid;
ASSERT(MUTEX_HELD(&ap->asru_lock));
ASSERT(ap->asru_uuid == NULL && ap->asru_log == NULL);
/*
* Generate a UUID for the ASRU. libuuid cleverly gives us no
* interface for specifying or learning the buffer size. Sigh.
* The spec says 36 bytes but we use a tunable just to be safe.
*/
(void) fmd_conf_getprop(fmd.d_conf,
"uuidlen", &ap->asru_uuidlen);
ap->asru_uuid = fmd_zalloc(ap->asru_uuidlen + 1, FMD_SLEEP);
uuid_generate(uuid);
uuid_unparse(uuid, ap->asru_uuid);
ASSERT(!(ap->asru_flags & FMD_ASRU_VALID));
ap->asru_flags |= FMD_ASRU_VALID;
ap->asru_refs++;
ASSERT(ap->asru_refs != 0);
(void) pthread_mutex_unlock(&ap->asru_lock);
(void) pthread_cond_broadcast(&ap->asru_cv);
TRACE((FMD_DBG_ASRU, "asru %s created as %p",
ap->asru_uuid, (void *)ap));
} else if (ap != NULL) {
(void) pthread_mutex_lock(&ap->asru_lock);
while (!(ap->asru_flags & FMD_ASRU_VALID))
(void) pthread_cond_wait(&ap->asru_cv, &ap->asru_lock);
ap->asru_refs++;
ASSERT(ap->asru_refs != 0);
(void) pthread_mutex_unlock(&ap->asru_lock);
}
return (ap);
}
/*
* Release the reference count on an asru obtained using fmd_asru_hash_lookup.
* We take 'ahp' for symmetry and in case we need to use it in future work.
*/
/*ARGSUSED*/
void
fmd_asru_hash_release(fmd_asru_hash_t *ahp, fmd_asru_t *ap)
{
(void) pthread_mutex_lock(&ap->asru_lock);
ASSERT(ap->asru_refs != 0);
if (--ap->asru_refs == 0)
fmd_asru_destroy(ap);
else
(void) pthread_mutex_unlock(&ap->asru_lock);
}
int
fmd_asru_hash_delete_name(fmd_asru_hash_t *ahp, const char *name)
{
fmd_asru_t *ap, **pp;
char path[PATH_MAX];
uint_t h;
(void) pthread_rwlock_wrlock(&ahp->ah_lock);
h = fmd_strhash(name) % ahp->ah_hashlen;
pp = &ahp->ah_hash[h];
for (ap = *pp; ap != NULL; ap = ap->asru_next) {
if (strcmp(ap->asru_name, name) == 0)
break;
else
pp = &ap->asru_next;
}
if (ap != NULL) {
*pp = ap->asru_next;
ap->asru_next = NULL;
ASSERT(ahp->ah_count != 0);
ahp->ah_count--;
}
(void) pthread_rwlock_unlock(&ahp->ah_lock);
if (ap == NULL)
return (fmd_set_errno(EFMD_ASRU_NOENT));
/*
* If we found a matching ASRU, unlink its log file and then release
* the hash entry. Note that it may still be referenced if another
* thread is manipulating it; this is ok because once we unlink, the
* log file will not be restored, and the log data will be freed when
* all of the referencing threads release their respective references.
*/
(void) snprintf(path, sizeof (path),
"%s/%s", ahp->ah_dirpath, ap->asru_uuid);
if (unlink(path) != 0)
fmd_error(EFMD_ASRU_UNLINK, "failed to unlink asru %s", path);
fmd_asru_hash_release(ahp, ap);
return (0);
}
static void
fmd_asru_logevent(fmd_asru_t *ap, uint_t state, const char *cid, nvlist_t *evl)
{
boolean_t f = (state & FMD_ASRU_FAULTY) != 0;
boolean_t u = (state & FMD_ASRU_UNUSABLE) != 0;
boolean_t m = (ap->asru_flags & FMD_ASRU_INVISIBLE) == 0;
fmd_event_t *e;
fmd_log_t *lp;
nvlist_t *nvl;
char *class;
ASSERT(MUTEX_HELD(&ap->asru_lock));
if ((lp = ap->asru_log) == NULL)
lp = fmd_log_open(ap->asru_root, ap->asru_uuid, FMD_LOG_ASRU);
if (lp == NULL)
return; /* can't log events if we can't open the log */
nvl = fmd_protocol_resource(_fmd_asru_events[f | (u << 1)],
ap->asru_fmri, cid, f, u, m, evl);
(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
fmd_event_hold(e);
fmd_log_append(lp, e, NULL);
fmd_event_rele(e);
/*
* For now, we close the log file after every update to conserve file
* descriptors and daemon overhead. If this becomes a performance
* issue this code can change to keep a fixed-size LRU cache of logs.
*/
fmd_log_rele(lp);
ap->asru_log = NULL;
}
int
fmd_asru_setflags(fmd_asru_t *ap, uint_t sflag, const char *cid, nvlist_t *nvl)
{
uint_t nstate, ostate;
char *uuid;
boolean_t msg;
ASSERT(!(sflag & ~FMD_ASRU_STATE));
ASSERT(sflag != FMD_ASRU_STATE);
uuid = fmd_strdup(cid, FMD_SLEEP);
(void) pthread_mutex_lock(&ap->asru_lock);
ostate = ap->asru_flags & FMD_ASRU_STATE;
ap->asru_flags |= sflag;
nstate = ap->asru_flags & FMD_ASRU_STATE;
if (nstate != ostate && uuid != NULL) {
fmd_strfree(ap->asru_case);
ap->asru_case = uuid;
} else if (nstate != ostate && uuid == NULL) {
cid = alloca(strlen(ap->asru_case) + 1);
(void) strcpy((char *)cid, ap->asru_case);
}
if (nstate != ostate) {
if (nvl != NULL && nvlist_lookup_boolean_value(nvl,
FM_SUSPECT_MESSAGE, &msg) == 0 && msg == B_FALSE)
ap->asru_flags |= FMD_ASRU_INVISIBLE;
TRACE((FMD_DBG_ASRU, "asru %s %s->%s", ap->asru_uuid,
_fmd_asru_snames[ostate], _fmd_asru_snames[nstate]));
fmd_asru_logevent(ap, nstate, cid, nvl);
(void) pthread_mutex_unlock(&ap->asru_lock);
(void) pthread_cond_broadcast(&ap->asru_cv);
return (1);
}
(void) pthread_mutex_unlock(&ap->asru_lock);
fmd_strfree(uuid);
return (0);
}
int
fmd_asru_clrflags(fmd_asru_t *ap, uint_t sflag, const char *cid, nvlist_t *nvl)
{
uint_t nstate, ostate;
char *uuid;
ASSERT(!(sflag & ~FMD_ASRU_STATE));
ASSERT(sflag != FMD_ASRU_STATE);
uuid = fmd_strdup(cid, FMD_SLEEP);
(void) pthread_mutex_lock(&ap->asru_lock);
ostate = ap->asru_flags & FMD_ASRU_STATE;
ap->asru_flags &= ~sflag;
nstate = ap->asru_flags & FMD_ASRU_STATE;
if (nstate != ostate && uuid != NULL) {
fmd_strfree(ap->asru_case);
ap->asru_case = uuid;
} else if (nstate != ostate && uuid == NULL) {
cid = alloca(strlen(ap->asru_case) + 1);
(void) strcpy((char *)cid, ap->asru_case);
}
if (nstate != ostate) {
TRACE((FMD_DBG_ASRU, "asru %s %s->%s", ap->asru_uuid,
_fmd_asru_snames[ostate], _fmd_asru_snames[nstate]));
fmd_asru_logevent(ap, nstate, cid, nvl);
(void) pthread_mutex_unlock(&ap->asru_lock);
(void) pthread_cond_broadcast(&ap->asru_cv);
return (1);
}
(void) pthread_mutex_unlock(&ap->asru_lock);
fmd_strfree(uuid);
return (0);
}
/*
* Report the current known state of the ASRU by refreshing its unusable status
* based upon the routines provided by the scheme module. If the unusable bit
* is different, we do *not* generate a state change here because that change
* may be unrelated to fmd activities and therefore we have no case or event.
* The absence of the transition is harmless as this function is only provided
* for RPC observability and fmd's clients are only concerned with ASRU_FAULTY.
*/
int
fmd_asru_getstate(fmd_asru_t *ap)
{
int us, st;
if (!(ap->asru_flags & FMD_ASRU_INTERNAL) &&
fmd_fmri_present(ap->asru_fmri) <= 0)
return (0); /* do not report non-fmd non-present resources */
us = fmd_fmri_unusable(ap->asru_fmri);
st = ap->asru_flags & FMD_ASRU_STATE;
if (us > 0)
st |= FMD_ASRU_UNUSABLE;
else if (us == 0)
st &= ~FMD_ASRU_UNUSABLE;
return (st);
}
char *
fmd_asru_getcase(fmd_asru_t *ap, char *buf, size_t buflen)
{
(void) pthread_mutex_lock(&ap->asru_lock);
if (ap->asru_case != NULL)
(void) strlcpy(buf, ap->asru_case, buflen);
else if (buflen != 0)
buf[0] = '\0';
(void) pthread_mutex_unlock(&ap->asru_lock);
return (buf);
}