#include <ctype.h>
#include <fm/fmd_api.h>
#include <fm/libtopo.h>
#include <fm/topo_hc.h>
#include <fm/topo_mod.h>
#include <limits.h>
#include <string.h>
#include <sys/fm/io/scsi.h>
#include <sys/fm/protocol.h>
#include <stdio.h>
#include <time.h>
#include <fm/libseslog.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
static const fmd_prop_t fmd_props[] = {
{ "interval", FMD_TYPE_TIME, "60s"},
{ "severity", FMD_TYPE_INT32, "-1"},
{ "path", FMD_TYPE_STRING, "/var/fm/fmd/ses_logs/"},
{ "logcount", FMD_TYPE_UINT32, "5"},
{ "maxlogsize", FMD_TYPE_UINT32, "1000000"},
{ NULL, 0, NULL}
};
static struct slt_stat
{
fmd_stat_t dropped;
} slt_stats = {
{ "dropped", FMD_TYPE_UINT64, "number of dropped ereports"}
};
typedef struct ses_log_monitor
{
fmd_hdl_t *slt_hdl;
fmd_xprt_t *slt_xprt;
id_t slt_timer;
hrtime_t slt_interval;
int32_t slt_severity;
char *slt_path;
int32_t slt_log_count;
int32_t slt_max_log_size;
nvlist_t *slt_expanders;
} ses_log_monitor_t;
typedef struct expander
{
char slt_label[MAXNAMELEN];
char slt_pid[MAXNAMELEN];
char slt_key[MAXNAMELEN];
char slt_path[MAXPATHLEN];
nvlist_t *fmri;
} expander_t;
#define DATA_FIELD "data"
#define DEFAULT_DATA "0"
#define MIN_LOG_SIZE 100000
#define MIN_LOG_COUNT 1
#define EXAMINE_FMRI_VALUE 0
#define INVERT_FMRI_INSTANCE 1
#define FATAL_ERROR "fatal"
#define NON_FATAL_ERROR "non-fatal"
#define INVALID_OPERATION 0x01
#define NULL_LOG_DATA 0x02
#define INVALID_SEVERITY 0x03
#define DATE_STRING_SIZE 16
static int invert_fmri(ses_log_monitor_t *, nvlist_t *);
typedef struct code_operation {
int code;
int (*func_ptr)(ses_log_monitor_t *, nvlist_t *);
} code_operation_t;
typedef struct platform {
const char *pid;
int count;
code_operation_t *codes;
} platform_t;
typedef struct platforms {
int pcount;
platform_t *plist;
} platforms_t;
static code_operation_t genesis_codes[] = {
{ 684002, invert_fmri },
{ 685002, invert_fmri }
};
static platform_t platform_list[] = {
{ "SUN-GENESIS",
sizeof (genesis_codes) / sizeof (code_operation_t),
genesis_codes }
};
static const platforms_t platforms = {
sizeof (platform_list) / sizeof (platform_t),
platform_list
};
static void
slt_post_ereport(fmd_hdl_t *hdl, fmd_xprt_t *xprt, const char *ereport_class,
uint64_t ena, nvlist_t *detector, nvlist_t *payload)
{
nvlist_t *nvl;
int e = 0;
char fullclass[PATH_MAX];
(void) snprintf(fullclass, sizeof (fullclass), "%s.io.sas.log.%s",
FM_EREPORT_CLASS, ereport_class);
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) == 0) {
e |= nvlist_add_string(nvl, FM_CLASS, fullclass);
e |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION);
e |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena);
e |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, detector);
e |= nvlist_merge(nvl, payload, 0);
if (e == 0) {
fmd_xprt_post(hdl, xprt, nvl, 0);
} else {
nvlist_free(nvl);
fmd_hdl_debug(hdl, "Error adding fields to ereport");
slt_stats.dropped.fmds_value.ui64++;
}
} else {
fmd_hdl_debug(hdl, "Could not allocate space for ereport");
slt_stats.dropped.fmds_value.ui64++;
}
}
static int
do_mkdir(const char *path, mode_t mode)
{
struct stat st;
int status = 0;
if (stat(path, &st) != 0) {
if (mkdir(path, mode) != 0)
status = -1;
} else if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
status = -1;
}
return (status);
}
static int
mkpath(char *path, mode_t mode)
{
char *pp;
char *sp;
int status = 0;
pp = path;
while (status == 0 && (sp = strchr(pp, '/')) != 0) {
if (sp != pp) {
*sp = '\0';
status = do_mkdir(path, mode);
*sp = '/';
}
pp = sp + 1;
}
return (status);
}
void
check_file_size(ses_log_monitor_t *slmp, char *file, int byte_count)
{
int i;
char newFile[MAXPATHLEN];
char oldName[MAXPATHLEN];
struct stat st;
int size;
stat(file, &st);
size = st.st_size;
if (size + byte_count < slmp->slt_max_log_size) {
return;
}
for (i = slmp->slt_log_count; i > 1; i--) {
(void) snprintf(newFile, MAXPATHLEN, "%s.%x", file, i);
(void) snprintf(oldName, MAXPATHLEN, "%s.%x", file, i - 1);
(void) rename(oldName, newFile);
}
(void) rename(file, oldName);
}
static char *
access_fmri(ses_log_monitor_t *slmp, nvlist_t *fmri, char *target,
int operation, int *err)
{
int i;
nvpair_t *nvp;
nvpair_t *nvp2;
uint_t nelem;
nvlist_t **nvl_array;
char *name;
int ival;
char ivs[25];
char *target_val = NULL;
if ((*err = nvlist_lookup_nvpair(fmri, "hc-list", &nvp)) != 0) {
fmd_hdl_debug(slmp->slt_hdl, "No hc-list in the fmri");
return (NULL);
}
(void) nvpair_value_nvlist_array(nvp, &nvl_array, &nelem);
for (i = 0; i < nelem; i++) {
if ((nvlist_lookup_nvpair(nvl_array[i], "hc-name", &nvp2))
!= 0) {
continue;
}
if (nvpair_value_string(nvp2, &name) != 0) {
continue;
}
if (strcmp(name, target) != 0) {
continue;
}
if ((*err = nvlist_lookup_nvpair(nvl_array[i], "hc-id", &nvp2))
!= 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Could not find hc-id in the fmri for %s", target);
return (NULL);
}
if ((*err = nvpair_value_string(nvp2, &target_val)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Target value not returned.");
return (NULL);
}
switch (operation) {
case INVERT_FMRI_INSTANCE:
ival = atoi(target_val);
ival = (ival + 1) % 2;
(void) snprintf(ivs, sizeof (ivs), "%d", ival);
if ((*err = nvlist_remove_nvpair(nvl_array[i], nvp2))
== 0) {
if ((*err = nvlist_add_string(nvl_array[i],
"hc-id", ivs)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error setting ivalue.");
}
} else {
fmd_hdl_debug(slmp->slt_hdl,
"Error removing original ivalue.");
}
break;
case EXAMINE_FMRI_VALUE:
break;
default:
*err = INVALID_OPERATION;
break;
}
break;
}
return (target_val);
}
static int
create_filename(char *fileName, expander_t *expander, ses_log_monitor_t *slmp,
int byte_count)
{
char *ses_node;
int i;
int label_length;
int status = 0;
char *subchassis_val = NULL;
(void) snprintf(fileName, MAXPATHLEN, "%s", slmp->slt_path);
ses_node = strrchr(fileName, '/');
if ((ses_node != NULL) && (ses_node[0] != '\0')) {
(void) strlcat(fileName, "/", MAXPATHLEN);
}
ses_node = strrchr(expander->slt_path, '/');
(void) strlcat(fileName, ses_node + 1, MAXPATHLEN);
subchassis_val = access_fmri(slmp, expander->fmri, SUBCHASSIS,
EXAMINE_FMRI_VALUE, &status);
if (subchassis_val != NULL) {
(void) strlcat(fileName, "_", MAXPATHLEN);
(void) strlcat(fileName, SUBCHASSIS, MAXPATHLEN);
(void) strlcat(fileName, subchassis_val, MAXPATHLEN);
}
(void) strlcat(fileName, "_", MAXPATHLEN);
label_length = strlen(expander->slt_label);
for (i = 0; i < label_length; i++) {
if ((!isspace(expander->slt_label[i])) &&
('/' != expander->slt_label[i])) {
(void) strncat(fileName, &expander->slt_label[i], 1);
}
}
(void) strlcat(fileName, "/log", MAXPATHLEN);
status = mkpath(fileName, 0744);
check_file_size(slmp, fileName, byte_count);
return (status);
}
static char *
error_type(int severity)
{
char *rval;
switch (severity) {
case SES_LOG_LEVEL_FATAL:
rval = FATAL_ERROR;
break;
case SES_LOG_LEVEL_ERROR:
rval = NON_FATAL_ERROR;
break;
default:
rval = NULL;
break;
}
return (rval);
}
static int
add_expander_record(ses_log_monitor_t *slmp, char *key)
{
nvlist_t *expanderDetails;
int status = 0;
if ((status = nvlist_alloc(&expanderDetails, NV_UNIQUE_NAME, 0)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error allocating expander detail space (%d)", status);
return (status);
}
if ((status = nvlist_add_string(expanderDetails, DATA_FIELD,
DEFAULT_DATA)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error adding default data to expander details (%d)",
status);
} else {
if ((status = nvlist_add_nvlist(slmp->slt_expanders, key,
expanderDetails)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error storing the default expander details (%d)",
status);
}
}
nvlist_free(expanderDetails);
return (status);
}
static int
get_last_entry(ses_log_monitor_t *slmp, char *key, char **expdata)
{
nvlist_t *expanderRecord;
int err = 0;
if ((err = nvlist_lookup_nvlist(slmp->slt_expanders, key,
&expanderRecord)) != 0) {
if ((err = add_expander_record(slmp, key)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Expander add failed for %s", key);
return (err);
}
if ((err = nvlist_lookup_nvlist(slmp->slt_expanders, key,
&expanderRecord)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Could not retrieve the data after adding it", key);
return (err);
}
}
if ((err = nvlist_lookup_string(expanderRecord, DATA_FIELD, expdata))
!= 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Could not retrieve the expander data field (%d)", err);
return (err);
}
return (err);
}
static int
check_code(ses_log_monitor_t *slmp, nvlist_t *fmri, char *pid, int code)
{
int status = 0;
int i, x;
for (i = 0; i < platforms.pcount; i++) {
if (strcmp(platforms.plist[i].pid, pid) == 0) {
for (x = 0; x < platforms.plist[i].count; x++) {
if (code == platforms.plist[i].codes[x].code) {
status = platforms.plist[i].codes[x].
func_ptr(slmp, fmri);
break;
}
}
break;
}
}
if (status != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error checking for a code action (%d)", status);
}
return (status);
}
static int
platform_supported(char *pid)
{
int supported = 0;
int i;
for (i = 0; i < platforms.pcount; i++) {
if (strcmp(platforms.plist[i].pid, pid) == 0) {
supported = 1;
break;
}
}
return (supported);
}
static int
invert_fmri(ses_log_monitor_t *slmp, nvlist_t *fmri)
{
int err = 0;
(void) access_fmri(slmp, fmri, CONTROLLER, INVERT_FMRI_INSTANCE, &err);
if (err != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"error inverting the controller instance: %d", err);
return (err);
}
(void) access_fmri(slmp, fmri, SASEXPANDER, INVERT_FMRI_INSTANCE, &err);
if (err != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"error inverting sas-expander instance: %d", err);
}
return (err);
}
static int
handle_log_entry(ses_log_monitor_t *slmp, nvpair_t *entry,
expander_t *expander, char *format_time, FILE *fp)
{
nvlist_t *entry_data;
char *log_entry;
char *severity;
int severityValue = 0;
char *code;
char *class_sev = NULL;
uint64_t ena;
int rval = 0;
if ((rval = nvpair_value_nvlist(entry, &entry_data)) != 0) {
fmd_hdl_debug(slmp->slt_hdl, "Unable to retrieve entry");
return (rval);
}
if ((rval = nvlist_lookup_string(entry_data, ENTRY_SEVERITY, &severity))
== 0) {
severityValue = atoi(severity);
if (severityValue >= slmp->slt_severity) {
if ((rval = nvlist_lookup_string(entry_data, ENTRY_CODE,
&code)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error retrieving code: %d", rval);
return (rval);
}
(void) check_code(slmp, expander->fmri,
expander->slt_pid, atoi(code));
class_sev = error_type(severityValue);
if (class_sev == NULL) {
fmd_hdl_debug(slmp->slt_hdl,
"log severity %d mapped to NULL", severity);
return (INVALID_SEVERITY);
}
ena = fmd_event_ena_create(slmp->slt_hdl);
slt_post_ereport(slmp->slt_hdl, slmp->slt_xprt,
class_sev, ena, expander->fmri, entry_data);
}
} else {
fmd_hdl_debug(slmp->slt_hdl,
"Unable to pull severity from the entry.");
return (rval);
}
if (fp) {
if ((rval = nvlist_lookup_string(entry_data, ENTRY_LOG,
&log_entry)) == 0) {
(void) fprintf(fp, "%s %s\n", format_time,
log_entry);
} else {
fmd_hdl_debug(slmp->slt_hdl,
"Unable to pull log from the entry.");
}
}
return (rval);
}
static void
free_expander(ses_log_monitor_t *slmp, expander_t *exp)
{
if (exp != NULL) {
if (exp->fmri != NULL) {
nvlist_free(exp->fmri);
}
fmd_hdl_free(slmp->slt_hdl, exp, sizeof (expander_t));
}
}
static int
get_log(ses_log_monitor_t *slmp, expander_t *expander,
struct ses_log_call_struct *lib_param)
{
char *expdata;
int err;
nvlist_t *expanderRecord;
if ((err = get_last_entry(slmp, expander->slt_key, &expdata)) != 0) {
fmd_hdl_debug(slmp->slt_hdl, "Error collecting expander entry");
return (err);
}
(void) strncpy(lib_param->target_path, expander->slt_path, MAXPATHLEN);
(void) strncpy(lib_param->product_id, expander->slt_pid, MAXNAMELEN);
(void) strncpy(lib_param->last_log_entry, expdata, MAXNAMELEN);
lib_param->poll_time = slmp->slt_interval;
if ((err = access_ses_log(lib_param)) != 0) {
fmd_hdl_debug(slmp->slt_hdl, "Library access error: %d", err);
}
if (lib_param->log_data == NULL) {
if (err != 0) {
return (err);
}
return (NULL_LOG_DATA);
}
if ((err = nvlist_lookup_nvlist(slmp->slt_expanders, expander->slt_key,
&expanderRecord)) == 0) {
if (nvlist_add_string(expanderRecord, DATA_FIELD,
lib_param->last_log_entry) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error saving buffer data in expander details");
}
} else {
fmd_hdl_debug(slmp->slt_hdl,
"Could not retrieve expander to store last entry: %d", err);
}
return (err);
}
static int
process_log(ses_log_monitor_t *slmp, expander_t *expander,
struct ses_log_call_struct *lib_param)
{
nvlist_t *result;
int err;
char *pairName;
nvpair_t *entry = NULL;
FILE *fp = NULL;
char fileName[MAXPATHLEN];
time_t now;
char format_time[30];
struct tm tim;
int output_count;
output_count = lib_param->number_log_entries * DATE_STRING_SIZE +
lib_param->size_of_log_entries;
err = create_filename(fileName, expander, slmp, output_count);
if (err == 0) {
fp = fopen(fileName, "a");
if (fp == NULL) {
fmd_hdl_debug(slmp->slt_hdl, "File open failed");
}
}
now = time(NULL);
tim = *(localtime(&now));
(void) strftime(format_time, 30, "%b %d %H:%M:%S ", &tim);
result = lib_param->log_data;
while ((entry = nvlist_next_nvpair(result, entry)) != NULL) {
pairName = nvpair_name(entry);
if (strncmp(ENTRY_PREFIX, pairName, 5) == 0) {
err = handle_log_entry(slmp, entry, expander,
format_time, fp);
}
}
if (fp) {
(void) fclose(fp);
fp = NULL;
}
nvlist_free(result);
return (0);
}
static int
slt_process_ses_log(topo_hdl_t *thp, tnode_t *node, void *arg)
{
ses_log_monitor_t *slmp = arg;
nvlist_t *fmri;
expander_t *expander;
struct ses_log_call_struct lib_param;
int err = 0;
char *label = NULL;
char *target_path = NULL;
char *product_id = NULL;
char *sas_address = NULL;
if (strcmp(SASEXPANDER, topo_node_name(node)) != 0) {
return (TOPO_WALK_NEXT);
}
if (topo_prop_get_string(node, "authority", "product-id",
&product_id, &err) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error collecting product_id %d", err);
return (TOPO_WALK_NEXT);
}
if (platform_supported(product_id) == 0) {
fmd_hdl_debug(slmp->slt_hdl, "Unsupported platform %d",
product_id);
topo_hdl_strfree(thp, product_id);
return (TOPO_WALK_NEXT);
}
expander = (expander_t *)fmd_hdl_zalloc(slmp->slt_hdl,
sizeof (expander_t), FMD_SLEEP);
(void) snprintf(expander->slt_pid, MAXNAMELEN, "%s", product_id);
topo_hdl_strfree(thp, product_id);
if (topo_prop_get_string(node, "protocol", "label", &label, &err)
!= 0) {
fmd_hdl_debug(slmp->slt_hdl, "Error collecting label %d", err);
free_expander(slmp, expander);
return (TOPO_WALK_NEXT);
}
(void) snprintf(expander->slt_label, MAXNAMELEN, "%s", label);
topo_hdl_strfree(thp, label);
if (topo_prop_get_string(node, TOPO_PGROUP_SES,
TOPO_PROP_SES_DEV_PATH, &target_path, &err) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error collecting ses-devfs-path for %s: %d",
expander->slt_label, err);
free_expander(slmp, expander);
return (TOPO_WALK_NEXT);
}
(void) snprintf(expander->slt_path, MAXPATHLEN, "%s", target_path);
topo_hdl_strfree(thp, target_path);
if (topo_prop_get_string(node, TOPO_PGROUP_STORAGE,
TOPO_PROP_SAS_ADDR, &sas_address, &err) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error collecting sas_address for %s: %d",
expander->slt_label, err);
free_expander(slmp, expander);
return (TOPO_WALK_NEXT);
}
if (strlen(sas_address) != 16) {
fmd_hdl_debug(slmp->slt_hdl,
"sas-address length is not 16: (%s)", sas_address);
free_expander(slmp, expander);
topo_hdl_strfree(thp, sas_address);
return (TOPO_WALK_NEXT);
}
(void) snprintf(expander->slt_key, MAXNAMELEN, "%s", sas_address);
topo_hdl_strfree(thp, sas_address);
if (topo_node_resource(node, &fmri, &err) != 0) {
fmd_hdl_debug(slmp->slt_hdl, "failed to get fmri for %s: %s",
expander->slt_label, topo_strerror(err));
free_expander(slmp, expander);
return (TOPO_WALK_NEXT);
} else {
expander->fmri = fmri;
}
if ((err = get_log(slmp, expander, &lib_param)) != 0) {
if (err != NULL_LOG_DATA) {
fmd_hdl_debug(slmp->slt_hdl,
"Error retrieving logs from %s: %d",
expander->slt_label, err);
}
free_expander(slmp, expander);
return (TOPO_WALK_NEXT);
}
if ((err = process_log(slmp, expander, &lib_param)) != 0) {
fmd_hdl_debug(slmp->slt_hdl,
"Error processing logs from %s: %d",
expander->slt_label, err);
}
free_expander(slmp, expander);
return (TOPO_WALK_NEXT);
}
static void
slt_timeout(fmd_hdl_t *hdl, id_t id, void *data)
{
topo_hdl_t *thp;
topo_walk_t *twp;
int err;
ses_log_monitor_t *slmp = fmd_hdl_getspecific(hdl);
if (slmp == NULL) {
fmd_hdl_abort(hdl, "Unable to retrieve log monitor structure.");
return;
}
slmp->slt_hdl = hdl;
thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, slt_process_ses_log,
slmp, &err)) == NULL) {
fmd_hdl_topo_rele(hdl, thp);
fmd_hdl_abort(hdl, "failed to get topology: %s\n",
topo_strerror(err));
return;
}
if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) {
topo_walk_fini(twp);
fmd_hdl_topo_rele(hdl, thp);
fmd_hdl_abort(hdl, "failed to walk topology\n");
return;
}
topo_walk_fini(twp);
fmd_hdl_topo_rele(hdl, thp);
slmp->slt_timer = fmd_timer_install(hdl, NULL, NULL,
slmp->slt_interval);
}
static const fmd_hdl_ops_t fmd_ops = {
NULL,
slt_timeout,
NULL,
NULL,
NULL,
NULL,
NULL,
};
static const fmd_hdl_info_t fmd_info = {
"SES Log Transport Agent", "1.0", &fmd_ops, fmd_props
};
void
_fmd_init(fmd_hdl_t *hdl)
{
ses_log_monitor_t *slmp;
int error;
nvlist_t *expanderList;
if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
return;
(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
sizeof (slt_stats) / sizeof (fmd_stat_t),
(fmd_stat_t *)&slt_stats);
slmp = fmd_hdl_zalloc(hdl, sizeof (ses_log_monitor_t), FMD_SLEEP);
fmd_hdl_setspecific(hdl, slmp);
slmp->slt_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL);
if (slmp->slt_xprt == NULL) {
fmd_hdl_error(hdl,
"Unable to obtain a reference to the transport");
fmd_hdl_free(hdl, slmp, sizeof (*slmp));
fmd_hdl_unregister(hdl);
return;
}
slmp->slt_interval = fmd_prop_get_int64(hdl, "interval");
slmp->slt_severity = fmd_prop_get_int32(hdl, "severity");
if (slmp->slt_severity < SES_LOG_LEVEL_NOTICE) {
slmp->slt_severity = SES_LOG_LEVEL_ERROR;
}
slmp->slt_log_count = fmd_prop_get_int32(hdl, "logcount");
if (slmp->slt_log_count < MIN_LOG_COUNT) {
slmp->slt_log_count = MIN_LOG_COUNT;
}
slmp->slt_max_log_size = fmd_prop_get_int32(hdl, "maxlogsize");
if (slmp->slt_max_log_size < MIN_LOG_SIZE) {
slmp->slt_max_log_size = MIN_LOG_SIZE;
}
slmp->slt_path = fmd_prop_get_string(hdl, "path");
if ((error = nvlist_alloc(&expanderList, NV_UNIQUE_NAME, 0)) != 0) {
fmd_xprt_close(hdl, slmp->slt_xprt);
fmd_hdl_strfree(hdl, slmp->slt_path);
fmd_hdl_free(hdl, slmp, sizeof (*slmp));
fmd_hdl_error(hdl,
"Error allocating space for the expander list: %d", error);
fmd_hdl_unregister(hdl);
return;
}
slmp->slt_expanders = expanderList;
slmp->slt_timer = fmd_timer_install(hdl, NULL, NULL, 0);
}
void
_fmd_fini(fmd_hdl_t *hdl)
{
ses_log_monitor_t *slmp;
slmp = fmd_hdl_getspecific(hdl);
if (slmp) {
fmd_timer_remove(hdl, slmp->slt_timer);
fmd_xprt_close(hdl, slmp->slt_xprt);
fmd_prop_free_string(hdl, slmp->slt_path);
nvlist_free(slmp->slt_expanders);
fmd_hdl_free(hdl, slmp, sizeof (*slmp));
}
}