Print this page
NEX-18695 libfmd_snmp is unable to handle multiple OIDs in GET request
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Cynthia Eastham <cynthia.eastham@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
NEX-17777 snmptable produces bogus output for FM tables
Reviewed by: Cynthia Eastham <cynthia.eastham@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-3125 libfmd_snmp should compile with newer net-snmp
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Jean McCormack <jean.mccormack@nexenta.com>
Reviewed by: Josef 'Jeff' Sipek <josef.sipek@nexenta.com>


   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


 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         }


 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 


 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 }


   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 /*
  28  * Copyright 2018 Nexenta Systems, Inc.
  29  */
  30 
  31 #include <fm/fmd_adm.h>
  32 #include <fm/fmd_snmp.h>
  33 
  34 #include <net-snmp/net-snmp-config.h>
  35 #include <net-snmp/net-snmp-includes.h>
  36 #include <net-snmp/agent/net-snmp-agent-includes.h>
  37 

  38 #include <errno.h>
  39 #include <libuutil.h>
  40 #include <pthread.h>
  41 #include <stddef.h>
  42 
  43 #include "sunFM_impl.h"
  44 #include "resource.h"
  45 
  46 static uu_avl_pool_t    *rsrc_fmri_avl_pool;
  47 static uu_avl_pool_t    *rsrc_index_avl_pool;
  48 static uu_avl_t         *rsrc_fmri_avl;
  49 static uu_avl_t         *rsrc_index_avl;
  50 
  51 #define VALID_AVL_STATE (rsrc_fmri_avl_pool != NULL &&          \
  52         rsrc_index_avl_pool != NULL && rsrc_fmri_avl != NULL && \
  53         rsrc_index_avl != NULL)
  54 


  55 /*
  56  * Update types: single-index and all are mutually exclusive; a count
  57  * update is optional.
  58  */
  59 #define UCT_INDEX       0x1
  60 #define UCT_ALL         0x2
  61 #define UCT_COUNT       0x4
  62 #define UCT_FLAGS       0x7
  63 
  64 #define RESOURCE_DATA_VALID(d)  ((d)->d_valid == valid_stamp)
  65 



  66 static ulong_t          max_index;
  67 static int              valid_stamp;
  68 static uint32_t         rsrc_count;
  69 static pthread_mutex_t  update_lock;


  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


 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 static void
 269 request_update(void)
 270 {
 271         sunFmResource_update_ctx_t      uc;
 272 
 273         /*
 274          * The current rsrcinfo_update implementation offers minimal savings
 275          * for the use of index-only updates; therefore we always do a full
 276          * update.  If it becomes advantageous to limit updates to a single
 277          * index, the contexts can be queued by the handler instead.
 278          */

 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         (void) rsrcinfo_update(&uc);

 289 }
 290 













 291 /*ARGSUSED*/
 292 static int
 293 resource_compare_fmri(const void *l, const void *r, void *private)
 294 {
 295         sunFmResource_data_t    *l_data = (sunFmResource_data_t *)l;
 296         sunFmResource_data_t    *r_data = (sunFmResource_data_t *)r;
 297 
 298         ASSERT(l_data != NULL && r_data != NULL);
 299 
 300         return (strcmp(l_data->d_ari_fmri, r_data->d_ari_fmri));
 301 }
 302 
 303 /*ARGSUSED*/
 304 static int
 305 resource_compare_index(const void *l, const void *r, void *private)
 306 {
 307         sunFmResource_data_t    *l_data = (sunFmResource_data_t *)l;
 308         sunFmResource_data_t    *r_data = (sunFmResource_data_t *)r;
 309 
 310         ASSERT(l_data != NULL && r_data != NULL);
 311 
 312         return (l_data->d_index < r_data->d_index ? -1 :
 313             l_data->d_index > r_data->d_index ? 1 : 0);
 314 }
 315 
 316 int
 317 sunFmResourceTable_init(void)
 318 {
 319         static oid sunFmResourceTable_oid[] = { SUNFMRESOURCETABLE_OID };
 320         static oid sunFmResourceCount_oid[] = { SUNFMRESOURCECOUNT_OID, 0 };
 321         netsnmp_table_registration_info *table_info;
 322         netsnmp_handler_registration *handler;
 323         int err;
 324 
 325         if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
 326                 (void) snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: "
 327                     "%s\n", strerror(err));
 328                 return (MIB_REGISTRATION_FAILED);
 329         }





 330 







 331         if ((table_info =
 332             SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
 333                 return (MIB_REGISTRATION_FAILED);
 334 
 335         if ((handler = netsnmp_create_handler_registration("sunFmResourceTable",
 336             sunFmResourceTable_handler, sunFmResourceTable_oid,
 337             OID_LENGTH(sunFmResourceTable_oid), HANDLER_CAN_RONLY)) == NULL) {
 338                 SNMP_FREE(table_info);
 339                 return (MIB_REGISTRATION_FAILED);
 340         }
 341 
 342         /*
 343          * The Net-SNMP template uses add_indexes here, but that
 344          * function is unsafe because it does not check for failure.
 345          */
 346         if (netsnmp_table_helper_add_index(table_info, ASN_UNSIGNED) == NULL) {
 347                 SNMP_FREE(table_info);
 348                 SNMP_FREE(handler);
 349                 return (MIB_REGISTRATION_FAILED);
 350         }


 411                 uu_avl_pool_destroy(rsrc_index_avl_pool);
 412                 return (err);
 413         }
 414 
 415         if ((err = netsnmp_register_read_only_instance(
 416             netsnmp_create_handler_registration("sunFmResourceCount",
 417             sunFmResourceCount_handler, sunFmResourceCount_oid,
 418             OID_LENGTH(sunFmResourceCount_oid), HANDLER_CAN_RONLY))) !=
 419             MIB_REGISTERED_OK) {
 420                 /*
 421                  * There's no way to unregister the table handler, so we
 422                  * can't free any of the data, either.
 423                  */
 424                 return (err);
 425         }
 426 
 427         return (MIB_REGISTERED_OK);
 428 }
 429 
 430 /*
 431  * These two functions form the core of GET/GETNEXT handling (the
 432  * only kind we do).  They perform two functions:
 433  *
 434  * - First, frob the request to set all the index variables to correspond
 435  *   to the value that's going to be returned.  For GET, this is a nop;
 436  *   for GETNEXT it always requires some work.
 437  * - Second, find and return the fmd resource information corresponding to
 438  *   the (possibly updated) indices.
 439  *
 440  * These should be as fast as possible; they run in the agent thread.
 441  */
 442 static sunFmResource_data_t *
 443 sunFmResourceTable_nextrsrc(netsnmp_handler_registration *reginfo,
 444     netsnmp_table_request_info *table_info)
 445 {
 446         sunFmResource_data_t    *data;
 447         netsnmp_variable_list   *var;
 448         ulong_t index;
 449 
 450         /*
 451          * If we have no index, we must make one.
 452          */
 453         if (table_info->number_indexes < 1) {
 454                 oid tmpoid[MAX_OID_LEN];
 455                 index = 1;
 456 


 509         table_info->indexes = var;
 510         table_info->number_indexes = 1;
 511 
 512         DEBUGMSGTL((MODNAME_STR, "matching data is %lu/%s@%p\n", data->d_index,
 513             data->d_ari_fmri, data));
 514 
 515         return (data);
 516 }
 517 
 518 /*ARGSUSED*/
 519 static sunFmResource_data_t *
 520 sunFmResourceTable_rsrc(netsnmp_handler_registration *reginfo,
 521     netsnmp_table_request_info *table_info)
 522 {
 523         ASSERT(table_info->number_indexes == 1);
 524 
 525         return (resource_lookup_index_exact(table_info->index_oid[0]));
 526 }
 527 
 528 /*ARGSUSED*/
 529 static int
 530 sunFmResourceTable_handler(netsnmp_mib_handler *handler,
 531     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
 532     netsnmp_request_info *request)
 533 {




 534         netsnmp_table_request_info      *table_info;
 535         sunFmResource_data_t            *data;
 536         ulong_t                         rsrcstate;
 537         int                             ret = SNMP_ERR_NOERROR;
 538 
 539         /*
 540          * We don't support MODE_GETBULK directly, so all bulk requests should
 541          * come through bulk_to_next helper.  Make sure it stays that way.
 542          */
 543         ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
 544 
 545         (void) pthread_mutex_lock(&update_lock);
 546         request_update();

 547 
 548         for (; request != NULL; request = request->next) {












 549                 table_info = netsnmp_extract_table_info(request);
 550                 if (table_info == NULL)
 551                         continue;
 552 
 553                 ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN);
 554                 ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX);
 555 
 556                 /*
 557                  * table_info->colnum contains the column number requested.
 558                  * table_info->indexes contains a linked list of snmp variable
 559                  * bindings for the indexes of the table.  Values in the list
 560                  * have been set corresponding to the indexes of the
 561                  * request.  We have other guarantees as well:
 562                  *
 563                  * - The column number is always within range.
 564                  * - If we have no index data, table_info->index_oid_len is 0.
 565                  * - We will never receive requests outside our table nor
 566                  *   those with the first subid anything other than 1 (Entry)
 567                  *   nor those without a column number.  This is true even
 568                  *   for GETNEXT requests.
 569                  */

 570                 switch (reqinfo->mode) {
 571                 case MODE_GET:
 572                         data = sunFmResourceTable_rsrc(reginfo, table_info);
 573                         if (data == NULL)
 574                                 goto out;



 575                         break;
 576                 case MODE_GETNEXT:
 577                         data = sunFmResourceTable_nextrsrc(reginfo, table_info);
 578                         if (data == NULL)
 579                                 goto out;




 580                         break;
 581                 default:
 582                         (void) snmp_log(LOG_ERR, MODNAME_STR
 583                             ": unsupported request mode %d\n", reqinfo->mode);
 584                         ret = SNMP_ERR_GENERR;
 585                         goto out;

 586                 }
 587 
 588                 switch (table_info->colnum) {
 589                 case SUNFMRESOURCE_COL_FMRI:
 590                         (void) netsnmp_table_build_result(reginfo, request,
 591                             table_info, ASN_OCTET_STR,
 592                             (uchar_t *)data->d_ari_fmri,
 593                             strlen(data->d_ari_fmri));
 594                         break;
 595                 case SUNFMRESOURCE_COL_STATUS:
 596                         switch (data->d_ari_flags &
 597                             (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) {
 598                         default:
 599                                 rsrcstate = SUNFMRESOURCE_STATE_OK;
 600                                 break;
 601                         case FMD_ADM_RSRC_FAULTY:
 602                                 rsrcstate = SUNFMRESOURCE_STATE_DEGRADED;
 603                                 break;
 604                         case FMD_ADM_RSRC_UNUSABLE:
 605                                 rsrcstate = SUNFMRESOURCE_STATE_UNKNOWN;
 606                                 break;
 607                         case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE:
 608                                 rsrcstate = SUNFMRESOURCE_STATE_FAULTED;
 609                                 break;
 610                         }
 611                         (void) netsnmp_table_build_result(reginfo, request,
 612                             table_info, ASN_INTEGER, (uchar_t *)&rsrcstate,
 613                             sizeof (rsrcstate));
 614                         break;
 615                 case SUNFMRESOURCE_COL_DIAGNOSISUUID:
 616                         (void) netsnmp_table_build_result(reginfo, request,
 617                             table_info, ASN_OCTET_STR,
 618                             (uchar_t *)data->d_ari_case,
 619                             strlen(data->d_ari_case));
 620                         break;
 621                 default:
 622                         break;
 623                 }
 624         }
 625 
 626 out:
 627         (void) pthread_mutex_unlock(&update_lock);
 628         return (ret);
 629 }
 630 
 631 /*ARGSUSED*/
 632 static int
 633 sunFmResourceCount_handler(netsnmp_mib_handler *handler,
 634     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
 635     netsnmp_request_info *request)
 636 {































 637         ulong_t                         rsrc_count_long;
 638         int                             ret = SNMP_ERR_NOERROR;
 639 
 640         /*
 641          * We don't support MODE_GETBULK directly, so all bulk requests should
 642          * come through bulk_to_next helper.  Make sure it stays that way.
 643          */
 644         ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
 645 
 646         (void) pthread_mutex_lock(&update_lock);
 647         request_update();

 648 
 649         for (; request != NULL; request = request->next) {













 650                 switch (reqinfo->mode) {
 651                 /*
 652                  * According to the documentation, it's not possible for us ever
 653                  * to be called with MODE_GETNEXT.  However, Net-SNMP does the
 654                  * following:
 655                  * - set reqinfo->mode to MODE_GET
 656                  * - invoke the handler
 657                  * - set reqinfo->mode to MODE_GETNEXT (even if the request was
 658                  *   not actually processed; i.e. it's been delegated)
 659                  * Since we're called back later with the same reqinfo, we see
 660                  * GETNEXT.  Therefore this case is needed to work around the
 661                  * Net-SNMP bug.
 662                  */
 663                 case MODE_GET:
 664                 case MODE_GETNEXT:
 665                         DEBUGMSGTL((MODNAME_STR, "resource count is %u\n",
 666                             rsrc_count));
 667                         rsrc_count_long = (ulong_t)rsrc_count;
 668                         (void) snmp_set_var_typed_value(request->requestvb,
 669                             ASN_GAUGE, (uchar_t *)&rsrc_count_long,
 670                             sizeof (rsrc_count_long));
 671                         break;
 672                 default:
 673                         (void) snmp_log(LOG_ERR, MODNAME_STR
 674                             ": unsupported request mode: %d\n", reqinfo->mode);
 675                         ret = SNMP_ERR_GENERR;
 676                 }
 677         }
 678 

 679         (void) pthread_mutex_unlock(&update_lock);
 680         return (ret);





























 681 }