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>
        
*** 22,40 ****
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
  #include <fm/fmd_adm.h>
  #include <fm/fmd_snmp.h>
  #include <net-snmp/net-snmp-config.h>
  #include <net-snmp/net-snmp-includes.h>
  #include <net-snmp/agent/net-snmp-agent-includes.h>
! #include <pthread.h>
! #include <stddef.h>
  #include <errno.h>
  #include <libuutil.h>
  #include "sunFM_impl.h"
  #include "resource.h"
  
  static uu_avl_pool_t    *rsrc_fmri_avl_pool;
  static uu_avl_pool_t    *rsrc_index_avl_pool;
--- 22,47 ----
  /*
   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
   */
  
+ /*
+  * Copyright 2018 Nexenta Systems, Inc.
+  */
+ 
  #include <fm/fmd_adm.h>
  #include <fm/fmd_snmp.h>
+ 
  #include <net-snmp/net-snmp-config.h>
  #include <net-snmp/net-snmp-includes.h>
  #include <net-snmp/agent/net-snmp-agent-includes.h>
! 
  #include <errno.h>
  #include <libuutil.h>
+ #include <pthread.h>
+ #include <stddef.h>
+ 
  #include "sunFM_impl.h"
  #include "resource.h"
  
  static uu_avl_pool_t    *rsrc_fmri_avl_pool;
  static uu_avl_pool_t    *rsrc_index_avl_pool;
*** 43,54 ****
  
  #define VALID_AVL_STATE (rsrc_fmri_avl_pool != NULL &&          \
          rsrc_index_avl_pool != NULL && rsrc_fmri_avl != NULL && \
          rsrc_index_avl != NULL)
  
- #define UPDATE_WAIT_MILLIS      10      /* poll interval in milliseconds */
- 
  /*
   * Update types: single-index and all are mutually exclusive; a count
   * update is optional.
   */
  #define UCT_INDEX       0x1
--- 50,59 ----
*** 56,74 ****
  #define UCT_COUNT       0x4
  #define UCT_FLAGS       0x7
  
  #define RESOURCE_DATA_VALID(d)  ((d)->d_valid == valid_stamp)
  
- /*
-  * Locking strategy is described in module.c.
-  */
  static ulong_t          max_index;
  static int              valid_stamp;
  static uint32_t         rsrc_count;
  static pthread_mutex_t  update_lock;
- static pthread_cond_t   update_cv;
- static volatile enum { US_QUIET, US_NEEDED, US_INPROGRESS } update_status;
  
  static Netsnmp_Node_Handler     sunFmResourceTable_handler;
  static Netsnmp_Node_Handler     sunFmResourceCount_handler;
  
  static sunFmResource_data_t *
--- 61,74 ----
*** 263,316 ****
          }
  
          return (SNMP_ERR_NOERROR);
  }
  
- /*ARGSUSED*/
  static void
! update_thread(void *arg)
  {
          /*
           * The current rsrcinfo_update implementation offers minimal savings
           * for the use of index-only updates; therefore we always do a full
           * update.  If it becomes advantageous to limit updates to a single
           * index, the contexts can be queued by the handler instead.
           */
-         sunFmResource_update_ctx_t      uc;
  
          uc.uc_host = NULL;
          uc.uc_prog = FMD_ADM_PROGRAM;
          uc.uc_version = FMD_ADM_VERSION;
  
          uc.uc_all = 0;
          uc.uc_index = 0;
          uc.uc_type = UCT_ALL;
  
-         for (;;) {
-                 (void) pthread_mutex_lock(&update_lock);
-                 update_status = US_QUIET;
-                 while (update_status == US_QUIET)
-                         (void) pthread_cond_wait(&update_cv, &update_lock);
-                 update_status = US_INPROGRESS;
-                 (void) pthread_mutex_unlock(&update_lock);
                  (void) rsrcinfo_update(&uc);
-         }
  }
  
- static void
- request_update(void)
- {
-         (void) pthread_mutex_lock(&update_lock);
-         if (update_status != US_QUIET) {
-                 (void) pthread_mutex_unlock(&update_lock);
-                 return;
-         }
-         update_status = US_NEEDED;
-         (void) pthread_cond_signal(&update_cv);
-         (void) pthread_mutex_unlock(&update_lock);
- }
- 
  /*ARGSUSED*/
  static int
  resource_compare_fmri(const void *l, const void *r, void *private)
  {
          sunFmResource_data_t    *l_data = (sunFmResource_data_t *)l;
--- 263,295 ----
          }
  
          return (SNMP_ERR_NOERROR);
  }
  
  static void
! request_update(void)
  {
+         sunFmResource_update_ctx_t      uc;
+ 
          /*
           * The current rsrcinfo_update implementation offers minimal savings
           * for the use of index-only updates; therefore we always do a full
           * update.  If it becomes advantageous to limit updates to a single
           * index, the contexts can be queued by the handler instead.
           */
  
          uc.uc_host = NULL;
          uc.uc_prog = FMD_ADM_PROGRAM;
          uc.uc_version = FMD_ADM_VERSION;
  
          uc.uc_all = 0;
          uc.uc_index = 0;
          uc.uc_type = UCT_ALL;
  
          (void) rsrcinfo_update(&uc);
  }
  
  /*ARGSUSED*/
  static int
  resource_compare_fmri(const void *l, const void *r, void *private)
  {
          sunFmResource_data_t    *l_data = (sunFmResource_data_t *)l;
*** 346,368 ****
          if ((err = pthread_mutex_init(&update_lock, NULL)) != 0) {
                  (void) snmp_log(LOG_ERR, MODNAME_STR ": mutex_init failure: "
                      "%s\n", strerror(err));
                  return (MIB_REGISTRATION_FAILED);
          }
-         if ((err = pthread_cond_init(&update_cv, NULL)) != 0) {
-                 (void) snmp_log(LOG_ERR, MODNAME_STR ": cond_init failure: "
-                     "%s\n", strerror(err));
-                 return (MIB_REGISTRATION_FAILED);
-         }
  
-         if ((err = pthread_create(NULL, NULL, (void *(*)(void *))update_thread,
-             NULL)) != 0) {
-                 (void) snmp_log(LOG_ERR, MODNAME_STR ": error creating update "
-                     "thread: %s\n", strerror(err));
-                 return (MIB_REGISTRATION_FAILED);
-         }
- 
          if ((table_info =
              SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info)) == NULL)
                  return (MIB_REGISTRATION_FAILED);
  
          if ((handler = netsnmp_create_handler_registration("sunFmResourceTable",
--- 325,335 ----
*** 459,474 ****
  
          return (MIB_REGISTERED_OK);
  }
  
  /*
!  * These two functions form the core of GET/GETNEXT/GETBULK handling (the
   * only kind we do).  They perform two functions:
   *
   * - First, frob the request to set all the index variables to correspond
   *   to the value that's going to be returned.  For GET, this is a nop;
!  *   for GETNEXT/GETBULK it always requires some work.
   * - Second, find and return the fmd resource information corresponding to
   *   the (possibly updated) indices.
   *
   * These should be as fast as possible; they run in the agent thread.
   */
--- 426,441 ----
  
          return (MIB_REGISTERED_OK);
  }
  
  /*
!  * These two functions form the core of GET/GETNEXT handling (the
   * only kind we do).  They perform two functions:
   *
   * - First, frob the request to set all the index variables to correspond
   *   to the value that's going to be returned.  For GET, this is a nop;
!  *   for GETNEXT it always requires some work.
   * - Second, find and return the fmd resource information corresponding to
   *   the (possibly updated) indices.
   *
   * These should be as fast as possible; they run in the agent thread.
   */
*** 557,598 ****
  
          return (resource_lookup_index_exact(table_info->index_oid[0]));
  }
  
  /*ARGSUSED*/
! static void
! sunFmResourceTable_return(unsigned int reg, void *arg)
  {
-         netsnmp_delegated_cache         *cache = (netsnmp_delegated_cache *)arg;
-         netsnmp_request_info            *request;
-         netsnmp_agent_request_info      *reqinfo;
-         netsnmp_handler_registration    *reginfo;
          netsnmp_table_request_info      *table_info;
          sunFmResource_data_t            *data;
          ulong_t                         rsrcstate;
  
!         ASSERT(netsnmp_handler_check_cache(cache) != NULL);
  
          (void) pthread_mutex_lock(&update_lock);
!         if (update_status != US_QUIET) {
!                 struct timeval                  tv;
  
!                 tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
!                 tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
! 
!                 (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return,
!                     cache);
!                 (void) pthread_mutex_unlock(&update_lock);
!                 return;
!         }
! 
!         request = cache->requests;
!         reqinfo = cache->reqinfo;
!         reginfo = cache->reginfo;
! 
          table_info = netsnmp_extract_table_info(request);
!         request->delegated = 0;
  
          ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN);
          ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX);
  
          /*
--- 524,556 ----
  
          return (resource_lookup_index_exact(table_info->index_oid[0]));
  }
  
  /*ARGSUSED*/
! static int
! sunFmResourceTable_handler(netsnmp_mib_handler *handler,
!     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
!     netsnmp_request_info *request)
  {
          netsnmp_table_request_info      *table_info;
          sunFmResource_data_t            *data;
          ulong_t                         rsrcstate;
+         int                             ret = SNMP_ERR_NOERROR;
  
!         /*
!          * We don't support MODE_GETBULK directly, so all bulk requests should
!          * come through bulk_to_next helper.  Make sure it stays that way.
!          */
!         ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
  
          (void) pthread_mutex_lock(&update_lock);
!         request_update();
  
!         for (; request != NULL; request = request->next) {
                  table_info = netsnmp_extract_table_info(request);
!                 if (table_info == NULL)
!                         continue;
  
                  ASSERT(table_info->colnum >= SUNFMRESOURCE_COLMIN);
                  ASSERT(table_info->colnum <= SUNFMRESOURCE_COLMAX);
  
                  /*
*** 607,647 ****
           * - We will never receive requests outside our table nor
           *   those with the first subid anything other than 1 (Entry)
           *   nor those without a column number.  This is true even
           *   for GETNEXT requests.
           */
- 
          switch (reqinfo->mode) {
          case MODE_GET:
!                 if ((data = sunFmResourceTable_rsrc(reginfo, table_info)) ==
!                     NULL) {
!                         netsnmp_free_delegated_cache(cache);
!                         (void) pthread_mutex_unlock(&update_lock);
!                         return;
!                 }
                  break;
          case MODE_GETNEXT:
!         case MODE_GETBULK:
!                 if ((data = sunFmResourceTable_nextrsrc(reginfo, table_info)) ==
!                     NULL) {
!                         netsnmp_free_delegated_cache(cache);
!                         (void) pthread_mutex_unlock(&update_lock);
!                         return;
!                 }
                  break;
          default:
!                 (void) snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request "
!                     "mode %d\n", reqinfo->mode);
!                 netsnmp_free_delegated_cache(cache);
!                 (void) pthread_mutex_unlock(&update_lock);
!                 return;
          }
  
          switch (table_info->colnum) {
          case SUNFMRESOURCE_COL_FMRI:
!                 (void) netsnmp_table_build_result(reginfo, request, table_info,
!                     ASN_OCTET_STR, (uchar_t *)data->d_ari_fmri,
                      strlen(data->d_ari_fmri));
                  break;
          case SUNFMRESOURCE_COL_STATUS:
                  switch (data->d_ari_flags &
                      (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) {
--- 565,597 ----
                   * - We will never receive requests outside our table nor
                   *   those with the first subid anything other than 1 (Entry)
                   *   nor those without a column number.  This is true even
                   *   for GETNEXT requests.
                   */
                  switch (reqinfo->mode) {
                  case MODE_GET:
!                         data = sunFmResourceTable_rsrc(reginfo, table_info);
!                         if (data == NULL)
!                                 goto out;
                          break;
                  case MODE_GETNEXT:
!                         data = sunFmResourceTable_nextrsrc(reginfo, table_info);
!                         if (data == NULL)
!                                 goto out;
                          break;
                  default:
!                         (void) snmp_log(LOG_ERR, MODNAME_STR
!                             ": unsupported request mode %d\n", reqinfo->mode);
!                         ret = SNMP_ERR_GENERR;
!                         goto out;
                  }
  
                  switch (table_info->colnum) {
                  case SUNFMRESOURCE_COL_FMRI:
!                         (void) netsnmp_table_build_result(reginfo, request,
!                             table_info, ASN_OCTET_STR,
!                             (uchar_t *)data->d_ari_fmri,
                              strlen(data->d_ari_fmri));
                          break;
                  case SUNFMRESOURCE_COL_STATUS:
                          switch (data->d_ari_flags &
                              (FMD_ADM_RSRC_FAULTY|FMD_ADM_RSRC_UNUSABLE)) {
*** 656,791 ****
                          break;
                  case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE:
                          rsrcstate = SUNFMRESOURCE_STATE_FAULTED;
                          break;
                  }
!                 (void) netsnmp_table_build_result(reginfo, request, table_info,
!                     ASN_INTEGER, (uchar_t *)&rsrcstate,
                      sizeof (rsrcstate));
                  break;
          case SUNFMRESOURCE_COL_DIAGNOSISUUID:
!                 (void) netsnmp_table_build_result(reginfo, request, table_info,
!                     ASN_OCTET_STR, (uchar_t *)data->d_ari_case,
                      strlen(data->d_ari_case));
                  break;
          default:
                  break;
          }
!         netsnmp_free_delegated_cache(cache);
          (void) pthread_mutex_unlock(&update_lock);
  }
  
  static int
! sunFmResourceTable_handler(netsnmp_mib_handler *handler,
      netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
!     netsnmp_request_info *requests)
  {
-         netsnmp_request_info            *request;
-         struct timeval                  tv;
- 
-         tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
-         tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
- 
-         request_update();
- 
-         for (request = requests; request; request = request->next) {
-                 if (request->processed != 0)
-                         continue;
- 
-                 if (netsnmp_extract_table_info(request) == NULL)
-                         continue;
- 
-                 request->delegated = 1;
-                 (void) snmp_alarm_register_hr(tv, 0, sunFmResourceTable_return,
-                     (void *) netsnmp_create_delegated_cache(handler, reginfo,
-                     reqinfo, request, NULL));
-         }
- 
-         return (SNMP_ERR_NOERROR);
- }
- 
- /*ARGSUSED*/
- static void
- sunFmResourceCount_return(unsigned int reg, void *arg)
- {
-         netsnmp_delegated_cache         *cache = (netsnmp_delegated_cache *)arg;
-         netsnmp_request_info            *request;
-         netsnmp_agent_request_info      *reqinfo;
          ulong_t                         rsrc_count_long;
  
!         ASSERT(netsnmp_handler_check_cache(cache) != NULL);
  
          (void) pthread_mutex_lock(&update_lock);
!         if (update_status != US_QUIET) {
!                 struct timeval  tv;
  
!                 tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
!                 tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
! 
!                 (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return,
!                     cache);
!                 (void) pthread_mutex_unlock(&update_lock);
!                 return;
!         }
! 
!         request = cache->requests;
!         reqinfo = cache->reqinfo;
! 
!         request->delegated = 0;
! 
          switch (reqinfo->mode) {
          /*
!          * According to the documentation, it's not possible for us ever to
!          * be called with MODE_GETNEXT.  However, Net-SNMP does the following:
           * - set reqinfo->mode to MODE_GET
           * - invoke the handler
!          * - set reqinfo->mode to MODE_GETNEXT (even if the request was not
!          *   actually processed; i.e. it's been delegated)
           * Since we're called back later with the same reqinfo, we see
           * GETNEXT.  Therefore this case is needed to work around the
           * Net-SNMP bug.
           */
          case MODE_GET:
          case MODE_GETNEXT:
!                 DEBUGMSGTL((MODNAME_STR, "resource count is %u\n", rsrc_count));
                  rsrc_count_long = (ulong_t)rsrc_count;
!                 (void) snmp_set_var_typed_value(request->requestvb, ASN_GAUGE,
!                     (uchar_t *)&rsrc_count_long, sizeof (rsrc_count_long));
                  break;
          default:
!                 (void) snmp_log(LOG_ERR, MODNAME_STR ": Unsupported request "
!                     "mode %d\n", reqinfo->mode);
          }
  
-         netsnmp_free_delegated_cache(cache);
          (void) pthread_mutex_unlock(&update_lock);
! }
! 
! static int
! sunFmResourceCount_handler(netsnmp_mib_handler *handler,
!     netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
!     netsnmp_request_info *requests)
! {
!         struct timeval  tv;
! 
!         tv.tv_sec = UPDATE_WAIT_MILLIS / 1000;
!         tv.tv_usec = (UPDATE_WAIT_MILLIS % 1000) * 1000;
! 
!         request_update();
! 
!         /*
!          * We are never called for a GETNEXT when registered as an
!          * instance; it's handled for us and converted to a GET.
!          * Also, an instance handler is given only one request at a time, so
!          * we don't need to loop over a list of requests.
!          */
! 
!         if (requests->processed != 0)
!                 return (SNMP_ERR_NOERROR);
! 
!         requests->delegated = 1;
!         (void) snmp_alarm_register_hr(tv, 0, sunFmResourceCount_return,
!             (void *) netsnmp_create_delegated_cache(handler, reginfo,
!             reqinfo, requests, NULL));
! 
!         return (SNMP_ERR_NOERROR);
  }
--- 606,681 ----
                                  break;
                          case FMD_ADM_RSRC_FAULTY | FMD_ADM_RSRC_UNUSABLE:
                                  rsrcstate = SUNFMRESOURCE_STATE_FAULTED;
                                  break;
                          }
!                         (void) netsnmp_table_build_result(reginfo, request,
!                             table_info, ASN_INTEGER, (uchar_t *)&rsrcstate,
                              sizeof (rsrcstate));
                          break;
                  case SUNFMRESOURCE_COL_DIAGNOSISUUID:
!                         (void) netsnmp_table_build_result(reginfo, request,
!                             table_info, ASN_OCTET_STR,
!                             (uchar_t *)data->d_ari_case,
                              strlen(data->d_ari_case));
                          break;
                  default:
                          break;
                  }
!         }
! 
! out:
          (void) pthread_mutex_unlock(&update_lock);
+         return (ret);
  }
  
+ /*ARGSUSED*/
  static int
! sunFmResourceCount_handler(netsnmp_mib_handler *handler,
      netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo,
!     netsnmp_request_info *request)
  {
          ulong_t                         rsrc_count_long;
+         int                             ret = SNMP_ERR_NOERROR;
  
!         /*
!          * We don't support MODE_GETBULK directly, so all bulk requests should
!          * come through bulk_to_next helper.  Make sure it stays that way.
!          */
!         ASSERT(reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT);
  
          (void) pthread_mutex_lock(&update_lock);
!         request_update();
  
!         for (; request != NULL; request = request->next) {
                  switch (reqinfo->mode) {
                  /*
!                  * According to the documentation, it's not possible for us ever
!                  * to be called with MODE_GETNEXT.  However, Net-SNMP does the
!                  * following:
                   * - set reqinfo->mode to MODE_GET
                   * - invoke the handler
!                  * - set reqinfo->mode to MODE_GETNEXT (even if the request was
!                  *   not actually processed; i.e. it's been delegated)
                   * Since we're called back later with the same reqinfo, we see
                   * GETNEXT.  Therefore this case is needed to work around the
                   * Net-SNMP bug.
                   */
                  case MODE_GET:
                  case MODE_GETNEXT:
!                         DEBUGMSGTL((MODNAME_STR, "resource count is %u\n",
!                             rsrc_count));
                          rsrc_count_long = (ulong_t)rsrc_count;
!                         (void) snmp_set_var_typed_value(request->requestvb,
!                             ASN_GAUGE, (uchar_t *)&rsrc_count_long,
!                             sizeof (rsrc_count_long));
                          break;
                  default:
!                         (void) snmp_log(LOG_ERR, MODNAME_STR
!                             ": unsupported request mode: %d\n", reqinfo->mode);
!                         ret = SNMP_ERR_GENERR;
                  }
+         }
  
          (void) pthread_mutex_unlock(&update_lock);
!         return (ret);
  }