11303Swesolows /* 21303Swesolows * CDDL HEADER START 31303Swesolows * 41303Swesolows * The contents of this file are subject to the terms of the 51303Swesolows * Common Development and Distribution License (the "License"). 61303Swesolows * You may not use this file except in compliance with the License. 71303Swesolows * 81303Swesolows * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91303Swesolows * or http://www.opensolaris.org/os/licensing. 101303Swesolows * See the License for the specific language governing permissions 111303Swesolows * and limitations under the License. 121303Swesolows * 131303Swesolows * When distributing Covered Code, include this CDDL HEADER in each 141303Swesolows * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151303Swesolows * If applicable, add the following below this CDDL HEADER, with the 161303Swesolows * fields enclosed by brackets "[]" replaced with your own identifying 171303Swesolows * information: Portions Copyright [yyyy] [name of copyright owner] 181303Swesolows * 191303Swesolows * CDDL HEADER END 201303Swesolows */ 211303Swesolows 221303Swesolows /* 231303Swesolows * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 241303Swesolows * Use is subject to license terms. 251303Swesolows */ 261303Swesolows 271303Swesolows #pragma ident "%Z%%M% %I% %E% SMI" 281303Swesolows 291303Swesolows #include <fm/fmd_adm.h> 301303Swesolows #include <fm/fmd_snmp.h> 311303Swesolows #include <net-snmp/net-snmp-config.h> 321303Swesolows #include <net-snmp/net-snmp-includes.h> 331303Swesolows #include <net-snmp/agent/net-snmp-agent-includes.h> 341303Swesolows #include <pthread.h> 351303Swesolows #include <stddef.h> 361303Swesolows #include <errno.h> 371303Swesolows #include <libuutil.h> 381303Swesolows #include "sunFM_impl.h" 391303Swesolows #include "resource.h" 401303Swesolows 411303Swesolows static uu_avl_pool_t *rsrc_fmri_avl_pool; 421303Swesolows static uu_avl_pool_t *rsrc_index_avl_pool; 431303Swesolows static uu_avl_t *rsrc_fmri_avl; 441303Swesolows static uu_avl_t *rsrc_index_avl; 451303Swesolows 461303Swesolows #define VALID_AVL_STATE (rsrc_fmri_avl_pool != NULL && \ 471303Swesolows rsrc_index_avl_pool != NULL && rsrc_fmri_avl != NULL && \ 481303Swesolows rsrc_index_avl != NULL) 491303Swesolows 501303Swesolows #define UPDATE_WAIT_MILLIS 10 /* poll interval in milliseconds */ 511303Swesolows 521303Swesolows /* 531303Swesolows * Update types: single-index and all are mutually exclusive; a count 541303Swesolows * update is optional. 551303Swesolows */ 561303Swesolows #define UCT_INDEX 0x1 571303Swesolows #define UCT_ALL 0x2 581303Swesolows #define UCT_COUNT 0x4 591303Swesolows #define UCT_FLAGS 0x7 601303Swesolows 611303Swesolows #define RESOURCE_DATA_VALID(d) ((d)->d_valid == valid_stamp) 621303Swesolows 631303Swesolows /* 641303Swesolows * Locking strategy is described in module.c. 651303Swesolows */ 661303Swesolows static ulong_t max_index; 671303Swesolows static int valid_stamp; 681303Swesolows static uint32_t rsrc_count; 691303Swesolows static pthread_mutex_t update_lock; 701303Swesolows static pthread_cond_t update_cv; 711303Swesolows static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status; 721303Swesolows 731303Swesolows static Netsnmp_Node_Handler sunFmResourceTable_handler; 741303Swesolows static Netsnmp_Node_Handler sunFmResourceCount_handler; 751303Swesolows 761303Swesolows static sunFmResource_data_t * 771303Swesolows key_build(const char *fmri, const ulong_t index) 781303Swesolows { 791303Swesolows static sunFmResource_data_t key; 801303Swesolows 811303Swesolows key.d_index = index; 821303Swesolows if (fmri) 83*2134Swesolows (void) strlcpy(key.d_ari_fmri, fmri, sizeof (key.d_ari_fmri)); 841303Swesolows else 851303Swesolows key.d_ari_fmri[0] = '\0'; 861303Swesolows 871303Swesolows return (&key); 881303Swesolows } 891303Swesolows 901303Swesolows /* 911303Swesolows * If fmri is the fmri of a resource we have previously seen and indexed, return 921303Swesolows * data for it. Otherwise, return NULL. Note that the resource may not be 931303Swesolows * valid; that is, it may have been removed from the fault manager since its 941303Swesolows * information was last updated. 951303Swesolows */ 961303Swesolows static sunFmResource_data_t * 971303Swesolows resource_lookup_fmri(const char *fmri) 981303Swesolows { 991303Swesolows sunFmResource_data_t *key; 1001303Swesolows 1011303Swesolows key = key_build(fmri, 0); 1021303Swesolows return (uu_avl_find(rsrc_fmri_avl, key, NULL, NULL)); 1031303Swesolows } 1041303Swesolows 1051303Swesolows /* 1061303Swesolows * If index corresponds to a resource we have previously seen and indexed, 1071303Swesolows * return data for it. Otherwise, return NULL. Note that the resource may 1081303Swesolows * not be valid; that is, it may have been expired from the fault manager 1091303Swesolows * since its information was last updated. 1101303Swesolows */ 1111303Swesolows static sunFmResource_data_t * 1121303Swesolows resource_lookup_index_exact(const ulong_t index) 1131303Swesolows { 1141303Swesolows sunFmResource_data_t *key; 1151303Swesolows 1161303Swesolows key = key_build(NULL, index); 1171303Swesolows return (uu_avl_find(rsrc_index_avl, key, NULL, NULL)); 1181303Swesolows } 1191303Swesolows 1201303Swesolows /* 1211303Swesolows * If index corresponds to a valid (that is, extant as of latest information 1221303Swesolows * from the fault manager) resource, return the data for that resource. 1231303Swesolows * Otherwise, return the data for the valid resource whose index is as close as 1241303Swesolows * possible to index but not lower. This preserves the lexicographical 1251303Swesolows * ordering required for GETNEXT processing. 1261303Swesolows */ 1271303Swesolows static sunFmResource_data_t * 1281303Swesolows resource_lookup_index_nextvalid(const ulong_t index) 1291303Swesolows { 1301303Swesolows sunFmResource_data_t *key, *data; 1311303Swesolows uu_avl_index_t idx; 1321303Swesolows 1331303Swesolows key = key_build(NULL, index); 1341303Swesolows 1351303Swesolows if ((data = uu_avl_find(rsrc_index_avl, key, NULL, &idx)) != NULL && 1361303Swesolows RESOURCE_DATA_VALID(data)) 1371303Swesolows return (data); 1381303Swesolows 1391303Swesolows data = uu_avl_nearest_next(rsrc_index_avl, idx); 1401303Swesolows 1411303Swesolows while (data != NULL && !RESOURCE_DATA_VALID(data)) { 1421303Swesolows (void) uu_avl_find(rsrc_index_avl, data, NULL, &idx); 1431303Swesolows data = uu_avl_nearest_next(rsrc_index_avl, idx); 1441303Swesolows } 1451303Swesolows 1461303Swesolows return (data); 1471303Swesolows } 1481303Swesolows 1491303Swesolows /* 1501303Swesolows * Possible update the contents of a single resource within the cache. This 1511303Swesolows * is our callback from fmd_rsrc_iter. 1521303Swesolows */ 1531303Swesolows static int 1541303Swesolows rsrcinfo_update_one(const fmd_adm_rsrcinfo_t *rsrcinfo, void *arg) 1551303Swesolows { 1561303Swesolows const sunFmResource_update_ctx_t *update_ctx = 1571303Swesolows (sunFmResource_update_ctx_t *)arg; 1581303Swesolows sunFmResource_data_t *data = resource_lookup_fmri(rsrcinfo->ari_fmri); 1591303Swesolows 1601303Swesolows ++rsrc_count; 1611303Swesolows 1621303Swesolows /* 1631303Swesolows * A resource we haven't seen before. We're obligated to index 1641303Swesolows * it and link it into our cache so that we can find it, but we're 1651303Swesolows * not obligated to fill it in completely unless we're doing a 1661303Swesolows * full update or this is the resource we were asked for. This 1671303Swesolows * avoids unnecessary iteration and memory manipulation for data 1681303Swesolows * we're not going to return for this request. 1691303Swesolows */ 1701303Swesolows if (data == NULL) { 1711303Swesolows uu_avl_index_t idx; 1721303Swesolows 1731303Swesolows DEBUGMSGTL((MODNAME_STR, "found new resource %s\n", 1741303Swesolows rsrcinfo->ari_fmri)); 1751303Swesolows if ((data = SNMP_MALLOC_TYPEDEF(sunFmResource_data_t)) == 1761303Swesolows NULL) { 1771303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " 1781303Swesolows "new resource data at %s:%d\n", __FILE__, __LINE__); 1791303Swesolows return (1); 1801303Swesolows } 1811303Swesolows /* 1821303Swesolows * We allocate indices sequentially and never reuse them. 1831303Swesolows * This ensures we can always return valid GETNEXT responses 1841303Swesolows * without having to reindex, and it provides the user a 1851303Swesolows * more consistent view of the fault manager. 1861303Swesolows */ 1871303Swesolows data->d_index = ++max_index; 1881303Swesolows DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index, 1891303Swesolows rsrcinfo->ari_fmri, data)); 1901303Swesolows 191*2134Swesolows (void) strlcpy(data->d_ari_fmri, rsrcinfo->ari_fmri, 1921303Swesolows sizeof (data->d_ari_fmri)); 1931303Swesolows 1941303Swesolows uu_avl_node_init(data, &data->d_fmri_avl, rsrc_fmri_avl_pool); 1951303Swesolows (void) uu_avl_find(rsrc_fmri_avl, data, NULL, &idx); 1961303Swesolows uu_avl_insert(rsrc_fmri_avl, data, idx); 1971303Swesolows 1981303Swesolows uu_avl_node_init(data, &data->d_index_avl, rsrc_index_avl_pool); 1991303Swesolows (void) uu_avl_find(rsrc_index_avl, data, NULL, &idx); 2001303Swesolows uu_avl_insert(rsrc_index_avl, data, idx); 2011303Swesolows 2021303Swesolows DEBUGMSGTL((MODNAME_STR, "completed new resource %lu/%s@%p\n", 2031303Swesolows data->d_index, data->d_ari_fmri, data)); 2041303Swesolows } 2051303Swesolows 2061303Swesolows data->d_valid = valid_stamp; 2071303Swesolows 2081303Swesolows DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n", 2091303Swesolows data->d_index, data->d_ari_fmri, data, data->d_valid)); 2101303Swesolows 2111303Swesolows if ((update_ctx->uc_type & UCT_ALL) || 2121303Swesolows update_ctx->uc_index == data->d_index) { 213*2134Swesolows (void) strlcpy(data->d_ari_case, rsrcinfo->ari_case, 2141303Swesolows sizeof (data->d_ari_case)); 2151303Swesolows data->d_ari_flags = rsrcinfo->ari_flags; 2161303Swesolows } 2171303Swesolows 2181303Swesolows return (!(update_ctx->uc_type & UCT_ALL) && 2191303Swesolows update_ctx->uc_index == data->d_index); 2201303Swesolows } 2211303Swesolows 2221303Swesolows /* 2231303Swesolows * Update some or all resource data from fmd. If type includes UCT_ALL, all 2241303Swesolows * resources will be indexed and their data cached. If type includes 2251303Swesolows * UCT_INDEX, updates will stop once the resource matching index has been 2261303Swesolows * updated. If UCT_COUNT is set, the number of faulted resources will be 2271303Swesolows * set. 2281303Swesolows * 2291303Swesolows * Returns appropriate SNMP error codes. 2301303Swesolows */ 2311303Swesolows static int 2321303Swesolows rsrcinfo_update(sunFmResource_update_ctx_t *update_ctx) 2331303Swesolows { 2341303Swesolows fmd_adm_t *adm; 2351303Swesolows int err; 2361303Swesolows 2371303Swesolows ASSERT(update_ctx != NULL); 2381303Swesolows ASSERT((update_ctx->uc_type & (UCT_ALL|UCT_INDEX)) != 2391303Swesolows (UCT_ALL|UCT_INDEX)); 2401303Swesolows ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); 2411303Swesolows ASSERT(VALID_AVL_STATE); 2421303Swesolows 2431303Swesolows if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, 2441303Swesolows update_ctx->uc_version)) == NULL) { 2451303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " 2461303Swesolows "failed: %s\n", strerror(errno)); 2471303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 2481303Swesolows } 2491303Swesolows 2501303Swesolows if (update_ctx->uc_type == UCT_COUNT) { 2511303Swesolows err = fmd_adm_rsrc_count(adm, update_ctx->uc_all, &rsrc_count); 2521303Swesolows } else { 2531303Swesolows ++valid_stamp; 2541303Swesolows rsrc_count = 0; 2551303Swesolows err = fmd_adm_rsrc_iter(adm, update_ctx->uc_all, 2561303Swesolows rsrcinfo_update_one, update_ctx); 2571303Swesolows DEBUGMSGTL((MODNAME_STR, "resource iteration completed\n")); 2581303Swesolows } 2591303Swesolows 2601303Swesolows fmd_adm_close(adm); 2611303Swesolows 2621303Swesolows if (err != 0) { 2631303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": fmd resource information " 2641303Swesolows "update failed: %s\n", fmd_adm_errmsg(adm)); 2651303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 2661303Swesolows } 2671303Swesolows 2681303Swesolows return (SNMP_ERR_NOERROR); 2691303Swesolows } 2701303Swesolows 2711303Swesolows /*ARGSUSED*/ 2721303Swesolows static void 2731303Swesolows update_thread(void *arg) 2741303Swesolows { 2751303Swesolows /* 2761303Swesolows * The current rsrcinfo_update implementation offers minimal savings 2771303Swesolows * for the use of index-only updates; therefore we always do a full 2781303Swesolows * update. If it becomes advantageous to limit updates to a single 2791303Swesolows * index, the contexts can be queued by the handler instead. 2801303Swesolows */ 2811303Swesolows sunFmResource_update_ctx_t uc; 2821303Swesolows 2831303Swesolows uc.uc_host = NULL; 2841303Swesolows uc.uc_prog = FMD_ADM_PROGRAM; 2851303Swesolows uc.uc_version = FMD_ADM_VERSION; 2861303Swesolows 287*2134Swesolows uc.uc_all = 0; 2881303Swesolows uc.uc_index = 0; 2891303Swesolows uc.uc_type = UCT_ALL; 2901303Swesolows 2911303Swesolows for (;;) { 2921303Swesolows (void) pthread_mutex_lock(&update_lock); 2931303Swesolows update_status = US_QUIET; 2941303Swesolows while (update_status == US_QUIET) 2951303Swesolows (void) pthread_cond_wait(&update_cv, &update_lock); 2961303Swesolows update_status = US_INPROGRESS; 2971303Swesolows (void) pthread_mutex_unlock(&update_lock); 2981303Swesolows (void) rsrcinfo_update(&uc); 2991303Swesolows } 3001303Swesolows } 3011303Swesolows 3021303Swesolows static void 3031303Swesolows request_update(void) 3041303Swesolows { 3051303Swesolows (void) pthread_mutex_lock(&update_lock); 3061303Swesolows if (update_status != US_QUIET) { 3071303Swesolows (void) pthread_mutex_unlock(&update_lock); 3081303Swesolows return; 3091303Swesolows } 3101303Swesolows update_status = US_NEEDED; 3111303Swesolows (void) pthread_cond_signal(&update_cv); 3121303Swesolows (void) pthread_mutex_unlock(&update_lock); 3131303Swesolows } 3141303Swesolows 3151303Swesolows /*ARGSUSED*/ 3161303Swesolows static int 3171303Swesolows resource_compare_fmri(const void *l, const void *r, void *private) 3181303Swesolows { 3191303Swesolows sunFmResource_data_t *l_data = (sunFmResource_data_t *)l; 3201303Swesolows sunFmResource_data_t *r_data = (sunFmResource_data_t *)r; 3211303Swesolows 3221303Swesolows ASSERT(l_data != NULL && r_data != NULL); 3231303Swesolows 3241303Swesolows return (strcmp(l_data->d_ari_fmri, r_data->d_ari_fmri)); 3251303Swesolows } 3261303Swesolows 3271303Swesolows /*ARGSUSED*/ 3281303Swesolows static int 3291303Swesolows resource_compare_index(const void *l, const void *r, void *private) 3301303Swesolows { 3311303Swesolows sunFmResource_data_t *l_data = (sunFmResource_data_t *)l; 3321303Swesolows sunFmResource_data_t *r_data = (sunFmResource_data_t *)r; 3331303Swesolows 3341303Swesolows ASSERT(l_data != NULL && r_data != NULL); 3351303Swesolows 3361303Swesolows return (l_data->d_index < r_data->d_index ? -1 : 3371303Swesolows l_data->d_index > r_data->d_index ? 1 : 0); 3381303Swesolows } 3391303Swesolows 3401303Swesolows int 3411303Swesolows sunFmResourceTable_init(void) 3421303Swesolows { 3431303Swesolows static oid sunFmResourceTable_oid[] = { SUNFMRESOURCETABLE_OID }; 3441303Swesolows static oid sunFmResourceCount_oid[] = { SUNFMRESOURCECOUNT_OID, 0 }; 3451303Swesolows netsnmp_table_registration_info *table_info; 3461303Swesolows netsnmp_handler_registration *handler; 3471303Swesolows int err; 3481303Swesolows 3491303Swesolows if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { 3501303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", 3511303Swesolows strerror(err)); 3521303Swesolows return (MIB_REGISTRATION_FAILED); 3531303Swesolows } 3541303Swesolows if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { 3551303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", 3561303Swesolows strerror(err)); 3571303Swesolows return (MIB_REGISTRATION_FAILED); 3581303Swesolows } 3591303Swesolows 3601303Swesolows if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, 3611303Swesolows NULL)) != 0) { 3621303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": error creating update " 3631303Swesolows "thread: %s\n", strerror(err)); 3641303Swesolows return (MIB_REGISTRATION_FAILED); 3651303Swesolows } 3661303Swesolows 3671303Swesolows if ((table_info = 3681303Swesolows SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) 3691303Swesolows return (MIB_REGISTRATION_FAILED); 3701303Swesolows 3711303Swesolows if ((handler = netsnmp_create_handler_registration("sunFmResourceTable", 3721303Swesolows sunFmResourceTable_handler, sunFmResourceTable_oid, 3731303Swesolows OID_LENGTH(sunFmResourceTable_oid), HANDLER_CAN_RONLY)) == NULL) { 3741303Swesolows SNMP_FREE(table_info); 3751303Swesolows return (MIB_REGISTRATION_FAILED); 3761303Swesolows } 3771303Swesolows 3781303Swesolows /* 3791303Swesolows * The Net-SNMP template uses add_indexes here, but that 3801303Swesolows * function is unsafe because it does not check for failure. 3811303Swesolows */ 3821303Swesolows if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { 3831303Swesolows SNMP_FREE(table_info); 3841303Swesolows SNMP_FREE(handler); 3851303Swesolows return (MIB_REGISTRATION_FAILED); 3861303Swesolows } 3871303Swesolows 3881303Swesolows table_info->min_column = SUNFMRESOURCE_COLMIN; 3891303Swesolows table_info->max_column = SUNFMRESOURCE_COLMAX; 3901303Swesolows 3911303Swesolows if ((rsrc_fmri_avl_pool = uu_avl_pool_create("rsrc_fmri", 3921303Swesolows sizeof (sunFmResource_data_t), 3931303Swesolows offsetof(sunFmResource_data_t, d_fmri_avl), resource_compare_fmri, 3941303Swesolows UU_AVL_DEBUG)) == NULL) { 3951303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl pool creation " 3961303Swesolows "failed: %s\n", uu_strerror(uu_error())); 3971303Swesolows snmp_free_varbind(table_info->indexes); 3981303Swesolows SNMP_FREE(table_info); 3991303Swesolows SNMP_FREE(handler); 4001303Swesolows } 4011303Swesolows 4021303Swesolows if ((rsrc_fmri_avl = uu_avl_create(rsrc_fmri_avl_pool, NULL, 4031303Swesolows UU_AVL_DEBUG)) == NULL) { 4041303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl creation " 4051303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4061303Swesolows snmp_free_varbind(table_info->indexes); 4071303Swesolows SNMP_FREE(table_info); 4081303Swesolows SNMP_FREE(handler); 4091303Swesolows uu_avl_pool_destroy(rsrc_fmri_avl_pool); 4101303Swesolows return (MIB_REGISTRATION_FAILED); 4111303Swesolows } 4121303Swesolows 4131303Swesolows if ((rsrc_index_avl_pool = uu_avl_pool_create("rsrc_index", 4141303Swesolows sizeof (sunFmResource_data_t), 4151303Swesolows offsetof(sunFmResource_data_t, d_index_avl), 4161303Swesolows resource_compare_index, UU_AVL_DEBUG)) == NULL) { 4171303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl pool creation " 4181303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4191303Swesolows snmp_free_varbind(table_info->indexes); 4201303Swesolows SNMP_FREE(table_info); 4211303Swesolows SNMP_FREE(handler); 4221303Swesolows uu_avl_destroy(rsrc_fmri_avl); 4231303Swesolows uu_avl_pool_destroy(rsrc_fmri_avl_pool); 4241303Swesolows } 4251303Swesolows 4261303Swesolows if ((rsrc_index_avl = uu_avl_create(rsrc_index_avl_pool, NULL, 4271303Swesolows UU_AVL_DEBUG)) == NULL) { 4281303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl creation " 4291303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4301303Swesolows snmp_free_varbind(table_info->indexes); 4311303Swesolows SNMP_FREE(table_info); 4321303Swesolows SNMP_FREE(handler); 4331303Swesolows uu_avl_destroy(rsrc_fmri_avl); 4341303Swesolows uu_avl_pool_destroy(rsrc_fmri_avl_pool); 4351303Swesolows uu_avl_pool_destroy(rsrc_index_avl_pool); 4361303Swesolows return (MIB_REGISTRATION_FAILED); 4371303Swesolows } 4381303Swesolows 4391303Swesolows if ((err = netsnmp_register_table(handler, table_info)) != 4401303Swesolows MIB_REGISTERED_OK) { 4411303Swesolows snmp_free_varbind(table_info->indexes); 4421303Swesolows SNMP_FREE(table_info); 4431303Swesolows SNMP_FREE(handler); 4441303Swesolows uu_avl_destroy(rsrc_fmri_avl); 4451303Swesolows uu_avl_pool_destroy(rsrc_fmri_avl_pool); 4461303Swesolows uu_avl_destroy(rsrc_index_avl); 4471303Swesolows uu_avl_pool_destroy(rsrc_index_avl_pool); 4481303Swesolows return (err); 4491303Swesolows } 4501303Swesolows 4511303Swesolows if ((err = netsnmp_register_read_only_instance( 4521303Swesolows netsnmp_create_handler_registration("sunFmResourceCount", 4531303Swesolows sunFmResourceCount_handler, sunFmResourceCount_oid, 4541303Swesolows OID_LENGTH(sunFmResourceCount_oid), HANDLER_CAN_RONLY))) != 4551303Swesolows MIB_REGISTERED_OK) { 4561303Swesolows /* 4571303Swesolows * There's no way to unregister the table handler, so we 4581303Swesolows * can't free any of the data, either. 4591303Swesolows */ 4601303Swesolows return (err); 4611303Swesolows } 4621303Swesolows 4631303Swesolows return (MIB_REGISTERED_OK); 4641303Swesolows } 4651303Swesolows 4661303Swesolows /* 4671303Swesolows * These two functions form the core of GET/GETNEXT/GETBULK handling (the 4681303Swesolows * only kind we do). They perform two functions: 4691303Swesolows * 4701303Swesolows * - First, frob the request to set all the index variables to correspond 4711303Swesolows * to the value that's going to be returned. For GET, this is a nop; 4721303Swesolows * for GETNEXT/GETBULK it always requires some work. 4731303Swesolows * - Second, find and return the fmd resource information corresponding to 4741303Swesolows * the (possibly updated) indices. 4751303Swesolows * 4761303Swesolows * These should be as fast as possible; they run in the agent thread. 4771303Swesolows */ 4781303Swesolows static sunFmResource_data_t * 4791303Swesolows sunFmResourceTable_nextrsrc(netsnmp_handler_registration *reginfo, 4801303Swesolows netsnmp_table_request_info *table_info) 4811303Swesolows { 4821303Swesolows sunFmResource_data_t *data; 4831303Swesolows netsnmp_variable_list *var; 4841303Swesolows ulong_t index; 4851303Swesolows 4861303Swesolows /* 4871303Swesolows * If we have no index, we must make one. 4881303Swesolows */ 4891303Swesolows if (table_info->number_indexes < 1) { 4901303Swesolows oid tmpoid[MAX_OID_LEN]; 4911303Swesolows index = 1; 4921303Swesolows 4931303Swesolows DEBUGMSGTL((MODNAME_STR, "nextrsrc: no indexes given\n")); 4941303Swesolows var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); 4951303Swesolows snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index, 4961303Swesolows sizeof (index)); 497*2134Swesolows (void) memcpy(tmpoid, reginfo->rootoid, 4981303Swesolows reginfo->rootoid_len * sizeof (oid)); 4991303Swesolows tmpoid[reginfo->rootoid_len] = 1; 5001303Swesolows tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; 5011303Swesolows if (build_oid(&var->name, &var->name_length, tmpoid, 5021303Swesolows reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) { 5031303Swesolows snmp_free_varbind(var); 5041303Swesolows return (NULL); 5051303Swesolows } 5061303Swesolows DEBUGMSGTL((MODNAME_STR, "nextrsrc: built fake index:\n")); 5071303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 5081303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 5091303Swesolows } else { 510*2134Swesolows var = snmp_clone_varbind(table_info->indexes); 5111303Swesolows index = *var->val.integer; 5121303Swesolows DEBUGMSGTL((MODNAME_STR, "nextrsrc: received index:\n")); 5131303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 5141303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 5151303Swesolows index++; 5161303Swesolows } 5171303Swesolows 518*2134Swesolows snmp_free_varbind(table_info->indexes); 519*2134Swesolows table_info->indexes = NULL; 520*2134Swesolows table_info->number_indexes = 0; 521*2134Swesolows 5221303Swesolows if ((data = resource_lookup_index_nextvalid(index)) == NULL) { 5231303Swesolows DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for " 5241303Swesolows "index %lu; trying next column\n", index)); 525*2134Swesolows if (table_info->colnum >= 526*2134Swesolows netsnmp_find_table_registration_info(reginfo)->max_column) { 5271303Swesolows snmp_free_varbind(var); 5281303Swesolows DEBUGMSGTL((MODNAME_STR, "nextrsrc: out of columns\n")); 5291303Swesolows return (NULL); 5301303Swesolows } 5311303Swesolows table_info->colnum++; 5321303Swesolows index = 1; 5331303Swesolows 5341303Swesolows data = resource_lookup_index_nextvalid(index); 5351303Swesolows } 5361303Swesolows 5371303Swesolows if (data == NULL) { 5381303Swesolows DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for " 5391303Swesolows "index %lu; stopping\n", index)); 5401303Swesolows snmp_free_varbind(var); 5411303Swesolows return (NULL); 5421303Swesolows } 5431303Swesolows 5441303Swesolows *var->val.integer = index; 5451303Swesolows table_info->indexes = var; 546*2134Swesolows table_info->number_indexes = 1; 5471303Swesolows 5481303Swesolows DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index, 5491303Swesolows data->d_ari_fmri, data)); 5501303Swesolows 5511303Swesolows return (data); 5521303Swesolows } 5531303Swesolows 5541303Swesolows /*ARGSUSED*/ 5551303Swesolows static sunFmResource_data_t * 5561303Swesolows sunFmResourceTable_rsrc(netsnmp_handler_registration *reginfo, 5571303Swesolows netsnmp_table_request_info *table_info) 5581303Swesolows { 5591303Swesolows ASSERT(table_info->number_indexes == 1); 5601303Swesolows 5611303Swesolows return (resource_lookup_index_exact(table_info->index_oid[0])); 5621303Swesolows } 5631303Swesolows 564*2134Swesolows /*ARGSUSED*/ 5651303Swesolows static void 5661303Swesolows sunFmResourceTable_return(unsigned int reg, void *arg) 5671303Swesolows { 5681303Swesolows netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; 5691303Swesolows netsnmp_request_info *request; 5701303Swesolows netsnmp_agent_request_info *reqinfo; 5711303Swesolows netsnmp_handler_registration *reginfo; 5721303Swesolows netsnmp_table_request_info *table_info; 5731303Swesolows sunFmResource_data_t *data; 5741303Swesolows ulong_t rsrcstate; 5751303Swesolows 5761303Swesolows ASSERT(netsnmp_handler_check_cache(cache) != NULL); 5771303Swesolows 5781303Swesolows (void) pthread_mutex_lock(&update_lock); 5791303Swesolows if (update_status != US_QUIET) { 5801303Swesolows struct timeval tv; 5811303Swesolows 5821303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 5831303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 5841303Swesolows 5851303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return, 5861303Swesolows cache); 5871303Swesolows (void) pthread_mutex_unlock(&update_lock); 5881303Swesolows return; 5891303Swesolows } 5901303Swesolows 5911303Swesolows request = cache->requests; 5921303Swesolows reqinfo = cache->reqinfo; 5931303Swesolows reginfo = cache->reginfo; 5941303Swesolows 5951303Swesolows table_info = netsnmp_extract_table_info(request); 5961303Swesolows request->delegated = 0; 5971303Swesolows 5981303Swesolows ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN); 5991303Swesolows ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX); 6001303Swesolows 6011303Swesolows /* 6021303Swesolows * table_info->colnum contains the column number requested. 6031303Swesolows * table_info->indexes contains a linked list of snmp variable 6041303Swesolows * bindings for the indexes of the table. Values in the list 6051303Swesolows * have been set corresponding to the indexes of the 6061303Swesolows * request. We have other guarantees as well: 6071303Swesolows * 6081303Swesolows * - The column number is always within range. 6091303Swesolows * - If we have no index data, table_info->index_oid_len is 0. 6101303Swesolows * - We will never receive requests outside our table nor 6111303Swesolows * those with the first subid anything other than 1 (Entry) 6121303Swesolows * nor those without a column number. This is true even 6131303Swesolows * for GETNEXT requests. 6141303Swesolows */ 6151303Swesolows 6161303Swesolows switch (reqinfo->mode) { 6171303Swesolows case MODE_GET: 6181303Swesolows if ((data = sunFmResourceTable_rsrc(reginfo, table_info)) == 6191303Swesolows NULL) { 6201303Swesolows netsnmp_free_delegated_cache(cache); 6211303Swesolows (void) pthread_mutex_unlock(&update_lock); 6221303Swesolows return; 6231303Swesolows } 6241303Swesolows break; 6251303Swesolows case MODE_GETNEXT: 6261303Swesolows case MODE_GETBULK: 6271303Swesolows if ((data = sunFmResourceTable_nextrsrc(reginfo, table_info)) == 6281303Swesolows NULL) { 6291303Swesolows netsnmp_free_delegated_cache(cache); 6301303Swesolows (void) pthread_mutex_unlock(&update_lock); 6311303Swesolows return; 6321303Swesolows } 6331303Swesolows break; 6341303Swesolows default: 6351303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n", 6361303Swesolows reqinfo->mode); 6371303Swesolows netsnmp_free_delegated_cache(cache); 6381303Swesolows (void) pthread_mutex_unlock(&update_lock); 6391303Swesolows return; 6401303Swesolows } 6411303Swesolows 6421303Swesolows switch (table_info->colnum) { 6431303Swesolows case SUNFMRESOURCE_COL_FMRI: 6441303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6451303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ari_fmri, 6461303Swesolows strlen(data->d_ari_fmri)); 6471303Swesolows break; 6481303Swesolows case SUNFMRESOURCE_COL_STATUS: 6491303Swesolows switch (data->d_ari_flags & 6501303Swesolows (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) { 6511303Swesolows default: 6521303Swesolows rsrcstate = SUNFMRESOURCE_STATE_OK; 6531303Swesolows break; 6541303Swesolows case FMD_ADM_RSRC_FAULTY: 6551303Swesolows rsrcstate = SUNFMRESOURCE_STATE_DEGRADED; 6561303Swesolows break; 6571303Swesolows case FMD_ADM_RSRC_UNUSABLE: 6581303Swesolows rsrcstate = SUNFMRESOURCE_STATE_UNKNOWN; 6591303Swesolows break; 6601303Swesolows case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE: 6611303Swesolows rsrcstate = SUNFMRESOURCE_STATE_FAULTED; 6621303Swesolows break; 6631303Swesolows } 6641303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6651303Swesolows ASN_INTEGER, (uchar_t *)&rsrcstate, 6661303Swesolows sizeof (rsrcstate)); 6671303Swesolows break; 6681303Swesolows case SUNFMRESOURCE_COL_DIAGNOSISUUID: 6691303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6701303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ari_case, 6711303Swesolows strlen(data->d_ari_case)); 6721303Swesolows break; 6731303Swesolows default: 6741303Swesolows break; 6751303Swesolows } 6761303Swesolows netsnmp_free_delegated_cache(cache); 6771303Swesolows (void) pthread_mutex_unlock(&update_lock); 6781303Swesolows } 6791303Swesolows 6801303Swesolows static int 6811303Swesolows sunFmResourceTable_handler(netsnmp_mib_handler *handler, 6821303Swesolows netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, 6831303Swesolows netsnmp_request_info *requests) 6841303Swesolows { 6851303Swesolows netsnmp_request_info *request; 6861303Swesolows struct timeval tv; 6871303Swesolows 6881303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 6891303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 6901303Swesolows 6911303Swesolows request_update(); 6921303Swesolows 6931303Swesolows for (request = requests; request; request = request->next) { 6941303Swesolows if (request->processed != 0) 6951303Swesolows continue; 6961303Swesolows 6971303Swesolows if (netsnmp_extract_table_info(request) == NULL) 6981303Swesolows continue; 6991303Swesolows 7001303Swesolows request->delegated = 1; 7011303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return, 7021303Swesolows (void *) netsnmp_create_delegated_cache(handler, reginfo, 7031303Swesolows reqinfo, request, NULL)); 7041303Swesolows } 7051303Swesolows 7061303Swesolows return (SNMP_ERR_NOERROR); 7071303Swesolows } 7081303Swesolows 709*2134Swesolows /*ARGSUSED*/ 7101303Swesolows static void 7111303Swesolows sunFmResourceCount_return(unsigned int reg, void *arg) 7121303Swesolows { 7131303Swesolows netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; 7141303Swesolows netsnmp_request_info *request; 7151303Swesolows netsnmp_agent_request_info *reqinfo; 7161303Swesolows ulong_t rsrc_count_long; 7171303Swesolows 7181303Swesolows ASSERT(netsnmp_handler_check_cache(cache) != NULL); 7191303Swesolows 7201303Swesolows (void) pthread_mutex_lock(&update_lock); 7211303Swesolows if (update_status != US_QUIET) { 7221303Swesolows struct timeval tv; 7231303Swesolows 7241303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 7251303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 7261303Swesolows 7271303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return, 7281303Swesolows cache); 7291303Swesolows (void) pthread_mutex_unlock(&update_lock); 7301303Swesolows return; 7311303Swesolows } 7321303Swesolows 7331303Swesolows request = cache->requests; 7341303Swesolows reqinfo = cache->reqinfo; 7351303Swesolows 7361303Swesolows request->delegated = 0; 7371303Swesolows 7381303Swesolows switch (reqinfo->mode) { 739*2134Swesolows /* 740*2134Swesolows * According to the documentation, it's not possible for us ever to 741*2134Swesolows * be called with MODE_GETNEXT. However, Net-SNMP does the following: 742*2134Swesolows * - set reqinfo->mode to MODE_GET 743*2134Swesolows * - invoke the handler 744*2134Swesolows * - set reqinfo->mode to MODE_GETNEXT (even if the request was not 745*2134Swesolows * actually processed; i.e. it's been delegated) 746*2134Swesolows * Since we're called back later with the same reqinfo, we see 747*2134Swesolows * GETNEXT. Therefore this case is needed to work around the 748*2134Swesolows * Net-SNMP bug. 749*2134Swesolows */ 7501303Swesolows case MODE_GET: 751*2134Swesolows case MODE_GETNEXT: 7521303Swesolows DEBUGMSGTL((MODNAME_STR, "resource count is %u\n", rsrc_count)); 7531303Swesolows rsrc_count_long = (ulong_t)rsrc_count; 7541303Swesolows snmp_set_var_typed_value(request->requestvb, ASN_GAUGE, 7551303Swesolows (uchar_t *)&rsrc_count_long, sizeof (rsrc_count_long)); 7561303Swesolows break; 7571303Swesolows default: 7581303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request mode %d\n", 7591303Swesolows reqinfo->mode); 7601303Swesolows } 7611303Swesolows 7621303Swesolows netsnmp_free_delegated_cache(cache); 7631303Swesolows (void) pthread_mutex_unlock(&update_lock); 7641303Swesolows } 7651303Swesolows 7661303Swesolows static int 7671303Swesolows sunFmResourceCount_handler(netsnmp_mib_handler *handler, 7681303Swesolows netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, 7691303Swesolows netsnmp_request_info *requests) 7701303Swesolows { 7711303Swesolows struct timeval tv; 7721303Swesolows 7731303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 7741303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 7751303Swesolows 7761303Swesolows request_update(); 7771303Swesolows 7781303Swesolows /* 7791303Swesolows * We are never called for a GETNEXT when registered as an 7801303Swesolows * instance; it's handled for us and converted to a GET. 7811303Swesolows * Also, an instance handler is given only one request at a time, so 7821303Swesolows * we don't need to loop over a list of requests. 7831303Swesolows */ 7841303Swesolows 7851303Swesolows if (requests->processed != 0) 7861303Swesolows return (SNMP_ERR_NOERROR); 7871303Swesolows 7881303Swesolows requests->delegated = 1; 7891303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return, 7901303Swesolows (void *) netsnmp_create_delegated_cache(handler, reginfo, 7911303Swesolows reqinfo, requests, NULL)); 7921303Swesolows 7931303Swesolows return (SNMP_ERR_NOERROR); 7941303Swesolows } 795