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