/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <strings.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <alloca.h>
#include <fmd_rpc_adm.h>
#include <fmd_rpc.h>
#include <fmd_module.h>
#include <fmd_ustat.h>
#include <fmd_error.h>
#include <fmd_asru.h>
#include <fmd_ckpt.h>
#include <fmd_case.h>
#include <fmd_fmri.h>
#include <fmd_idspace.h>
#include <fmd_xprt.h>
#include <fmd.h>
bool_t
fmd_adm_modinfo_1_svc(struct fmd_rpc_modlist *rvp, struct svc_req *req)
{
struct fmd_rpc_modinfo *rmi;
fmd_module_t *mp;
rvp->rml_list = NULL;
rvp->rml_err = 0;
rvp->rml_len = 0;
if (fmd_rpc_deny(req)) {
rvp->rml_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
(void) pthread_mutex_lock(&fmd.d_mod_lock);
for (mp = fmd_list_next(&fmd.d_mod_list);
mp != NULL; mp = fmd_list_next(mp)) {
if ((rmi = malloc(sizeof (struct fmd_rpc_modinfo))) == NULL) {
rvp->rml_err = FMD_ADM_ERR_NOMEM;
break;
}
fmd_module_lock(mp);
/*
* If mod_info is NULL, the module is in the middle of loading:
* do not report its presence to observability tools yet.
*/
if (mp->mod_info == NULL) {
fmd_module_unlock(mp);
free(rmi);
continue;
}
rmi->rmi_name = strdup(mp->mod_name);
rmi->rmi_desc = strdup(mp->mod_info->fmdi_desc);
rmi->rmi_vers = strdup(mp->mod_info->fmdi_vers);
rmi->rmi_faulty = mp->mod_error != 0;
rmi->rmi_next = rvp->rml_list;
fmd_module_unlock(mp);
rvp->rml_list = rmi;
rvp->rml_len++;
if (rmi->rmi_desc == NULL || rmi->rmi_vers == NULL) {
rvp->rml_err = FMD_ADM_ERR_NOMEM;
break;
}
}
(void) pthread_mutex_unlock(&fmd.d_mod_lock);
return (TRUE);
}
bool_t
fmd_adm_modcstat_1_svc(char *name,
struct fmd_rpc_modstat *rms, struct svc_req *req)
{
fmd_ustat_snap_t snap;
fmd_module_t *mp;
rms->rms_buf.rms_buf_val = NULL;
rms->rms_buf.rms_buf_len = 0;
rms->rms_err = 0;
if (fmd_rpc_deny(req)) {
rms->rms_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
rms->rms_err = FMD_ADM_ERR_MODSRCH;
return (TRUE);
}
if (fmd_modstat_snapshot(mp, &snap) == 0) {
rms->rms_buf.rms_buf_val = snap.uss_buf;
rms->rms_buf.rms_buf_len = snap.uss_len;
} else if (errno == EFMD_HDL_ABORT) {
rms->rms_err = FMD_ADM_ERR_MODFAIL;
} else
rms->rms_err = FMD_ADM_ERR_NOMEM;
fmd_module_rele(mp);
return (TRUE);
}
bool_t
fmd_adm_moddstat_1_svc(char *name,
struct fmd_rpc_modstat *rms, struct svc_req *req)
{
fmd_module_t *mp;
rms->rms_buf.rms_buf_val = NULL;
rms->rms_buf.rms_buf_len = 0;
rms->rms_err = 0;
if (fmd_rpc_deny(req)) {
rms->rms_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
rms->rms_err = FMD_ADM_ERR_MODSRCH;
return (TRUE);
}
rms->rms_buf.rms_buf_val = malloc(sizeof (fmd_modstat_t));
rms->rms_buf.rms_buf_len = sizeof (fmd_modstat_t) / sizeof (fmd_stat_t);
if (rms->rms_buf.rms_buf_val == NULL) {
rms->rms_err = FMD_ADM_ERR_NOMEM;
rms->rms_buf.rms_buf_len = 0;
fmd_module_rele(mp);
return (TRUE);
}
/*
* Note: the bcopy() here is valid only if no FMD_TYPE_STRING stats
* are present in mp->mod_stats. We don't use any for the daemon-
* maintained stats and provide this function in order to reduce the
* overhead of the fmstat(1M) default view, where these minimal stats
* must be retrieved for all of the active modules.
*/
(void) pthread_mutex_lock(&mp->mod_stats_lock);
if (mp->mod_stats != NULL) {
mp->mod_stats->ms_snaptime.fmds_value.ui64 = gethrtime();
bcopy(mp->mod_stats, rms->rms_buf.rms_buf_val,
sizeof (fmd_modstat_t));
} else {
free(rms->rms_buf.rms_buf_val);
rms->rms_buf.rms_buf_val = NULL;
rms->rms_buf.rms_buf_len = 0;
rms->rms_err = FMD_ADM_ERR_MODFAIL;
}
(void) pthread_mutex_unlock(&mp->mod_stats_lock);
fmd_module_rele(mp);
return (TRUE);
}
bool_t
fmd_adm_modgstat_1_svc(struct fmd_rpc_modstat *rms, struct svc_req *req)
{
const size_t size = sizeof (fmd_statistics_t);
if (fmd_rpc_deny(req)) {
rms->rms_buf.rms_buf_val = NULL;
rms->rms_buf.rms_buf_len = 0;
rms->rms_err = FMD_ADM_ERR_PERM;
} else if ((rms->rms_buf.rms_buf_val = malloc(size)) != NULL) {
/*
* Note: the bcopy() here is valid only if no FMD_TYPE_STRING
* stats are present in fmd.d_stats (see definition in fmd.c).
*/
(void) pthread_mutex_lock(&fmd.d_stats_lock);
bcopy(fmd.d_stats, rms->rms_buf.rms_buf_val, size);
(void) pthread_mutex_unlock(&fmd.d_stats_lock);
rms->rms_buf.rms_buf_len = size / sizeof (fmd_stat_t);
rms->rms_err = 0;
} else {
rms->rms_buf.rms_buf_len = 0;
rms->rms_err = FMD_ADM_ERR_NOMEM;
}
return (TRUE);
}
bool_t
fmd_adm_modload_1_svc(char *path, int *rvp, struct svc_req *req)
{
fmd_module_t *mp;
const char *p;
int err = 0;
if (fmd_rpc_deny(req)) {
*rvp = FMD_ADM_ERR_PERM;
return (TRUE);
}
/*
* Before we endure the expense of constructing a module and attempting
* to load it, do a quick check to see if the pathname is valid.
*/
if (access(path, F_OK) != 0) {
*rvp = FMD_ADM_ERR_MODNOENT;
return (TRUE);
}
if ((p = strrchr(path, '.')) != NULL && strcmp(p, ".so") == 0)
mp = fmd_modhash_load(fmd.d_mod_hash, path, &fmd_rtld_ops);
else
mp = fmd_modhash_load(fmd.d_mod_hash, path, &fmd_proc_ops);
if (mp == NULL) {
switch (errno) {
case EFMD_MOD_LOADED:
err = FMD_ADM_ERR_MODEXIST;
break;
case EFMD_MOD_INIT:
err = FMD_ADM_ERR_MODINIT;
break;
default:
err = FMD_ADM_ERR_MODLOAD;
break;
}
}
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_modunload_1_svc(char *name, int *rvp, struct svc_req *req)
{
fmd_module_t *mp = NULL;
int err = 0;
if (fmd_rpc_deny(req))
err = FMD_ADM_ERR_PERM;
else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL)
err = FMD_ADM_ERR_MODSRCH;
else if (mp == fmd.d_self)
err = FMD_ADM_ERR_MODBUSY;
else if (fmd_modhash_unload(fmd.d_mod_hash, name) != 0)
err = FMD_ADM_ERR_MODSRCH;
if (mp != NULL)
fmd_module_rele(mp);
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_modreset_1_svc(char *name, int *rvp, struct svc_req *req)
{
fmd_module_t *mp = NULL;
int err = 0;
if (fmd_rpc_deny(req))
err = FMD_ADM_ERR_PERM;
else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL)
err = FMD_ADM_ERR_MODSRCH;
else if (mp == fmd.d_self)
err = FMD_ADM_ERR_MODBUSY;
else if (fmd_modhash_unload(fmd.d_mod_hash, name) != 0)
err = FMD_ADM_ERR_MODSRCH;
if (err == 0)
fmd_ckpt_delete(mp); /* erase any saved checkpoints */
if (err == 0 && fmd_modhash_load(fmd.d_mod_hash,
mp->mod_path, mp->mod_ops) == NULL) {
if (errno == EFMD_MOD_INIT)
err = FMD_ADM_ERR_MODINIT;
else
err = FMD_ADM_ERR_MODLOAD;
}
if (mp != NULL)
fmd_module_rele(mp);
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_modgc_1_svc(char *name, int *rvp, struct svc_req *req)
{
fmd_module_t *mp;
int err = 0;
if (fmd_rpc_deny(req))
err = FMD_ADM_ERR_PERM;
else if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL)
err = FMD_ADM_ERR_MODSRCH;
else {
fmd_module_gc(mp);
fmd_module_rele(mp);
}
*rvp = err;
return (TRUE);
}
/*
* Unlike our other RPC callbacks, fmd_adm_rsrclist_1 can return large amounts
* of data that may exceed the underlying RPC transport buffer size if the
* resource cache is heavily populated and/or all resources are requested.
* To minimize the likelihood of running out of RPC buffer space and having to
* fail the client request, fmd_adm_rsrclist_1 returns a snapshot of the
* relevant FMRI strings only: the client can use fmd_adm_rsrcinfo_1 on an
* individual FMRI if more information is needed. To further reduce the XDR
* overhead, the string list is represented as XDR-opaque data where the
* entire list is returned as a string table (e.g. "fmriA\0fmriB\0...").
*/
static void
fmd_adm_rsrclist_asru(fmd_asru_t *ap, void *arg)
{
struct fmd_rpc_rsrclist *rrl = arg;
size_t name_len, buf_len;
void *p;
/*
* Skip the ASRU if this fault is marked as invisible.
* If rrl_all is false, we take a quick look at asru_flags with no lock
* held to see if the ASRU is not faulty. If so,
* we don't want to report it by default and can just skip this ASRU.
* This helps keep overhead low in the common case, as the call to
* fmd_asru_getstate() can be expensive depending on the scheme.
*/
if (ap->asru_flags & FMD_ASRU_INVISIBLE)
return;
if (rrl->rrl_all == B_FALSE && !(ap->asru_flags & FMD_ASRU_FAULTY))
return;
if (rrl->rrl_err != 0 || fmd_asru_getstate(ap) == 0)
return; /* error has occurred or resource is in 'ok' state */
/*
* Lock the ASRU and reallocate rrl_buf[] to be large enough to hold
* another string, doubling it as needed. Then copy the new string
* on to the end, and increment rrl_len to indicate the used space.
*/
(void) pthread_mutex_lock(&ap->asru_lock);
name_len = strlen(ap->asru_name) + 1;
while (rrl->rrl_len + name_len > rrl->rrl_buf.rrl_buf_len) {
if (rrl->rrl_buf.rrl_buf_len != 0)
buf_len = rrl->rrl_buf.rrl_buf_len * 2;
else
buf_len = 1024; /* default buffer size */
if ((p = realloc(rrl->rrl_buf.rrl_buf_val, buf_len)) != NULL) {
bzero((char *)p + rrl->rrl_buf.rrl_buf_len,
buf_len - rrl->rrl_buf.rrl_buf_len);
rrl->rrl_buf.rrl_buf_val = p;
rrl->rrl_buf.rrl_buf_len = buf_len;
} else {
rrl->rrl_err = FMD_ADM_ERR_NOMEM;
break;
}
}
if (rrl->rrl_err == 0) {
bcopy(ap->asru_name, (char *)rrl->rrl_buf.rrl_buf_val +
rrl->rrl_len, name_len);
rrl->rrl_len += name_len;
rrl->rrl_cnt++;
}
(void) pthread_mutex_unlock(&ap->asru_lock);
}
bool_t
fmd_adm_rsrclist_1_svc(bool_t all,
struct fmd_rpc_rsrclist *rvp, struct svc_req *req)
{
rvp->rrl_buf.rrl_buf_len = 0;
rvp->rrl_buf.rrl_buf_val = NULL;
rvp->rrl_len = 0;
rvp->rrl_cnt = 0;
rvp->rrl_err = 0;
rvp->rrl_all = all;
if (fmd_rpc_deny(req))
rvp->rrl_err = FMD_ADM_ERR_PERM;
else
fmd_asru_hash_apply(fmd.d_asrus, fmd_adm_rsrclist_asru, rvp);
return (TRUE);
}
bool_t
fmd_adm_rsrcinfo_1_svc(char *fmri,
struct fmd_rpc_rsrcinfo *rvp, struct svc_req *req)
{
fmd_asru_t *ap;
fmd_case_impl_t *cip;
int state;
bzero(rvp, sizeof (struct fmd_rpc_rsrcinfo));
if (fmd_rpc_deny(req)) {
rvp->rri_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((ap = fmd_asru_hash_lookup_name(fmd.d_asrus, fmri)) == NULL) {
rvp->rri_err = FMD_ADM_ERR_RSRCSRCH;
return (TRUE);
}
state = fmd_asru_getstate(ap);
(void) pthread_mutex_lock(&ap->asru_lock);
cip = (fmd_case_impl_t *)ap->asru_case;
rvp->rri_fmri = strdup(ap->asru_name);
rvp->rri_uuid = strdup(ap->asru_uuid);
rvp->rri_case = cip ? strdup(cip->ci_uuid) : NULL;
rvp->rri_faulty = (state & FMD_ASRU_FAULTY) != 0;
rvp->rri_unusable = (state & FMD_ASRU_UNUSABLE) != 0;
rvp->rri_invisible = (ap->asru_flags & FMD_ASRU_INVISIBLE) != 0;
(void) pthread_mutex_unlock(&ap->asru_lock);
fmd_asru_hash_release(fmd.d_asrus, ap);
if (rvp->rri_fmri == NULL || rvp->rri_uuid == NULL)
rvp->rri_err = FMD_ADM_ERR_NOMEM;
return (TRUE);
}
static void
fmd_adm_do_repair(char *name, struct svc_req *req, int *errp, uint8_t reason,
char *uuid)
{
if (fmd_rpc_deny(req))
*errp = FMD_ADM_ERR_PERM;
else {
fmd_asru_rep_arg_t fara;
int err = FARA_ERR_RSRCNOTF;
fara.fara_reason = reason;
fara.fara_rval = &err;
fara.fara_uuid = uuid;
fara.fara_bywhat = FARA_BY_ASRU;
fmd_asru_hash_apply_by_asru(fmd.d_asrus, name,
fmd_asru_repaired, &fara);
fara.fara_bywhat = FARA_BY_LABEL;
fmd_asru_hash_apply_by_label(fmd.d_asrus, name,
fmd_asru_repaired, &fara);
fara.fara_bywhat = FARA_BY_FRU;
fmd_asru_hash_apply_by_fru(fmd.d_asrus, name,
fmd_asru_repaired, &fara);
fara.fara_bywhat = FARA_BY_RSRC;
fmd_asru_hash_apply_by_rsrc(fmd.d_asrus, name,
fmd_asru_repaired, &fara);
if (err == FARA_ERR_RSRCNOTR)
*errp = FMD_ADM_ERR_RSRCNOTR;
else if (err == FARA_OK)
*errp = 0;
}
}
bool_t
fmd_adm_rsrcflush_1_svc(char *name, int *rvp, struct svc_req *req)
{
int err = FMD_ADM_ERR_RSRCNOTF;
/*
* If anyone does an fmadm flush command, discard any resolved
* cases that were being retained for historic diagnosis.
*/
if (fmd_rpc_deny(req))
err = FMD_ADM_ERR_PERM;
else {
fmd_asru_hash_apply_by_asru(fmd.d_asrus, name,
fmd_asru_flush, &err);
fmd_asru_hash_apply_by_label(fmd.d_asrus, name,
fmd_asru_flush, &err);
fmd_asru_hash_apply_by_fru(fmd.d_asrus, name,
fmd_asru_flush, &err);
fmd_asru_hash_apply_by_rsrc(fmd.d_asrus, name,
fmd_asru_flush, &err);
}
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_rsrcrepaired_1_svc(char *name, int *rvp, struct svc_req *req)
{
int err = FMD_ADM_ERR_RSRCNOTF;
fmd_adm_do_repair(name, req, &err, FMD_ASRU_REPAIRED, NULL);
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_rsrcreplaced_1_svc(char *name, int *rvp, struct svc_req *req)
{
int err = FMD_ADM_ERR_RSRCNOTF;
fmd_adm_do_repair(name, req, &err, FMD_ASRU_REPLACED, NULL);
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_rsrcacquit_1_svc(char *name, char *uuid, int *rvp, struct svc_req *req)
{
int err = FMD_ADM_ERR_RSRCNOTF;
fmd_adm_do_repair(name, req, &err, FMD_ASRU_ACQUITTED, uuid);
*rvp = err;
return (TRUE);
}
static void
fmd_adm_serdlist_measure(fmd_serd_eng_t *sgp, void *arg)
{
struct fmd_rpc_serdlist *rsl = arg;
rsl->rsl_len += strlen(sgp->sg_name) + 1;
rsl->rsl_cnt++;
}
static void
fmd_adm_serdlist_record(fmd_serd_eng_t *sgp, void *arg)
{
struct fmd_rpc_serdlist *rsl = arg;
bcopy(sgp->sg_name, rsl->rsl_buf.rsl_buf_val + rsl->rsl_len,
strlen(sgp->sg_name));
rsl->rsl_len += strlen(sgp->sg_name) + 1;
}
bool_t
fmd_adm_serdlist_1_svc(char *name, struct fmd_rpc_serdlist *rvp,
struct svc_req *req)
{
fmd_module_t *mp;
void *p;
rvp->rsl_buf.rsl_buf_len = 0;
rvp->rsl_buf.rsl_buf_val = NULL;
rvp->rsl_len = 0;
rvp->rsl_cnt = 0;
rvp->rsl_err = 0;
if (fmd_rpc_deny(req)) {
rvp->rsl_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, name)) == NULL) {
rvp->rsl_err = FMD_ADM_ERR_MODSRCH;
return (TRUE);
}
fmd_module_lock(mp);
/* In the first pass, collect the overall length of the buffer. */
fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdlist_measure, rvp);
if (rvp->rsl_len == 0) {
fmd_module_unlock(mp);
fmd_module_rele(mp);
return (TRUE);
}
p = malloc(rvp->rsl_len);
if (p) {
rvp->rsl_buf.rsl_buf_val = p;
rvp->rsl_buf.rsl_buf_len = rvp->rsl_len;
bzero(rvp->rsl_buf.rsl_buf_val, rvp->rsl_buf.rsl_buf_len);
rvp->rsl_len = 0;
/* In the second pass, populate the buffer with data. */
fmd_serd_hash_apply(&mp->mod_serds, fmd_adm_serdlist_record,
rvp);
} else {
rvp->rsl_err = FMD_ADM_ERR_NOMEM;
}
fmd_module_unlock(mp);
fmd_module_rele(mp);
return (TRUE);
}
static void
fmd_adm_serdinfo_record(fmd_serd_eng_t *sgp, struct fmd_rpc_serdinfo *rsi)
{
uint64_t old, now = fmd_time_gethrtime();
const fmd_serd_elem_t *oep;
if ((rsi->rsi_name = strdup(sgp->sg_name)) == NULL) {
rsi->rsi_err = FMD_ADM_ERR_NOMEM;
return;
}
if ((oep = fmd_list_next(&sgp->sg_list)) != NULL)
old = fmd_event_hrtime(oep->se_event);
else
old = now;
rsi->rsi_delta = now >= old ? now - old : (UINT64_MAX - old) + now + 1;
rsi->rsi_count = sgp->sg_count;
rsi->rsi_fired = fmd_serd_eng_fired(sgp) != 0;
rsi->rsi_n = sgp->sg_n;
rsi->rsi_t = sgp->sg_t;
}
bool_t
fmd_adm_serdinfo_1_svc(char *mname, char *sname, struct fmd_rpc_serdinfo *rvp,
struct svc_req *req)
{
fmd_module_t *mp;
fmd_serd_eng_t *sgp;
bzero(rvp, sizeof (struct fmd_rpc_serdinfo));
if (fmd_rpc_deny(req)) {
rvp->rsi_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) {
rvp->rsi_err = FMD_ADM_ERR_MODSRCH;
return (TRUE);
}
fmd_module_lock(mp);
if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) {
fmd_adm_serdinfo_record(sgp, rvp);
} else
rvp->rsi_err = FMD_ADM_ERR_SERDSRCH;
fmd_module_unlock(mp);
fmd_module_rele(mp);
return (TRUE);
}
/*ARGSUSED*/
bool_t
fmd_adm_serdinfo_old_1_svc(char *name, struct fmd_rpc_serdlist *rvp,
struct svc_req *req)
{
return (FALSE);
}
bool_t
fmd_adm_serdreset_1_svc(char *mname, char *sname, int *rvp, struct svc_req *req)
{
fmd_module_t *mp;
fmd_serd_eng_t *sgp;
int err = 0;
if (fmd_rpc_deny(req)) {
*rvp = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((mp = fmd_modhash_lookup(fmd.d_mod_hash, mname)) == NULL) {
*rvp = FMD_ADM_ERR_MODSRCH;
return (TRUE);
}
fmd_module_lock(mp);
if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, sname)) != NULL) {
if (fmd_serd_eng_fired(sgp)) {
err = FMD_ADM_ERR_SERDFIRED;
} else {
fmd_serd_eng_reset(sgp);
fmd_module_setdirty(mp);
}
} else
err = FMD_ADM_ERR_SERDSRCH;
fmd_module_unlock(mp);
fmd_module_rele(mp);
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_logrotate_1_svc(char *name, int *rvp, struct svc_req *req)
{
fmd_log_t **lpp, *old, *new;
int try = 1, trylimit = 1;
pthread_rwlock_t *lockp;
hrtime_t nsec = 0;
timespec_t tv;
if (fmd_rpc_deny(req)) {
*rvp = FMD_ADM_ERR_PERM;
return (TRUE);
}
if (strcmp(name, "errlog") == 0) {
lpp = &fmd.d_errlog;
lockp = &fmd.d_log_lock;
} else if (strcmp(name, "fltlog") == 0) {
lpp = &fmd.d_fltlog;
lockp = &fmd.d_log_lock;
} else if (strcmp(name, "infolog") == 0) {
lpp = &fmd.d_ilog;
lockp = &fmd.d_ilog_lock;
} else if (strcmp(name, "infolog_hival") == 0) {
lpp = &fmd.d_hvilog;
lockp = &fmd.d_hvilog_lock;
} else {
*rvp = FMD_ADM_ERR_ROTSRCH;
return (TRUE);
}
(void) fmd_conf_getprop(fmd.d_conf, "log.tryrotate", &trylimit);
(void) fmd_conf_getprop(fmd.d_conf, "log.waitrotate", &nsec);
tv.tv_sec = nsec / NANOSEC;
tv.tv_nsec = nsec % NANOSEC;
/*
* To rotate a log file, grab d_log_lock as writer to make sure no
* one else can discover the current log pointer. Then try to rotate
* the log. If we're successful, release the old log pointer.
*/
do {
if (try > 1)
(void) nanosleep(&tv, NULL); /* wait for checkpoints */
(void) pthread_rwlock_wrlock(lockp);
old = *lpp;
if ((new = fmd_log_rotate(old)) != NULL) {
fmd_log_rele(old);
*lpp = new;
}
(void) pthread_rwlock_unlock(lockp);
} while (new == NULL && errno == EFMD_LOG_ROTBUSY && try++ < trylimit);
if (new != NULL)
*rvp = 0;
else if (errno == EFMD_LOG_ROTBUSY)
*rvp = FMD_ADM_ERR_ROTBUSY;
else
*rvp = FMD_ADM_ERR_ROTFAIL;
return (TRUE);
}
bool_t
fmd_adm_caserepair_1_svc(char *uuid, int *rvp, struct svc_req *req)
{
fmd_case_t *cp = NULL;
int err = 0;
if (fmd_rpc_deny(req))
err = FMD_ADM_ERR_PERM;
else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL)
err = FMD_ADM_ERR_CASESRCH;
else if (fmd_case_repair(cp) != 0) {
err = errno == EFMD_CASE_OWNER ?
FMD_ADM_ERR_CASEXPRT : FMD_ADM_ERR_CASEOPEN;
}
if (cp != NULL)
fmd_case_rele(cp);
*rvp = err;
return (TRUE);
}
bool_t
fmd_adm_caseacquit_1_svc(char *uuid, int *rvp, struct svc_req *req)
{
fmd_case_t *cp = NULL;
int err = 0;
if (fmd_rpc_deny(req))
err = FMD_ADM_ERR_PERM;
else if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL)
err = FMD_ADM_ERR_CASESRCH;
else if (fmd_case_acquit(cp) != 0) {
err = errno == EFMD_CASE_OWNER ?
FMD_ADM_ERR_CASEXPRT : FMD_ADM_ERR_CASEOPEN;
}
if (cp != NULL)
fmd_case_rele(cp);
*rvp = err;
return (TRUE);
}
void
fmd_adm_caselist_case(fmd_case_t *cp, void *arg)
{
fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
struct fmd_rpc_caselist *rcl = arg;
size_t uuid_len, buf_len;
void *p;
if (rcl->rcl_err != 0)
return;
/*
* skip invisible cases
*/
if (cip->ci_flags & FMD_CF_INVISIBLE)
return;
/*
* Lock the case and reallocate rcl_buf[] to be large enough to hold
* another string, doubling it as needed. Then copy the new string
* on to the end, and increment rcl_len to indicate the used space.
*/
if (!(cip->ci_flags & FMD_CF_SOLVED))
return;
(void) pthread_mutex_lock(&cip->ci_lock);
uuid_len = cip->ci_uuidlen + 1;
while (rcl->rcl_len + uuid_len > rcl->rcl_buf.rcl_buf_len) {
if (rcl->rcl_buf.rcl_buf_len != 0)
buf_len = rcl->rcl_buf.rcl_buf_len * 2;
else
buf_len = 1024; /* default buffer size */
if ((p = realloc(rcl->rcl_buf.rcl_buf_val, buf_len)) != NULL) {
bzero((char *)p + rcl->rcl_buf.rcl_buf_len,
buf_len - rcl->rcl_buf.rcl_buf_len);
rcl->rcl_buf.rcl_buf_val = p;
rcl->rcl_buf.rcl_buf_len = buf_len;
} else {
rcl->rcl_err = FMD_ADM_ERR_NOMEM;
break;
}
}
if (rcl->rcl_err == 0) {
bcopy(cip->ci_uuid, (char *)rcl->rcl_buf.rcl_buf_val +
rcl->rcl_len, uuid_len);
rcl->rcl_len += uuid_len;
rcl->rcl_cnt++;
}
(void) pthread_mutex_unlock(&cip->ci_lock);
}
bool_t
fmd_adm_caselist_1_svc(struct fmd_rpc_caselist *rvp, struct svc_req *req)
{
rvp->rcl_buf.rcl_buf_len = 0;
rvp->rcl_buf.rcl_buf_val = NULL;
rvp->rcl_len = 0;
rvp->rcl_cnt = 0;
rvp->rcl_err = 0;
if (fmd_rpc_deny(req))
rvp->rcl_err = FMD_ADM_ERR_PERM;
else
fmd_case_hash_apply(fmd.d_cases, fmd_adm_caselist_case, rvp);
return (TRUE);
}
bool_t
fmd_adm_caseinfo_1_svc(char *uuid, struct fmd_rpc_caseinfo *rvp,
struct svc_req *req)
{
fmd_case_t *cp;
nvlist_t *nvl;
int err = 0;
bzero(rvp, sizeof (struct fmd_rpc_caseinfo));
if (fmd_rpc_deny(req)) {
rvp->rci_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuid)) == NULL) {
rvp->rci_err = FMD_ADM_ERR_CASESRCH;
return (TRUE);
}
if (!(((fmd_case_impl_t *)cp)->ci_flags & FMD_CF_SOLVED)) {
fmd_case_rele(cp);
rvp->rci_err = FMD_ADM_ERR_CASESRCH;
return (TRUE);
}
nvl = fmd_case_mkevent(cp, FM_LIST_SUSPECT_CLASS);
err = nvlist_pack(nvl, &rvp->rci_evbuf.rci_evbuf_val,
&rvp->rci_evbuf.rci_evbuf_len, NV_ENCODE_XDR, 0);
nvlist_free(nvl);
if (err != 0)
rvp->rci_err = FMD_ADM_ERR_NOMEM;
fmd_case_rele(cp);
return (TRUE);
}
/*ARGSUSED*/
static void
fmd_adm_xprtlist_one(fmd_idspace_t *ids, id_t id, void *arg)
{
struct fmd_rpc_xprtlist *rvp = arg;
if (rvp->rxl_len < rvp->rxl_buf.rxl_buf_len)
rvp->rxl_buf.rxl_buf_val[rvp->rxl_len++] = id;
}
bool_t
fmd_adm_xprtlist_1_svc(struct fmd_rpc_xprtlist *rvp, struct svc_req *req)
{
if (fmd_rpc_deny(req)) {
rvp->rxl_buf.rxl_buf_len = 0;
rvp->rxl_buf.rxl_buf_val = NULL;
rvp->rxl_len = 0;
rvp->rxl_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
/*
* Since we're taking a snapshot of the transports, and these could
* change after we return our result, there's no need to hold any kind
* of lock between retrieving ids_count and taking the snapshot. We'll
* just capture up to a maximum of whatever ids_count value we sampled.
*/
rvp->rxl_buf.rxl_buf_len = fmd.d_xprt_ids->ids_count;
rvp->rxl_buf.rxl_buf_val = malloc(sizeof (int32_t) *
rvp->rxl_buf.rxl_buf_len);
rvp->rxl_len = 0;
rvp->rxl_err = 0;
if (rvp->rxl_buf.rxl_buf_val == NULL) {
rvp->rxl_err = FMD_ADM_ERR_NOMEM;
return (TRUE);
}
fmd_idspace_apply(fmd.d_xprt_ids, fmd_adm_xprtlist_one, rvp);
return (TRUE);
}
bool_t
fmd_adm_xprtstat_1_svc(int32_t id,
struct fmd_rpc_modstat *rms, struct svc_req *req)
{
fmd_xprt_impl_t *xip;
fmd_stat_t *sp, *ep, *cp;
if (fmd_rpc_deny(req)) {
rms->rms_buf.rms_buf_val = NULL;
rms->rms_buf.rms_buf_len = 0;
rms->rms_err = FMD_ADM_ERR_PERM;
return (TRUE);
}
rms->rms_buf.rms_buf_val = malloc(sizeof (fmd_xprt_stat_t));
rms->rms_buf.rms_buf_len = sizeof (fmd_xprt_stat_t) /
sizeof (fmd_stat_t);
rms->rms_err = 0;
if (rms->rms_buf.rms_buf_val == NULL) {
rms->rms_err = FMD_ADM_ERR_NOMEM;
rms->rms_buf.rms_buf_len = 0;
return (TRUE);
}
if ((xip = fmd_idspace_hold(fmd.d_xprt_ids, id)) == NULL) {
rms->rms_err = FMD_ADM_ERR_XPRTSRCH;
return (TRUE);
}
/*
* Grab the stats lock and bcopy the entire transport stats array in
* one shot. Then go back through and duplicate any string values.
*/
(void) pthread_mutex_lock(&xip->xi_stats_lock);
sp = (fmd_stat_t *)xip->xi_stats;
ep = sp + rms->rms_buf.rms_buf_len;
cp = rms->rms_buf.rms_buf_val;
bcopy(sp, cp, sizeof (fmd_xprt_stat_t));
for (; sp < ep; sp++, cp++) {
if (sp->fmds_type == FMD_TYPE_STRING &&
sp->fmds_value.str != NULL)
cp->fmds_value.str = strdup(sp->fmds_value.str);
}
(void) pthread_mutex_unlock(&xip->xi_stats_lock);
fmd_idspace_rele(fmd.d_xprt_ids, id);
return (TRUE);
}
int
fmd_adm_1_freeresult(SVCXPRT *xprt, xdrproc_t proc, caddr_t data)
{
xdr_free(proc, data);
svc_done(xprt);
return (TRUE);
}
/*
* Custom XDR routine for our API structure fmd_stat_t. This function must
* match the definition of fmd_stat_t in <fmd_api.h> and must also match
* the corresponding routine in usr/src/lib/fm/libfmd_adm/common/fmd_adm.c.
*/
bool_t
xdr_fmd_stat(XDR *xp, fmd_stat_t *sp)
{
bool_t rv = TRUE;
rv &= xdr_opaque(xp, sp->fmds_name, sizeof (sp->fmds_name));
rv &= xdr_u_int(xp, &sp->fmds_type);
rv &= xdr_opaque(xp, sp->fmds_desc, sizeof (sp->fmds_desc));
switch (sp->fmds_type) {
case FMD_TYPE_BOOL:
rv &= xdr_int(xp, &sp->fmds_value.bool);
break;
case FMD_TYPE_INT32:
rv &= xdr_int32_t(xp, &sp->fmds_value.i32);
break;
case FMD_TYPE_UINT32:
rv &= xdr_uint32_t(xp, &sp->fmds_value.ui32);
break;
case FMD_TYPE_INT64:
rv &= xdr_int64_t(xp, &sp->fmds_value.i64);
break;
case FMD_TYPE_UINT64:
case FMD_TYPE_TIME:
case FMD_TYPE_SIZE:
rv &= xdr_uint64_t(xp, &sp->fmds_value.ui64);
break;
case FMD_TYPE_STRING:
rv &= xdr_string(xp, &sp->fmds_value.str, ~0);
break;
}
return (rv);
}