1*1303Swesolows /* 2*1303Swesolows * CDDL HEADER START 3*1303Swesolows * 4*1303Swesolows * The contents of this file are subject to the terms of the 5*1303Swesolows * Common Development and Distribution License (the "License"). 6*1303Swesolows * You may not use this file except in compliance with the License. 7*1303Swesolows * 8*1303Swesolows * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*1303Swesolows * or http://www.opensolaris.org/os/licensing. 10*1303Swesolows * See the License for the specific language governing permissions 11*1303Swesolows * and limitations under the License. 12*1303Swesolows * 13*1303Swesolows * When distributing Covered Code, include this CDDL HEADER in each 14*1303Swesolows * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*1303Swesolows * If applicable, add the following below this CDDL HEADER, with the 16*1303Swesolows * fields enclosed by brackets "[]" replaced with your own identifying 17*1303Swesolows * information: Portions Copyright [yyyy] [name of copyright owner] 18*1303Swesolows * 19*1303Swesolows * CDDL HEADER END 20*1303Swesolows */ 21*1303Swesolows 22*1303Swesolows /* 23*1303Swesolows * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*1303Swesolows * Use is subject to license terms. 25*1303Swesolows */ 26*1303Swesolows 27*1303Swesolows #pragma ident "%Z%%M% %I% %E% SMI" 28*1303Swesolows 29*1303Swesolows #include <fm/fmd_adm.h> 30*1303Swesolows #include <fm/fmd_snmp.h> 31*1303Swesolows #include <net-snmp/net-snmp-config.h> 32*1303Swesolows #include <net-snmp/net-snmp-includes.h> 33*1303Swesolows #include <net-snmp/agent/net-snmp-agent-includes.h> 34*1303Swesolows #include <pthread.h> 35*1303Swesolows #include <stddef.h> 36*1303Swesolows #include <errno.h> 37*1303Swesolows #include <libuutil.h> 38*1303Swesolows #include "sunFM_impl.h" 39*1303Swesolows #include "module.h" 40*1303Swesolows 41*1303Swesolows static uu_avl_pool_t *mod_name_avl_pool; 42*1303Swesolows static uu_avl_pool_t *mod_index_avl_pool; 43*1303Swesolows static uu_avl_t *mod_name_avl; 44*1303Swesolows static uu_avl_t *mod_index_avl; 45*1303Swesolows 46*1303Swesolows #define VALID_AVL_STATE (mod_name_avl_pool != NULL && \ 47*1303Swesolows mod_index_avl_pool != NULL && mod_name_avl != NULL && \ 48*1303Swesolows mod_index_avl != NULL) 49*1303Swesolows 50*1303Swesolows #define UPDATE_WAIT_MILLIS 10 /* poll interval in milliseconds */ 51*1303Swesolows 52*1303Swesolows /* 53*1303Swesolows * Update types. Single-index and all are mutually exclusive. 54*1303Swesolows */ 55*1303Swesolows #define UCT_INDEX 0x1 56*1303Swesolows #define UCT_ALL 0x2 57*1303Swesolows #define UCT_FLAGS 0x3 58*1303Swesolows 59*1303Swesolows #define MODULE_DATA_VALID(d) ((d)->d_valid == valid_stamp) 60*1303Swesolows 61*1303Swesolows /* 62*1303Swesolows * Locking rules are straightforward. There is only one updater thread 63*1303Swesolows * for each table, and requests for update that are received while 64*1303Swesolows * another update is in progress are ignored. The single per-table lock 65*1303Swesolows * protects all the data for the table, the valid_stamp and max_index 66*1303Swesolows * tags for new data, and - importantly - all the hidden static data 67*1303Swesolows * used by the Net-SNMP library. The result return callbacks are always 68*1303Swesolows * called in the master agent thread; holding the table lock is 69*1303Swesolows * therefore sufficient since only one table's callback can be run at 70*1303Swesolows * any one time. Finer-grained locking is possible here but 71*1303Swesolows * substantially more difficult because nearly all Net-SNMP functions 72*1303Swesolows * are unsafe. 73*1303Swesolows * 74*1303Swesolows * In practice this is more than adequate, since the purpose of 75*1303Swesolows * threading out the update is to prevent excessively time-consuming 76*1303Swesolows * data collection from bottlenecking the entire agent, not to improve 77*1303Swesolows * result throughput (SNMP is not intended to be used for applications 78*1303Swesolows * requiring high throughput anyway). If the agent itself ever becomes 79*1303Swesolows * multithreaded, locking requirements become limited to our local 80*1303Swesolows * per-table data (the tree, max_index, and valid_stamp), and the 81*1303Swesolows * implementation could be revisited for better performance. 82*1303Swesolows */ 83*1303Swesolows 84*1303Swesolows static ulong_t max_index; 85*1303Swesolows static int valid_stamp; 86*1303Swesolows static pthread_mutex_t update_lock; 87*1303Swesolows static pthread_cond_t update_cv; 88*1303Swesolows static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status; 89*1303Swesolows 90*1303Swesolows static Netsnmp_Node_Handler sunFmModuleTable_handler; 91*1303Swesolows 92*1303Swesolows static sunFmModule_data_t * 93*1303Swesolows key_build(const char *name, const ulong_t index) 94*1303Swesolows { 95*1303Swesolows static sunFmModule_data_t key; 96*1303Swesolows 97*1303Swesolows key.d_index = index; 98*1303Swesolows if (name) 99*1303Swesolows strlcpy(key.d_ami_name, name, sizeof (key.d_ami_name)); 100*1303Swesolows else 101*1303Swesolows key.d_ami_name[0] = '\0'; 102*1303Swesolows 103*1303Swesolows return (&key); 104*1303Swesolows } 105*1303Swesolows 106*1303Swesolows /* 107*1303Swesolows * If name is the name of a module we have previously seen and indexed, return 108*1303Swesolows * data for it. Otherwise, return NULL. Note that the module may not be 109*1303Swesolows * valid; that is, it may have been removed from the fault manager since its 110*1303Swesolows * information was last updated. 111*1303Swesolows */ 112*1303Swesolows static sunFmModule_data_t * 113*1303Swesolows module_lookup_name(const char *name) 114*1303Swesolows { 115*1303Swesolows sunFmModule_data_t *key; 116*1303Swesolows 117*1303Swesolows key = key_build(name, 0); 118*1303Swesolows return (uu_avl_find(mod_name_avl, key, NULL, NULL)); 119*1303Swesolows } 120*1303Swesolows 121*1303Swesolows /* 122*1303Swesolows * If index corresponds to a module we have previously seen and indexed, return 123*1303Swesolows * data for it. Otherwise, return NULL. Note that the module may not be 124*1303Swesolows * valid; that is, it may have been removed from the fault manager since its 125*1303Swesolows * information was last updated. 126*1303Swesolows */ 127*1303Swesolows static sunFmModule_data_t * 128*1303Swesolows module_lookup_index_exact(const ulong_t index) 129*1303Swesolows { 130*1303Swesolows sunFmModule_data_t *key; 131*1303Swesolows 132*1303Swesolows key = key_build(NULL, index); 133*1303Swesolows return (uu_avl_find(mod_index_avl, key, NULL, NULL)); 134*1303Swesolows } 135*1303Swesolows 136*1303Swesolows /* 137*1303Swesolows * If index corresponds to a valid (that is, extant as of latest information 138*1303Swesolows * from the fault manager) fmd module, return the data for that module. 139*1303Swesolows * Otherwise, return the data for the valid module whose index is as close as 140*1303Swesolows * possible to index but not lower. This preserves the lexicographical 141*1303Swesolows * ordering required for GETNEXT processing. 142*1303Swesolows */ 143*1303Swesolows static sunFmModule_data_t * 144*1303Swesolows module_lookup_index_nextvalid(const ulong_t index) 145*1303Swesolows { 146*1303Swesolows sunFmModule_data_t *key, *data; 147*1303Swesolows uu_avl_index_t idx; 148*1303Swesolows 149*1303Swesolows key = key_build(NULL, index); 150*1303Swesolows 151*1303Swesolows if ((data = uu_avl_find(mod_index_avl, key, NULL, &idx)) != NULL && 152*1303Swesolows MODULE_DATA_VALID(data)) 153*1303Swesolows return (data); 154*1303Swesolows 155*1303Swesolows data = uu_avl_nearest_next(mod_index_avl, idx); 156*1303Swesolows 157*1303Swesolows while (data != NULL && !MODULE_DATA_VALID(data)) { 158*1303Swesolows (void) uu_avl_find(mod_index_avl, data, NULL, &idx); 159*1303Swesolows data = uu_avl_nearest_next(mod_index_avl, idx); 160*1303Swesolows } 161*1303Swesolows 162*1303Swesolows return (data); 163*1303Swesolows } 164*1303Swesolows 165*1303Swesolows /* 166*1303Swesolows * Possible update the contents of a single module within the cache. This 167*1303Swesolows * is our callback from fmd_module_iter. 168*1303Swesolows */ 169*1303Swesolows static int 170*1303Swesolows modinfo_update_one(const fmd_adm_modinfo_t *modinfo, void *arg) 171*1303Swesolows { 172*1303Swesolows const sunFmModule_update_ctx_t *update_ctx = 173*1303Swesolows (sunFmModule_update_ctx_t *)arg; 174*1303Swesolows sunFmModule_data_t *data = module_lookup_name(modinfo->ami_name); 175*1303Swesolows 176*1303Swesolows /* 177*1303Swesolows * An fmd module we haven't seen before. We're obligated to index 178*1303Swesolows * it and link it into our cache so that we can find it, but we're 179*1303Swesolows * not obligated to fill it in completely unless we're doing a 180*1303Swesolows * thorough update or this is the module we were asked for. This 181*1303Swesolows * avoids unnecessary iteration and memory manipulation for data 182*1303Swesolows * we're not going to return for this request. 183*1303Swesolows */ 184*1303Swesolows if (data == NULL) { 185*1303Swesolows uu_avl_index_t idx; 186*1303Swesolows 187*1303Swesolows DEBUGMSGTL((MODNAME_STR, "found new fmd module %s\n", 188*1303Swesolows modinfo->ami_name)); 189*1303Swesolows if ((data = SNMP_MALLOC_TYPEDEF(sunFmModule_data_t)) == NULL) { 190*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Out of memory for " 191*1303Swesolows "new module data at %s:%d\n", __FILE__, __LINE__); 192*1303Swesolows return (1); 193*1303Swesolows } 194*1303Swesolows /* 195*1303Swesolows * We allocate indices sequentially and never reuse them. 196*1303Swesolows * This ensures we can always return valid GETNEXT responses 197*1303Swesolows * without having to reindex, and it provides the user a 198*1303Swesolows * more consistent view of the fault manager. 199*1303Swesolows */ 200*1303Swesolows data->d_index = ++max_index; 201*1303Swesolows DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index, 202*1303Swesolows modinfo->ami_name, data)); 203*1303Swesolows 204*1303Swesolows strlcpy(data->d_ami_name, modinfo->ami_name, 205*1303Swesolows sizeof (data->d_ami_name)); 206*1303Swesolows 207*1303Swesolows uu_avl_node_init(data, &data->d_name_avl, mod_name_avl_pool); 208*1303Swesolows (void) uu_avl_find(mod_name_avl, data, NULL, &idx); 209*1303Swesolows uu_avl_insert(mod_name_avl, data, idx); 210*1303Swesolows 211*1303Swesolows uu_avl_node_init(data, &data->d_index_avl, mod_index_avl_pool); 212*1303Swesolows (void) uu_avl_find(mod_index_avl, data, NULL, &idx); 213*1303Swesolows uu_avl_insert(mod_index_avl, data, idx); 214*1303Swesolows 215*1303Swesolows DEBUGMSGTL((MODNAME_STR, "completed new module %lu/%s@%p\n", 216*1303Swesolows data->d_index, data->d_ami_name, data)); 217*1303Swesolows } 218*1303Swesolows 219*1303Swesolows data->d_valid = valid_stamp; 220*1303Swesolows 221*1303Swesolows DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %lu\n", 222*1303Swesolows data->d_index, data->d_ami_name, data, data->d_valid)); 223*1303Swesolows 224*1303Swesolows if ((update_ctx->uc_type & UCT_ALL) || 225*1303Swesolows update_ctx->uc_index == data->d_index) { 226*1303Swesolows strlcpy(data->d_ami_vers, modinfo->ami_vers, 227*1303Swesolows sizeof (data->d_ami_vers)); 228*1303Swesolows strlcpy(data->d_ami_desc, modinfo->ami_desc, 229*1303Swesolows sizeof (data->d_ami_desc)); 230*1303Swesolows data->d_ami_flags = modinfo->ami_flags; 231*1303Swesolows } 232*1303Swesolows 233*1303Swesolows return (!(update_ctx->uc_type & UCT_ALL) && 234*1303Swesolows update_ctx->uc_index == data->d_index); 235*1303Swesolows } 236*1303Swesolows 237*1303Swesolows /* 238*1303Swesolows * Update some or all module data from fmd. If thorough is set, all modules 239*1303Swesolows * will be indexed and their data cached. Otherwise, updates will stop once 240*1303Swesolows * the module matching index has been updated. 241*1303Swesolows * 242*1303Swesolows * Returns appropriate SNMP error codes. 243*1303Swesolows */ 244*1303Swesolows static int 245*1303Swesolows modinfo_update(sunFmModule_update_ctx_t *update_ctx) 246*1303Swesolows { 247*1303Swesolows fmd_adm_t *adm; 248*1303Swesolows 249*1303Swesolows ASSERT(update_ctx != NULL); 250*1303Swesolows ASSERT((update_ctx->uc_type & (UCT_INDEX|UCT_ALL)) != 251*1303Swesolows (UCT_INDEX|UCT_ALL)); 252*1303Swesolows ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0); 253*1303Swesolows ASSERT(VALID_AVL_STATE); 254*1303Swesolows 255*1303Swesolows if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog, 256*1303Swesolows update_ctx->uc_version)) == NULL) { 257*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd " 258*1303Swesolows "failed: %s\n", strerror(errno)); 259*1303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 260*1303Swesolows } 261*1303Swesolows 262*1303Swesolows ++valid_stamp; 263*1303Swesolows if (fmd_adm_module_iter(adm, modinfo_update_one, update_ctx) != 0) { 264*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": fmd module information update " 265*1303Swesolows "failed: %s\n", fmd_adm_errmsg(adm)); 266*1303Swesolows fmd_adm_close(adm); 267*1303Swesolows return (SNMP_ERR_RESOURCEUNAVAILABLE); 268*1303Swesolows } 269*1303Swesolows 270*1303Swesolows DEBUGMSGTL((MODNAME_STR, "module iteration completed\n")); 271*1303Swesolows 272*1303Swesolows fmd_adm_close(adm); 273*1303Swesolows return (SNMP_ERR_NOERROR); 274*1303Swesolows } 275*1303Swesolows 276*1303Swesolows /*ARGSUSED*/ 277*1303Swesolows static void 278*1303Swesolows update_thread(void *arg) 279*1303Swesolows { 280*1303Swesolows /* 281*1303Swesolows * The current modinfo_update implementation offers minimal savings 282*1303Swesolows * for the use of index-only updates; therefore we always do a full 283*1303Swesolows * update. If it becomes advantageous to limit updates to a single 284*1303Swesolows * index, the contexts can be queued by the handler instead. 285*1303Swesolows */ 286*1303Swesolows sunFmModule_update_ctx_t uc; 287*1303Swesolows 288*1303Swesolows uc.uc_host = NULL; 289*1303Swesolows uc.uc_prog = FMD_ADM_PROGRAM; 290*1303Swesolows uc.uc_version = FMD_ADM_VERSION; 291*1303Swesolows 292*1303Swesolows uc.uc_index = 0; 293*1303Swesolows uc.uc_type = UCT_ALL; 294*1303Swesolows 295*1303Swesolows for (;;) { 296*1303Swesolows (void) pthread_mutex_lock(&update_lock); 297*1303Swesolows update_status = US_QUIET; 298*1303Swesolows while (update_status == US_QUIET) 299*1303Swesolows (void) pthread_cond_wait(&update_cv, &update_lock); 300*1303Swesolows update_status = US_INPROGRESS; 301*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 302*1303Swesolows (void) modinfo_update(&uc); 303*1303Swesolows } 304*1303Swesolows } 305*1303Swesolows 306*1303Swesolows static void 307*1303Swesolows request_update(void) 308*1303Swesolows { 309*1303Swesolows (void) pthread_mutex_lock(&update_lock); 310*1303Swesolows if (update_status != US_QUIET) { 311*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 312*1303Swesolows return; 313*1303Swesolows } 314*1303Swesolows update_status = US_NEEDED; 315*1303Swesolows (void) pthread_cond_signal(&update_cv); 316*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 317*1303Swesolows } 318*1303Swesolows 319*1303Swesolows /*ARGSUSED*/ 320*1303Swesolows static int 321*1303Swesolows module_compare_name(const void *l, const void *r, void *private) 322*1303Swesolows { 323*1303Swesolows sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; 324*1303Swesolows sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; 325*1303Swesolows 326*1303Swesolows ASSERT(l_data != NULL && r_data != NULL); 327*1303Swesolows 328*1303Swesolows return (strcmp(l_data->d_ami_name, r_data->d_ami_name)); 329*1303Swesolows } 330*1303Swesolows 331*1303Swesolows /*ARGSUSED*/ 332*1303Swesolows static int 333*1303Swesolows module_compare_index(const void *l, const void *r, void *private) 334*1303Swesolows { 335*1303Swesolows sunFmModule_data_t *l_data = (sunFmModule_data_t *)l; 336*1303Swesolows sunFmModule_data_t *r_data = (sunFmModule_data_t *)r; 337*1303Swesolows 338*1303Swesolows ASSERT(l_data != NULL && r_data != NULL); 339*1303Swesolows 340*1303Swesolows return (l_data->d_index < r_data->d_index ? -1 : 341*1303Swesolows l_data->d_index > r_data->d_index ? 1 : 0); 342*1303Swesolows } 343*1303Swesolows 344*1303Swesolows int 345*1303Swesolows sunFmModuleTable_init(void) 346*1303Swesolows { 347*1303Swesolows static oid sunFmModuleTable_oid[] = { SUNFMMODULETABLE_OID }; 348*1303Swesolows netsnmp_table_registration_info *table_info; 349*1303Swesolows netsnmp_handler_registration *handler; 350*1303Swesolows int err; 351*1303Swesolows 352*1303Swesolows if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) { 353*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: %s\n", 354*1303Swesolows strerror(err)); 355*1303Swesolows return (MIB_REGISTRATION_FAILED); 356*1303Swesolows } 357*1303Swesolows if ((err = pthread_cond_init(&update_cv, NULL)) != 0) { 358*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: %s\n", 359*1303Swesolows strerror(err)); 360*1303Swesolows return (MIB_REGISTRATION_FAILED); 361*1303Swesolows } 362*1303Swesolows 363*1303Swesolows if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread, 364*1303Swesolows NULL)) != 0) { 365*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": error creating update " 366*1303Swesolows "thread: %s\n", strerror(err)); 367*1303Swesolows return (MIB_REGISTRATION_FAILED); 368*1303Swesolows } 369*1303Swesolows 370*1303Swesolows if ((table_info = 371*1303Swesolows SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL) 372*1303Swesolows return (MIB_REGISTRATION_FAILED); 373*1303Swesolows 374*1303Swesolows if ((handler = netsnmp_create_handler_registration("sunFmModuleTable", 375*1303Swesolows sunFmModuleTable_handler, sunFmModuleTable_oid, 376*1303Swesolows OID_LENGTH(sunFmModuleTable_oid), HANDLER_CAN_RONLY)) == NULL) { 377*1303Swesolows SNMP_FREE(table_info); 378*1303Swesolows return (MIB_REGISTRATION_FAILED); 379*1303Swesolows } 380*1303Swesolows 381*1303Swesolows /* 382*1303Swesolows * The Net-SNMP template uses add_indexes here, but that 383*1303Swesolows * function is unsafe because it does not check for failure. 384*1303Swesolows */ 385*1303Swesolows if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) { 386*1303Swesolows SNMP_FREE(table_info); 387*1303Swesolows SNMP_FREE(handler); 388*1303Swesolows return (MIB_REGISTRATION_FAILED); 389*1303Swesolows } 390*1303Swesolows 391*1303Swesolows table_info->min_column = SUNFMMODULE_COLMIN; 392*1303Swesolows table_info->max_column = SUNFMMODULE_COLMAX; 393*1303Swesolows 394*1303Swesolows if ((mod_name_avl_pool = uu_avl_pool_create("mod_name", 395*1303Swesolows sizeof (sunFmModule_data_t), 396*1303Swesolows offsetof(sunFmModule_data_t, d_name_avl), module_compare_name, 397*1303Swesolows UU_AVL_DEBUG)) == NULL) { 398*1303Swesolows snmp_free_varbind(table_info->indexes); 399*1303Swesolows SNMP_FREE(table_info); 400*1303Swesolows SNMP_FREE(handler); 401*1303Swesolows } 402*1303Swesolows 403*1303Swesolows if ((mod_name_avl = uu_avl_create(mod_name_avl_pool, NULL, 404*1303Swesolows UU_AVL_DEBUG)) == NULL) { 405*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mod_name_avl creation " 406*1303Swesolows "failed: %s\n", uu_strerror(uu_error())); 407*1303Swesolows snmp_free_varbind(table_info->indexes); 408*1303Swesolows SNMP_FREE(table_info); 409*1303Swesolows SNMP_FREE(handler); 410*1303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 411*1303Swesolows return (MIB_REGISTRATION_FAILED); 412*1303Swesolows } 413*1303Swesolows 414*1303Swesolows if ((mod_index_avl_pool = uu_avl_pool_create("mod_index", 415*1303Swesolows sizeof (sunFmModule_data_t), 416*1303Swesolows offsetof(sunFmModule_data_t, d_index_avl), 417*1303Swesolows module_compare_index, UU_AVL_DEBUG)) == NULL) { 418*1303Swesolows snmp_free_varbind(table_info->indexes); 419*1303Swesolows SNMP_FREE(table_info); 420*1303Swesolows SNMP_FREE(handler); 421*1303Swesolows uu_avl_destroy(mod_name_avl); 422*1303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 423*1303Swesolows } 424*1303Swesolows 425*1303Swesolows if ((mod_index_avl = uu_avl_create(mod_index_avl_pool, NULL, 426*1303Swesolows UU_AVL_DEBUG)) == NULL) { 427*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": mod_index_avl creation " 428*1303Swesolows "failed: %s\n", uu_strerror(uu_error())); 429*1303Swesolows snmp_free_varbind(table_info->indexes); 430*1303Swesolows SNMP_FREE(table_info); 431*1303Swesolows SNMP_FREE(handler); 432*1303Swesolows uu_avl_destroy(mod_name_avl); 433*1303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 434*1303Swesolows uu_avl_pool_destroy(mod_index_avl_pool); 435*1303Swesolows return (MIB_REGISTRATION_FAILED); 436*1303Swesolows } 437*1303Swesolows 438*1303Swesolows if ((err = netsnmp_register_table(handler, table_info)) != 439*1303Swesolows MIB_REGISTERED_OK) { 440*1303Swesolows snmp_free_varbind(table_info->indexes); 441*1303Swesolows SNMP_FREE(table_info); 442*1303Swesolows SNMP_FREE(handler); 443*1303Swesolows uu_avl_destroy(mod_name_avl); 444*1303Swesolows uu_avl_pool_destroy(mod_name_avl_pool); 445*1303Swesolows uu_avl_destroy(mod_index_avl); 446*1303Swesolows uu_avl_pool_destroy(mod_index_avl_pool); 447*1303Swesolows return (err); 448*1303Swesolows } 449*1303Swesolows 450*1303Swesolows return (MIB_REGISTERED_OK); 451*1303Swesolows } 452*1303Swesolows 453*1303Swesolows /* 454*1303Swesolows * These two functions form the core of GET/GETNEXT/GETBULK handling (the 455*1303Swesolows * only kind we do). They perform two functions: 456*1303Swesolows * 457*1303Swesolows * - First, frob the request to set all the index variables to correspond 458*1303Swesolows * to the value that's going to be returned. For GET, this is a nop; 459*1303Swesolows * for GETNEXT/GETBULK it always requires some work. 460*1303Swesolows * - Second, find and return the fmd module information corresponding to 461*1303Swesolows * the (possibly updated) indices. 462*1303Swesolows * 463*1303Swesolows * These should be as fast as possible; they run in the agent thread. 464*1303Swesolows */ 465*1303Swesolows static sunFmModule_data_t * 466*1303Swesolows sunFmModuleTable_nextmod(netsnmp_handler_registration *reginfo, 467*1303Swesolows netsnmp_table_request_info *table_info) 468*1303Swesolows { 469*1303Swesolows sunFmModule_data_t *data; 470*1303Swesolows netsnmp_variable_list *var; 471*1303Swesolows ulong_t index; 472*1303Swesolows 473*1303Swesolows /* 474*1303Swesolows * If we have no index, we must make one. 475*1303Swesolows */ 476*1303Swesolows if (table_info->number_indexes < 1) { 477*1303Swesolows oid tmpoid[MAX_OID_LEN]; 478*1303Swesolows index = 1; 479*1303Swesolows 480*1303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: no indexes given\n")); 481*1303Swesolows var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); 482*1303Swesolows snmp_set_var_typed_value(var, ASN_UNSIGNED, (uchar_t *)&index, 483*1303Swesolows sizeof (index)); 484*1303Swesolows memcpy(tmpoid, reginfo->rootoid, 485*1303Swesolows reginfo->rootoid_len * sizeof (oid)); 486*1303Swesolows tmpoid[reginfo->rootoid_len] = 1; /* Entry is .1 */ 487*1303Swesolows tmpoid[reginfo->rootoid_len + 1] = table_info->colnum; 488*1303Swesolows if (build_oid(&var->name, &var->name_length, tmpoid, 489*1303Swesolows reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) 490*1303Swesolows return (NULL); 491*1303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: built fake index:\n")); 492*1303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 493*1303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 494*1303Swesolows } else { 495*1303Swesolows var = table_info->indexes; 496*1303Swesolows index = *var->val.integer; 497*1303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: received index:\n")); 498*1303Swesolows DEBUGMSGVAR((MODNAME_STR, var)); 499*1303Swesolows DEBUGMSG((MODNAME_STR, "\n")); 500*1303Swesolows index++; 501*1303Swesolows } 502*1303Swesolows 503*1303Swesolows if ((data = module_lookup_index_nextvalid(index)) == NULL) { 504*1303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " 505*1303Swesolows "index %lu; trying next column\n", index)); 506*1303Swesolows if (table_info->colnum >= SUNFMMODULE_COLMAX) { 507*1303Swesolows snmp_free_varbind(var); 508*1303Swesolows table_info->indexes = NULL; 509*1303Swesolows table_info->number_indexes = 0; 510*1303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: out of columns\n")); 511*1303Swesolows return (NULL); 512*1303Swesolows } 513*1303Swesolows table_info->colnum++; 514*1303Swesolows index = 1; 515*1303Swesolows 516*1303Swesolows data = module_lookup_index_nextvalid(index); 517*1303Swesolows } 518*1303Swesolows 519*1303Swesolows if (data == NULL) { 520*1303Swesolows DEBUGMSGTL((MODNAME_STR, "nextmod: exact match not found for " 521*1303Swesolows "index %lu; stopping\n", index)); 522*1303Swesolows snmp_free_varbind(var); 523*1303Swesolows table_info->indexes = NULL; 524*1303Swesolows table_info->number_indexes = 0; 525*1303Swesolows return (NULL); 526*1303Swesolows } 527*1303Swesolows 528*1303Swesolows *var->val.integer = index; 529*1303Swesolows table_info->indexes = var; 530*1303Swesolows 531*1303Swesolows DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index, 532*1303Swesolows data->d_ami_name, data)); 533*1303Swesolows 534*1303Swesolows return (data); 535*1303Swesolows } 536*1303Swesolows 537*1303Swesolows /*ARGSUSED*/ 538*1303Swesolows static sunFmModule_data_t * 539*1303Swesolows sunFmModuleTable_mod(netsnmp_handler_registration *reginfo, 540*1303Swesolows netsnmp_table_request_info *table_info) 541*1303Swesolows { 542*1303Swesolows sunFmModule_data_t *data; 543*1303Swesolows netsnmp_variable_list *var; 544*1303Swesolows 545*1303Swesolows ASSERT(table_info->number_indexes == 1); 546*1303Swesolows 547*1303Swesolows return (module_lookup_index_exact(table_info->index_oid[0])); 548*1303Swesolows } 549*1303Swesolows 550*1303Swesolows static void 551*1303Swesolows sunFmModuleTable_return(unsigned int reg, void *arg) 552*1303Swesolows { 553*1303Swesolows netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *)arg; 554*1303Swesolows netsnmp_request_info *request; 555*1303Swesolows netsnmp_agent_request_info *reqinfo; 556*1303Swesolows netsnmp_handler_registration *reginfo; 557*1303Swesolows netsnmp_mib_handler *handler; 558*1303Swesolows netsnmp_table_request_info *table_info; 559*1303Swesolows netsnmp_variable_list *var; 560*1303Swesolows sunFmModule_data_t *data; 561*1303Swesolows ulong_t modstate; 562*1303Swesolows 563*1303Swesolows ASSERT(netsnmp_handler_check_cache(cache) != NULL); 564*1303Swesolows 565*1303Swesolows (void) pthread_mutex_lock(&update_lock); 566*1303Swesolows if (update_status != US_QUIET) { 567*1303Swesolows struct timeval tv; 568*1303Swesolows 569*1303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 570*1303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 571*1303Swesolows 572*1303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, 573*1303Swesolows cache); 574*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 575*1303Swesolows return; 576*1303Swesolows } 577*1303Swesolows 578*1303Swesolows request = cache->requests; 579*1303Swesolows reqinfo = cache->reqinfo; 580*1303Swesolows reginfo = cache->reginfo; 581*1303Swesolows handler = cache->handler; 582*1303Swesolows 583*1303Swesolows var = request->requestvb; 584*1303Swesolows table_info = netsnmp_extract_table_info(request); 585*1303Swesolows request->delegated = 0; 586*1303Swesolows 587*1303Swesolows ASSERT(table_info->colnum >= SUNFMMODULE_COLMIN); 588*1303Swesolows ASSERT(table_info->colnum <= SUNFMMODULE_COLMAX); 589*1303Swesolows 590*1303Swesolows /* 591*1303Swesolows * table_info->colnum contains the column number requested. 592*1303Swesolows * table_info->indexes contains a linked list of snmp variable 593*1303Swesolows * bindings for the indexes of the table. Values in the list 594*1303Swesolows * have been set corresponding to the indexes of the 595*1303Swesolows * request. We have other guarantees as well: 596*1303Swesolows * 597*1303Swesolows * - The column number is always within range. 598*1303Swesolows * - If we have no index data, table_info->index_oid_len is 0. 599*1303Swesolows * - We will never receive requests outside our table nor 600*1303Swesolows * those with the first subid anything other than 1 (Entry) 601*1303Swesolows * nor those without a column number. This is true even 602*1303Swesolows * for GETNEXT requests. 603*1303Swesolows */ 604*1303Swesolows 605*1303Swesolows switch (reqinfo->mode) { 606*1303Swesolows case MODE_GET: 607*1303Swesolows if ((data = sunFmModuleTable_mod(reginfo, table_info)) == 608*1303Swesolows NULL) { 609*1303Swesolows netsnmp_free_delegated_cache(cache); 610*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 611*1303Swesolows return; 612*1303Swesolows } 613*1303Swesolows break; 614*1303Swesolows case MODE_GETNEXT: 615*1303Swesolows case MODE_GETBULK: 616*1303Swesolows if ((data = sunFmModuleTable_nextmod(reginfo, table_info)) == 617*1303Swesolows NULL) { 618*1303Swesolows netsnmp_free_delegated_cache(cache); 619*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 620*1303Swesolows return; 621*1303Swesolows } 622*1303Swesolows break; 623*1303Swesolows default: 624*1303Swesolows snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request " 625*1303Swesolows "mode %d\n", reqinfo->mode); 626*1303Swesolows netsnmp_free_delegated_cache(cache); 627*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 628*1303Swesolows return; 629*1303Swesolows } 630*1303Swesolows 631*1303Swesolows switch (table_info->colnum) { 632*1303Swesolows case SUNFMMODULE_COL_NAME: 633*1303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 634*1303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_name, 635*1303Swesolows strlen(data->d_ami_name)); 636*1303Swesolows break; 637*1303Swesolows case SUNFMMODULE_COL_VERSION: 638*1303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 639*1303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_vers, 640*1303Swesolows strlen(data->d_ami_vers)); 641*1303Swesolows break; 642*1303Swesolows case SUNFMMODULE_COL_STATUS: 643*1303Swesolows modstate = (data->d_ami_flags & FMD_ADM_MOD_FAILED) ? 644*1303Swesolows SUNFMMODULE_STATE_FAILED : SUNFMMODULE_STATE_ACTIVE; 645*1303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 646*1303Swesolows ASN_INTEGER, (uchar_t *)&modstate, 647*1303Swesolows sizeof (modstate)); 648*1303Swesolows break; 649*1303Swesolows case SUNFMMODULE_COL_DESCRIPTION: 650*1303Swesolows netsnmp_table_build_result(reginfo, request, table_info, 651*1303Swesolows ASN_OCTET_STR, (uchar_t *)data->d_ami_desc, 652*1303Swesolows strlen(data->d_ami_desc)); 653*1303Swesolows break; 654*1303Swesolows default: 655*1303Swesolows break; 656*1303Swesolows } 657*1303Swesolows netsnmp_free_delegated_cache(cache); 658*1303Swesolows (void) pthread_mutex_unlock(&update_lock); 659*1303Swesolows } 660*1303Swesolows 661*1303Swesolows static int 662*1303Swesolows sunFmModuleTable_handler(netsnmp_mib_handler *handler, 663*1303Swesolows netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, 664*1303Swesolows netsnmp_request_info *requests) 665*1303Swesolows { 666*1303Swesolows netsnmp_request_info *request; 667*1303Swesolows struct timeval tv; 668*1303Swesolows 669*1303Swesolows tv.tv_sec = UPDATE_WAIT_MILLIS / 1000; 670*1303Swesolows tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000; 671*1303Swesolows 672*1303Swesolows request_update(); 673*1303Swesolows 674*1303Swesolows for (request = requests; request; request = request->next) { 675*1303Swesolows if (request->processed != 0) 676*1303Swesolows continue; 677*1303Swesolows 678*1303Swesolows if (netsnmp_extract_table_info(request) == NULL) 679*1303Swesolows continue; 680*1303Swesolows 681*1303Swesolows request->delegated = 1; 682*1303Swesolows (void) snmp_alarm_register_hr(tv, 0, sunFmModuleTable_return, 683*1303Swesolows (void *) netsnmp_create_delegated_cache(handler, reginfo, 684*1303Swesolows reqinfo, request, NULL)); 685*1303Swesolows } 686*1303Swesolows 687*1303Swesolows return (SNMP_ERR_NOERROR); 688*1303Swesolows } 689