#include <strings.h>
#include <sys/panic.h>
#include <alloca.h>
#include <zone.h>
#include "../../common/sw.h"
#include "panic.h"
#define MAX_STRING_LEN 160
static id_t myid;
static id_t mytimerid;
#define SWDE_PANIC_CASEDATA_VERS 1
typedef struct swde_panic_casedata {
uint32_t scd_vers;
uint64_t scd_receive_time;
size_t scd_nvlbufsz;
} swde_panic_casedata_t;
static struct {
fmd_stat_t swde_panic_diagnosed;
fmd_stat_t swde_panic_badclass;
fmd_stat_t swde_panic_noattr;
fmd_stat_t swde_panic_unexpected_fm_panic;
fmd_stat_t swde_panic_badattr;
fmd_stat_t swde_panic_badfmri;
fmd_stat_t swde_panic_noinstance;
fmd_stat_t swde_panic_nouuid;
fmd_stat_t swde_panic_dupuuid;
fmd_stat_t swde_panic_nocase;
fmd_stat_t swde_panic_notime;
fmd_stat_t swde_panic_nopanicstr;
fmd_stat_t swde_panic_nodumpdir;
fmd_stat_t swde_panic_nostack;
fmd_stat_t swde_panic_incomplete;
fmd_stat_t swde_panic_failed;
fmd_stat_t swde_panic_basecasedata;
fmd_stat_t swde_panic_failsrlz;
} swde_panic_stats = {
{ "swde_panic_diagnosed", FMD_TYPE_UINT64,
"panic defects published" },
{ "swde_panic_badclass", FMD_TYPE_UINT64,
"incorrect event class received" },
{ "swde_panic_noattr", FMD_TYPE_UINT64,
"malformed event - missing attr nvlist" },
{ "swde_panic_unexpected_fm_panic", FMD_TYPE_UINT64,
"dump available for an fm_panic()" },
{ "swde_panic_badattr", FMD_TYPE_UINT64,
"malformed event - invalid attr list" },
{ "swde_panic_badfmri", FMD_TYPE_UINT64,
"malformed event - fmri2str fails" },
{ "swde_panic_noinstance", FMD_TYPE_UINT64,
"malformed event - no instance number" },
{ "swde_panic_nouuid", FMD_TYPE_UINT64,
"malformed event - missing uuid" },
{ "swde_panic_dupuuid", FMD_TYPE_UINT64,
"duplicate events received" },
{ "swde_panic_nocase", FMD_TYPE_UINT64,
"case missing for uuid" },
{ "swde_panic_notime", FMD_TYPE_UINT64,
"missing crash dump time" },
{ "swde_panic_nopanicstr", FMD_TYPE_UINT64,
"missing panic string" },
{ "swde_panic_nodumpdir", FMD_TYPE_UINT64,
"missing crashdump save directory" },
{ "swde_panic_nostack", FMD_TYPE_UINT64,
"missing panic stack" },
{ "swde_panic_incomplete", FMD_TYPE_UINT64,
"missing panic incomplete" },
{ "swde_panic_failed", FMD_TYPE_UINT64,
"missing panic failed" },
{ "swde_panic_badcasedata", FMD_TYPE_UINT64,
"bad case data during timeout" },
{ "swde_panic_failsrlz", FMD_TYPE_UINT64,
"failures to serialize case data" },
};
#define BUMPSTAT(stat) swde_panic_stats.stat.fmds_value.ui64++
static nvlist_t *
panic_sw_fmri(fmd_hdl_t *hdl, char *object)
{
nvlist_t *fmri;
nvlist_t *sw_obj;
int err = 0;
fmri = fmd_nvl_alloc(hdl, FMD_SLEEP);
err |= nvlist_add_uint8(fmri, FM_VERSION, FM_SW_SCHEME_VERSION);
err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW);
sw_obj = fmd_nvl_alloc(hdl, FMD_SLEEP);
err |= nvlist_add_string(sw_obj, FM_FMRI_SW_OBJ_PATH, object);
err |= nvlist_add_nvlist(fmri, FM_FMRI_SW_OBJ, sw_obj);
nvlist_free(sw_obj);
if (!err)
return (fmri);
else
return (0);
}
static const char *dumpfiles[2] = { "unix.%lld", "vmcore.%lld" };
static const char *dumpfiles_comp[2] = { "vmdump.%lld", NULL};
static void
swde_panic_solve(fmd_hdl_t *hdl, fmd_case_t *cp,
nvlist_t *attr, fmd_event_t *ep, boolean_t savecore_success)
{
char *dumpdir, *path, *uuid;
nvlist_t *defect, *rsrc;
nvpair_t *nvp;
int i;
const char *toadd[] = {
"os-instance-uuid",
"panicstr",
"panicstack",
"crashtime",
"panic-time",
};
if (ep != NULL)
fmd_case_add_ereport(hdl, cp, ep);
(void) nvlist_lookup_string(attr, "dumpdir", &dumpdir);
(void) nvlist_lookup_string(attr, "os-instance-uuid", &uuid);
path = alloca(strlen(dumpdir) + 1 + 1 + 36 + 1);
(void) sprintf(path, "%s/.%s", dumpdir, uuid);
rsrc = panic_sw_fmri(hdl, path);
defect = fmd_nvl_create_defect(hdl, SW_SUNOS_PANIC_DEFECT,
100, rsrc, NULL, rsrc);
nvlist_free(rsrc);
(void) nvlist_add_boolean_value(defect, "savecore-succcess",
savecore_success);
if (savecore_success) {
boolean_t compressed;
int64_t instance;
const char **pathfmts;
char buf[2][32];
int files = 0;
char *arr[2];
int i;
(void) nvlist_lookup_int64(attr, "instance", &instance);
(void) nvlist_lookup_boolean_value(attr, "compressed",
&compressed);
pathfmts = compressed ? &dumpfiles_comp[0] : &dumpfiles[0];
for (i = 0; i < 2; i++) {
if (pathfmts[i] == NULL) {
arr[i] = NULL;
continue;
}
(void) snprintf(buf[i], 32, pathfmts[i], instance);
arr[i] = buf[i];
files++;
}
(void) nvlist_add_string(defect, "dump-dir", dumpdir);
(void) nvlist_add_string_array(defect, "dump-files", arr,
files);
} else {
char *rsn;
if (nvlist_lookup_string(attr, "failure-reason", &rsn) == 0)
(void) nvlist_add_string(defect, "failure-reason", rsn);
}
for (i = 0; i < sizeof (toadd) / sizeof (toadd[0]); i++) {
if (nvlist_lookup_nvpair(attr, toadd[i], &nvp) == 0)
(void) nvlist_add_nvpair(defect, nvp);
}
fmd_case_add_suspect(hdl, cp, defect);
fmd_case_solve(hdl, cp);
fmd_case_close(hdl, cp);
BUMPSTAT(swde_panic_diagnosed);
}
static void
swde_panic_timeout(fmd_hdl_t *hdl, id_t timerid, void *data)
{
fmd_case_t *cp = swde_case_first(hdl, myid);
swde_panic_casedata_t *cdp;
time_t now = time(NULL);
nvlist_t *attr;
int remain = 0;
uint32_t vers;
while (cp != NULL) {
cdp = swde_case_data(hdl, cp, &vers);
if (vers != SWDE_PANIC_CASEDATA_VERS)
fmd_hdl_abort(hdl, "case data version confused\n");
if (now > cdp->scd_receive_time + 30 * 60) {
if (nvlist_unpack((char *)cdp + sizeof (*cdp),
cdp->scd_nvlbufsz, &attr, 0) == 0) {
swde_panic_solve(hdl, cp, attr, NULL, B_FALSE);
nvlist_free(attr);
} else {
BUMPSTAT(swde_panic_basecasedata);
fmd_case_close(hdl, cp);
}
} else {
remain++;
}
cp = swde_case_next(hdl, cp);
}
if (remain) {
mytimerid = sw_timer_install(hdl, myid, NULL, NULL,
10ULL * NANOSEC * 60);
}
}
static int
swde_panic_vrfy(fmd_hdl_t *hdl, fmd_case_t *cp)
{
swde_panic_casedata_t *cdp;
time_t now = time(NULL);
nvlist_t *attr;
uint32_t vers;
cdp = swde_case_data(hdl, cp, &vers);
if (vers != SWDE_PANIC_CASEDATA_VERS)
return (0);
if (now > cdp->scd_receive_time + 30 * 60) {
if (nvlist_unpack((char *)cdp + sizeof (*cdp),
cdp->scd_nvlbufsz, &attr, 0) == 0) {
swde_panic_solve(hdl, cp, attr, NULL, B_FALSE);
nvlist_free(attr);
return (1);
} else {
return (0);
}
}
if (mytimerid != 0)
mytimerid = sw_timer_install(hdl, myid,
NULL, NULL, 10ULL * NANOSEC * 60);
return (1);
}
void
swde_panic_detected(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
const char *class, void *arg)
{
boolean_t fm_panic, expect_savecore;
swde_panic_casedata_t *cdp;
nvlist_t *attr;
fmd_case_t *cp;
char *fmribuf;
char *uuid;
size_t sz;
fmd_hdl_debug(hdl, "swde_panic_detected\n");
if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) {
BUMPSTAT(swde_panic_noattr);
return;
}
if (nvlist_lookup_string(attr, "os-instance-uuid", &uuid) != 0) {
BUMPSTAT(swde_panic_nouuid);
return;
}
fmd_hdl_debug(hdl, "swde_panic_detected: OS instance %s\n", uuid);
if (nvlist_lookup_boolean_value(attr, "fm-panic", &fm_panic) != 0 ||
fm_panic == B_TRUE) {
BUMPSTAT(swde_panic_unexpected_fm_panic);
return;
}
if (nvlist_size(attr, &sz, NV_ENCODE_NATIVE) != 0) {
BUMPSTAT(swde_panic_failsrlz);
return;
}
cdp = fmd_hdl_zalloc(hdl, sizeof (*cdp) + sz, FMD_SLEEP);
fmribuf = (char *)cdp + sizeof (*cdp);
cdp->scd_vers = SWDE_PANIC_CASEDATA_VERS;
cdp->scd_receive_time = time(NULL);
cdp->scd_nvlbufsz = sz;
if ((cp = swde_case_open(hdl, myid, uuid, SWDE_PANIC_CASEDATA_VERS,
cdp, sizeof (*cdp) + sz)) == NULL) {
BUMPSTAT(swde_panic_dupuuid);
fmd_hdl_debug(hdl, "swde_case_open returned NULL - dup?\n");
fmd_hdl_free(hdl, cdp, sizeof (*cdp) + sz);
return;
}
fmd_case_setprincipal(hdl, cp, ep);
if (nvlist_lookup_boolean_value(attr, "will-attempt-savecore",
&expect_savecore) != 0 || expect_savecore == B_FALSE) {
fmd_hdl_debug(hdl, "savecore not being attempted - "
"solve now\n");
swde_panic_solve(hdl, cp, attr, ep, B_FALSE);
return;
}
fmd_case_add_ereport(hdl, cp, ep);
(void) nvlist_pack(attr, &fmribuf, &sz, NV_ENCODE_NATIVE, 0);
swde_case_data_write(hdl, cp);
if (mytimerid == 0) {
mytimerid = sw_timer_install(hdl, myid, NULL, ep,
10ULL * NANOSEC * 60);
fmd_hdl_debug(hdl, "armed timer\n");
} else {
fmd_hdl_debug(hdl, "timer already armed\n");
}
}
void
swde_panic_savecore_done(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
const char *class, void *arg)
{
boolean_t savecore_success = (arg != NULL);
boolean_t fm_panic;
nvlist_t *attr;
fmd_case_t *cp;
char *uuid;
fmd_hdl_debug(hdl, "savecore_done (%s)\n", savecore_success ?
"success" : "fail");
if (nvlist_lookup_nvlist(nvl, FM_IREPORT_ATTRIBUTES, &attr) != 0) {
BUMPSTAT(swde_panic_noattr);
return;
}
if (nvlist_lookup_boolean_value(attr, "fm-panic", &fm_panic) != 0 ||
fm_panic == B_TRUE) {
return;
}
if (nvlist_lookup_string(attr, "os-instance-uuid", &uuid) != 0) {
BUMPSTAT(swde_panic_nouuid);
return;
}
cp = fmd_case_uulookup(hdl, uuid);
if (!cp) {
fmd_hdl_debug(hdl, "savecore_done: can't find case for "
"image %s\n", uuid);
BUMPSTAT(swde_panic_nocase);
return;
}
fmd_hdl_debug(hdl, "savecore_done: solving case %s\n", uuid);
swde_panic_solve(hdl, cp, attr, ep, savecore_success);
}
const struct sw_disp swde_panic_disp[] = {
{ SW_SUNOS_PANIC_DETECTED, swde_panic_detected, NULL },
{ SW_SUNOS_PANIC_AVAIL, swde_panic_savecore_done, (void *)1 },
{ SW_SUNOS_PANIC_FAILURE, swde_panic_savecore_done, NULL },
{ SW_SUNOS_PANIC_DEFECT, NULL, NULL },
{ NULL, NULL, NULL }
};
int
swde_panic_init(fmd_hdl_t *hdl, id_t id, const struct sw_disp **dpp,
int *nelemp)
{
myid = id;
if (getzoneid() != GLOBAL_ZONEID)
return (SW_SUB_INIT_FAIL_VOLUNTARY);
(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
sizeof (swde_panic_stats) / sizeof (fmd_stat_t),
(fmd_stat_t *)&swde_panic_stats);
fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_DETECTED);
fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_FAILURE);
fmd_hdl_subscribe(hdl, SW_SUNOS_PANIC_AVAIL);
*dpp = &swde_panic_disp[0];
*nelemp = sizeof (swde_panic_disp) / sizeof (swde_panic_disp[0]);
return (SW_SUB_INIT_SUCCESS);
}
void
swde_panic_fini(fmd_hdl_t *hdl)
{
if (mytimerid)
sw_timer_remove(hdl, myid, mytimerid);
}
const struct sw_subinfo panic_diag_info = {
"panic diagnosis",
SW_CASE_PANIC,
swde_panic_init,
swde_panic_fini,
swde_panic_timeout,
NULL,
swde_panic_vrfy,
};