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 "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) 99*2134Swesolows (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 1571303Swesolows while (data != NULL && !MODULE_DATA_VALID(data)) { 1581303Swesolows (void) uu_avl_find(mod_index_avl, data, NULL, &idx); 1591303Swesolows data = uu_avl_nearest_next(mod_index_avl, idx); 1601303Swesolows } 1611303Swesolows 1621303Swesolows return (data); 1631303Swesolows } 1641303Swesolows 1651303Swesolows /* 1661303Swesolows * Possible update the contents of a single module within the cache. This 1671303Swesolows * is our callback from fmd_module_iter. 1681303Swesolows */ 1691303Swesolows static int 1701303Swesolows modinfo_update_one(const fmd_adm_modinfo_t *modinfo, void *arg) 1711303Swesolows { 1721303Swesolows const sunFmModule_update_ctx_t *update_ctx = 1731303Swesolows (sunFmModule_update_ctx_t *)arg; 1741303Swesolows sunFmModule_data_t *data = module_lookup_name(modinfo->ami_name); 1751303Swesolows 1761303Swesolows /* 1771303Swesolows * An fmd module we haven't seen before. We're obligated to index 1781303Swesolows * it and link it into our cache so that we can find it, but we're 1791303Swesolows * not obligated to fill it in completely unless we're doing a 1801303Swesolows * thorough update or this is the module we were asked for. This 1811303Swesolows * avoids unnecessary iteration and memory manipulation for data 1821303Swesolows * we're not going to return for this request. 1831303Swesolows */ 1841303Swesolows if (data == NULL) { 1851303Swesolows uu_avl_index_t idx; 1861303Swesolows 1871303Swesolows DEBUGMSGTL((MODNAME_STR, "found new fmd module %s\n", 1881303Swesolows modinfo->ami_name)); 1891303Swesolows if ((data = SNMP_MALLOC_TYPEDEF(sunFmModule_data_t)) == NULL) { 1901303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " 1911303Swesolows "new module data at %s:%d\n", __FILE__, __LINE__); 1921303Swesolows return (1); 1931303Swesolows } 1941303Swesolows /* 1951303Swesolows * We allocate indices sequentially and never reuse them. 1961303Swesolows * This ensures we can always return valid GETNEXT responses 1971303Swesolows * without having to reindex, and it provides the user a 1981303Swesolows * more consistent view of the fault manager. 1991303Swesolows */ 2001303Swesolows data->d_index = ++max_index; 2011303Swesolows DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index, 2021303Swesolows modinfo->ami_name, data)); 2031303Swesolows 204*2134Swesolows (void) strlcpy(data->d_ami_name, modinfo->ami_name, 2051303Swesolows sizeof (data->d_ami_name)); 2061303Swesolows 2071303Swesolows uu_avl_node_init(data, &data->d_name_avl, mod_name_avl_pool); 2081303Swesolows (void) uu_avl_find(mod_name_avl, data, NULL, &idx); 2091303Swesolows uu_avl_insert(mod_name_avl, data, idx); 2101303Swesolows 2111303Swesolows uu_avl_node_init(data, &data->d_index_avl, mod_index_avl_pool); 2121303Swesolows (void) uu_avl_find(mod_index_avl, data, NULL, &idx); 2131303Swesolows uu_avl_insert(mod_index_avl, data, idx); 2141303Swesolows 2151303Swesolows DEBUGMSGTL((MODNAME_STR, "completed new module %lu/%s@%p\n", 2161303Swesolows data->d_index, data->d_ami_name, data)); 2171303Swesolows } 2181303Swesolows 2191303Swesolows data->d_valid = valid_stamp; 2201303Swesolows 2211303Swesolows DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n", 2221303Swesolows data->d_index, data->d_ami_name, data, data->d_valid)); 2231303Swesolows 2241303Swesolows if ((update_ctx->uc_type & UCT_ALL) || 2251303Swesolows update_ctx->uc_index == data->d_index) { 226*2134Swesolows (void) strlcpy(data->d_ami_vers, modinfo->ami_vers, 2271303Swesolows sizeof (data->d_ami_vers)); 228*2134Swesolows (void) strlcpy(data->d_ami_desc, modinfo->ami_desc, 2291303Swesolows sizeof (data->d_ami_desc)); 2301303Swesolows data->d_ami_flags = modinfo->ami_flags; 2311303Swesolows } 2321303Swesolows 2331303Swesolows return (!(update_ctx->uc_type & UCT_ALL) && 2341303Swesolows update_ctx->uc_index == data->d_index); 2351303Swesolows } 2361303Swesolows 2371303Swesolows /* 2381303Swesolows * Update some or all module data from fmd. If thorough is set, all modules 2391303Swesolows * will be indexed and their data cached. Otherwise, updates will stop once 2401303Swesolows * the module matching index has been updated. 2411303Swesolows * 2421303Swesolows * Returns appropriate SNMP error codes. 2431303Swesolows */ 2441303Swesolows static int 2451303Swesolows modinfo_update(sunFmModule_update_ctx_t *update_ctx) 2461303Swesolows { 2471303Swesolows fmd_adm_t *adm; 2481303Swesolows 2491303Swesolows ASSERT(update_ctx != NULL); 2501303Swesolows ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) != 2511303Swesolows (UCT_INDEX|UCT_ALL)); 2521303Swesolows ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); 2531303Swesolows ASSERT(VALID_AVL_STATE); 2541303Swesolows 2551303Swesolows if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, 2561303Swesolows update_ctx->uc_version)) == NULL) { 2571303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " 2581303Swesolows "failed: %s\n", strerror(errno)); 2591303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 2601303Swesolows } 2611303Swesolows 2621303Swesolows ++valid_stamp; 2631303Swesolows if (fmd_adm_module_iter(adm, modinfo_update_one, update_ctx) != 0) { 2641303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": fmd module information update " 2651303Swesolows "failed: %s\n", fmd_adm_errmsg(adm)); 2661303Swesolows fmd_adm_close(adm); 2671303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 2681303Swesolows } 2691303Swesolows 2701303Swesolows DEBUGMSGTL((MODNAME_STR, "module iteration completed\n")); 2711303Swesolows 2721303Swesolows fmd_adm_close(adm); 2731303Swesolows return (SNMP_ERR_NOERROR); 2741303Swesolows } 2751303Swesolows 2761303Swesolows /*ARGSUSED*/ 2771303Swesolows static void 2781303Swesolows update_thread(void *arg) 2791303Swesolows { 2801303Swesolows /* 2811303Swesolows * The current modinfo_update implementation offers minimal savings 2821303Swesolows * for the use of index-only updates; therefore we always do a full 2831303Swesolows * update. If it becomes advantageous to limit updates to a single 2841303Swesolows * index, the contexts can be queued by the handler instead. 2851303Swesolows */ 2861303Swesolows sunFmModule_update_ctx_t uc; 2871303Swesolows 2881303Swesolows uc.uc_host = NULL; 2891303Swesolows uc.uc_prog = FMD_ADM_PROGRAM; 2901303Swesolows uc.uc_version = FMD_ADM_VERSION; 2911303Swesolows 2921303Swesolows uc.uc_index = 0; 2931303Swesolows uc.uc_type = UCT_ALL; 2941303Swesolows 2951303Swesolows for (;;) { 2961303Swesolows (void) pthread_mutex_lock(&update_lock); 2971303Swesolows update_status = US_QUIET; 2981303Swesolows while (update_status == US_QUIET) 2991303Swesolows (void) pthread_cond_wait(&update_cv, &update_lock); 3001303Swesolows update_status = US_INPROGRESS; 3011303Swesolows (void) pthread_mutex_unlock(&update_lock); 3021303Swesolows (void) modinfo_update(&uc); 3031303Swesolows } 3041303Swesolows } 3051303Swesolows 3061303Swesolows static void 3071303Swesolows request_update(void) 3081303Swesolows { 3091303Swesolows (void) pthread_mutex_lock(&update_lock); 3101303Swesolows if (update_status != US_QUIET) { 3111303Swesolows (void) pthread_mutex_unlock(&update_lock); 3121303Swesolows return; 3131303Swesolows } 3141303Swesolows update_status = US_NEEDED; 3151303Swesolows (void) pthread_cond_signal(&update_cv); 3161303Swesolows (void) pthread_mutex_unlock(&update_lock); 3171303Swesolows } 3181303Swesolows 3191303Swesolows /*ARGSUSED*/ 3201303Swesolows static int 3211303Swesolows module_compare_name(const void *l, const void *r, void *private) 3221303Swesolows { 3231303Swesolows sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; 3241303Swesolows sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; 3251303Swesolows 3261303Swesolows ASSERT(l_data != NULL && r_data != NULL); 3271303Swesolows 3281303Swesolows return (strcmp(l_data->d_ami_name, r_data->d_ami_name)); 3291303Swesolows } 3301303Swesolows 3311303Swesolows /*ARGSUSED*/ 3321303Swesolows static int 3331303Swesolows module_compare_index(const void *l, const void *r, void *private) 3341303Swesolows { 3351303Swesolows sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; 3361303Swesolows sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; 3371303Swesolows 3381303Swesolows ASSERT(l_data != NULL && r_data != NULL); 3391303Swesolows 3401303Swesolows return (l_data->d_index < r_data->d_index ? -1 : 3411303Swesolows l_data->d_index > r_data->d_index ? 1 : 0); 3421303Swesolows } 3431303Swesolows 3441303Swesolows int 3451303Swesolows sunFmModuleTable_init(void) 3461303Swesolows { 3471303Swesolows static oid sunFmModuleTable_oid[] = { SUNFMMODULETABLE_OID }; 3481303Swesolows netsnmp_table_registration_info *table_info; 3491303Swesolows netsnmp_handler_registration *handler; 3501303Swesolows int err; 3511303Swesolows 3521303Swesolows if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { 3531303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", 3541303Swesolows strerror(err)); 3551303Swesolows return (MIB_REGISTRATION_FAILED); 3561303Swesolows } 3571303Swesolows if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { 3581303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", 3591303Swesolows strerror(err)); 3601303Swesolows return (MIB_REGISTRATION_FAILED); 3611303Swesolows } 3621303Swesolows 3631303Swesolows if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, 3641303Swesolows NULL)) != 0) { 3651303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": error creating update " 3661303Swesolows "thread: %s\n", strerror(err)); 3671303Swesolows return (MIB_REGISTRATION_FAILED); 3681303Swesolows } 3691303Swesolows 3701303Swesolows if ((table_info = 3711303Swesolows SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) 3721303Swesolows return (MIB_REGISTRATION_FAILED); 3731303Swesolows 3741303Swesolows if ((handler = netsnmp_create_handler_registration("sunFmModuleTable", 3751303Swesolows sunFmModuleTable_handler, sunFmModuleTable_oid, 3761303Swesolows OID_LENGTH(sunFmModuleTable_oid), HANDLER_CAN_RONLY)) == NULL) { 3771303Swesolows SNMP_FREE(table_info); 3781303Swesolows return (MIB_REGISTRATION_FAILED); 3791303Swesolows } 3801303Swesolows 3811303Swesolows /* 3821303Swesolows * The Net-SNMP template uses add_indexes here, but that 3831303Swesolows * function is unsafe because it does not check for failure. 3841303Swesolows */ 3851303Swesolows if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { 3861303Swesolows SNMP_FREE(table_info); 3871303Swesolows SNMP_FREE(handler); 3881303Swesolows return (MIB_REGISTRATION_FAILED); 3891303Swesolows } 3901303Swesolows 3911303Swesolows table_info->min_column = SUNFMMODULE_COLMIN; 3921303Swesolows table_info->max_column = SUNFMMODULE_COLMAX; 3931303Swesolows 3941303Swesolows if ((mod_name_avl_pool = uu_avl_pool_create("mod_name", 3951303Swesolows sizeof (sunFmModule_data_t), 3961303Swesolows offsetof(sunFmModule_data_t, d_name_avl), module_compare_name, 3971303Swesolows UU_AVL_DEBUG)) == NULL) { 3981303Swesolows snmp_free_varbind(table_info->indexes); 3991303Swesolows SNMP_FREE(table_info); 4001303Swesolows SNMP_FREE(handler); 4011303Swesolows } 4021303Swesolows 4031303Swesolows if ((mod_name_avl = uu_avl_create(mod_name_avl_pool, NULL, 4041303Swesolows UU_AVL_DEBUG)) == NULL) { 4051303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mod_name_avl creation " 4061303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4071303Swesolows snmp_free_varbind(table_info->indexes); 4081303Swesolows SNMP_FREE(table_info); 4091303Swesolows SNMP_FREE(handler); 4101303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4111303Swesolows return (MIB_REGISTRATION_FAILED); 4121303Swesolows } 4131303Swesolows 4141303Swesolows if ((mod_index_avl_pool = uu_avl_pool_create("mod_index", 4151303Swesolows sizeof (sunFmModule_data_t), 4161303Swesolows offsetof(sunFmModule_data_t, d_index_avl), 4171303Swesolows module_compare_index, UU_AVL_DEBUG)) == NULL) { 4181303Swesolows snmp_free_varbind(table_info->indexes); 4191303Swesolows SNMP_FREE(table_info); 4201303Swesolows SNMP_FREE(handler); 4211303Swesolows uu_avl_destroy(mod_name_avl); 4221303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4231303Swesolows } 4241303Swesolows 4251303Swesolows if ((mod_index_avl = uu_avl_create(mod_index_avl_pool, NULL, 4261303Swesolows UU_AVL_DEBUG)) == NULL) { 4271303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mod_index_avl creation " 4281303Swesolows "failed: %s\n", uu_strerror(uu_error())); 4291303Swesolows snmp_free_varbind(table_info->indexes); 4301303Swesolows SNMP_FREE(table_info); 4311303Swesolows SNMP_FREE(handler); 4321303Swesolows uu_avl_destroy(mod_name_avl); 4331303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4341303Swesolows uu_avl_pool_destroy(mod_index_avl_pool); 4351303Swesolows return (MIB_REGISTRATION_FAILED); 4361303Swesolows } 4371303Swesolows 4381303Swesolows if ((err = netsnmp_register_table(handler, table_info)) != 4391303Swesolows MIB_REGISTERED_OK) { 4401303Swesolows snmp_free_varbind(table_info->indexes); 4411303Swesolows SNMP_FREE(table_info); 4421303Swesolows SNMP_FREE(handler); 4431303Swesolows uu_avl_destroy(mod_name_avl); 4441303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 4451303Swesolows uu_avl_destroy(mod_index_avl); 4461303Swesolows uu_avl_pool_destroy(mod_index_avl_pool); 4471303Swesolows return (err); 4481303Swesolows } 4491303Swesolows 4501303Swesolows return (MIB_REGISTERED_OK); 4511303Swesolows } 4521303Swesolows 4531303Swesolows /* 4541303Swesolows * These two functions form the core of GET/GETNEXT/GETBULK handling (the 4551303Swesolows * only kind we do). They perform two functions: 4561303Swesolows * 4571303Swesolows * - First, frob the request to set all the index variables to correspond 4581303Swesolows * to the value that's going to be returned. For GET, this is a nop; 4591303Swesolows * for GETNEXT/GETBULK it always requires some work. 4601303Swesolows * - Second, find and return the fmd module information corresponding to 4611303Swesolows * the (possibly updated) indices. 4621303Swesolows * 4631303Swesolows * These should be as fast as possible; they run in the agent thread. 4641303Swesolows */ 4651303Swesolows static sunFmModule_data_t * 4661303Swesolows sunFmModuleTable_nextmod(netsnmp_handler_registration *reginfo, 4671303Swesolows netsnmp_table_request_info *table_info) 4681303Swesolows { 4691303Swesolows sunFmModule_data_t *data; 4701303Swesolows netsnmp_variable_list *var; 4711303Swesolows ulong_t index; 4721303Swesolows 4731303Swesolows /* 4741303Swesolows * If we have no index, we must make one. 4751303Swesolows */ 4761303Swesolows if (table_info->number_indexes < 1) { 4771303Swesolows oid tmpoid[MAX_OID_LEN]; 4781303Swesolows index = 1; 4791303Swesolows 4801303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: no indexes given\n")); 4811303Swesolows var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); 4821303Swesolows snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index, 4831303Swesolows sizeof (index)); 484*2134Swesolows (void) memcpy(tmpoid, reginfo->rootoid, 4851303Swesolows reginfo->rootoid_len * sizeof (oid)); 4861303Swesolows tmpoid[reginfo->rootoid_len] = 1; /* Entry is .1 */ 4871303Swesolows tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; 4881303Swesolows if (build_oid(&var->name, &var->name_length, tmpoid, 489*2134Swesolows reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) { 490*2134Swesolows snmp_free_varbind(var); 4911303Swesolows return (NULL); 492*2134Swesolows } 4931303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: built fake index:\n")); 4941303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 4951303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 4961303Swesolows } else { 497*2134Swesolows var = snmp_clone_varbind(table_info->indexes); 4981303Swesolows index = *var->val.integer; 4991303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: received index:\n")); 5001303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 5011303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 5021303Swesolows index++; 5031303Swesolows } 5041303Swesolows 505*2134Swesolows snmp_free_varbind(table_info->indexes); 506*2134Swesolows table_info->indexes = NULL; 507*2134Swesolows table_info->number_indexes = 0; 508*2134Swesolows 5091303Swesolows if ((data = module_lookup_index_nextvalid(index)) == NULL) { 5101303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " 5111303Swesolows "index %lu; trying next column\n", index)); 512*2134Swesolows if (table_info->colnum >= 513*2134Swesolows netsnmp_find_table_registration_info(reginfo)->max_column) { 5141303Swesolows snmp_free_varbind(var); 5151303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: out of columns\n")); 5161303Swesolows return (NULL); 5171303Swesolows } 5181303Swesolows table_info->colnum++; 5191303Swesolows index = 1; 5201303Swesolows 5211303Swesolows data = module_lookup_index_nextvalid(index); 5221303Swesolows } 5231303Swesolows 5241303Swesolows if (data == NULL) { 5251303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " 5261303Swesolows "index %lu; stopping\n", index)); 5271303Swesolows snmp_free_varbind(var); 5281303Swesolows return (NULL); 5291303Swesolows } 5301303Swesolows 5311303Swesolows *var->val.integer = index; 5321303Swesolows table_info->indexes = var; 533*2134Swesolows table_info->number_indexes = 1; 5341303Swesolows 5351303Swesolows DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index, 5361303Swesolows data->d_ami_name, data)); 5371303Swesolows 5381303Swesolows return (data); 5391303Swesolows } 5401303Swesolows 5411303Swesolows /*ARGSUSED*/ 5421303Swesolows static sunFmModule_data_t * 5431303Swesolows sunFmModuleTable_mod(netsnmp_handler_registration *reginfo, 5441303Swesolows netsnmp_table_request_info *table_info) 5451303Swesolows { 5461303Swesolows ASSERT(table_info->number_indexes == 1); 5471303Swesolows 5481303Swesolows return (module_lookup_index_exact(table_info->index_oid[0])); 5491303Swesolows } 5501303Swesolows 551*2134Swesolows /*ARGSUSED*/ 5521303Swesolows static void 5531303Swesolows sunFmModuleTable_return(unsigned int reg, void *arg) 5541303Swesolows { 5551303Swesolows netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; 5561303Swesolows netsnmp_request_info *request; 5571303Swesolows netsnmp_agent_request_info *reqinfo; 5581303Swesolows netsnmp_handler_registration *reginfo; 5591303Swesolows netsnmp_table_request_info *table_info; 5601303Swesolows sunFmModule_data_t *data; 5611303Swesolows ulong_t modstate; 5621303Swesolows 5631303Swesolows ASSERT(netsnmp_handler_check_cache(cache) != NULL); 5641303Swesolows 5651303Swesolows (void) pthread_mutex_lock(&update_lock); 5661303Swesolows if (update_status != US_QUIET) { 5671303Swesolows struct timeval tv; 5681303Swesolows 5691303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 5701303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 5711303Swesolows 5721303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, 5731303Swesolows cache); 5741303Swesolows (void) pthread_mutex_unlock(&update_lock); 5751303Swesolows return; 5761303Swesolows } 5771303Swesolows 5781303Swesolows request = cache->requests; 5791303Swesolows reqinfo = cache->reqinfo; 5801303Swesolows reginfo = cache->reginfo; 5811303Swesolows 5821303Swesolows table_info = netsnmp_extract_table_info(request); 5831303Swesolows request->delegated = 0; 5841303Swesolows 5851303Swesolows ASSERT(table_info->colnum >= SUNFMMODULE_COLMIN); 5861303Swesolows ASSERT(table_info->colnum <= SUNFMMODULE_COLMAX); 5871303Swesolows 5881303Swesolows /* 5891303Swesolows * table_info->colnum contains the column number requested. 5901303Swesolows * table_info->indexes contains a linked list of snmp variable 5911303Swesolows * bindings for the indexes of the table. Values in the list 5921303Swesolows * have been set corresponding to the indexes of the 5931303Swesolows * request. We have other guarantees as well: 5941303Swesolows * 5951303Swesolows * - The column number is always within range. 5961303Swesolows * - If we have no index data, table_info->index_oid_len is 0. 5971303Swesolows * - We will never receive requests outside our table nor 5981303Swesolows * those with the first subid anything other than 1 (Entry) 5991303Swesolows * nor those without a column number. This is true even 6001303Swesolows * for GETNEXT requests. 6011303Swesolows */ 6021303Swesolows 6031303Swesolows switch (reqinfo->mode) { 6041303Swesolows case MODE_GET: 6051303Swesolows if ((data = sunFmModuleTable_mod(reginfo, table_info)) == 6061303Swesolows NULL) { 6071303Swesolows netsnmp_free_delegated_cache(cache); 6081303Swesolows (void) pthread_mutex_unlock(&update_lock); 6091303Swesolows return; 6101303Swesolows } 6111303Swesolows break; 6121303Swesolows case MODE_GETNEXT: 6131303Swesolows case MODE_GETBULK: 6141303Swesolows if ((data = sunFmModuleTable_nextmod(reginfo, table_info)) == 6151303Swesolows NULL) { 6161303Swesolows netsnmp_free_delegated_cache(cache); 6171303Swesolows (void) pthread_mutex_unlock(&update_lock); 6181303Swesolows return; 6191303Swesolows } 6201303Swesolows break; 6211303Swesolows default: 6221303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request " 6231303Swesolows "mode %d\n", reqinfo->mode); 6241303Swesolows netsnmp_free_delegated_cache(cache); 6251303Swesolows (void) pthread_mutex_unlock(&update_lock); 6261303Swesolows return; 6271303Swesolows } 6281303Swesolows 6291303Swesolows switch (table_info->colnum) { 6301303Swesolows case SUNFMMODULE_COL_NAME: 6311303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6321303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_name, 6331303Swesolows strlen(data->d_ami_name)); 6341303Swesolows break; 6351303Swesolows case SUNFMMODULE_COL_VERSION: 6361303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6371303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_vers, 6381303Swesolows strlen(data->d_ami_vers)); 6391303Swesolows break; 6401303Swesolows case SUNFMMODULE_COL_STATUS: 6411303Swesolows modstate = (data->d_ami_flags & FMD_ADM_MOD_FAILED) ? 6421303Swesolows SUNFMMODULE_STATE_FAILED : SUNFMMODULE_STATE_ACTIVE; 6431303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6441303Swesolows ASN_INTEGER, (uchar_t *)&modstate, 6451303Swesolows sizeof (modstate)); 6461303Swesolows break; 6471303Swesolows case SUNFMMODULE_COL_DESCRIPTION: 6481303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 6491303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_desc, 6501303Swesolows strlen(data->d_ami_desc)); 6511303Swesolows break; 6521303Swesolows default: 6531303Swesolows break; 6541303Swesolows } 6551303Swesolows netsnmp_free_delegated_cache(cache); 6561303Swesolows (void) pthread_mutex_unlock(&update_lock); 6571303Swesolows } 6581303Swesolows 6591303Swesolows static int 6601303Swesolows sunFmModuleTable_handler(netsnmp_mib_handler *handler, 6611303Swesolows netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, 6621303Swesolows netsnmp_request_info *requests) 6631303Swesolows { 6641303Swesolows netsnmp_request_info *request; 6651303Swesolows struct timeval tv; 6661303Swesolows 6671303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 6681303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 6691303Swesolows 6701303Swesolows request_update(); 6711303Swesolows 6721303Swesolows for (request = requests; request; request = request->next) { 6731303Swesolows if (request->processed != 0) 6741303Swesolows continue; 6751303Swesolows 6761303Swesolows if (netsnmp_extract_table_info(request) == NULL) 6771303Swesolows continue; 6781303Swesolows 6791303Swesolows request->delegated = 1; 6801303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, 6811303Swesolows (void *) netsnmp_create_delegated_cache(handler, reginfo, 6821303Swesolows reqinfo, request, NULL)); 6831303Swesolows } 6841303Swesolows 6851303Swesolows return (SNMP_ERR_NOERROR); 6861303Swesolows } 687