summaryrefslogtreecommitdiff
path: root/usr/src/lib/fm/libfmd_snmp/common
diff options
context:
space:
mode:
authorwesolows <none@none>2006-01-20 16:06:55 -0800
committerwesolows <none@none>2006-01-20 16:06:55 -0800
commit749f21d359d8fbd020c974a1a5227316221bfc9c (patch)
tree41bfaf56a9f0c31430c9dd2e35fedffd5d69b220 /usr/src/lib/fm/libfmd_snmp/common
parent1f017db093f7568e41a3d8c6429e13daf5424ff8 (diff)
downloadillumos-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.c60
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/fmd_snmp.h114
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/init.c86
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/llib-lfmd_snmp32
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/module.c688
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/module.h64
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/problem.c1015
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/problem.h70
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/resource.c789
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/resource.h65
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/scheme.c297
-rw-r--r--usr/src/lib/fm/libfmd_snmp/common/sunFM_impl.h82
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 */