#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <libnvpair.h>
#include <fm/fmd_api.h>
#include <fm/fmd_fmri.h>
#include <sys/fm/protocol.h>
#include <sys/fm/io/disk.h>
#include <fm/libtopo.h>
#include "disk_monitor.h"
#include "hotplug_mgr.h"
#include "schg_mgr.h"
#include "topo_gather.h"
#include "dm_platform.h"
#define THIS_FMD_MODULE_NAME "disk-monitor"
static enum disk_init_state {
INIT_STATE_NONE = 0,
STATE_CHANGE_MGR_INITTED = 2,
HOTPLUG_MGR_INITTED = 4
} g_init_state = INIT_STATE_NONE;
typedef enum {
LT_SUSPECT,
LT_REPAIRED
} fm_list_type_t;
log_class_t g_verbose = 0;
cfgdata_t *config_data = NULL;
fmd_hdl_t *g_fm_hdl = NULL;
static const fmd_prop_t fmd_props[];
static void
diskmon_teardown_all(void)
{
cleanup_hotplug_manager();
cleanup_state_change_manager(config_data);
config_fini();
}
static int
count_disks(diskmon_t *disklistp)
{
int i = 0;
while (disklistp != NULL) {
i++;
disklistp = disklistp->next;
}
return (i);
}
static int
diskmon_init(void)
{
block_state_change_events();
if (dm_platform_init() != 0)
goto cleanup;
if (init_hotplug_manager() != 0)
goto cleanup;
else
g_init_state |= HOTPLUG_MGR_INITTED;
if (init_state_change_manager(config_data) != 0)
goto cleanup;
else
g_init_state |= STATE_CHANGE_MGR_INITTED;
return (E_SUCCESS);
cleanup:
unblock_state_change_events();
if (g_init_state & HOTPLUG_MGR_INITTED)
cleanup_hotplug_manager();
if (g_init_state & STATE_CHANGE_MGR_INITTED)
cleanup_state_change_manager(config_data);
dm_platform_fini();
return (E_ERROR);
}
static void
dm_fault_execute_actions(fmd_hdl_t *hdl, diskmon_t *diskp, nvlist_t *nvl)
{
const char *action_prop = NULL;
const char *action_string;
if (fmd_nvl_class_match(hdl, nvl,
DISK_ERROR_CLASS "." FM_FAULT_DISK_OVERTEMP))
action_prop = DISK_PROP_OTEMPACTION;
if (fmd_nvl_class_match(hdl, nvl,
DISK_ERROR_CLASS "." FM_FAULT_DISK_TESTFAIL))
action_prop = DISK_PROP_STFAILACTION;
if (fmd_nvl_class_match(hdl, nvl,
DISK_ERROR_CLASS "." FM_FAULT_SSM_WEAROUT))
action_prop = DISK_PROP_SSMWEAROUTACTION;
dm_fault_indicator_set(diskp, INDICATOR_ON);
if (action_prop != NULL &&
(action_string = dm_prop_lookup(diskp->props, action_prop))
!= NULL) {
if (dm_platform_indicator_execute(action_string) != 0) {
log_warn("Fault action `%s' did not successfully "
"complete.\n", action_string);
}
}
}
static void
diskmon_agent_repair(fmd_hdl_t *hdl, nvlist_t *nvl, int repair)
{
char *uuid = NULL;
nvlist_t **nva;
uint_t nvc;
diskmon_t *diskp;
nvlist_t *fmri;
nvlist_t *fltnvl;
int err = 0;
err |= nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid);
err |= nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,
&nva, &nvc);
if (err != 0)
return;
while (nvc-- != 0) {
fltnvl = *nva++;
if (nvlist_lookup_nvlist(fltnvl, FM_FAULT_RESOURCE, &fmri)
!= 0)
continue;
if ((diskp = dm_fmri_to_diskmon(hdl, fmri)) == NULL)
continue;
log_msg(MM_MAIN, "Disk %s repaired!\n",
diskp->location);
dm_fault_indicator_set(diskp, INDICATOR_OFF);
dm_state_change(diskp, HPS_REPAIRED);
}
if (repair)
fmd_case_uuresolved(hdl, uuid);
}
static void
diskmon_agent_suspect(fmd_hdl_t *hdl, nvlist_t *nvl)
{
char *uuid = NULL;
nvlist_t **nva;
uint_t nvc;
diskmon_t *diskp;
nvlist_t *fmri;
nvlist_t *fltnvl;
int err = 0;
err |= nvlist_lookup_string(nvl, FM_SUSPECT_UUID, &uuid);
err |= nvlist_lookup_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,
&nva, &nvc);
if (err != 0)
return;
while (nvc-- != 0 && !fmd_case_uuclosed(hdl, uuid)) {
fltnvl = *nva++;
if (nvlist_lookup_nvlist(fltnvl, FM_FAULT_RESOURCE, &fmri) != 0)
continue;
if ((diskp = dm_fmri_to_diskmon(hdl, fmri)) == NULL)
continue;
dm_fault_execute_actions(hdl, diskp, fltnvl);
dm_state_change(diskp, HPS_FAULTED);
}
if (!fmd_case_uuclosed(hdl, uuid)) {
fmd_case_uuclose(hdl, uuid);
}
}
static void
diskmon_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
{
diskmon_t *diskp;
nvlist_t *fmri;
if (g_verbose & MM_MAIN)
nvlist_print(stderr, nvl);
if (fmd_nvl_class_match(hdl, nvl, FM_LIST_REPAIRED_CLASS)) {
diskmon_agent_repair(hdl, nvl, 1);
return;
} else if (fmd_nvl_class_match(hdl, nvl, FM_LIST_UPDATED_CLASS)) {
diskmon_agent_repair(hdl, nvl, 0);
return;
} else if (fmd_nvl_class_match(hdl, nvl, FM_LIST_SUSPECT_CLASS)) {
diskmon_agent_suspect(hdl, nvl);
return;
} else if (fmd_nvl_class_match(hdl, nvl, FM_LIST_RESOLVED_CLASS)) {
return;
}
if (fmd_nvl_class_match(hdl, nvl, DISK_ERROR_CLASS ".*")) {
if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE,
&fmri) != 0)
return;
if ((diskp = dm_fmri_to_diskmon(hdl, fmri)) == NULL)
return;
dm_fault_execute_actions(hdl, diskp, nvl);
dm_state_change(diskp, HPS_FAULTED);
return;
}
}
static const fmd_hdl_ops_t fmd_ops = {
diskmon_recv,
NULL,
NULL,
NULL,
NULL,
};
static const fmd_prop_t fmd_props[] = {
{ GLOBAL_PROP_LOG_LEVEL, FMD_TYPE_UINT32, "0" },
{ NULL, 0, NULL }
};
static const fmd_hdl_info_t fmd_info = {
"Disk Monitor",
DISK_MONITOR_MODULE_VERSION,
&fmd_ops,
fmd_props
};
void
_fmd_init(fmd_hdl_t *hdl)
{
fmd_case_t *cp;
int disk_count;
g_fm_hdl = hdl;
if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) {
return;
}
if (config_init()) {
log_err("Could not initialize configuration!\n");
fmd_hdl_unregister(hdl);
return;
}
if (config_get(hdl, fmd_props)) {
config_fini();
log_err("Could not retrieve configuration from libtopo!\n");
fmd_hdl_unregister(hdl);
return;
}
if ((disk_count = count_disks(config_data->disk_list)) == 0) {
config_fini();
fmd_hdl_unregister(hdl);
return;
}
if (diskmon_init() == E_ERROR) {
config_fini();
fmd_hdl_unregister(hdl);
return;
}
log_msg(MM_MAIN, "Monitoring %d disks.\n", disk_count);
for (cp = fmd_case_next(hdl, NULL);
cp != NULL; cp = fmd_case_next(hdl, cp)) {
if (!fmd_case_solved(hdl, cp))
fmd_case_solve(hdl, cp);
}
}
void
_fmd_fini(fmd_hdl_t *hdl)
{
diskmon_teardown_all();
g_fm_hdl = NULL;
}