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 "resource.h"
  38 
  39 static uu_avl_pool_t    *rsrc_fmri_avl_pool;
  40 static uu_avl_pool_t    *rsrc_index_avl_pool;
  41 static uu_avl_t         *rsrc_fmri_avl;
  42 static uu_avl_t         *rsrc_index_avl;
  43 
  44 #define VALID_AVL_STATE (rsrc_fmri_avl_pool != NULL &&          \
  45         rsrc_index_avl_pool != NULL && rsrc_fmri_avl != NULL && \
  46         rsrc_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; a count
  52  * update is optional.
  53  */
  54 #define UCT_INDEX       0x1
  55 #define UCT_ALL         0x2
  56 #define UCT_COUNT       0x4
  57 #define UCT_FLAGS       0x7
  58 
  59 #define RESOURCE_DATA_VALID(d)  ((d)->d_valid == valid_stamp)
  60 
  61 /*
  62  * Locking strategy is described in module.c.
  63  */
  64 static ulong_t          max_index;
  65 static int              valid_stamp;
  66 static uint32_t         rsrc_count;
  67 static pthread_mutex_t  update_lock;
  68 static pthread_cond_t   update_cv;
  69 static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status;
  70 
  71 static Netsnmp_Node_Handler     sunFmResourceTable_handler;
  72 static Netsnmp_Node_Handler     sunFmResourceCount_handler;
  73 
  74 static sunFmResource_data_t *
  75 key_build(const char *fmri, const ulong_t index)
  76 {
  77         static sunFmResource_data_t     key;
  78 
  79         key.d_index = index;
  80         if (fmri)
  81                 (void) strlcpy(key.d_ari_fmri, fmri, sizeof (key.d_ari_fmri));
  82         else
  83                 key.d_ari_fmri[0] = '\0';
  84 
  85         return (&key);
  86 }
  87 
  88 /*
  89  * If fmri is the fmri of a resource we have previously seen and indexed, return
  90  * data for it.  Otherwise, return NULL.  Note that the resource may not be
  91  * valid; that is, it may have been removed from the fault manager since its
  92  * information was last updated.
  93  */
  94 static sunFmResource_data_t *
  95 resource_lookup_fmri(const char *fmri)
  96 {
  97         sunFmResource_data_t    *key;
  98 
  99         key = key_build(fmri, 0);
 100         return (uu_avl_find(rsrc_fmri_avl, key, NULL, NULL));
 101 }
 102 
 103 /*
 104  * If index corresponds to a resource we have previously seen and indexed,
 105  * return data for it.  Otherwise, return NULL.  Note that the resource may
 106  * not be valid; that is, it may have been expired from the fault manager
 107  * since its information was last updated.
 108  */
 109 static sunFmResource_data_t *
 110 resource_lookup_index_exact(const ulong_t index)
 111 {
 112         sunFmResource_data_t    *key;
 113 
 114         key = key_build(NULL, index);
 115         return (uu_avl_find(rsrc_index_avl, key, NULL, NULL));
 116 }
 117 
 118 /*
 119  * If index corresponds to a valid (that is, extant as of latest information
 120  * from the fault manager) resource, return the data for that resource.
 121  * Otherwise, return the data for the valid resource whose index is as close as
 122  * possible to index but not lower.  This preserves the lexicographical
 123  * ordering required for GETNEXT processing.
 124  */
 125 static sunFmResource_data_t *
 126 resource_lookup_index_nextvalid(const ulong_t index)
 127 {
 128         sunFmResource_data_t    *key, *data;
 129         uu_avl_index_t          idx;
 130 
 131         key = key_build(NULL, index);
 132 
 133         if ((data = uu_avl_find(rsrc_index_avl, key, NULL, &idx)) != NULL &&
 134             RESOURCE_DATA_VALID(data))
 135                 return (data);
 136 
 137         data = uu_avl_nearest_next(rsrc_index_avl, idx);
 138 
 139         while (data != NULL && !RESOURCE_DATA_VALID(data))
 140                 data = uu_avl_next(rsrc_index_avl, data);
 141 
 142         return (data);
 143 }
 144 
 145 /*
 146  * Possible update the contents of a single resource within the cache.  This
 147  * is our callback from fmd_rsrc_iter.
 148  */
 149 static int
 150 rsrcinfo_update_one(const fmd_adm_rsrcinfo_t *rsrcinfo, void *arg)
 151 {
 152         const sunFmResource_update_ctx_t *update_ctx =
 153             (sunFmResource_update_ctx_t *)arg;
 154         sunFmResource_data_t *data = resource_lookup_fmri(rsrcinfo->ari_fmri);
 155 
 156         ++rsrc_count;
 157 
 158         /*
 159          * A resource we haven't seen before.  We're obligated to index
 160          * it and link it into our cache so that we can find it, but we're
 161          * not obligated to fill it in completely unless we're doing a
 162          * full update or this is the resource we were asked for.  This
 163          * avoids unnecessary iteration and memory manipulation for data
 164          * we're not going to return for this request.
 165          */
 166         if (data == NULL) {
 167                 uu_avl_index_t idx;
 168 
 169                 DEBUGMSGTL((MODNAME_STR, "found new resource %s\n",
 170                     rsrcinfo->ari_fmri));
 171                 if ((data = SNMP_MALLOC_TYPEDEF(sunFmResource_data_t)) ==
 172                     NULL) {
 173                         (void) snmp_log(LOG_ERR, MODNAME_STR ": Out of memory "
 174                             "for new resource data at %s:%d\n", __FILE__,
 175                             __LINE__);
 176                         return (1);
 177                 }
 178                 /*
 179                  * We allocate indices sequentially and never reuse them.
 180                  * This ensures we can always return valid GETNEXT responses
 181                  * without having to reindex, and it provides the user a
 182                  * more consistent view of the fault manager.
 183                  */
 184                 data->d_index = ++max_index;
 185                 DEBUGMSGTL((MODNAME_STR, "index %lu is %s@%p\n", data->d_index,
 186                     rsrcinfo->ari_fmri, data));
 187 
 188                 (void) strlcpy(data->d_ari_fmri, rsrcinfo->ari_fmri,
 189                     sizeof (data->d_ari_fmri));
 190 
 191                 uu_avl_node_init(data, &data->d_fmri_avl, rsrc_fmri_avl_pool);
 192                 (void) uu_avl_find(rsrc_fmri_avl, data, NULL, &idx);
 193                 uu_avl_insert(rsrc_fmri_avl, data, idx);
 194 
 195                 uu_avl_node_init(data, &data->d_index_avl, rsrc_index_avl_pool);
 196                 (void) uu_avl_find(rsrc_index_avl, data, NULL, &idx);
 197                 uu_avl_insert(rsrc_index_avl, data, idx);
 198 
 199                 DEBUGMSGTL((MODNAME_STR, "completed new resource %lu/%s@%p\n",
 200                     data->d_index, data->d_ari_fmri, data));
 201         }
 202 
 203         data->d_valid = valid_stamp;
 204 
 205         DEBUGMSGTL((MODNAME_STR, "timestamp updated for %lu/%s@%p: %d\n",
 206             data->d_index, data->d_ari_fmri, data, data->d_valid));
 207 
 208         if ((update_ctx->uc_type & UCT_ALL) ||
 209             update_ctx->uc_index == data->d_index) {
 210                 (void) strlcpy(data->d_ari_case, rsrcinfo->ari_case,
 211                     sizeof (data->d_ari_case));
 212                 data->d_ari_flags = rsrcinfo->ari_flags;
 213         }
 214 
 215         return (!(update_ctx->uc_type & UCT_ALL) &&
 216             update_ctx->uc_index == data->d_index);
 217 }
 218 
 219 /*
 220  * Update some or all resource data from fmd.  If type includes UCT_ALL, all
 221  * resources will be indexed and their data cached.  If type includes
 222  * UCT_INDEX, updates will stop once the resource matching index has been
 223  * updated.  If UCT_COUNT is set, the number of faulted resources will be
 224  * set.
 225  *
 226  * Returns appropriate SNMP error codes.
 227  */
 228 static int
 229 rsrcinfo_update(sunFmResource_update_ctx_t *update_ctx)
 230 {
 231         fmd_adm_t *adm;
 232         int err;
 233 
 234         ASSERT(update_ctx != NULL);
 235         ASSERT((update_ctx->uc_type & (UCT_ALL|UCT_INDEX)) !=
 236             (UCT_ALL|UCT_INDEX));
 237         ASSERT((update_ctx->uc_type & ~UCT_FLAGS) == 0);
 238         ASSERT(VALID_AVL_STATE);
 239 
 240         if ((adm = fmd_adm_open(update_ctx->uc_host, update_ctx->uc_prog,
 241             update_ctx->uc_version)) == NULL) {
 242                 (void) snmp_log(LOG_ERR, MODNAME_STR ": Communication with fmd "
 243                     "failed: %s\n", strerror(errno));
 244                 return (SNMP_ERR_RESOURCEUNAVAILABLE);
 245         }
 246 
 247         if (update_ctx->uc_type == UCT_COUNT) {
 248                 err = fmd_adm_rsrc_count(adm, update_ctx->uc_all, &rsrc_count);
 249         } else {
 250                 ++valid_stamp;
 251                 rsrc_count = 0;
 252                 err = fmd_adm_rsrc_iter(adm, update_ctx->uc_all,
 253                     rsrcinfo_update_one, update_ctx);
 254                 DEBUGMSGTL((MODNAME_STR, "resource iteration completed\n"));
 255         }
 256 
 257         fmd_adm_close(adm);
 258 
 259         if (err != 0) {
 260                 (void) snmp_log(LOG_ERR, MODNAME_STR ": fmd resource "
 261                     "information update failed: %s\n", fmd_adm_errmsg(adm));
 262                 return (SNMP_ERR_RESOURCEUNAVAILABLE);
 263         }
 264 
 265         return (SNMP_ERR_NOERROR);
 266 }
 267 
 268 /*ARGSUSED*/
 269 static void
 270 update_thread(void *arg)
 271 {
 272         /*
 273          * The current rsrcinfo_update implementation offers minimal savings
 274          * for the use of index-only updates; therefore we always do a full
 275          * update.  If it becomes advantageous to limit updates to a single
 276          * index, the contexts can be queued by the handler instead.
 277          */
 278         sunFmResource_update_ctx_t      uc;
 279 
 280         uc.uc_host = NULL;
 281         uc.uc_prog = FMD_ADM_PROGRAM;
 282         uc.uc_version = FMD_ADM_VERSION;
 283 
 284         uc.uc_all = 0;
 285         uc.uc_index = 0;
 286         uc.uc_type = UCT_ALL;
 287 
 288         for (;;) {
 289                 (void) pthread_mutex_lock(&update_lock);
 290                 update_status = US_QUIET;
 291                 while (update_status == US_QUIET)
 292                         (void) pthread_cond_wait(&update_cv, &update_lock);
 293                 update_status = US_INPROGRESS;
 294                 (void) pthread_mutex_unlock(&update_lock);
 295                 (void) rsrcinfo_update(&uc);
 296         }
 297 }
 298 
 299 static void
 300 request_update(void)
 301 {
 302         (void) pthread_mutex_lock(&update_lock);
 303         if (update_status != US_QUIET) {
 304                 (void) pthread_mutex_unlock(&update_lock);
 305                 return;
 306         }
 307         update_status = US_NEEDED;
 308         (void) pthread_cond_signal(&update_cv);
 309         (void) pthread_mutex_unlock(&update_lock);
 310 }
 311 
 312 /*ARGSUSED*/
 313 static int
 314 resource_compare_fmri(const void *l, const void *r, void *private)
 315 {
 316         sunFmResource_data_t    *l_data = (sunFmResource_data_t *)l;
 317         sunFmResource_data_t    *r_data = (sunFmResource_data_t *)r;
 318 
 319         ASSERT(l_data != NULL && r_data != NULL);
 320 
 321         return (strcmp(l_data->d_ari_fmri, r_data->d_ari_fmri));
 322 }
 323 
 324 /*ARGSUSED*/
 325 static int
 326 resource_compare_index(const void *l, const void *r, void *private)
 327 {
 328         sunFmResource_data_t    *l_data = (sunFmResource_data_t *)l;
 329         sunFmResource_data_t    *r_data = (sunFmResource_data_t *)r;
 330 
 331         ASSERT(l_data != NULL && r_data != NULL);
 332 
 333         return (l_data->d_index < r_data->d_index ? -1 :
 334             l_data->d_index > r_data->d_index ? 1 : 0);
 335 }
 336 
 337 int
 338 sunFmResourceTable_init(void)
 339 {
 340         static oid sunFmResourceTable_oid[] = { SUNFMRESOURCETABLE_OID };
 341         static oid sunFmResourceCount_oid[] = { SUNFMRESOURCECOUNT_OID, 0 };
 342         netsnmp_table_registration_info *table_info;
 343         netsnmp_handler_registration *handler;
 344         int err;
 345 
 346         if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
 347                 (void) snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: "
 348                     "%s\n", strerror(err));
 349                 return (MIB_REGISTRATION_FAILED);
 350         }
 351         if ((err = pthread_cond_init(&update_cv, NULL)) != 0) {
 352                 (void) snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: "
 353                     "%s\n", strerror(err));
 354                 return (MIB_REGISTRATION_FAILED);
 355         }
 356 
 357         if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread,
 358             NULL)) != 0) {
 359                 (void) snmp_log(LOG_ERR, MODNAME_STR ": error creating update "
 360                     "thread: %s\n", strerror(err));
 361                 return (MIB_REGISTRATION_FAILED);
 362         }
 363 
 364         if ((table_info =
 365             SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
 366                 return (MIB_REGISTRATION_FAILED);
 367 
 368         if ((handler = netsnmp_create_handler_registration("sunFmResourceTable",
 369             sunFmResourceTable_handler, sunFmResourceTable_oid,
 370             OID_LENGTH(sunFmResourceTable_oid), HANDLER_CAN_RONLY)) == NULL) {
 371                 SNMP_FREE(table_info);
 372                 return (MIB_REGISTRATION_FAILED);
 373         }
 374 
 375         /*
 376          * The Net-SNMP template uses add_indexes here, but that
 377          * function is unsafe because it does not check for failure.
 378          */
 379         if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) {
 380                 SNMP_FREE(table_info);
 381                 SNMP_FREE(handler);
 382                 return (MIB_REGISTRATION_FAILED);
 383         }
 384 
 385         table_info->min_column = SUNFMRESOURCE_COLMIN;
 386         table_info->max_column = SUNFMRESOURCE_COLMAX;
 387 
 388         if ((rsrc_fmri_avl_pool = uu_avl_pool_create("rsrc_fmri",
 389             sizeof (sunFmResource_data_t),
 390             offsetof(sunFmResource_data_t, d_fmri_avl), resource_compare_fmri,
 391             UU_AVL_DEBUG)) == NULL) {
 392                 (void) snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl pool "
 393                     "creation failed: %s\n", uu_strerror(uu_error()));
 394                 snmp_free_varbind(table_info->indexes);
 395                 SNMP_FREE(table_info);
 396                 SNMP_FREE(handler);
 397         }
 398 
 399         if ((rsrc_fmri_avl = uu_avl_create(rsrc_fmri_avl_pool, NULL,
 400             UU_AVL_DEBUG)) == NULL) {
 401                 (void) snmp_log(LOG_ERR, MODNAME_STR ": rsrc_fmri avl creation "
 402                     "failed: %s\n", uu_strerror(uu_error()));
 403                 snmp_free_varbind(table_info->indexes);
 404                 SNMP_FREE(table_info);
 405                 SNMP_FREE(handler);
 406                 uu_avl_pool_destroy(rsrc_fmri_avl_pool);
 407                 return (MIB_REGISTRATION_FAILED);
 408         }
 409 
 410         if ((rsrc_index_avl_pool = uu_avl_pool_create("rsrc_index",
 411             sizeof (sunFmResource_data_t),
 412             offsetof(sunFmResource_data_t, d_index_avl),
 413             resource_compare_index, UU_AVL_DEBUG)) == NULL) {
 414                 (void) snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl pool "
 415                     "creation failed: %s\n", uu_strerror(uu_error()));
 416                 snmp_free_varbind(table_info->indexes);
 417                 SNMP_FREE(table_info);
 418                 SNMP_FREE(handler);
 419                 uu_avl_destroy(rsrc_fmri_avl);
 420                 uu_avl_pool_destroy(rsrc_fmri_avl_pool);
 421         }
 422 
 423         if ((rsrc_index_avl = uu_avl_create(rsrc_index_avl_pool, NULL,
 424             UU_AVL_DEBUG)) == NULL) {
 425                 (void) snmp_log(LOG_ERR, MODNAME_STR ": rsrc_index avl "
 426                     "creation failed: %s\n", uu_strerror(uu_error()));
 427                 snmp_free_varbind(table_info->indexes);
 428                 SNMP_FREE(table_info);
 429                 SNMP_FREE(handler);
 430                 uu_avl_destroy(rsrc_fmri_avl);
 431                 uu_avl_pool_destroy(rsrc_fmri_avl_pool);
 432                 uu_avl_pool_destroy(rsrc_index_avl_pool);
 433                 return (MIB_REGISTRATION_FAILED);
 434         }
 435 
 436         if ((err = netsnmp_register_table(handler, table_info)) !=
 437             MIB_REGISTERED_OK) {
 438                 snmp_free_varbind(table_info->indexes);
 439                 SNMP_FREE(table_info);
 440                 SNMP_FREE(handler);
 441                 uu_avl_destroy(rsrc_fmri_avl);
 442                 uu_avl_pool_destroy(rsrc_fmri_avl_pool);
 443                 uu_avl_destroy(rsrc_index_avl);
 444                 uu_avl_pool_destroy(rsrc_index_avl_pool);
 445                 return (err);
 446         }
 447 
 448         if ((err = netsnmp_register_read_only_instance(
 449             netsnmp_create_handler_registration("sunFmResourceCount",
 450             sunFmResourceCount_handler, sunFmResourceCount_oid,
 451             OID_LENGTH(sunFmResourceCount_oid), HANDLER_CAN_RONLY))) !=
 452             MIB_REGISTERED_OK) {
 453                 /*
 454                  * There's no way to unregister the table handler, so we
 455                  * can't free any of the data, either.
 456                  */
 457                 return (err);
 458         }
 459 
 460         return (MIB_REGISTERED_OK);
 461 }
 462 
 463 /*
 464  * These two functions form the core of GET/GETNEXT/GETBULK handling (the
 465  * only kind we do).  They perform two functions:
 466  *
 467  * - First, frob the request to set all the index variables to correspond
 468  *   to the value that's going to be returned.  For GET, this is a nop;
 469  *   for GETNEXT/GETBULK it always requires some work.
 470  * - Second, find and return the fmd resource information corresponding to
 471  *   the (possibly updated) indices.
 472  *
 473  * These should be as fast as possible; they run in the agent thread.
 474  */
 475 static sunFmResource_data_t *
 476 sunFmResourceTable_nextrsrc(netsnmp_handler_registration *reginfo,
 477     netsnmp_table_request_info *table_info)
 478 {
 479         sunFmResource_data_t    *data;
 480         netsnmp_variable_list   *var;
 481         ulong_t index;
 482 
 483         /*
 484          * If we have no index, we must make one.
 485          */
 486         if (table_info->number_indexes < 1) {
 487                 oid tmpoid[MAX_OID_LEN];
 488                 index = 1;
 489 
 490                 DEBUGMSGTL((MODNAME_STR, "nextrsrc: no indexes given\n"));
 491                 var = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list);
 492                 (void) snmp_set_var_typed_value(var, ASN_UNSIGNED,
 493                     (uchar_t *)&index, sizeof (index));
 494                 (void) memcpy(tmpoid, reginfo->rootoid,
 495                     reginfo->rootoid_len * sizeof (oid));
 496                 tmpoid[reginfo->rootoid_len] = 1;
 497                 tmpoid[reginfo->rootoid_len + 1] = table_info->colnum;
 498                 if (build_oid(&var->name, &var->name_length, tmpoid,
 499                     reginfo->rootoid_len + 2, var) != SNMPERR_SUCCESS) {
 500                         snmp_free_varbind(var);
 501                         return (NULL);
 502                 }
 503                 DEBUGMSGTL((MODNAME_STR, "nextrsrc: built fake index:\n"));
 504                 DEBUGMSGVAR((MODNAME_STR, var));
 505                 DEBUGMSG((MODNAME_STR, "\n"));
 506         } else {
 507                 var = snmp_clone_varbind(table_info->indexes);
 508                 index = *var->val.integer;
 509                 DEBUGMSGTL((MODNAME_STR, "nextrsrc: received index:\n"));
 510                 DEBUGMSGVAR((MODNAME_STR, var));
 511                 DEBUGMSG((MODNAME_STR, "\n"));
 512                 index++;
 513         }
 514 
 515         snmp_free_varbind(table_info->indexes);
 516         table_info->indexes = NULL;
 517         table_info->number_indexes = 0;
 518 
 519         if ((data = resource_lookup_index_nextvalid(index)) == NULL) {
 520                 DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for "
 521                     "index %lu; trying next column\n", index));
 522                 if (table_info->colnum >=
 523                     netsnmp_find_table_registration_info(reginfo)->max_column) {
 524                         snmp_free_varbind(var);
 525                         DEBUGMSGTL((MODNAME_STR, "nextrsrc: out of columns\n"));
 526                         return (NULL);
 527                 }
 528                 table_info->colnum++;
 529                 index = 1;
 530 
 531                 data = resource_lookup_index_nextvalid(index);
 532         }
 533 
 534         if (data == NULL) {
 535                 DEBUGMSGTL((MODNAME_STR, "nextrsrc: exact match not found for "
 536                     "index %lu; stopping\n", index));
 537                 snmp_free_varbind(var);
 538                 return (NULL);
 539         }
 540 
 541         *var->val.integer = data->d_index;
 542         table_info->indexes = var;
 543         table_info->number_indexes = 1;
 544 
 545         DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index,
 546             data->d_ari_fmri, data));
 547 
 548         return (data);
 549 }
 550 
 551 /*ARGSUSED*/
 552 static sunFmResource_data_t *
 553 sunFmResourceTable_rsrc(netsnmp_handler_registration *reginfo,
 554     netsnmp_table_request_info *table_info)
 555 {
 556         ASSERT(table_info->number_indexes == 1);
 557 
 558         return (resource_lookup_index_exact(table_info->index_oid[0]));
 559 }
 560 
 561 /*ARGSUSED*/
 562 static void
 563 sunFmResourceTable_return(unsigned int reg, void *arg)
 564 {
 565         netsnmp_delegated_cache         *cache = (netsnmp_delegated_cache *)arg;
 566         netsnmp_request_info            *request;
 567         netsnmp_agent_request_info      *reqinfo;
 568         netsnmp_handler_registration    *reginfo;
 569         netsnmp_table_request_info      *table_info;
 570         sunFmResource_data_t            *data;
 571         ulong_t                         rsrcstate;
 572 
 573         ASSERT(netsnmp_handler_check_cache(cache) != NULL);
 574 
 575         (void) pthread_mutex_lock(&update_lock);
 576         if (update_status != US_QUIET) {
 577                 struct timeval                  tv;
 578 
 579                 tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
 580                 tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
 581 
 582                 (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return,
 583                     cache);
 584                 (void) pthread_mutex_unlock(&update_lock);
 585                 return;
 586         }
 587 
 588         request = cache->requests;
 589         reqinfo = cache->reqinfo;
 590         reginfo = cache->reginfo;
 591 
 592         table_info = netsnmp_extract_table_info(request);
 593         request->delegated = 0;
 594 
 595         ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN);
 596         ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX);
 597 
 598         /*
 599          * table_info->colnum contains the column number requested.
 600          * table_info->indexes contains a linked list of snmp variable
 601          * bindings for the indexes of the table.  Values in the list
 602          * have been set corresponding to the indexes of the
 603          * request.  We have other guarantees as well:
 604          *
 605          * - The column number is always within range.
 606          * - If we have no index data, table_info->index_oid_len is 0.
 607          * - We will never receive requests outside our table nor
 608          *   those with the first subid anything other than 1 (Entry)
 609          *   nor those without a column number.  This is true even
 610          *   for GETNEXT requests.
 611          */
 612 
 613         switch (reqinfo->mode) {
 614         case MODE_GET:
 615                 if ((data = sunFmResourceTable_rsrc(reginfo, table_info)) ==
 616                     NULL) {
 617                         netsnmp_free_delegated_cache(cache);
 618                         (void) pthread_mutex_unlock(&update_lock);
 619                         return;
 620                 }
 621                 break;
 622         case MODE_GETNEXT:
 623         case MODE_GETBULK:
 624                 if ((data = sunFmResourceTable_nextrsrc(reginfo, table_info)) ==
 625                     NULL) {
 626                         netsnmp_free_delegated_cache(cache);
 627                         (void) pthread_mutex_unlock(&update_lock);
 628                         return;
 629                 }
 630                 break;
 631         default:
 632                 (void) snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request "
 633                     "mode %d\n", reqinfo->mode);
 634                 netsnmp_free_delegated_cache(cache);
 635                 (void) pthread_mutex_unlock(&update_lock);
 636                 return;
 637         }
 638 
 639         switch (table_info->colnum) {
 640         case SUNFMRESOURCE_COL_FMRI:
 641                 (void) netsnmp_table_build_result(reginfo, request, table_info,
 642                     ASN_OCTET_STR, (uchar_t *)data->d_ari_fmri,
 643                     strlen(data->d_ari_fmri));
 644                 break;
 645         case SUNFMRESOURCE_COL_STATUS:
 646                 switch (data->d_ari_flags &
 647                     (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) {
 648                 default:
 649                         rsrcstate = SUNFMRESOURCE_STATE_OK;
 650                         break;
 651                 case FMD_ADM_RSRC_FAULTY:
 652                         rsrcstate = SUNFMRESOURCE_STATE_DEGRADED;
 653                         break;
 654                 case FMD_ADM_RSRC_UNUSABLE:
 655                         rsrcstate = SUNFMRESOURCE_STATE_UNKNOWN;
 656                         break;
 657                 case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE:
 658                         rsrcstate = SUNFMRESOURCE_STATE_FAULTED;
 659                         break;
 660                 }
 661                 (void) netsnmp_table_build_result(reginfo, request, table_info,
 662                     ASN_INTEGER, (uchar_t *)&rsrcstate,
 663                     sizeof (rsrcstate));
 664                 break;
 665         case SUNFMRESOURCE_COL_DIAGNOSISUUID:
 666                 (void) netsnmp_table_build_result(reginfo, request, table_info,
 667                     ASN_OCTET_STR, (uchar_t *)data->d_ari_case,
 668                     strlen(data->d_ari_case));
 669                 break;
 670         default:
 671                 break;
 672         }
 673         netsnmp_free_delegated_cache(cache);
 674         (void) pthread_mutex_unlock(&update_lock);
 675 }
 676 
 677 static int
 678 sunFmResourceTable_handler(netsnmp_mib_handler *handler,
 679     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
 680     netsnmp_request_info *requests)
 681 {
 682         netsnmp_request_info            *request;
 683         struct timeval                  tv;
 684 
 685         tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
 686         tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
 687 
 688         request_update();
 689 
 690         for (request = requests; request; request = request->next) {
 691                 if (request->processed != 0)
 692                         continue;
 693 
 694                 if (netsnmp_extract_table_info(request) == NULL)
 695                         continue;
 696 
 697                 request->delegated = 1;
 698                 (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return,
 699                     (void *) netsnmp_create_delegated_cache(handler, reginfo,
 700                     reqinfo, request, NULL));
 701         }
 702 
 703         return (SNMP_ERR_NOERROR);
 704 }
 705 
 706 /*ARGSUSED*/
 707 static void
 708 sunFmResourceCount_return(unsigned int reg, void *arg)
 709 {
 710         netsnmp_delegated_cache         *cache = (netsnmp_delegated_cache *)arg;
 711         netsnmp_request_info            *request;
 712         netsnmp_agent_request_info      *reqinfo;
 713         ulong_t                         rsrc_count_long;
 714 
 715         ASSERT(netsnmp_handler_check_cache(cache) != NULL);
 716 
 717         (void) pthread_mutex_lock(&update_lock);
 718         if (update_status != US_QUIET) {
 719                 struct timeval  tv;
 720 
 721                 tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
 722                 tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
 723 
 724                 (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return,
 725                     cache);
 726                 (void) pthread_mutex_unlock(&update_lock);
 727                 return;
 728         }
 729 
 730         request = cache->requests;
 731         reqinfo = cache->reqinfo;
 732 
 733         request->delegated = 0;
 734 
 735         switch (reqinfo->mode) {
 736         /*
 737          * According to the documentation, it's not possible for us ever to
 738          * be called with MODE_GETNEXT.  However, Net-SNMP does the following:
 739          * - set reqinfo->mode to MODE_GET
 740          * - invoke the handler
 741          * - set reqinfo->mode to MODE_GETNEXT (even if the request was not
 742          *   actually processed; i.e. it's been delegated)
 743          * Since we're called back later with the same reqinfo, we see
 744          * GETNEXT.  Therefore this case is needed to work around the
 745          * Net-SNMP bug.
 746          */
 747         case MODE_GET:
 748         case MODE_GETNEXT:
 749                 DEBUGMSGTL((MODNAME_STR, "resource count is %u\n", rsrc_count));
 750                 rsrc_count_long = (ulong_t)rsrc_count;
 751                 (void) snmp_set_var_typed_value(request->requestvb, ASN_GAUGE,
 752                     (uchar_t *)&rsrc_count_long, sizeof (rsrc_count_long));
 753                 break;
 754         default:
 755                 (void) snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request "
 756                     "mode %d\n", reqinfo->mode);
 757         }
 758 
 759         netsnmp_free_delegated_cache(cache);
 760         (void) pthread_mutex_unlock(&update_lock);
 761 }
 762 
 763 static int
 764 sunFmResourceCount_handler(netsnmp_mib_handler *handler,
 765     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
 766     netsnmp_request_info *requests)
 767 {
 768         struct timeval  tv;
 769 
 770         tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
 771         tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
 772 
 773         request_update();
 774 
 775         /*
 776          * We are never called for a GETNEXT when registered as an
 777          * instance; it's handled for us and converted to a GET.
 778          * Also, an instance handler is given only one request at a time, so
 779          * we don't need to loop over a list of requests.
 780          */
 781 
 782         if (requests->processed != 0)
 783                 return (SNMP_ERR_NOERROR);
 784 
 785         requests->delegated = 1;
 786         (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return,
 787             (void *) netsnmp_create_delegated_cache(handler, reginfo,
 788             reqinfo, requests, NULL));
 789 
 790         return (SNMP_ERR_NOERROR);
 791 }