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 /* 23*3955Swesolows * Copyright 2007 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 "module.h" 401303Swesolows 411303Swesolows static uu_avl_pool_t *mod_name_avl_pool; 421303Swesolows static uu_avl_pool_t *mod_index_avl_pool; 431303Swesolows static uu_avl_t *mod_name_avl; 441303Swesolows static uu_avl_t *mod_index_avl; 451303Swesolows 461303Swesolows #define VALID_AVL_STATE (mod_name_avl_pool != NULL && \ 471303Swesolows mod_index_avl_pool != NULL && mod_name_avl != NULL && \ 481303Swesolows mod_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. 541303Swesolows */ 551303Swesolows #define UCT_INDEX 0x1 561303Swesolows #define UCT_ALL 0x2 571303Swesolows #define UCT_FLAGS 0x3 581303Swesolows 591303Swesolows #define MODULE_DATA_VALID(d) ((d)->d_valid == valid_stamp) 601303Swesolows 611303Swesolows /* 621303Swesolows * Locking rules are straightforward. There is only one updater thread 631303Swesolows * for each table, and requests for update that are received while 641303Swesolows * another update is in progress are ignored. The single per-table lock 651303Swesolows * protects all the data for the table, the valid_stamp and max_index 661303Swesolows * tags for new data, and - importantly - all the hidden static data 671303Swesolows * used by the Net-SNMP library. The result return callbacks are always 681303Swesolows * called in the master agent thread; holding the table lock is 691303Swesolows * therefore sufficient since only one table's callback can be run at 701303Swesolows * any one time. Finer-grained locking is possible here but 711303Swesolows * substantially more difficult because nearly all Net-SNMP functions 721303Swesolows * are unsafe. 731303Swesolows * 741303Swesolows * In practice this is more than adequate, since the purpose of 751303Swesolows * threading out the update is to prevent excessively time-consuming 761303Swesolows * data collection from bottlenecking the entire agent, not to improve 771303Swesolows * result throughput (SNMP is not intended to be used for applications 781303Swesolows * requiring high throughput anyway). If the agent itself ever becomes 791303Swesolows * multithreaded, locking requirements become limited to our local 801303Swesolows * per-table data (the tree, max_index, and valid_stamp), and the 811303Swesolows * implementation could be revisited for better performance. 821303Swesolows */ 831303Swesolows 841303Swesolows static ulong_t max_index; 851303Swesolows static int valid_stamp; 861303Swesolows static pthread_mutex_t update_lock; 871303Swesolows static pthread_cond_t update_cv; 881303Swesolows static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status; 891303Swesolows 901303Swesolows static Netsnmp_Node_Handler sunFmModuleTable_handler; 911303Swesolows 921303Swesolows static sunFmModule_data_t * 931303Swesolows key_build(const char *name, const ulong_t index) 941303Swesolows { 951303Swesolows static sunFmModule_data_t key; 961303Swesolows 971303Swesolows key.d_index = index; 981303Swesolows if (name) 992134Swesolows (void) strlcpy(key.d_ami_name, name, sizeof (key.d_ami_name)); 1001303Swesolows else 1011303Swesolows key.d_ami_name[0] = '\0'; 1021303Swesolows 1031303Swesolows return (&key); 1041303Swesolows } 1051303Swesolows 1061303Swesolows /* 1071303Swesolows * If name is the name of a module we have previously seen and indexed, return 1081303Swesolows * data for it. Otherwise, return NULL. Note that the module may not be 1091303Swesolows * valid; that is, it may have been removed from the fault manager since its 1101303Swesolows * information was last updated. 1111303Swesolows */ 1121303Swesolows static sunFmModule_data_t * 1131303Swesolows module_lookup_name(const char *name) 1141303Swesolows { 1151303Swesolows sunFmModule_data_t *key; 1161303Swesolows 1171303Swesolows key = key_build(name, 0); 1181303Swesolows return (uu_avl_find(mod_name_avl, key, NULL, NULL)); 1191303Swesolows } 1201303Swesolows 1211303Swesolows /* 1221303Swesolows * If index corresponds to a module we have previously seen and indexed, return 1231303Swesolows * data for it. Otherwise, return NULL. Note that the module may not be 1241303Swesolows * valid; that is, it may have been removed from the fault manager since its 1251303Swesolows * information was last updated. 1261303Swesolows */ 1271303Swesolows static sunFmModule_data_t * 1281303Swesolows module_lookup_index_exact(const ulong_t index) 1291303Swesolows { 1301303Swesolows sunFmModule_data_t *key; 1311303Swesolows 1321303Swesolows key = key_build(NULL, index); 1331303Swesolows return (uu_avl_find(mod_index_avl, key, NULL, NULL)); 1341303Swesolows } 1351303Swesolows 1361303Swesolows /* 1371303Swesolows * If index corresponds to a valid (that is, extant as of latest information 1381303Swesolows * from the fault manager) fmd module, return the data for that module. 1391303Swesolows * Otherwise, return the data for the valid module whose index is as close as 1401303Swesolows * possible to index but not lower. This preserves the lexicographical 1411303Swesolows * ordering required for GETNEXT processing. 1421303Swesolows */ 1431303Swesolows static sunFmModule_data_t * 1441303Swesolows module_lookup_index_nextvalid(const ulong_t index) 1451303Swesolows { 1461303Swesolows sunFmModule_data_t *key, *data; 1471303Swesolows uu_avl_index_t idx; 1481303Swesolows 1491303Swesolows key = key_build(NULL, index); 1501303Swesolows 1511303Swesolows if ((data = uu_avl_find(mod_index_avl, key, NULL, &idx)) != NULL && 1521303Swesolows MODULE_DATA_VALID(data)) 1531303Swesolows return (data); 1541303Swesolows 1551303Swesolows data = uu_avl_nearest_next(mod_index_avl, idx); 1561303Swesolows 157*3955Swesolows while (data != NULL && !MODULE_DATA_VALID(data)) 158*3955Swesolows data = uu_avl_next(mod_index_avl, data); 1591303Swesolows 1601303Swesolows return (data); 1611303Swesolows } 1621303Swesolows 1631303Swesolows /* 1641303Swesolows * Possible update the contents of a single module within the cache. This 1651303Swesolows * is our callback from fmd_module_iter. 1661303Swesolows */ 1671303Swesolows static int 1681303Swesolows modinfo_update_one(const fmd_adm_modinfo_t *modinfo, void *arg) 1691303Swesolows { 1701303Swesolows const sunFmModule_update_ctx_t *update_ctx = 1711303Swesolows (sunFmModule_update_ctx_t *)arg; 1721303Swesolows sunFmModule_data_t *data = module_lookup_name(modinfo->ami_name); 1731303Swesolows 1741303Swesolows /* 1751303Swesolows * An fmd module we haven't seen before. We're obligated to index 1761303Swesolows * it and link it into our cache so that we can find it, but we're 1771303Swesolows * not obligated to fill it in completely unless we're doing a 1781303Swesolows * thorough update or this is the module we were asked for. This 1791303Swesolows * avoids unnecessary iteration and memory manipulation for data 1801303Swesolows * we're not going to return for this request. 1811303Swesolows */ 1821303Swesolows if (data == NULL) { 1831303Swesolows uu_avl_index_t idx; 1841303Swesolows 1851303Swesolows DEBUGMSGTL((MODNAME_STR, "found new fmd module %s\n", 1861303Swesolows modinfo->ami_name)); 1871303Swesolows if ((data = SNMP_MALLOC_TYPEDEF(sunFmModule_data_t)) == NULL) { 1881303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " 1891303Swesolows "new module data at %s:%d\n", __FILE__, __LINE__); 1901303Swesolows return (1); 1911303Swesolows } 1921303Swesolows /* 1931303Swesolows * We allocate indices sequentially and never reuse them. 1941303Swesolows * This ensures we can always return valid GETNEXT responses 1951303Swesolows * without having to reindex, and it provides the user a 1961303Swesolows * more consistent view of the fault manager. 1971303Swesolows */ 1981303Swesolows data->d_index = ++max_index; 1991303Swesolows DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index, 2001303Swesolows modinfo->ami_name, data)); 2011303Swesolows 2022134Swesolows (void) strlcpy(data->d_ami_name, modinfo->ami_name, 2031303Swesolows sizeof (data->d_ami_name)); 2041303Swesolows 2051303Swesolows uu_avl_node_init(data, &data->d_name_avl, mod_name_avl_pool); 2061303Swesolows (void) uu_avl_find(mod_name_avl, data, NULL, &idx); 2071303Swesolows uu_avl_insert(mod_name_avl, data, idx); 2081303Swesolows 2091303Swesolows uu_avl_node_init(data, &data->d_index_avl, mod_index_avl_pool); 2101303Swesolows (void) uu_avl_find(mod_index_avl, data, NULL, &idx); 2111303Swesolows uu_avl_insert(mod_index_avl, data, idx); 2121303Swesolows 2131303Swesolows DEBUGMSGTL((MODNAME_STR, "completed new module %lu/%s@%p\n", 2141303Swesolows data->d_index, data->d_ami_name, data)); 2151303Swesolows } 2161303Swesolows 2171303Swesolows data->d_valid = valid_stamp; 2181303Swesolows 2191303Swesolows DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n", 2201303Swesolows data->d_index, data->d_ami_name, data, data->d_valid)); 2211303Swesolows 2221303Swesolows if ((update_ctx->uc_type & UCT_ALL) || 2231303Swesolows update_ctx->uc_index == data->d_index) { 2242134Swesolows (void) strlcpy(data->d_ami_vers, modinfo->ami_vers, 2251303Swesolows sizeof (data->d_ami_vers)); 2262134Swesolows (void) strlcpy(data->d_ami_desc, modinfo->ami_desc, 2271303Swesolows sizeof (data->d_ami_desc)); 2281303Swesolows data->d_ami_flags = modinfo->ami_flags; 2291303Swesolows } 2301303Swesolows 2311303Swesolows return (!(update_ctx->uc_type & UCT_ALL) && 2321303Swesolows update_ctx->uc_index == data->d_index); 2331303Swesolows } 2341303Swesolows 2351303Swesolows /* 2361303Swesolows * Update some or all module data from fmd. If thorough is set, all modules 2371303Swesolows * will be indexed and their data cached. Otherwise, updates will stop once 2381303Swesolows * the module matching index has been updated. 2391303Swesolows * 2401303Swesolows * Returns appropriate SNMP error codes. 2411303Swesolows */ 2421303Swesolows static int 2431303Swesolows modinfo_update(sunFmModule_update_ctx_t *update_ctx) 2441303Swesolows { 2451303Swesolows fmd_adm_t *adm; 2461303Swesolows 2471303Swesolows ASSERT(update_ctx != NULL); 2481303Swesolows ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) != 2491303Swesolows (UCT_INDEX|UCT_ALL)); 2501303Swesolows ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); 2511303Swesolows ASSERT(VALID_AVL_STATE); 2521303Swesolows 2531303Swesolows if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, 2541303Swesolows update_ctx->uc_version)) == NULL) { 2551303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " 2561303Swesolows "failed: %s\n", strerror(errno)); 2571303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 2581303Swesolows } 2591303Swesolows 2601303Swesolows ++valid_stamp; 2611303Swesolows if (fmd_adm_module_iter(adm, modinfo_update_one, update_ctx) != 0) { 2621303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": fmd module information update " 2631303Swesolows "failed: %s\n", fmd_adm_errmsg(adm)); 2641303Swesolows fmd_adm_close(adm); 2651303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 2661303Swesolows } 2671303Swesolows 2681303Swesolows DEBUGMSGTL((MODNAME_STR, "module iteration completed\n")); 2691303Swesolows 2701303Swesolows fmd_adm_close(adm); 2711303Swesolows return (SNMP_ERR_NOERROR); 2721303Swesolows } 2731303Swesolows 2741303Swesolows /*ARGSUSED*/ 2751303Swesolows static void 2761303Swesolows update_thread(void *arg) 2771303Swesolows { 2781303Swesolows /* 2791303Swesolows * The current modinfo_update implementation offers minimal savings 2801303Swesolows * for the use of index-only updates; therefore we always do a full 2811303Swesolows * update. If it becomes advantageous to limit updates to a single 2821303Swesolows * index, the contexts can be queued by the handler instead. 2831303Swesolows */ 2841303Swesolows sunFmModule_update_ctx_t uc; 2851303Swesolows 2861303Swesolows uc.uc_host = NULL; 2871303Swesolows uc.uc_prog = FMD_ADM_PROGRAM; 2881303Swesolows uc.uc_version = FMD_ADM_VERSION; 2891303Swesolows 2901303Swesolows uc.uc_index = 0; 2911303Swesolows uc.uc_type = UCT_ALL; 2921303Swesolows 2931303Swesolows for (;;) { 2941303Swesolows (void) pthread_mutex_lock(&update_lock); 2951303Swesolows update_status = US_QUIET; 2961303Swesolows while (update_status == US_QUIET) 2971303Swesolows (void) pthread_cond_wait(&update_cv, &update_lock); 2981303Swesolows update_status = US_INPROGRESS; 2991303Swesolows (void) pthread_mutex_unlock(&update_lock); 3001303Swesolows (void) modinfo_update(&uc); 3011303Swesolows } 3021303Swesolows } 3031303Swesolows 3041303Swesolows static void 3051303Swesolows request_update(void) 3061303Swesolows { 3071303Swesolows (void) pthread_mutex_lock(&update_lock); 3081303Swesolows if (update_status != US_QUIET) { 3091303Swesolows (void) pthread_mutex_unlock(&update_lock); 3101303Swesolows return; 3111303Swesolows } 3121303Swesolows update_status = US_NEEDED; 3131303Swesolows (void) pthread_cond_signal(&update_cv); 3141303Swesolows (void) pthread_mutex_unlock(&update_lock); 3151303Swesolows } 3161303Swesolows 3171303Swesolows /*ARGSUSED*/ 3181303Swesolows static int 3191303Swesolows module_compare_name(const void *l, const void *r, void *private) 3201303Swesolows { 3211303Swesolows sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; 3221303Swesolows sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; 3231303Swesolows 3241303Swesolows ASSERT(l_data != NULL && r_data != NULL); 3251303Swesolows 3261303Swesolows return (strcmp(l_data->d_ami_name, r_data->d_ami_name)); 3271303Swesolows } 3281303Swesolows 3291303Swesolows /*ARGSUSED*/ 3301303Swesolows static int 3311303Swesolows module_compare_index(const void *l, const void *r, void *private) 3321303Swesolows { 3331303Swesolows sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; 3341303Swesolows sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; 3351303Swesolows 3361303Swesolows ASSERT(l_data != NULL && r_data != NULL); 3371303Swesolows 3381303Swesolows return (l_data->d_index < r_data->d_index ? -1 : 3391303Swesolows l_data->d_index > r_data->d_index ? 1 : 0); 3401303Swesolows } 3411303Swesolows 3421303Swesolows int 3431303Swesolows sunFmModuleTable_init(void) 3441303Swesolows { 3451303Swesolows static oid sunFmModuleTable_oid[] = { SUNFMMODULETABLE_OID }; 3461303Swesolows netsnmp_table_registration_info *table_info; 3471303Swesolows netsnmp_handler_registration *handler; 3481303Swesolows int err; 3491303Swesolows 3501303Swesolows if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { 3511303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", 3521303Swesolows strerror(err)); 3531303Swesolows return (MIB_REGISTRATION_FAILED); 3541303Swesolows } 3551303Swesolows if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { 3561303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", 3571303Swesolows strerror(err)); 3581303Swesolows return (MIB_REGISTRATION_FAILED); 3591303Swesolows } 3601303Swesolows 3611303Swesolows if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, 3621303Swesolows NULL)) != 0) { 3631303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": error creating update " 3641303Swesolows "thread: %s\n", strerror(err)); 3651303Swesolows return (MIB_REGISTRATION_FAILED); 3661303Swesolows } 3671303Swesolows 3681303Swesolows if ((table_info = 3691303Swesolows SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) 3701303Swesolows return (MIB_REGISTRATION_FAILED); 3711303Swesolows 3721303Swesolows if ((handler = netsnmp_create_handler_registration("sunFmModuleTable", 3731303Swesolows sunFmModuleTable_handler, sunFmModuleTable_oid, 3741303Swesolows OID_LENGTH(sunFmModuleTable_oid), HANDLER_CAN_RONLY)) == NULL) { 3751303Swesolows SNMP_FREE(table_info); 3761303Swesolows return (MIB_REGISTRATION_FAILED); 3771303Swesolows } 3781303Swesolows 3791303Swesolows /* 3801303Swesolows * The Net-SNMP template uses add_indexes here, but that 3811303Swesolows * function is unsafe because it does not check for failure. 3821303Swesolows */ 3831303Swesolows if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { 3841303Swesolows SNMP_FREE(table_info); 3851303Swesolows SNMP_FREE(handler); 3861303Swesolows return (MIB_REGISTRATION_FAILED); 3871303Swesolows } 3881303Swesolows 3891303Swesolows table_info->min_column = SUNFMMODULE_COLMIN; 3901303Swesolows table_info->max_column = SUNFMMODULE_COLMAX; 3911303Swesolows 3921303Swesolows if ((mod_name_avl_pool = uu_avl_pool_create("mod_name", 3931303Swesolows sizeof (sunFmModule_data_t), 3941303Swesolows offsetof(sunFmModule_data_t, d_name_avl), module_compare_name, 3951303Swesolows UU_AVL_DEBUG)) == NULL) { 3961303Swesolows snmp_free_varbind(table_info->indexes); 3971303Swesolows SNMP_FREE(table_info); 3981303Swesolows SNMP_FREE(handler); 3991303Swesolows } 4001303Swesolows 4011303Swesolows if ((mod_name_avl = uu_avl_create(mod_name_avl_pool, NULL, 4021303Swesolows UU_AVL_DEBUG)) == NULL) { 4031303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mod_name_avl creation " 4041303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4051303Swesolows snmp_free_varbind(table_info->indexes); 4061303Swesolows SNMP_FREE(table_info); 4071303Swesolows SNMP_FREE(handler); 4081303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4091303Swesolows return (MIB_REGISTRATION_FAILED); 4101303Swesolows } 4111303Swesolows 4121303Swesolows if ((mod_index_avl_pool = uu_avl_pool_create("mod_index", 4131303Swesolows sizeof (sunFmModule_data_t), 4141303Swesolows offsetof(sunFmModule_data_t, d_index_avl), 4151303Swesolows module_compare_index, UU_AVL_DEBUG)) == NULL) { 4161303Swesolows snmp_free_varbind(table_info->indexes); 4171303Swesolows SNMP_FREE(table_info); 4181303Swesolows SNMP_FREE(handler); 4191303Swesolows uu_avl_destroy(mod_name_avl); 4201303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4211303Swesolows } 4221303Swesolows 4231303Swesolows if ((mod_index_avl = uu_avl_create(mod_index_avl_pool, NULL, 4241303Swesolows UU_AVL_DEBUG)) == NULL) { 4251303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mod_index_avl creation " 4261303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4271303Swesolows snmp_free_varbind(table_info->indexes); 4281303Swesolows SNMP_FREE(table_info); 4291303Swesolows SNMP_FREE(handler); 4301303Swesolows uu_avl_destroy(mod_name_avl); 4311303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4321303Swesolows uu_avl_pool_destroy(mod_index_avl_pool); 4331303Swesolows return (MIB_REGISTRATION_FAILED); 4341303Swesolows } 4351303Swesolows 4361303Swesolows if ((err = netsnmp_register_table(handler, table_info)) != 4371303Swesolows MIB_REGISTERED_OK) { 4381303Swesolows snmp_free_varbind(table_info->indexes); 4391303Swesolows SNMP_FREE(table_info); 4401303Swesolows SNMP_FREE(handler); 4411303Swesolows uu_avl_destroy(mod_name_avl); 4421303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4431303Swesolows uu_avl_destroy(mod_index_avl); 4441303Swesolows uu_avl_pool_destroy(mod_index_avl_pool); 4451303Swesolows return (err); 4461303Swesolows } 4471303Swesolows 4481303Swesolows return (MIB_REGISTERED_OK); 4491303Swesolows } 4501303Swesolows 4511303Swesolows /* 4521303Swesolows * These two functions form the core of GET/GETNEXT/GETBULK handling (the 4531303Swesolows * only kind we do). They perform two functions: 4541303Swesolows * 4551303Swesolows * - First, frob the request to set all the index variables to correspond 4561303Swesolows * to the value that's going to be returned. For GET, this is a nop; 4571303Swesolows * for GETNEXT/GETBULK it always requires some work. 4581303Swesolows * - Second, find and return the fmd module information corresponding to 4591303Swesolows * the (possibly updated) indices. 4601303Swesolows * 4611303Swesolows * These should be as fast as possible; they run in the agent thread. 4621303Swesolows */ 4631303Swesolows static sunFmModule_data_t * 4641303Swesolows sunFmModuleTable_nextmod(netsnmp_handler_registration *reginfo, 4651303Swesolows netsnmp_table_request_info *table_info) 4661303Swesolows { 4671303Swesolows sunFmModule_data_t *data; 4681303Swesolows netsnmp_variable_list *var; 4691303Swesolows ulong_t index; 4701303Swesolows 4711303Swesolows /* 4721303Swesolows * If we have no index, we must make one. 4731303Swesolows */ 4741303Swesolows if (table_info->number_indexes < 1) { 4751303Swesolows oid tmpoid[MAX_OID_LEN]; 4761303Swesolows index = 1; 4771303Swesolows 4781303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: no indexes given\n")); 4791303Swesolows var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); 4801303Swesolows snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index, 4811303Swesolows sizeof (index)); 4822134Swesolows (void) memcpy(tmpoid, reginfo->rootoid, 4831303Swesolows reginfo->rootoid_len * sizeof (oid)); 4841303Swesolows tmpoid[reginfo->rootoid_len] = 1; /* Entry is .1 */ 4851303Swesolows tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; 4861303Swesolows if (build_oid(&var->name, &var->name_length, tmpoid, 4872134Swesolows reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) { 4882134Swesolows snmp_free_varbind(var); 4891303Swesolows return (NULL); 4902134Swesolows } 4911303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: built fake index:\n")); 4921303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 4931303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 4941303Swesolows } else { 4952134Swesolows var = snmp_clone_varbind(table_info->indexes); 4961303Swesolows index = *var->val.integer; 4971303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: received index:\n")); 4981303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 4991303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 5001303Swesolows index++; 5011303Swesolows } 5021303Swesolows 5032134Swesolows snmp_free_varbind(table_info->indexes); 5042134Swesolows table_info->indexes = NULL; 5052134Swesolows table_info->number_indexes = 0; 5062134Swesolows 5071303Swesolows if ((data = module_lookup_index_nextvalid(index)) == NULL) { 5081303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " 5091303Swesolows "index %lu; trying next column\n", index)); 5102134Swesolows if (table_info->colnum >= 5112134Swesolows netsnmp_find_table_registration_info(reginfo)->max_column) { 5121303Swesolows snmp_free_varbind(var); 5131303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: out of columns\n")); 5141303Swesolows return (NULL); 5151303Swesolows } 5161303Swesolows table_info->colnum++; 5171303Swesolows index = 1; 5181303Swesolows 5191303Swesolows data = module_lookup_index_nextvalid(index); 5201303Swesolows } 5211303Swesolows 5221303Swesolows if (data == NULL) { 5231303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " 5241303Swesolows "index %lu; stopping\n", index)); 5251303Swesolows snmp_free_varbind(var); 5261303Swesolows return (NULL); 5271303Swesolows } 5281303Swesolows 529*3955Swesolows *var->val.integer = data->d_index; 5301303Swesolows table_info->indexes = var; 5312134Swesolows table_info->number_indexes = 1; 5321303Swesolows 5331303Swesolows DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index, 5341303Swesolows data->d_ami_name, data)); 5351303Swesolows 5361303Swesolows return (data); 5371303Swesolows } 5381303Swesolows 5391303Swesolows /*ARGSUSED*/ 5401303Swesolows static sunFmModule_data_t * 5411303Swesolows sunFmModuleTable_mod(netsnmp_handler_registration *reginfo, 5421303Swesolows netsnmp_table_request_info *table_info) 5431303Swesolows { 5441303Swesolows ASSERT(table_info->number_indexes == 1); 5451303Swesolows 5461303Swesolows return (module_lookup_index_exact(table_info->index_oid[0])); 5471303Swesolows } 5481303Swesolows 5492134Swesolows /*ARGSUSED*/ 5501303Swesolows static void 5511303Swesolows sunFmModuleTable_return(unsigned int reg, void *arg) 5521303Swesolows { 5531303Swesolows netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; 5541303Swesolows netsnmp_request_info *request; 5551303Swesolows netsnmp_agent_request_info *reqinfo; 5561303Swesolows netsnmp_handler_registration *reginfo; 5571303Swesolows netsnmp_table_request_info *table_info; 5581303Swesolows sunFmModule_data_t *data; 5591303Swesolows ulong_t modstate; 5601303Swesolows 5611303Swesolows ASSERT(netsnmp_handler_check_cache(cache) != NULL); 5621303Swesolows 5631303Swesolows (void) pthread_mutex_lock(&update_lock); 5641303Swesolows if (update_status != US_QUIET) { 5651303Swesolows struct timeval tv; 5661303Swesolows 5671303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 5681303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 5691303Swesolows 5701303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, 5711303Swesolows cache); 5721303Swesolows (void) pthread_mutex_unlock(&update_lock); 5731303Swesolows return; 5741303Swesolows } 5751303Swesolows 5761303Swesolows request = cache->requests; 5771303Swesolows reqinfo = cache->reqinfo; 5781303Swesolows reginfo = cache->reginfo; 5791303Swesolows 5801303Swesolows table_info = netsnmp_extract_table_info(request); 5811303Swesolows request->delegated = 0; 5821303Swesolows 5831303Swesolows ASSERT(table_info->colnum >= SUNFMMODULE_COLMIN); 5841303Swesolows ASSERT(table_info->colnum <= SUNFMMODULE_COLMAX); 5851303Swesolows 5861303Swesolows /* 5871303Swesolows * table_info->colnum contains the column number requested. 5881303Swesolows * table_info->indexes contains a linked list of snmp variable 5891303Swesolows * bindings for the indexes of the table. Values in the list 5901303Swesolows * have been set corresponding to the indexes of the 5911303Swesolows * request. We have other guarantees as well: 5921303Swesolows * 5931303Swesolows * - The column number is always within range. 5941303Swesolows * - If we have no index data, table_info->index_oid_len is 0. 5951303Swesolows * - We will never receive requests outside our table nor 5961303Swesolows * those with the first subid anything other than 1 (Entry) 5971303Swesolows * nor those without a column number. This is true even 5981303Swesolows * for GETNEXT requests. 5991303Swesolows */ 6001303Swesolows 6011303Swesolows switch (reqinfo->mode) { 6021303Swesolows case MODE_GET: 6031303Swesolows if ((data = sunFmModuleTable_mod(reginfo, table_info)) == 6041303Swesolows NULL) { 6051303Swesolows netsnmp_free_delegated_cache(cache); 6061303Swesolows (void) pthread_mutex_unlock(&update_lock); 6071303Swesolows return; 6081303Swesolows } 6091303Swesolows break; 6101303Swesolows case MODE_GETNEXT: 6111303Swesolows case MODE_GETBULK: 6121303Swesolows if ((data = sunFmModuleTable_nextmod(reginfo, table_info)) == 6131303Swesolows NULL) { 6141303Swesolows netsnmp_free_delegated_cache(cache); 6151303Swesolows (void) pthread_mutex_unlock(&update_lock); 6161303Swesolows return; 6171303Swesolows } 6181303Swesolows break; 6191303Swesolows default: 6201303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request " 6211303Swesolows "mode %d\n", reqinfo->mode); 6221303Swesolows netsnmp_free_delegated_cache(cache); 6231303Swesolows (void) pthread_mutex_unlock(&update_lock); 6241303Swesolows return; 6251303Swesolows } 6261303Swesolows 6271303Swesolows switch (table_info->colnum) { 6281303Swesolows case SUNFMMODULE_COL_NAME: 6291303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6301303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_name, 6311303Swesolows strlen(data->d_ami_name)); 6321303Swesolows break; 6331303Swesolows case SUNFMMODULE_COL_VERSION: 6341303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6351303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_vers, 6361303Swesolows strlen(data->d_ami_vers)); 6371303Swesolows break; 6381303Swesolows case SUNFMMODULE_COL_STATUS: 6391303Swesolows modstate = (data->d_ami_flags & FMD_ADM_MOD_FAILED) ? 6401303Swesolows SUNFMMODULE_STATE_FAILED : SUNFMMODULE_STATE_ACTIVE; 6411303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6421303Swesolows ASN_INTEGER, (uchar_t *)&modstate, 6431303Swesolows sizeof (modstate)); 6441303Swesolows break; 6451303Swesolows case SUNFMMODULE_COL_DESCRIPTION: 6461303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6471303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_desc, 6481303Swesolows strlen(data->d_ami_desc)); 6491303Swesolows break; 6501303Swesolows default: 6511303Swesolows break; 6521303Swesolows } 6531303Swesolows netsnmp_free_delegated_cache(cache); 6541303Swesolows (void) pthread_mutex_unlock(&update_lock); 6551303Swesolows } 6561303Swesolows 6571303Swesolows static int 6581303Swesolows sunFmModuleTable_handler(netsnmp_mib_handler *handler, 6591303Swesolows netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, 6601303Swesolows netsnmp_request_info *requests) 6611303Swesolows { 6621303Swesolows netsnmp_request_info *request; 6631303Swesolows struct timeval tv; 6641303Swesolows 6651303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 6661303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 6671303Swesolows 6681303Swesolows request_update(); 6691303Swesolows 6701303Swesolows for (request = requests; request; request = request->next) { 6711303Swesolows if (request->processed != 0) 6721303Swesolows continue; 6731303Swesolows 6741303Swesolows if (netsnmp_extract_table_info(request) == NULL) 6751303Swesolows continue; 6761303Swesolows 6771303Swesolows request->delegated = 1; 6781303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, 6791303Swesolows (void *) netsnmp_create_delegated_cache(handler, reginfo, 6801303Swesolows reqinfo, request, NULL)); 6811303Swesolows } 6821303Swesolows 6831303Swesolows return (SNMP_ERR_NOERROR); 6841303Swesolows } 685