diff options
| author | wesolows <none@none> | 2006-01-20 16:06:55 -0800 |
|---|---|---|
| committer | wesolows <none@none> | 2006-01-20 16:06:55 -0800 |
| commit | 749f21d359d8fbd020c974a1a5227316221bfc9c (patch) | |
| tree | 41bfaf56a9f0c31430c9dd2e35fedffd5d69b220 /usr/src/lib/fm/libfmd_snmp/common | |
| parent | 1f017db093f7568e41a3d8c6429e13daf5424ff8 (diff) | |
| download | illumos-gate-749f21d359d8fbd020c974a1a5227316221bfc9c.tar.gz | |
PSARC 2005/754 FMA SNMP Agent
6365742 fault management data needs an SNMP interface
6365743 need 64-bit fmd scheme plugins
Diffstat (limited to 'usr/src/lib/fm/libfmd_snmp/common')
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/debug_subr.c | 60 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h | 114 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/init.c | 86 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/llib-lfmd_snmp | 32 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/module.c | 688 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/module.h | 64 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/problem.c | 1015 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/problem.h | 70 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/resource.c | 789 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/resource.h | 65 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/scheme.c | 297 | ||||
| -rw-r--r-- | usr/src/lib/fm/libfmd_snmp/common/sunFM_impl.h | 82 |
12 files changed, 3362 insertions, 0 deletions
diff --git a/usr/src/lib/fm/libfmd_snmp/common/debug_subr.c b/usr/src/lib/fm/libfmd_snmp/common/debug_subr.c new file mode 100644 index 0000000000..695f582d58 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/debug_subr.c @@ -0,0 +1,60 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <stdarg.h> + +void +sunFm_vpanic(const char *format, va_list ap) +{ + snmp_vlog(LOG_ERR, format, ap); +#ifdef DEBUG + abort(); + exit(1); +#endif +} + +void +sunFm_panic(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + sunFm_vpanic(format, ap); + va_end(ap); +} + +int +sunFm_assert(const char *expr, const char *file, int line) +{ + sunFm_panic("\"%s\", line %d: assertion failed: %s\n", file, line, + expr); + return (0); +} diff --git a/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h b/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h new file mode 100644 index 0000000000..049ed6e3c6 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h @@ -0,0 +1,114 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SUNFM_H +#define _SUNFM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * These values are derived from, and must remain consistent with, the + * MIB definitions. + */ +#define MODNAME_STR "sunFM" +#define SUNFM_OID 1, 3, 6, 1, 4, 1, 42, 2, 195, 1 + +#define SUNFMPROBLEMTABLE_OID SUNFM_OID, 1 + +#define SUNFMPROBLEM_COL_UUIDINDEX 1 +#define SUNFMPROBLEM_COL_UUID 2 +#define SUNFMPROBLEM_COL_CODE 3 +#define SUNFMPROBLEM_COL_URL 4 +#define SUNFMPROBLEM_COL_DIAGENGINE 5 +#define SUNFMPROBLEM_COL_DIAGTIME 6 +#define SUNFMPROBLEM_COL_SUSPECTCOUNT 7 + +#define SUNFMPROBLEM_COLMIN SUNFMPROBLEM_COL_UUID +#define SUNFMPROBLEM_COLMAX SUNFMPROBLEM_COL_SUSPECTCOUNT + +#define SUNFMFAULTEVENTTABLE_OID SUNFM_OID, 2 + +#define SUNFMFAULTEVENT_COL_UUIDINDEX 1 +#define SUNFMFAULTEVENT_COL_INDEX 2 +#define SUNFMFAULTEVENT_COL_PROBLEMUUID 3 +#define SUNFMFAULTEVENT_COL_CLASS 4 +#define SUNFMFAULTEVENT_COL_CERTAINTY 5 +#define SUNFMFAULTEVENT_COL_ASRU 6 +#define SUNFMFAULTEVENT_COL_FRU 7 +#define SUNFMFAULTEVENT_COL_RESOURCE 8 + +#define SUNFMFAULTEVENT_COLMIN SUNFMFAULTEVENT_COL_PROBLEMUUID +#define SUNFMFAULTEVENT_COLMAX SUNFMFAULTEVENT_COL_RESOURCE + +#define SUNFMMODULETABLE_OID SUNFM_OID, 3 + +#define SUNFMMODULE_COL_INDEX 1 +#define SUNFMMODULE_COL_NAME 2 +#define SUNFMMODULE_COL_VERSION 3 +#define SUNFMMODULE_COL_STATUS 4 +#define SUNFMMODULE_COL_DESCRIPTION 5 + +#define SUNFMMODULE_COLMIN SUNFMMODULE_COL_NAME +#define SUNFMMODULE_COLMAX SUNFMMODULE_COL_DESCRIPTION + +#define SUNFMMODULE_STATE_OTHER 1 +#define SUNFMMODULE_STATE_ACTIVE 2 +#define SUNFMMODULE_STATE_FAILED 3 + +#define SUNFMRESOURCECOUNT_OID SUNFM_OID, 4 + +#define SUNFMRESOURCETABLE_OID SUNFM_OID, 5 + +#define SUNFMRESOURCE_COL_INDEX 1 +#define SUNFMRESOURCE_COL_FMRI 2 +#define SUNFMRESOURCE_COL_STATUS 3 +#define SUNFMRESOURCE_COL_DIAGNOSISUUID 4 + +#define SUNFMRESOURCE_COLMIN SUNFMRESOURCE_COL_FMRI +#define SUNFMRESOURCE_COLMAX SUNFMRESOURCE_COL_DIAGNOSISUUID + +#define SUNFMRESOURCE_STATE_OTHER 1 +#define SUNFMRESOURCE_STATE_OK 2 +#define SUNFMRESOURCE_STATE_DEGRADED 3 +#define SUNFMRESOURCE_STATE_UNKNOWN 4 +#define SUNFMRESOURCE_STATE_FAULTED 5 + +#define SUNFMTRAPS_OID SUNFM_OID, 7, 0 +#define SUNFMPROBLEMTRAP_OID SUNFMTRAPS_OID, 1 + +#define SNMP_URL_MSG "snmp-url" + +extern int init_sunFM(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SUNFM_H */ diff --git a/usr/src/lib/fm/libfmd_snmp/common/init.c b/usr/src/lib/fm/libfmd_snmp/common/init.c new file mode 100644 index 0000000000..f46fe30b75 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/init.c @@ -0,0 +1,86 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fm/fmd_snmp.h> +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include "sunFM_impl.h" +#include "module.h" +#include "resource.h" +#include "problem.h" + +static const sunFm_table_t sun_fm_tables[] = { + TABLE_REG(sunFmModuleTable), + TABLE_REG(sunFmResourceTable), + TABLE_REG(sunFmProblemTable), + TABLE_REG(sunFmFaultEventTable), + TABLE_NULL +}; + +/* + * This is our entry point for initialization by the agent, which + * (for reasons unknown) ignores the return value. The name is fixed + * by the agent API. + */ +int +init_sunFM(void) +{ + int max_err = MIB_REGISTERED_OK; + const sunFm_table_t *table; + + for (table = sun_fm_tables; table->t_name != NULL; table++) { + int err = table->t_init(); + + switch (err) { + case MIB_REGISTERED_OK: + DEBUGMSGTL((MODNAME_STR, "registered table %s\n", + table->t_name)); + break; + case MIB_DUPLICATE_REGISTRATION: + (void) snmp_log(LOG_ERR, MODNAME_STR + ": table %s initialization failed: duplicate " + "registration\n", table->t_name); + break; + case MIB_REGISTRATION_FAILED: + (void) snmp_log(LOG_ERR, MODNAME_STR + ": table %s initialization failed: agent " + "registration failure\n", table->t_name); + break; + default: + snmp_log(LOG_ERR, MODNAME_STR + ": table %s initialization failed: " + "unknown reason\n", table->t_name); + } + + if (err > max_err) + max_err = err; + } + + return (max_err); +} diff --git a/usr/src/lib/fm/libfmd_snmp/common/llib-lfmd_snmp b/usr/src/lib/fm/libfmd_snmp/common/llib-lfmd_snmp new file mode 100644 index 0000000000..abcf460a60 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/llib-lfmd_snmp @@ -0,0 +1,32 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ +/*PROTOLIB1*/ + +#include <fm/fmd_snmp.h> diff --git a/usr/src/lib/fm/libfmd_snmp/common/module.c b/usr/src/lib/fm/libfmd_snmp/common/module.c new file mode 100644 index 0000000000..10782d9f66 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/module.c @@ -0,0 +1,688 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fm/fmd_adm.h> +#include <fm/fmd_snmp.h> +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <pthread.h> +#include <stddef.h> +#include <errno.h> +#include <libuutil.h> +#include "sunFM_impl.h" +#include "module.h" + +static uu_avl_pool_t *mod_name_avl_pool; +static uu_avl_pool_t *mod_index_avl_pool; +static uu_avl_t *mod_name_avl; +static uu_avl_t *mod_index_avl; + +#define VALID_AVL_STATE (mod_name_avl_pool != NULL && \ + mod_index_avl_pool != NULL && mod_name_avl != NULL && \ + mod_index_avl != NULL) + +#define UPDATE_WAIT_MILLIS 10 /* poll interval in milliseconds */ + +/* + * Update types. Single-index and all are mutually exclusive. + */ +#define UCT_INDEX 0x1 +#define UCT_ALL 0x2 +#define UCT_FLAGS 0x3 + +#define MODULE_DATA_VALID(d) ((d)->d_valid == valid_stamp) + +/* + * Locking rules are straightforward. There is only one updater thread + * for each table, and requests for update that are received while + * another update is in progress are ignored. The single per-table lock + * protects all the data for the table, the valid_stamp and max_index + * tags for new data, and - importantly - all the hidden static data + * used by the Net-SNMP library. The result return callbacks are always + * called in the master agent thread; holding the table lock is + * therefore sufficient since only one table's callback can be run at + * any one time. Finer-grained locking is possible here but + * substantially more difficult because nearly all Net-SNMP functions + * are unsafe. + * + * In practice this is more than adequate, since the purpose of + * threading out the update is to prevent excessively time-consuming + * data collection from bottlenecking the entire agent, not to improve + * result throughput (SNMP is not intended to be used for applications + * requiring high throughput anyway). If the agent itself ever becomes + * multithreaded, locking requirements become limited to our local + * per-table data (the tree, max_index, and valid_stamp), and the + * implementation could be revisited for better performance. + */ + +static ulong_t max_index; +static int valid_stamp; +static pthread_mutex_t update_lock; +static pthread_cond_t update_cv; +static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status; + +static Netsnmp_Node_Handler sunFmModuleTable_handler; + +static sunFmModule_data_t * +key_build(const char *name, const ulong_t index) +{ + static sunFmModule_data_t key; + + key.d_index = index; + if (name) + strlcpy(key.d_ami_name, name, sizeof (key.d_ami_name)); + else + key.d_ami_name[0] = '\0'; + + return (&key); +} + +/* + * If name is the name of a module we have previously seen and indexed, return + * data for it. Otherwise, return NULL. Note that the module may not be + * valid; that is, it may have been removed from the fault manager since its + * information was last updated. + */ +static sunFmModule_data_t * +module_lookup_name(const char *name) +{ + sunFmModule_data_t *key; + + key = key_build(name, 0); + return (uu_avl_find(mod_name_avl, key, NULL, NULL)); +} + +/* + * If index corresponds to a module we have previously seen and indexed, return + * data for it. Otherwise, return NULL. Note that the module may not be + * valid; that is, it may have been removed from the fault manager since its + * information was last updated. + */ +static sunFmModule_data_t * +module_lookup_index_exact(const ulong_t index) +{ + sunFmModule_data_t *key; + + key = key_build(NULL, index); + return (uu_avl_find(mod_index_avl, key, NULL, NULL)); +} + +/* + * If index corresponds to a valid (that is, extant as of latest information + * from the fault manager) fmd module, return the data for that module. + * Otherwise, return the data for the valid module whose index is as close as + * possible to index but not lower. This preserves the lexicographical + * ordering required for GETNEXT processing. + */ +static sunFmModule_data_t * +module_lookup_index_nextvalid(const ulong_t index) +{ + sunFmModule_data_t *key, *data; + uu_avl_index_t idx; + + key = key_build(NULL, index); + + if ((data = uu_avl_find(mod_index_avl, key, NULL, &idx)) != NULL && + MODULE_DATA_VALID(data)) + return (data); + + data = uu_avl_nearest_next(mod_index_avl, idx); + + while (data != NULL && !MODULE_DATA_VALID(data)) { + (void) uu_avl_find(mod_index_avl, data, NULL, &idx); + data = uu_avl_nearest_next(mod_index_avl, idx); + } + + return (data); +} + +/* + * Possible update the contents of a single module within the cache. This + * is our callback from fmd_module_iter. + */ +static int +modinfo_update_one(const fmd_adm_modinfo_t *modinfo, void *arg) +{ + const sunFmModule_update_ctx_t *update_ctx = + (sunFmModule_update_ctx_t *)arg; + sunFmModule_data_t *data = module_lookup_name(modinfo->ami_name); + + /* + * An fmd module we haven't seen before. We're obligated to index + * it and link it into our cache so that we can find it, but we're + * not obligated to fill it in completely unless we're doing a + * thorough update or this is the module we were asked for. This + * avoids unnecessary iteration and memory manipulation for data + * we're not going to return for this request. + */ + if (data == NULL) { + uu_avl_index_t idx; + + DEBUGMSGTL((MODNAME_STR, "found new fmd module %s\n", + modinfo->ami_name)); + if ((data = SNMP_MALLOC_TYPEDEF(sunFmModule_data_t)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " + "new module data at %s:%d\n", __FILE__, __LINE__); + return (1); + } + /* + * We allocate indices sequentially and never reuse them. + * This ensures we can always return valid GETNEXT responses + * without having to reindex, and it provides the user a + * more consistent view of the fault manager. + */ + data->d_index = ++max_index; + DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index, + modinfo->ami_name, data)); + + strlcpy(data->d_ami_name, modinfo->ami_name, + sizeof (data->d_ami_name)); + + uu_avl_node_init(data, &data->d_name_avl, mod_name_avl_pool); + (void) uu_avl_find(mod_name_avl, data, NULL, &idx); + uu_avl_insert(mod_name_avl, data, idx); + + uu_avl_node_init(data, &data->d_index_avl, mod_index_avl_pool); + (void) uu_avl_find(mod_index_avl, data, NULL, &idx); + uu_avl_insert(mod_index_avl, data, idx); + + DEBUGMSGTL((MODNAME_STR, "completed new module %lu/%s@%p\n", + data->d_index, data->d_ami_name, data)); + } + + data->d_valid = valid_stamp; + + DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n", + data->d_index, data->d_ami_name, data, data->d_valid)); + + if ((update_ctx->uc_type & UCT_ALL) || + update_ctx->uc_index == data->d_index) { + strlcpy(data->d_ami_vers, modinfo->ami_vers, + sizeof (data->d_ami_vers)); + strlcpy(data->d_ami_desc, modinfo->ami_desc, + sizeof (data->d_ami_desc)); + data->d_ami_flags = modinfo->ami_flags; + } + + return (!(update_ctx->uc_type & UCT_ALL) && + update_ctx->uc_index == data->d_index); +} + +/* + * Update some or all module data from fmd. If thorough is set, all modules + * will be indexed and their data cached. Otherwise, updates will stop once + * the module matching index has been updated. + * + * Returns appropriate SNMP error codes. + */ +static int +modinfo_update(sunFmModule_update_ctx_t *update_ctx) +{ + fmd_adm_t *adm; + + ASSERT(update_ctx != NULL); + ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) != + (UCT_INDEX|UCT_ALL)); + ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); + ASSERT(VALID_AVL_STATE); + + if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, + update_ctx->uc_version)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " + "failed: %s\n", strerror(errno)); + return (SNMP_ERR_RESOURCEUNAVAILABLE); + } + + ++valid_stamp; + if (fmd_adm_module_iter(adm, modinfo_update_one, update_ctx) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": fmd module information update " + "failed: %s\n", fmd_adm_errmsg(adm)); + fmd_adm_close(adm); + return (SNMP_ERR_RESOURCEUNAVAILABLE); + } + + DEBUGMSGTL((MODNAME_STR, "module iteration completed\n")); + + fmd_adm_close(adm); + return (SNMP_ERR_NOERROR); +} + +/*ARGSUSED*/ +static void +update_thread(void *arg) +{ + /* + * The current modinfo_update implementation offers minimal savings + * for the use of index-only updates; therefore we always do a full + * update. If it becomes advantageous to limit updates to a single + * index, the contexts can be queued by the handler instead. + */ + sunFmModule_update_ctx_t uc; + + uc.uc_host = NULL; + uc.uc_prog = FMD_ADM_PROGRAM; + uc.uc_version = FMD_ADM_VERSION; + + uc.uc_index = 0; + uc.uc_type = UCT_ALL; + + for (;;) { + (void) pthread_mutex_lock(&update_lock); + update_status = US_QUIET; + while (update_status == US_QUIET) + (void) pthread_cond_wait(&update_cv, &update_lock); + update_status = US_INPROGRESS; + (void) pthread_mutex_unlock(&update_lock); + (void) modinfo_update(&uc); + } +} + +static void +request_update(void) +{ + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + (void) pthread_mutex_unlock(&update_lock); + return; + } + update_status = US_NEEDED; + (void) pthread_cond_signal(&update_cv); + (void) pthread_mutex_unlock(&update_lock); +} + +/*ARGSUSED*/ +static int +module_compare_name(const void *l, const void *r, void *private) +{ + sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; + sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; + + ASSERT(l_data != NULL && r_data != NULL); + + return (strcmp(l_data->d_ami_name, r_data->d_ami_name)); +} + +/*ARGSUSED*/ +static int +module_compare_index(const void *l, const void *r, void *private) +{ + sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; + sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; + + ASSERT(l_data != NULL && r_data != NULL); + + return (l_data->d_index < r_data->d_index ? -1 : + l_data->d_index > r_data->d_index ? 1 : 0); +} + +int +sunFmModuleTable_init(void) +{ + static oid sunFmModuleTable_oid[] = { SUNFMMODULETABLE_OID }; + netsnmp_table_registration_info *table_info; + netsnmp_handler_registration *handler; + int err; + + if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", + strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", + strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + + if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, + NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": error creating update " + "thread: %s\n", strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + + if ((table_info = + SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) + return (MIB_REGISTRATION_FAILED); + + if ((handler = netsnmp_create_handler_registration("sunFmModuleTable", + sunFmModuleTable_handler, sunFmModuleTable_oid, + OID_LENGTH(sunFmModuleTable_oid), HANDLER_CAN_RONLY)) == NULL) { + SNMP_FREE(table_info); + return (MIB_REGISTRATION_FAILED); + } + + /* + * The Net-SNMP template uses add_indexes here, but that + * function is unsafe because it does not check for failure. + */ + if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (MIB_REGISTRATION_FAILED); + } + + table_info->min_column = SUNFMMODULE_COLMIN; + table_info->max_column = SUNFMMODULE_COLMAX; + + if ((mod_name_avl_pool = uu_avl_pool_create("mod_name", + sizeof (sunFmModule_data_t), + offsetof(sunFmModule_data_t, d_name_avl), module_compare_name, + UU_AVL_DEBUG)) == NULL) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + } + + if ((mod_name_avl = uu_avl_create(mod_name_avl_pool, NULL, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": mod_name_avl creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_pool_destroy(mod_name_avl_pool); + return (MIB_REGISTRATION_FAILED); + } + + if ((mod_index_avl_pool = uu_avl_pool_create("mod_index", + sizeof (sunFmModule_data_t), + offsetof(sunFmModule_data_t, d_index_avl), + module_compare_index, UU_AVL_DEBUG)) == NULL) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(mod_name_avl); + uu_avl_pool_destroy(mod_name_avl_pool); + } + + if ((mod_index_avl = uu_avl_create(mod_index_avl_pool, NULL, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": mod_index_avl creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(mod_name_avl); + uu_avl_pool_destroy(mod_name_avl_pool); + uu_avl_pool_destroy(mod_index_avl_pool); + return (MIB_REGISTRATION_FAILED); + } + + if ((err = netsnmp_register_table(handler, table_info)) != + MIB_REGISTERED_OK) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(mod_name_avl); + uu_avl_pool_destroy(mod_name_avl_pool); + uu_avl_destroy(mod_index_avl); + uu_avl_pool_destroy(mod_index_avl_pool); + return (err); + } + + return (MIB_REGISTERED_OK); +} + +/* + * These two functions form the core of GET/GETNEXT/GETBULK handling (the + * only kind we do). They perform two functions: + * + * - First, frob the request to set all the index variables to correspond + * to the value that's going to be returned. For GET, this is a nop; + * for GETNEXT/GETBULK it always requires some work. + * - Second, find and return the fmd module information corresponding to + * the (possibly updated) indices. + * + * These should be as fast as possible; they run in the agent thread. + */ +static sunFmModule_data_t * +sunFmModuleTable_nextmod(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmModule_data_t *data; + netsnmp_variable_list *var; + ulong_t index; + + /* + * If we have no index, we must make one. + */ + if (table_info->number_indexes < 1) { + oid tmpoid[MAX_OID_LEN]; + index = 1; + + DEBUGMSGTL((MODNAME_STR, "nextmod: no indexes given\n")); + var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); + snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index, + sizeof (index)); + memcpy(tmpoid, reginfo->rootoid, + reginfo->rootoid_len * sizeof (oid)); + tmpoid[reginfo->rootoid_len] = 1; /* Entry is .1 */ + tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; + if (build_oid(&var->name, &var->name_length, tmpoid, + reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) + return (NULL); + DEBUGMSGTL((MODNAME_STR, "nextmod: built fake index:\n")); + DEBUGMSGVAR((MODNAME_STR, var)); + DEBUGMSG((MODNAME_STR, "\n")); + } else { + var = table_info->indexes; + index = *var->val.integer; + DEBUGMSGTL((MODNAME_STR, "nextmod: received index:\n")); + DEBUGMSGVAR((MODNAME_STR, var)); + DEBUGMSG((MODNAME_STR, "\n")); + index++; + } + + if ((data = module_lookup_index_nextvalid(index)) == NULL) { + DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " + "index %lu; trying next column\n", index)); + if (table_info->colnum >= SUNFMMODULE_COLMAX) { + snmp_free_varbind(var); + table_info->indexes = NULL; + table_info->number_indexes = 0; + DEBUGMSGTL((MODNAME_STR, "nextmod: out of columns\n")); + return (NULL); + } + table_info->colnum++; + index = 1; + + data = module_lookup_index_nextvalid(index); + } + + if (data == NULL) { + DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " + "index %lu; stopping\n", index)); + snmp_free_varbind(var); + table_info->indexes = NULL; + table_info->number_indexes = 0; + return (NULL); + } + + *var->val.integer = index; + table_info->indexes = var; + + DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index, + data->d_ami_name, data)); + + return (data); +} + +/*ARGSUSED*/ +static sunFmModule_data_t * +sunFmModuleTable_mod(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmModule_data_t *data; + netsnmp_variable_list *var; + + ASSERT(table_info->number_indexes == 1); + + return (module_lookup_index_exact(table_info->index_oid[0])); +} + +static void +sunFmModuleTable_return(unsigned int reg, void *arg) +{ + netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; + netsnmp_request_info *request; + netsnmp_agent_request_info *reqinfo; + netsnmp_handler_registration *reginfo; + netsnmp_mib_handler *handler; + netsnmp_table_request_info *table_info; + netsnmp_variable_list *var; + sunFmModule_data_t *data; + ulong_t modstate; + + ASSERT(netsnmp_handler_check_cache(cache) != NULL); + + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, + cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + request = cache->requests; + reqinfo = cache->reqinfo; + reginfo = cache->reginfo; + handler = cache->handler; + + var = request->requestvb; + table_info = netsnmp_extract_table_info(request); + request->delegated = 0; + + ASSERT(table_info->colnum >= SUNFMMODULE_COLMIN); + ASSERT(table_info->colnum <= SUNFMMODULE_COLMAX); + + /* + * table_info->colnum contains the column number requested. + * table_info->indexes contains a linked list of snmp variable + * bindings for the indexes of the table. Values in the list + * have been set corresponding to the indexes of the + * request. We have other guarantees as well: + * + * - The column number is always within range. + * - If we have no index data, table_info->index_oid_len is 0. + * - We will never receive requests outside our table nor + * those with the first subid anything other than 1 (Entry) + * nor those without a column number. This is true even + * for GETNEXT requests. + */ + + switch (reqinfo->mode) { + case MODE_GET: + if ((data = sunFmModuleTable_mod(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + case MODE_GETNEXT: + case MODE_GETBULK: + if ((data = sunFmModuleTable_nextmod(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + default: + snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request " + "mode %d\n", reqinfo->mode); + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + switch (table_info->colnum) { + case SUNFMMODULE_COL_NAME: + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_ami_name, + strlen(data->d_ami_name)); + break; + case SUNFMMODULE_COL_VERSION: + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_ami_vers, + strlen(data->d_ami_vers)); + break; + case SUNFMMODULE_COL_STATUS: + modstate = (data->d_ami_flags & FMD_ADM_MOD_FAILED) ? + SUNFMMODULE_STATE_FAILED : SUNFMMODULE_STATE_ACTIVE; + netsnmp_table_build_result(reginfo, request, table_info, + ASN_INTEGER, (uchar_t *)&modstate, + sizeof (modstate)); + break; + case SUNFMMODULE_COL_DESCRIPTION: + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_ami_desc, + strlen(data->d_ami_desc)); + break; + default: + break; + } + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); +} + +static int +sunFmModuleTable_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + request_update(); + + for (request = requests; request; request = request->next) { + if (request->processed != 0) + continue; + + if (netsnmp_extract_table_info(request) == NULL) + continue; + + request->delegated = 1; + (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, + (void *) netsnmp_create_delegated_cache(handler, reginfo, + reqinfo, request, NULL)); + } + + return (SNMP_ERR_NOERROR); +} diff --git a/usr/src/lib/fm/libfmd_snmp/common/module.h b/usr/src/lib/fm/libfmd_snmp/common/module.h new file mode 100644 index 0000000000..cec0e90ed2 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/module.h @@ -0,0 +1,64 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _MODULE_H +#define _MODULE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <libuutil.h> + +typedef struct sunFmModule_data { + ulong_t d_index; /* MIB index */ + int d_valid; /* iteration stamp */ + uu_avl_node_t d_name_avl; /* by-name AVL node */ + uu_avl_node_t d_index_avl; /* by-index AVL node */ + char d_ami_name[256]; /* fmd module name */ + char d_ami_vers[256]; /* fmd module version */ + char d_ami_desc[256]; /* fmd module description */ + uint_t d_ami_flags; /* fmd module flags */ +} sunFmModule_data_t; + +typedef struct sunFmModule_update_ctx { + const char *uc_host; + uint32_t uc_prog; + int uc_version; + ulong_t uc_index; + int uc_type; +} sunFmModule_update_ctx_t; + +int sunFmModuleTable_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _MODULE_H */ diff --git a/usr/src/lib/fm/libfmd_snmp/common/problem.c b/usr/src/lib/fm/libfmd_snmp/common/problem.c new file mode 100644 index 0000000000..3b159e6170 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/problem.c @@ -0,0 +1,1015 @@ +/* + * 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 2006 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 <fm/fmd_adm.h> +#include <fm/fmd_snmp.h> +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <pthread.h> +#include <stddef.h> +#include <errno.h> +#include <alloca.h> +#include <locale.h> +#include <libuutil.h> +#include <libnvpair.h> +#include "sunFM_impl.h" +#include "problem.h" + +/* + * We assume that the number of suspect fault events associated with a + * particular case will generally be sufficiently small that the overhead + * associated with indexing them in a tree would exceed the gain from + * not traversing the fault list for each request. + */ +static uu_avl_pool_t *problem_uuid_avl_pool; +static uu_avl_t *problem_uuid_avl; + +#define VALID_AVL_STATE (problem_uuid_avl_pool != NULL && \ + problem_uuid_avl != NULL) + +#define UPDATE_WAIT_MILLIS 10 /* poll interval in milliseconds */ + +/* + * Update types. Single-index and all are mutually exclusive. + */ +#define UCT_INDEX 0x1 +#define UCT_ALL 0x2 +#define UCT_FLAGS 0x3 + +#define MODULE_DATA_VALID(d) ((d)->d_valid == valid_stamp) + +/* + * Locking strategy is described in module.c. + */ +static int valid_stamp; +static pthread_mutex_t update_lock; +static pthread_cond_t update_cv; +static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status; + +static const char SNMP_URL[] = SNMP_URL_MSG; +#define URL_BASE "http://sun.com/msg/" + +static Netsnmp_Node_Handler sunFmProblemTable_handler; +static Netsnmp_Node_Handler sunFmFaultEventTable_handler; + +static sunFmProblem_data_t * +problem_key_build(const char *uuid) +{ + static sunFmProblem_data_t key; + + key.d_aci_uuid = uuid; + + return (&key); +} + +static sunFmProblem_data_t * +problem_lookup_uuid_exact(const char *uuid) +{ + sunFmProblem_data_t *key, *data; + + key = problem_key_build(uuid); + + DEBUGMSGTL((MODNAME_STR, "lookup_exact for uuid %s\n", uuid)); + data = uu_avl_find(problem_uuid_avl, key, NULL, NULL); + + return (data); +} + +static sunFmProblem_data_t * +problem_lookup_uuid_next(const char *uuid) +{ + sunFmProblem_data_t *key, *data; + uu_avl_index_t idx; + + key = problem_key_build(uuid); + + DEBUGMSGTL((MODNAME_STR, "lookup_next for uuid %s\n", uuid)); + (void) uu_avl_find(problem_uuid_avl, key, NULL, &idx); + + data = uu_avl_nearest_next(problem_uuid_avl, idx); + + DEBUGMSGTL((MODNAME_STR, "lookup_next: entry is %p\n", data)); + + return (data); +} + +static sunFmFaultEvent_data_t * +faultevent_lookup_index_exact(sunFmProblem_data_t *data, ulong_t index) +{ + if (index > data->d_nsuspects) + return (NULL); + + if (data->d_suspects == NULL) + return (NULL); + + return (data->d_suspects[index - 1]); +} + +static int +problem_update_one(const fmd_adm_caseinfo_t *acp, void *arg) +{ + const sunFmProblem_update_ctx_t *update_ctx = + (sunFmProblem_update_ctx_t *)arg; + sunFmProblem_data_t *data; + nvlist_t *nvl; + int64_t *diag_time; + uint_t nelem; + uint32_t nsusp; + int err; + + DEBUGMSGTL((MODNAME_STR, "update_one\n")); + + ASSERT(acp->aci_uuid != NULL); + + if ((data = problem_lookup_uuid_exact(acp->aci_uuid)) == NULL) { + uu_avl_index_t idx; + + DEBUGMSGTL((MODNAME_STR, "found new problem %s\n", + acp->aci_uuid)); + if ((data = SNMP_MALLOC_TYPEDEF(sunFmProblem_data_t)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " + "new problem data at %s:%d\n", __FILE__, __LINE__); + return (0); + } + if ((err = nvlist_dup(acp->aci_event, &data->d_aci_event, 0)) + != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": Problem data setup " + "failed: %s\n", strerror(err)); + SNMP_FREE(data); + return (0); + } + + data->d_aci_uuid = data->d_aci_code = data->d_aci_url = "-"; + (void) nvlist_lookup_string(data->d_aci_event, FM_SUSPECT_UUID, + (char **)&data->d_aci_uuid); + (void) nvlist_lookup_string(data->d_aci_event, + FM_SUSPECT_DIAG_CODE, (char **)&data->d_aci_code); + data->d_aci_url = strdup(acp->aci_url); + + if (nvlist_lookup_nvlist(data->d_aci_event, FM_SUSPECT_DE, + &nvl) == 0) + if ((data->d_diag_engine = sunFm_nvl2str(nvl)) == NULL) + data->d_diag_engine = "-"; + + if (nvlist_lookup_int64_array(data->d_aci_event, + FM_SUSPECT_DIAG_TIME, &diag_time, &nelem) == 0 && + nelem >= 2) { + data->d_diag_time.tv_sec = (long)diag_time[0]; + data->d_diag_time.tv_usec = (long)diag_time[1]; + } + + (void) nvlist_lookup_uint32(data->d_aci_event, + FM_SUSPECT_FAULT_SZ, &nsusp); + data->d_nsuspects = (ulong_t)nsusp; + + (void) nvlist_lookup_nvlist_array(data->d_aci_event, + FM_SUSPECT_FAULT_LIST, &data->d_suspects, &nelem); + + ASSERT(nelem == data->d_nsuspects); + + uu_avl_node_init(data, &data->d_uuid_avl, + problem_uuid_avl_pool); + (void) uu_avl_find(problem_uuid_avl, data, NULL, &idx); + uu_avl_insert(problem_uuid_avl, data, idx); + + data->d_valid = valid_stamp; + + DEBUGMSGTL((MODNAME_STR, "completed new problem %s@%p\n", + data->d_aci_uuid, data)); + } + + /* + * We don't touch problems we've seen before; they shouldn't change + * in any way we care about, since they've already been solved. The + * state, however, could change, and if we later expose that to the + * client we need to update it here. + */ + + return (0); +} + +static int +problem_update(sunFmProblem_update_ctx_t *update_ctx) +{ + fmd_adm_t *adm; + + ASSERT(update_ctx != NULL); + ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) != + (UCT_INDEX|UCT_ALL)); + ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); + ASSERT(VALID_AVL_STATE); + + if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, + update_ctx->uc_version)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " + "failed: %s\n", strerror(errno)); + return (SNMP_ERR_RESOURCEUNAVAILABLE); + } + + ++valid_stamp; + if (fmd_adm_case_iter(adm, SNMP_URL_MSG, problem_update_one, + update_ctx) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": fmd case information update " + "failed: %s\n", fmd_adm_errmsg(adm)); + fmd_adm_close(adm); + return (SNMP_ERR_RESOURCEUNAVAILABLE); + } + + DEBUGMSGTL((MODNAME_STR, "case iteration completed\n")); + + fmd_adm_close(adm); + return (SNMP_ERR_NOERROR); +} + +/*ARGSUSED*/ +static void +update_thread(void *arg) +{ + /* + * The current problem_update implementation offers minimal savings + * for the use of index-only updates; therefore we always do a full + * update. If it becomes advantageous to limit updates to a single + * index, the contexts can be queued by the handler instead. + */ + sunFmProblem_update_ctx_t uc; + + uc.uc_host = NULL; + uc.uc_prog = FMD_ADM_PROGRAM; + uc.uc_version = FMD_ADM_VERSION; + + uc.uc_index = NULL; + uc.uc_type = UCT_ALL; + + for (;;) { + (void) pthread_mutex_lock(&update_lock); + update_status = US_QUIET; + while (update_status == US_QUIET) + (void) pthread_cond_wait(&update_cv, &update_lock); + update_status = US_INPROGRESS; + (void) pthread_mutex_unlock(&update_lock); + (void) problem_update(&uc); + } +} + +static void +request_update(void) +{ + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + (void) pthread_mutex_unlock(&update_lock); + return; + } + update_status = US_NEEDED; + (void) pthread_cond_signal(&update_cv); + (void) pthread_mutex_unlock(&update_lock); +} + +/*ARGSUSED*/ +static int +problem_compare_uuid(const void *l, const void *r, void *private) +{ + sunFmProblem_data_t *l_data = (sunFmProblem_data_t *)l; + sunFmProblem_data_t *r_data = (sunFmProblem_data_t *)r; + + ASSERT(l_data != NULL && r_data != NULL); + + return (strcmp(l_data->d_aci_uuid, r_data->d_aci_uuid)); +} + +int +sunFmProblemTable_init(void) +{ + static oid sunFmProblemTable_oid[] = { SUNFMPROBLEMTABLE_OID }; + netsnmp_table_registration_info *table_info; + netsnmp_handler_registration *handler; + int err; + + if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", + strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", + strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + + if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, + NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": error creating update " + "thread: %s\n", strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + + if ((table_info = + SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) + return (MIB_REGISTRATION_FAILED); + + if ((handler = netsnmp_create_handler_registration("sunFmProblemTable", + sunFmProblemTable_handler, sunFmProblemTable_oid, + OID_LENGTH(sunFmProblemTable_oid), HANDLER_CAN_RONLY)) == NULL) { + SNMP_FREE(table_info); + return (MIB_REGISTRATION_FAILED); + } + + /* + * The Net-SNMP template uses add_indexes here, but that + * function is unsafe because it does not check for failure. + */ + if (netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR) == NULL) { + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (MIB_REGISTRATION_FAILED); + } + + table_info->min_column = SUNFMPROBLEM_COLMIN; + table_info->max_column = SUNFMPROBLEM_COLMAX; + + if ((problem_uuid_avl_pool = uu_avl_pool_create("problem_uuid", + sizeof (sunFmProblem_data_t), + offsetof(sunFmProblem_data_t, d_uuid_avl), problem_compare_uuid, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": problem_uuid avl pool " + "creation failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (MIB_REGISTRATION_FAILED); + } + + if ((problem_uuid_avl = uu_avl_create(problem_uuid_avl_pool, NULL, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": problem_uuid avl creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_pool_destroy(problem_uuid_avl_pool); + return (MIB_REGISTRATION_FAILED); + } + + if ((err = netsnmp_register_table(handler, table_info)) != + MIB_REGISTERED_OK) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(problem_uuid_avl); + uu_avl_pool_destroy(problem_uuid_avl_pool); + return (err); + } + + return (MIB_REGISTERED_OK); +} + +int +sunFmFaultEventTable_init(void) +{ + static oid sunFmFaultEventTable_oid[] = { SUNFMFAULTEVENTTABLE_OID }; + netsnmp_table_registration_info *table_info; + netsnmp_handler_registration *handler; + int err; + + if ((table_info = + SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) + return (MIB_REGISTRATION_FAILED); + + if ((handler = + netsnmp_create_handler_registration("sunFmFaultEventTable", + sunFmFaultEventTable_handler, sunFmFaultEventTable_oid, + OID_LENGTH(sunFmFaultEventTable_oid), HANDLER_CAN_RONLY)) == NULL) { + SNMP_FREE(table_info); + return (MIB_REGISTRATION_FAILED); + } + + /* + * The Net-SNMP template uses add_indexes here, but that + * function is unsafe because it does not check for failure. + */ + if (netsnmp_table_helper_add_index(table_info, ASN_OCTET_STR) == NULL) { + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (MIB_REGISTRATION_FAILED); + } + if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (MIB_REGISTRATION_FAILED); + } + + table_info->min_column = SUNFMFAULTEVENT_COLMIN; + table_info->max_column = SUNFMFAULTEVENT_COLMAX; + + if ((err = netsnmp_register_table(handler, table_info)) != + MIB_REGISTERED_OK) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (err); + } + + return (MIB_REGISTERED_OK); +} + +/* + * Returns the problem data for the problem whose uuid is next according + * to ASN.1 lexical ordering after the request in table_info. Indexes are + * updated to reflect the OID of the value being returned. This allows + * us to implement GETNEXT. + */ +static sunFmProblem_data_t * +sunFmProblemTable_nextpr(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmProblem_data_t *data; + netsnmp_variable_list *var; + char *uuid = ""; + + if (table_info->number_indexes < 1) { + oid tmpoid[MAX_OID_LEN]; + + DEBUGMSGTL((MODNAME_STR, "nextpr: no indexes given\n")); + + table_info->indexes = + SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); + snmp_set_var_typed_value(table_info->indexes, ASN_OCTET_STR, + (const uchar_t *)uuid, 0); + memcpy(tmpoid, reginfo->rootoid, + reginfo->rootoid_len * sizeof (oid)); + tmpoid[reginfo->rootoid_len] = 1; + tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; + if (build_oid_segment(table_info->indexes) != SNMPERR_SUCCESS) { + snmp_free_varbind(table_info->indexes); + return (NULL); + } + table_info->number_indexes = 1; + table_info->index_oid_len = table_info->indexes->name_length; + memcpy(table_info->index_oid, table_info->indexes->name, + table_info->indexes->name_length); + + DEBUGMSGTL((MODNAME_STR, "nextpr: built fake index:\n")); + DEBUGMSGVAR((MODNAME_STR, table_info->indexes)); + DEBUGMSG((MODNAME_STR, "\n")); + } else { + /* + * Construct the next possible UUID to look for. We can + * simply increment the least significant byte of the last + * UUID because (a) that preserves SNMP lex order and (b) + * the characters that may appear in a UUID do not include + * 127 nor 255. + */ + uuid = alloca(table_info->indexes->val_len + 1); + (void) strlcpy(uuid, + (const char *)table_info->indexes->val.string, + table_info->indexes->val_len + 1); + ++uuid[table_info->indexes->val_len - 1]; + + DEBUGMSGTL((MODNAME_STR, "nextpr: received index:\n")); + DEBUGMSGVAR((MODNAME_STR, table_info->indexes)); + DEBUGMSG((MODNAME_STR, "\n")); + } + + if ((data = problem_lookup_uuid_next(uuid)) == NULL) { + DEBUGMSGTL((MODNAME_STR, "nextpr: next match not found for " + "%s; trying next column\n", uuid)); + if (table_info->colnum >= SUNFMPROBLEM_COLMAX) { + snmp_free_varbind(table_info->indexes); + table_info->indexes = NULL; + table_info->number_indexes = 0; + DEBUGMSGTL((MODNAME_STR, "nextpr: out of columns\n")); + return (NULL); + } + table_info->colnum++; + DEBUGMSGTL((MODNAME_STR, "nextpr: search for col %u empty " + "uuid\n", table_info->colnum, uuid)); + + if ((data = problem_lookup_uuid_next("")) == NULL) { + DEBUGMSGTL((MODNAME_STR, "nextpr: next match not found " + "for empty uuid; stopping\n")); + snmp_free_varbind(table_info->indexes); + table_info->indexes = NULL; + table_info->number_indexes = 0; + return (NULL); + } + } + + snmp_set_var_typed_value(table_info->indexes, ASN_OCTET_STR, + (uchar_t *)data->d_aci_uuid, strlen(data->d_aci_uuid)); + table_info->number_indexes = 1; + + DEBUGMSGTL((MODNAME_STR, "matching data is %s@%p\n", data->d_aci_uuid, + data)); + + return (data); +} + +/* + * Returns the problem data corresponding to the request in table_info. + * All request parameters are unmodified. + */ +static sunFmProblem_data_t * +sunFmProblemTable_pr(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmProblem_data_t *data; + char *uuid; + + ASSERT(table_info->number_indexes >= 1); + + uuid = alloca(table_info->indexes->val_len + 1); + (void) strlcpy(uuid, (const char *)table_info->indexes->val.string, + table_info->indexes->val_len + 1); + + return (problem_lookup_uuid_exact(uuid)); +} + +/* + * Returns the ASN.1 lexicographically first fault event after the one + * identified by table_info. Indexes are updated to reflect the OID + * of the data returned. This allows us to implement GETNEXT. + */ +static sunFmFaultEvent_data_t * +sunFmFaultEventTable_nextfe(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmProblem_data_t *data; + sunFmFaultEvent_data_t *rv; + netsnmp_variable_list *var; + char *uuid; + ulong_t index; + + for (;;) { + switch (table_info->number_indexes) { + case 2: + default: + DEBUGMSGTL((MODNAME_STR, "nextfe: 2 indices:\n")); + DEBUGMSGVAR((MODNAME_STR, table_info->indexes)); + DEBUGMSG((MODNAME_STR, "\n")); + DEBUGMSGVAR((MODNAME_STR, + table_info->indexes->next_variable)); + DEBUGMSG((MODNAME_STR, "\n")); + index = *(ulong_t *) + table_info->indexes->next_variable->val.integer + 1; + + if ((data = sunFmProblemTable_pr(reginfo, + table_info)) != NULL && + (rv = faultevent_lookup_index_exact(data, index)) != + NULL) { + snmp_set_var_typed_value( + table_info->indexes->next_variable, + ASN_UNSIGNED, (uchar_t *)&index, + sizeof (index)); + return (rv); + } + + if (sunFmProblemTable_nextpr(reginfo, table_info) == + NULL) + return (NULL); + break; + case 1: + if ((data = sunFmProblemTable_pr(reginfo, + table_info)) != NULL) { + oid tmpoid[MAX_OID_LEN]; + index = 0; + + DEBUGMSGTL((MODNAME_STR, "nextfe: 1 index:\n")); + DEBUGMSGVAR((MODNAME_STR, table_info->indexes)); + DEBUGMSG((MODNAME_STR, "\n")); + var = + SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); + snmp_set_var_typed_value(var, ASN_UNSIGNED, + (uchar_t *)&index, sizeof (index)); + memcpy(tmpoid, reginfo->rootoid, + reginfo->rootoid_len * sizeof (oid)); + tmpoid[reginfo->rootoid_len] = 1; + tmpoid[reginfo->rootoid_len + 1] = + table_info->colnum; + if (build_oid_segment(var) != SNMPERR_SUCCESS) { + snmp_free_varbind(var); + return (NULL); + } + table_info->indexes->next_variable = var; + table_info->number_indexes = 2; + DEBUGMSGTL((MODNAME_STR, "nextfe: built fake " + "index:\n")); + DEBUGMSGVAR((MODNAME_STR, table_info->indexes)); + DEBUGMSG((MODNAME_STR, "\n")); + DEBUGMSGVAR((MODNAME_STR, + table_info->indexes->next_variable)); + DEBUGMSG((MODNAME_STR, "\n")); + } else { + if (sunFmProblemTable_nextpr(reginfo, + table_info) == NULL) + return (NULL); + } + break; + case 0: + if (sunFmProblemTable_nextpr(reginfo, table_info) == + NULL) + return (NULL); + break; + } + } +} + +static sunFmFaultEvent_data_t * +sunFmFaultEventTable_fe(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmProblem_data_t *data; + + ASSERT(table_info->number_indexes == 2); + + if ((data = sunFmProblemTable_pr(reginfo, table_info)) == NULL) + return (NULL); + + return (faultevent_lookup_index_exact(data, + *(ulong_t *)table_info->indexes->next_variable->val.integer)); +} + +static void +sunFmProblemTable_return(unsigned int reg, void *arg) +{ + netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; + netsnmp_request_info *request; + netsnmp_agent_request_info *reqinfo; + netsnmp_handler_registration *reginfo; + netsnmp_mib_handler *handler; + netsnmp_table_request_info *table_info; + netsnmp_variable_list *var; + sunFmProblem_data_t *data; + + ASSERT(netsnmp_handler_check_cache(cache) != NULL); + + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + (void) snmp_alarm_register_hr(tv, 0, sunFmProblemTable_return, + cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + request = cache->requests; + reqinfo = cache->reqinfo; + reginfo = cache->reginfo; + handler = cache->handler; + + var = request->requestvb; + table_info = netsnmp_extract_table_info(request); + request->delegated = 0; + + ASSERT(table_info->colnum >= SUNFMPROBLEM_COLMIN); + ASSERT(table_info->colnum <= SUNFMPROBLEM_COLMAX); + + /* + * table_info->colnum contains the column number requested. + * table_info->indexes contains a linked list of snmp variable + * bindings for the indexes of the table. Values in the list + * have been set corresponding to the indexes of the + * request. We have other guarantees as well: + * + * - The column number is always within range. + * - If we have no index data, table_info->index_oid_len is 0. + * - We will never receive requests outside our table nor + * those with the first subid anything other than 1 (Entry) + * nor those without a column number. This is true even + * for GETNEXT requests. + */ + + switch (reqinfo->mode) { + case MODE_GET: + if ((data = sunFmProblemTable_pr(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + case MODE_GETNEXT: + case MODE_GETBULK: + if ((data = sunFmProblemTable_nextpr(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + default: + snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n", + reqinfo->mode); + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + switch (table_info->colnum) { + case SUNFMPROBLEM_COL_UUID: + { + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_aci_uuid, + strlen(data->d_aci_uuid)); + break; + } + case SUNFMPROBLEM_COL_CODE: + { + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_aci_code, + strlen(data->d_aci_code)); + break; + } + case SUNFMPROBLEM_COL_URL: + { + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_aci_url, + strlen(data->d_aci_url)); + break; + } + case SUNFMPROBLEM_COL_DIAGENGINE: + { + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_diag_engine, + strlen(data->d_diag_engine)); + break; + } + case SUNFMPROBLEM_COL_DIAGTIME: + { + /* + * The date_n_time function is not Y2038-safe; this may + * need to be updated when a suitable Y2038-safe Net-SNMP + * API is available. + */ + size_t dt_size; + time_t dt_time = (time_t)data->d_diag_time.tv_sec; + uchar_t *dt = date_n_time(&dt_time, &dt_size); + + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, dt, dt_size); + break; + } + case SUNFMPROBLEM_COL_SUSPECTCOUNT: + { + netsnmp_table_build_result(reginfo, request, table_info, + ASN_UNSIGNED, (uchar_t *)&data->d_nsuspects, + sizeof (data->d_nsuspects)); + break; + } + default: + break; + } + + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); +} + +static int +sunFmProblemTable_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + request_update(); + + for (request = requests; request; request = request->next) { + if (request->processed != 0) + continue; + + if (netsnmp_extract_table_info(request) == NULL) + continue; + + request->delegated = 1; + (void) snmp_alarm_register_hr(tv, 0, + sunFmProblemTable_return, + (void *) netsnmp_create_delegated_cache(handler, reginfo, + reqinfo, request, NULL)); + } + + return (SNMP_ERR_NOERROR); +} + +static void +sunFmFaultEventTable_return(unsigned int reg, void *arg) +{ + netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; + netsnmp_request_info *request; + netsnmp_agent_request_info *reqinfo; + netsnmp_handler_registration *reginfo; + netsnmp_mib_handler *handler; + netsnmp_table_request_info *table_info; + netsnmp_variable_list *var; + sunFmProblem_data_t *pdata; + sunFmFaultEvent_data_t *data; + + ASSERT(netsnmp_handler_check_cache(cache) != NULL); + + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + (void) snmp_alarm_register_hr(tv, 0, + sunFmFaultEventTable_return, cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + request = cache->requests; + reqinfo = cache->reqinfo; + reginfo = cache->reginfo; + handler = cache->handler; + + var = request->requestvb; + table_info = netsnmp_extract_table_info(request); + request->delegated = 0; + + ASSERT(table_info->colnum >= SUNFMFAULTEVENT_COLMIN); + ASSERT(table_info->colnum <= SUNFMFAULTEVENT_COLMAX); + + /* + * table_info->colnum contains the column number requested. + * table_info->indexes contains a linked list of snmp variable + * bindings for the indexes of the table. Values in the list + * have been set corresponding to the indexes of the + * request. We have other guarantees as well: + * + * - The column number is always within range. + * - If we have no index data, table_info->index_oid_len is 0. + * - We will never receive requests outside our table nor + * those with the first subid anything other than 1 (Entry) + * nor those without a column number. This is true even + * for GETNEXT requests. + */ + + switch (reqinfo->mode) { + case MODE_GET: + if ((data = sunFmFaultEventTable_fe(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + case MODE_GETNEXT: + case MODE_GETBULK: + if ((data = sunFmFaultEventTable_nextfe(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + default: + snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n", + reqinfo->mode); + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + switch (table_info->colnum) { + case SUNFMFAULTEVENT_COL_PROBLEMUUID: + { + if ((pdata = sunFmProblemTable_pr(reginfo, table_info)) + == NULL) { + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, NULL, 0); + break; + } + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)pdata->d_aci_uuid, + strlen(pdata->d_aci_uuid)); + break; + } + case SUNFMFAULTEVENT_COL_CLASS: + { + char *class = "-"; + + (void) nvlist_lookup_string(data, FM_CLASS, &class); + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)class, strlen(class)); + break; + } + case SUNFMFAULTEVENT_COL_CERTAINTY: + { + uint8_t pct = 0; + ulong_t pl; + + (void) nvlist_lookup_uint8(data, FM_FAULT_CERTAINTY, + &pct); + pl = (ulong_t)pct; + netsnmp_table_build_result(reginfo, request, table_info, + ASN_UNSIGNED, (uchar_t *)&pl, sizeof (pl)); + break; + } + case SUNFMFAULTEVENT_COL_ASRU: + { + nvlist_t *asru = NULL; + char *fmri; + + (void) nvlist_lookup_nvlist(data, FM_FAULT_ASRU, &asru); + if ((fmri = sunFm_nvl2str(asru)) == NULL) + fmri = "-"; + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)fmri, strlen(fmri)); + break; + } + case SUNFMFAULTEVENT_COL_FRU: + { + nvlist_t *fru = NULL; + char *fmri; + + (void) nvlist_lookup_nvlist(data, FM_FAULT_FRU, &fru); + if ((fmri = sunFm_nvl2str(fru)) == NULL) + fmri = "-"; + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)fmri, strlen(fmri)); + break; + } + case SUNFMFAULTEVENT_COL_RESOURCE: + { + nvlist_t *rsrc = NULL; + char *fmri; + + (void) nvlist_lookup_nvlist(data, FM_FAULT_ASRU, &rsrc); + if ((fmri = sunFm_nvl2str(rsrc)) == NULL) + fmri = "-"; + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)fmri, strlen(fmri)); + break; + } + default: + break; + } + + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); +} + +static int +sunFmFaultEventTable_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + request_update(); + + for (request = requests; request; request = request->next) { + if (request->processed != 0) + continue; + + if (netsnmp_extract_table_info(request) == NULL) + continue; + + request->delegated = 1; + (void) snmp_alarm_register_hr(tv, 0, + sunFmFaultEventTable_return, + (void *) netsnmp_create_delegated_cache(handler, reginfo, + reqinfo, request, NULL)); + } + + return (SNMP_ERR_NOERROR); +} diff --git a/usr/src/lib/fm/libfmd_snmp/common/problem.h b/usr/src/lib/fm/libfmd_snmp/common/problem.h new file mode 100644 index 0000000000..fb935e9ed5 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/problem.h @@ -0,0 +1,70 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _PROBLEM_H +#define _PROBLEM_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <libuutil.h> +#include <libnvpair.h> + +typedef struct sunFmProblem_data { + int d_valid; + uu_avl_node_t d_uuid_avl; + const char *d_aci_uuid; + const char *d_aci_code; + const char *d_aci_url; + const char *d_diag_engine; + struct timeval d_diag_time; + ulong_t d_nsuspects; + nvlist_t **d_suspects; + nvlist_t *d_aci_event; +} sunFmProblem_data_t; + +typedef struct sunFmProblem_update_ctx { + const char *uc_host; + uint32_t uc_prog; + int uc_version; + const char *uc_index; + uint32_t uc_type; +} sunFmProblem_update_ctx_t; + +typedef nvlist_t sunFmFaultEvent_data_t; + +int sunFmProblemTable_init(void); +int sunFmFaultEventTable_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _PROBLEM_H */ diff --git a/usr/src/lib/fm/libfmd_snmp/common/resource.c b/usr/src/lib/fm/libfmd_snmp/common/resource.c new file mode 100644 index 0000000000..79c287af9a --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/resource.c @@ -0,0 +1,789 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <fm/fmd_adm.h> +#include <fm/fmd_snmp.h> +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <pthread.h> +#include <stddef.h> +#include <errno.h> +#include <libuutil.h> +#include "sunFM_impl.h" +#include "resource.h" + +static uu_avl_pool_t *rsrc_fmri_avl_pool; +static uu_avl_pool_t *rsrc_index_avl_pool; +static uu_avl_t *rsrc_fmri_avl; +static uu_avl_t *rsrc_index_avl; + +#define VALID_AVL_STATE (rsrc_fmri_avl_pool != NULL && \ + rsrc_index_avl_pool != NULL && rsrc_fmri_avl != NULL && \ + rsrc_index_avl != NULL) + +#define UPDATE_WAIT_MILLIS 10 /* poll interval in milliseconds */ + +/* + * Update types: single-index and all are mutually exclusive; a count + * update is optional. + */ +#define UCT_INDEX 0x1 +#define UCT_ALL 0x2 +#define UCT_COUNT 0x4 +#define UCT_FLAGS 0x7 + +#define RESOURCE_DATA_VALID(d) ((d)->d_valid == valid_stamp) + +/* + * Locking strategy is described in module.c. + */ +static ulong_t max_index; +static int valid_stamp; +static uint32_t rsrc_count; +static pthread_mutex_t update_lock; +static pthread_cond_t update_cv; +static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status; + +static Netsnmp_Node_Handler sunFmResourceTable_handler; +static Netsnmp_Node_Handler sunFmResourceCount_handler; + +static sunFmResource_data_t * +key_build(const char *fmri, const ulong_t index) +{ + static sunFmResource_data_t key; + + key.d_index = index; + if (fmri) + strlcpy(key.d_ari_fmri, fmri, sizeof (key.d_ari_fmri)); + else + key.d_ari_fmri[0] = '\0'; + + return (&key); +} + +/* + * If fmri is the fmri of a resource we have previously seen and indexed, return + * data for it. Otherwise, return NULL. Note that the resource may not be + * valid; that is, it may have been removed from the fault manager since its + * information was last updated. + */ +static sunFmResource_data_t * +resource_lookup_fmri(const char *fmri) +{ + sunFmResource_data_t *key; + + key = key_build(fmri, 0); + return (uu_avl_find(rsrc_fmri_avl, key, NULL, NULL)); +} + +/* + * If index corresponds to a resource we have previously seen and indexed, + * return data for it. Otherwise, return NULL. Note that the resource may + * not be valid; that is, it may have been expired from the fault manager + * since its information was last updated. + */ +static sunFmResource_data_t * +resource_lookup_index_exact(const ulong_t index) +{ + sunFmResource_data_t *key; + + key = key_build(NULL, index); + return (uu_avl_find(rsrc_index_avl, key, NULL, NULL)); +} + +/* + * If index corresponds to a valid (that is, extant as of latest information + * from the fault manager) resource, return the data for that resource. + * Otherwise, return the data for the valid resource whose index is as close as + * possible to index but not lower. This preserves the lexicographical + * ordering required for GETNEXT processing. + */ +static sunFmResource_data_t * +resource_lookup_index_nextvalid(const ulong_t index) +{ + sunFmResource_data_t *key, *data; + uu_avl_index_t idx; + + key = key_build(NULL, index); + + if ((data = uu_avl_find(rsrc_index_avl, key, NULL, &idx)) != NULL && + RESOURCE_DATA_VALID(data)) + return (data); + + data = uu_avl_nearest_next(rsrc_index_avl, idx); + + while (data != NULL && !RESOURCE_DATA_VALID(data)) { + (void) uu_avl_find(rsrc_index_avl, data, NULL, &idx); + data = uu_avl_nearest_next(rsrc_index_avl, idx); + } + + return (data); +} + +/* + * Possible update the contents of a single resource within the cache. This + * is our callback from fmd_rsrc_iter. + */ +static int +rsrcinfo_update_one(const fmd_adm_rsrcinfo_t *rsrcinfo, void *arg) +{ + const sunFmResource_update_ctx_t *update_ctx = + (sunFmResource_update_ctx_t *)arg; + sunFmResource_data_t *data = resource_lookup_fmri(rsrcinfo->ari_fmri); + + ++rsrc_count; + + /* + * A resource we haven't seen before. We're obligated to index + * it and link it into our cache so that we can find it, but we're + * not obligated to fill it in completely unless we're doing a + * full update or this is the resource we were asked for. This + * avoids unnecessary iteration and memory manipulation for data + * we're not going to return for this request. + */ + if (data == NULL) { + uu_avl_index_t idx; + + DEBUGMSGTL((MODNAME_STR, "found new resource %s\n", + rsrcinfo->ari_fmri)); + if ((data = SNMP_MALLOC_TYPEDEF(sunFmResource_data_t)) == + NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " + "new resource data at %s:%d\n", __FILE__, __LINE__); + return (1); + } + /* + * We allocate indices sequentially and never reuse them. + * This ensures we can always return valid GETNEXT responses + * without having to reindex, and it provides the user a + * more consistent view of the fault manager. + */ + data->d_index = ++max_index; + DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index, + rsrcinfo->ari_fmri, data)); + + strlcpy(data->d_ari_fmri, rsrcinfo->ari_fmri, + sizeof (data->d_ari_fmri)); + + uu_avl_node_init(data, &data->d_fmri_avl, rsrc_fmri_avl_pool); + (void) uu_avl_find(rsrc_fmri_avl, data, NULL, &idx); + uu_avl_insert(rsrc_fmri_avl, data, idx); + + uu_avl_node_init(data, &data->d_index_avl, rsrc_index_avl_pool); + (void) uu_avl_find(rsrc_index_avl, data, NULL, &idx); + uu_avl_insert(rsrc_index_avl, data, idx); + + DEBUGMSGTL((MODNAME_STR, "completed new resource %lu/%s@%p\n", + data->d_index, data->d_ari_fmri, data)); + } + + data->d_valid = valid_stamp; + + DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n", + data->d_index, data->d_ari_fmri, data, data->d_valid)); + + if ((update_ctx->uc_type & UCT_ALL) || + update_ctx->uc_index == data->d_index) { + strlcpy(data->d_ari_case, rsrcinfo->ari_case, + sizeof (data->d_ari_case)); + data->d_ari_flags = rsrcinfo->ari_flags; + } + + return (!(update_ctx->uc_type & UCT_ALL) && + update_ctx->uc_index == data->d_index); +} + +/* + * Update some or all resource data from fmd. If type includes UCT_ALL, all + * resources will be indexed and their data cached. If type includes + * UCT_INDEX, updates will stop once the resource matching index has been + * updated. If UCT_COUNT is set, the number of faulted resources will be + * set. + * + * Returns appropriate SNMP error codes. + */ +static int +rsrcinfo_update(sunFmResource_update_ctx_t *update_ctx) +{ + fmd_adm_t *adm; + int err; + + ASSERT(update_ctx != NULL); + ASSERT((update_ctx->uc_type & (UCT_ALL|UCT_INDEX)) != + (UCT_ALL|UCT_INDEX)); + ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); + ASSERT(VALID_AVL_STATE); + + if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, + update_ctx->uc_version)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " + "failed: %s\n", strerror(errno)); + return (SNMP_ERR_RESOURCEUNAVAILABLE); + } + + if (update_ctx->uc_type == UCT_COUNT) { + err = fmd_adm_rsrc_count(adm, update_ctx->uc_all, &rsrc_count); + } else { + ++valid_stamp; + rsrc_count = 0; + err = fmd_adm_rsrc_iter(adm, update_ctx->uc_all, + rsrcinfo_update_one, update_ctx); + DEBUGMSGTL((MODNAME_STR, "resource iteration completed\n")); + } + + fmd_adm_close(adm); + + if (err != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": fmd resource information " + "update failed: %s\n", fmd_adm_errmsg(adm)); + return (SNMP_ERR_RESOURCEUNAVAILABLE); + } + + return (SNMP_ERR_NOERROR); +} + +/*ARGSUSED*/ +static void +update_thread(void *arg) +{ + /* + * The current rsrcinfo_update implementation offers minimal savings + * for the use of index-only updates; therefore we always do a full + * update. If it becomes advantageous to limit updates to a single + * index, the contexts can be queued by the handler instead. + */ + sunFmResource_update_ctx_t uc; + + uc.uc_host = NULL; + uc.uc_prog = FMD_ADM_PROGRAM; + uc.uc_version = FMD_ADM_VERSION; + + uc.uc_index = 0; + uc.uc_type = UCT_ALL; + + for (;;) { + (void) pthread_mutex_lock(&update_lock); + update_status = US_QUIET; + while (update_status == US_QUIET) + (void) pthread_cond_wait(&update_cv, &update_lock); + update_status = US_INPROGRESS; + (void) pthread_mutex_unlock(&update_lock); + (void) rsrcinfo_update(&uc); + } +} + +static void +request_update(void) +{ + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + (void) pthread_mutex_unlock(&update_lock); + return; + } + update_status = US_NEEDED; + (void) pthread_cond_signal(&update_cv); + (void) pthread_mutex_unlock(&update_lock); +} + +/*ARGSUSED*/ +static int +resource_compare_fmri(const void *l, const void *r, void *private) +{ + sunFmResource_data_t *l_data = (sunFmResource_data_t *)l; + sunFmResource_data_t *r_data = (sunFmResource_data_t *)r; + + ASSERT(l_data != NULL && r_data != NULL); + + return (strcmp(l_data->d_ari_fmri, r_data->d_ari_fmri)); +} + +/*ARGSUSED*/ +static int +resource_compare_index(const void *l, const void *r, void *private) +{ + sunFmResource_data_t *l_data = (sunFmResource_data_t *)l; + sunFmResource_data_t *r_data = (sunFmResource_data_t *)r; + + ASSERT(l_data != NULL && r_data != NULL); + + return (l_data->d_index < r_data->d_index ? -1 : + l_data->d_index > r_data->d_index ? 1 : 0); +} + +int +sunFmResourceTable_init(void) +{ + static oid sunFmResourceTable_oid[] = { SUNFMRESOURCETABLE_OID }; + static oid sunFmResourceCount_oid[] = { SUNFMRESOURCECOUNT_OID, 0 }; + netsnmp_table_registration_info *table_info; + netsnmp_handler_registration *handler; + int err; + + if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", + strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", + strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + + if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, + NULL)) != 0) { + snmp_log(LOG_ERR, MODNAME_STR ": error creating update " + "thread: %s\n", strerror(err)); + return (MIB_REGISTRATION_FAILED); + } + + if ((table_info = + SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) + return (MIB_REGISTRATION_FAILED); + + if ((handler = netsnmp_create_handler_registration("sunFmResourceTable", + sunFmResourceTable_handler, sunFmResourceTable_oid, + OID_LENGTH(sunFmResourceTable_oid), HANDLER_CAN_RONLY)) == NULL) { + SNMP_FREE(table_info); + return (MIB_REGISTRATION_FAILED); + } + + /* + * The Net-SNMP template uses add_indexes here, but that + * function is unsafe because it does not check for failure. + */ + if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { + SNMP_FREE(table_info); + SNMP_FREE(handler); + return (MIB_REGISTRATION_FAILED); + } + + table_info->min_column = SUNFMRESOURCE_COLMIN; + table_info->max_column = SUNFMRESOURCE_COLMAX; + + if ((rsrc_fmri_avl_pool = uu_avl_pool_create("rsrc_fmri", + sizeof (sunFmResource_data_t), + offsetof(sunFmResource_data_t, d_fmri_avl), resource_compare_fmri, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl pool creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + } + + if ((rsrc_fmri_avl = uu_avl_create(rsrc_fmri_avl_pool, NULL, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_pool_destroy(rsrc_fmri_avl_pool); + return (MIB_REGISTRATION_FAILED); + } + + if ((rsrc_index_avl_pool = uu_avl_pool_create("rsrc_index", + sizeof (sunFmResource_data_t), + offsetof(sunFmResource_data_t, d_index_avl), + resource_compare_index, UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl pool creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(rsrc_fmri_avl); + uu_avl_pool_destroy(rsrc_fmri_avl_pool); + } + + if ((rsrc_index_avl = uu_avl_create(rsrc_index_avl_pool, NULL, + UU_AVL_DEBUG)) == NULL) { + snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl creation " + "failed: %s\n", uu_strerror(uu_error())); + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(rsrc_fmri_avl); + uu_avl_pool_destroy(rsrc_fmri_avl_pool); + uu_avl_pool_destroy(rsrc_index_avl_pool); + return (MIB_REGISTRATION_FAILED); + } + + if ((err = netsnmp_register_table(handler, table_info)) != + MIB_REGISTERED_OK) { + snmp_free_varbind(table_info->indexes); + SNMP_FREE(table_info); + SNMP_FREE(handler); + uu_avl_destroy(rsrc_fmri_avl); + uu_avl_pool_destroy(rsrc_fmri_avl_pool); + uu_avl_destroy(rsrc_index_avl); + uu_avl_pool_destroy(rsrc_index_avl_pool); + return (err); + } + + if ((err = netsnmp_register_read_only_instance( + netsnmp_create_handler_registration("sunFmResourceCount", + sunFmResourceCount_handler, sunFmResourceCount_oid, + OID_LENGTH(sunFmResourceCount_oid), HANDLER_CAN_RONLY))) != + MIB_REGISTERED_OK) { + /* + * There's no way to unregister the table handler, so we + * can't free any of the data, either. + */ + return (err); + } + + return (MIB_REGISTERED_OK); +} + +/* + * These two functions form the core of GET/GETNEXT/GETBULK handling (the + * only kind we do). They perform two functions: + * + * - First, frob the request to set all the index variables to correspond + * to the value that's going to be returned. For GET, this is a nop; + * for GETNEXT/GETBULK it always requires some work. + * - Second, find and return the fmd resource information corresponding to + * the (possibly updated) indices. + * + * These should be as fast as possible; they run in the agent thread. + */ +static sunFmResource_data_t * +sunFmResourceTable_nextrsrc(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmResource_data_t *data; + netsnmp_variable_list *var; + ulong_t index; + + /* + * If we have no index, we must make one. + */ + if (table_info->number_indexes < 1) { + oid tmpoid[MAX_OID_LEN]; + index = 1; + + DEBUGMSGTL((MODNAME_STR, "nextrsrc: no indexes given\n")); + var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); + snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index, + sizeof (index)); + memcpy(tmpoid, reginfo->rootoid, + reginfo->rootoid_len * sizeof (oid)); + tmpoid[reginfo->rootoid_len] = 1; + tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; + if (build_oid(&var->name, &var->name_length, tmpoid, + reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) { + snmp_free_varbind(var); + return (NULL); + } + DEBUGMSGTL((MODNAME_STR, "nextrsrc: built fake index:\n")); + DEBUGMSGVAR((MODNAME_STR, var)); + DEBUGMSG((MODNAME_STR, "\n")); + } else { + var = table_info->indexes; + index = *var->val.integer; + DEBUGMSGTL((MODNAME_STR, "nextrsrc: received index:\n")); + DEBUGMSGVAR((MODNAME_STR, var)); + DEBUGMSG((MODNAME_STR, "\n")); + index++; + } + + if ((data = resource_lookup_index_nextvalid(index)) == NULL) { + DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for " + "index %lu; trying next column\n", index)); + if (table_info->colnum >= SUNFMRESOURCE_COLMAX) { + snmp_free_varbind(var); + table_info->indexes = NULL; + table_info->number_indexes = 0; + DEBUGMSGTL((MODNAME_STR, "nextrsrc: out of columns\n")); + return (NULL); + } + table_info->colnum++; + index = 1; + + data = resource_lookup_index_nextvalid(index); + } + + if (data == NULL) { + DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for " + "index %lu; stopping\n", index)); + snmp_free_varbind(var); + table_info->indexes = NULL; + table_info->number_indexes = 0; + return (NULL); + } + + *var->val.integer = index; + table_info->indexes = var; + + DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index, + data->d_ari_fmri, data)); + + return (data); +} + +/*ARGSUSED*/ +static sunFmResource_data_t * +sunFmResourceTable_rsrc(netsnmp_handler_registration *reginfo, + netsnmp_table_request_info *table_info) +{ + sunFmResource_data_t *data; + netsnmp_variable_list *var; + + ASSERT(table_info->number_indexes == 1); + + return (resource_lookup_index_exact(table_info->index_oid[0])); +} + +static void +sunFmResourceTable_return(unsigned int reg, void *arg) +{ + netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; + netsnmp_request_info *request; + netsnmp_agent_request_info *reqinfo; + netsnmp_handler_registration *reginfo; + netsnmp_mib_handler *handler; + netsnmp_table_request_info *table_info; + netsnmp_variable_list *var; + sunFmResource_data_t *data; + ulong_t rsrcstate; + + ASSERT(netsnmp_handler_check_cache(cache) != NULL); + + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return, + cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + request = cache->requests; + reqinfo = cache->reqinfo; + reginfo = cache->reginfo; + handler = cache->handler; + + var = request->requestvb; + table_info = netsnmp_extract_table_info(request); + request->delegated = 0; + + ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN); + ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX); + + /* + * table_info->colnum contains the column number requested. + * table_info->indexes contains a linked list of snmp variable + * bindings for the indexes of the table. Values in the list + * have been set corresponding to the indexes of the + * request. We have other guarantees as well: + * + * - The column number is always within range. + * - If we have no index data, table_info->index_oid_len is 0. + * - We will never receive requests outside our table nor + * those with the first subid anything other than 1 (Entry) + * nor those without a column number. This is true even + * for GETNEXT requests. + */ + + switch (reqinfo->mode) { + case MODE_GET: + if ((data = sunFmResourceTable_rsrc(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + case MODE_GETNEXT: + case MODE_GETBULK: + if ((data = sunFmResourceTable_nextrsrc(reginfo, table_info)) == + NULL) { + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + break; + default: + snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n", + reqinfo->mode); + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + switch (table_info->colnum) { + case SUNFMRESOURCE_COL_FMRI: + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_ari_fmri, + strlen(data->d_ari_fmri)); + break; + case SUNFMRESOURCE_COL_STATUS: + switch (data->d_ari_flags & + (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) { + default: + rsrcstate = SUNFMRESOURCE_STATE_OK; + break; + case FMD_ADM_RSRC_FAULTY: + rsrcstate = SUNFMRESOURCE_STATE_DEGRADED; + break; + case FMD_ADM_RSRC_UNUSABLE: + rsrcstate = SUNFMRESOURCE_STATE_UNKNOWN; + break; + case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE: + rsrcstate = SUNFMRESOURCE_STATE_FAULTED; + break; + } + netsnmp_table_build_result(reginfo, request, table_info, + ASN_INTEGER, (uchar_t *)&rsrcstate, + sizeof (rsrcstate)); + break; + case SUNFMRESOURCE_COL_DIAGNOSISUUID: + netsnmp_table_build_result(reginfo, request, table_info, + ASN_OCTET_STR, (uchar_t *)data->d_ari_case, + strlen(data->d_ari_case)); + break; + default: + break; + } + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); +} + +static int +sunFmResourceTable_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + netsnmp_request_info *request; + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + request_update(); + + for (request = requests; request; request = request->next) { + if (request->processed != 0) + continue; + + if (netsnmp_extract_table_info(request) == NULL) + continue; + + request->delegated = 1; + (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return, + (void *) netsnmp_create_delegated_cache(handler, reginfo, + reqinfo, request, NULL)); + } + + return (SNMP_ERR_NOERROR); +} + +static void +sunFmResourceCount_return(unsigned int reg, void *arg) +{ + netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; + netsnmp_request_info *request; + netsnmp_agent_request_info *reqinfo; + netsnmp_handler_registration *reginfo; + netsnmp_mib_handler *handler; + ulong_t rsrc_count_long; + int err; + + ASSERT(netsnmp_handler_check_cache(cache) != NULL); + + (void) pthread_mutex_lock(&update_lock); + if (update_status != US_QUIET) { + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return, + cache); + (void) pthread_mutex_unlock(&update_lock); + return; + } + + request = cache->requests; + reqinfo = cache->reqinfo; + reginfo = cache->reginfo; + handler = cache->handler; + + request->delegated = 0; + + switch (reqinfo->mode) { + case MODE_GET: + DEBUGMSGTL((MODNAME_STR, "resource count is %u\n", rsrc_count)); + rsrc_count_long = (ulong_t)rsrc_count; + snmp_set_var_typed_value(request->requestvb, ASN_GAUGE, + (uchar_t *)&rsrc_count_long, sizeof (rsrc_count_long)); + break; + default: + snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n", + reqinfo->mode); + } + + netsnmp_free_delegated_cache(cache); + (void) pthread_mutex_unlock(&update_lock); +} + +static int +sunFmResourceCount_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + struct timeval tv; + + tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; + tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; + + request_update(); + + /* + * We are never called for a GETNEXT when registered as an + * instance; it's handled for us and converted to a GET. + * Also, an instance handler is given only one request at a time, so + * we don't need to loop over a list of requests. + */ + + if (requests->processed != 0) + return (SNMP_ERR_NOERROR); + + requests->delegated = 1; + (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return, + (void *) netsnmp_create_delegated_cache(handler, reginfo, + reqinfo, requests, NULL)); + + return (SNMP_ERR_NOERROR); +} diff --git a/usr/src/lib/fm/libfmd_snmp/common/resource.h b/usr/src/lib/fm/libfmd_snmp/common/resource.h new file mode 100644 index 0000000000..149e7793fa --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/resource.h @@ -0,0 +1,65 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _RESOURCE_H +#define _RESOURCE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> +#include <libuutil.h> + +typedef struct sunFmResource_data { + ulong_t d_index; /* MIB index */ + int d_valid; /* iteration stamp */ + uu_avl_node_t d_fmri_avl; /* by-FMRI AVL node */ + uu_avl_node_t d_index_avl; /* by-index AVL node */ + char d_ari_fmri[256]; /* resource FMRI */ + char d_ari_case[256]; /* resource state case UUID */ + uint_t d_ari_flags; /* resource flags */ +} sunFmResource_data_t; + +typedef struct sunFmResource_update_ctx { + const char *uc_host; + uint32_t uc_prog; + int uc_version; + int uc_all; + ulong_t uc_index; + uint32_t uc_type; +} sunFmResource_update_ctx_t; + +int sunFmResourceTable_init(void); +int sunFmResourceCount_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _RESOURCE_H */ diff --git a/usr/src/lib/fm/libfmd_snmp/common/scheme.c b/usr/src/lib/fm/libfmd_snmp/common/scheme.c new file mode 100644 index 0000000000..a357c23e71 --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/scheme.c @@ -0,0 +1,297 @@ +/* + * 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 2006 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 <sys/types.h> +#include <sys/systeminfo.h> +#include <fm/fmd_snmp.h> +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <libnvpair.h> +#include <limits.h> +#include <strings.h> +#include <stddef.h> +#include <unistd.h> +#include <dlfcn.h> +#include <errno.h> + +#define SCHEMEDIR_BASE "/usr/lib/fm/fmd/schemes" + +#if defined(__sparcv9) +#define DEFAULTSCHEMEDIR SCHEMEDIR_BASE "/sparcv9" +#elif defined(__amd64) +#define DEFAULTSCHEMEDIR SCHEMEDIR_BASE "/amd64" +#else +#define DEFAULTSCHEMEDIR SCHEMEDIR_BASE +#endif + +typedef struct fmd_scheme_ops { + int (*sop_init)(void); + void (*sop_fini)(void); + ssize_t (*sop_nvl2str)(nvlist_t *, char *, size_t); +} fmd_scheme_ops_t; + +typedef struct fmd_scheme_opd { + const char *opd_name; /* symbol name of scheme function */ + size_t opd_off; /* offset within fmd_scheme_ops_t */ +} fmd_scheme_opd_t; + +typedef struct fmd_scheme { + struct fmd_scheme *sch_next; /* next scheme on list of schemes */ + char *sch_name; /* name of this scheme (fmri prefix) */ + void *sch_dlp; /* libdl(3DL) shared library handle */ + int sch_err; /* if negative entry, errno to return */ + fmd_scheme_ops_t sch_ops; /* scheme function pointers */ +} fmd_scheme_t; + +static fmd_scheme_t *sch_list; /* list of cached schemes */ +static char *g_root; /* fmd root dir */ + +static long +fmd_scheme_notsup(void) +{ + errno = ENOTSUP; + return (-1); +} + +static int +fmd_scheme_nop(void) +{ + return (0); +} + +/* + * Default values for the scheme ops. If a scheme function is not defined in + * the module, then this operation is implemented using the default function. + */ +static const fmd_scheme_ops_t _fmd_scheme_default_ops = { + (int (*)())fmd_scheme_nop, /* sop_init */ + (void (*)())fmd_scheme_nop, /* sop_fini */ + (ssize_t (*)())fmd_scheme_notsup, /* sop_nvl2str */ +}; + +/* + * Scheme ops descriptions. These names and offsets are used by the function + * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t. + */ +static const fmd_scheme_opd_t _fmd_scheme_ops[] = { + { "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) }, + { "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) }, + { "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) }, + { NULL, 0 } +}; + +static fmd_scheme_t * +fmd_scheme_create(const char *name) +{ + fmd_scheme_t *sp; + + if ((sp = malloc(sizeof (fmd_scheme_t))) == NULL || + (sp->sch_name = strdup(name)) == NULL) { + free(sp); + return (NULL); + } + + sp->sch_next = sch_list; + sp->sch_dlp = NULL; + sp->sch_err = 0; + sp->sch_ops = _fmd_scheme_default_ops; + + sch_list = sp; + return (sp); +} + +static int +fmd_scheme_rtld_init(fmd_scheme_t *sp) +{ + const fmd_scheme_opd_t *opd; + void *p; + + for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) { + if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL) + *(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p; + } + + return (sp->sch_ops.sop_init()); +} + +static fmd_scheme_t * +fmd_scheme_lookup(const char *dir, const char *name) +{ + fmd_scheme_t *sp; + char path[PATH_MAX]; + + for (sp = sch_list; sp != NULL; sp = sp->sch_next) { + if (strcmp(name, sp->sch_name) == 0) + return (sp); + } + + if ((sp = fmd_scheme_create(name)) == NULL) + return (NULL); /* errno is set for us */ + + (void) snprintf(path, sizeof (path), "%s%s/%s.so", + g_root ? g_root : "", dir, name); + + if (access(path, F_OK) != 0) { + sp->sch_err = errno; + return (sp); + } + + if ((sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_PARENT)) == + NULL) { + sp->sch_err = ELIBACC; + return (sp); + } + + if (fmd_scheme_rtld_init(sp) != 0) { + sp->sch_err = errno; + (void) dlclose(sp->sch_dlp); + sp->sch_dlp = NULL; + } + + return (sp); +} + +char * +sunFm_nvl2str(nvlist_t *nvl) +{ + fmd_scheme_t *sp; + char c, *name, *s = NULL; + ssize_t len; + + if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) { + DEBUGMSGTL((MODNAME_STR, "fmri does not contain required " + "'%s' nvpair\n", FM_FMRI_SCHEME)); + return (NULL); + } + + if ((sp = fmd_scheme_lookup(DEFAULTSCHEMEDIR, name)) == NULL || + sp->sch_dlp == NULL || sp->sch_err != 0) { + const char *msg = + sp->sch_err == ELIBACC ? dlerror() : strerror(sp->sch_err); + DEBUGMSGTL((MODNAME_STR, "cannot init '%s' scheme library to " + "format fmri: %s\n", name, msg ? msg : "unknown error")); + return (NULL); + } + + if ((len = sp->sch_ops.sop_nvl2str(nvl, &c, sizeof (c))) == -1 || + (s = malloc(len + 1)) == NULL || + sp->sch_ops.sop_nvl2str(nvl, s, len + 1) == -1) { + DEBUGMSGTL((MODNAME_STR, "cannot format fmri using scheme '%s'", + name)); + free(s); + return (NULL); + } + + return (s); +} + +void * +fmd_fmri_alloc(size_t size) +{ + return (malloc(size)); +} + +void * +fmd_fmri_zalloc(size_t size) +{ + void *data; + + if ((data = malloc(size)) != NULL) + bzero(data, size); + + return (data); +} + +/*ARGSUSED*/ +void +fmd_fmri_free(void *data, size_t size) +{ + free(data); +} + +int +fmd_fmri_error(int err) +{ + errno = err; + return (-1); +} + +char * +fmd_fmri_strescape(const char *s) +{ + return (strdup(s)); +} + +char * +fmd_fmri_strdup(const char *s) +{ + return (strdup(s)); +} + +void +fmd_fmri_strfree(char *s) +{ + free(s); +} + +const char * +fmd_fmri_get_rootdir(void) +{ + return (g_root ? g_root : ""); +} + +const char * +fmd_fmri_get_platform(void) +{ + static char platform[MAXNAMELEN]; + + if (platform[0] == '\0') + (void) sysinfo(SI_PLATFORM, platform, sizeof (platform)); + + return (platform); +} + +uint64_t +fmd_fmri_get_drgen(void) +{ + return (0); +} + +int +fmd_fmri_set_errno(int err) +{ + errno = err; + return (-1); +} + +void +fmd_fmri_warn(const char *format, ...) +{ +} diff --git a/usr/src/lib/fm/libfmd_snmp/common/sunFM_impl.h b/usr/src/lib/fm/libfmd_snmp/common/sunFM_impl.h new file mode 100644 index 0000000000..4f32bee13c --- /dev/null +++ b/usr/src/lib/fm/libfmd_snmp/common/sunFM_impl.h @@ -0,0 +1,82 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SUNFM_IMPL_H +#define _SUNFM_IMPL_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#include <net-snmp/net-snmp-config.h> +#include <net-snmp/net-snmp-includes.h> +#include <net-snmp/agent/net-snmp-agent-includes.h> +#include <libnvpair.h> +#include <stdarg.h> + +#ifdef DEBUG +extern void sunFm_panic(const char *format, ...) __NORETURN; +extern void sunFm_vpanic(const char *format, va_list ap) __NORETURN; +extern int sunFm_assert(const char *, const char *, int); +#define ASSERT(x) ((void)((x) || sunFm_assert(#x, __FILE__, __LINE__))) +#else +extern void sunFm_panic(const char *format, ...); +extern void sunFm_vpanic(const char *format, va_list ap); +#define ASSERT(x) +#endif + +typedef int (*sunFm_table_init_func_t)(void); + +typedef struct sunFm_table { + const char *t_name; + sunFm_table_init_func_t t_init; +} sunFm_table_t; + +#define TABLE_INIT(__t) __t##_init +#define TABLE_NAME(__t) #__t +#define TABLE_REG(__t) { TABLE_NAME(__t), TABLE_INIT(__t) } +#define TABLE_NULL { NULL, NULL } + +/* + * The definition of netsnmp_table_helper_add_index in <net-snmp/agent/table.h> + * is defective; it includes a ; at the end. We have to use our own. + */ +#ifdef netsnmp_table_helper_add_index +#undef netsnmp_table_helper_add_index +#define netsnmp_table_helper_add_index(tinfo, type) \ + snmp_varlist_add_variable(&tinfo->indexes, NULL, 0, (uchar_t)type, \ + NULL, 0) +#endif /* netsnmp_table_helper_add_index */ + +extern char *sunFm_nvl2str(nvlist_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _SUNFM_IMPL_H */ |
