view agent/src/heapstats-engines/trapSender.cpp @ 274:fa115df256ab

Bug 3767: Compiler warnings at `snmp_session::remote_port` Reviewed-by: ykubota https://github.com/HeapStats/heapstats/pull/153
author Yasumasa Suenaga <yasuenag@gmail.com>
date Mon, 11 Nov 2019 21:08:24 +0900
parents b7dba538ad10
children
line wrap: on
line source

/*!
 * \file trapSender.cpp
 * \brief This file is used to send SNMP trap.
 * Copyright (C) 2016-2019 Yasumasa Suenaga
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

#include <set>
#include <sys/time.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <pthread.h>
#include <dlfcn.h>

#include "globals.hpp"
#include "util.hpp"
#include "trapSender.hpp"


/*!
 * \brief Datetime of agent initialization.
 */
unsigned long int TTrapSender::initializeTime;

/*!
 * \brief Mutex for TTrapSender.<br>
 * <br>
 * This mutex used in below process.<br>
 *   - TTrapSender::initialize()
 *   - TTrapSender::finalize()
 *   - TTrapSender::~TTrapSender
 *   - TTrapSender::sendTrap
 */
pthread_mutex_t TTrapSender::senderMutex =
                                PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;

/*!
 * \brief Flags whether libnetsnmp.so is loaded.
 */
bool TTrapSender::is_netsnmp_loaded = false;

/*!
 * \brief Library handle of libnetsnmp.so .
 */
void * TTrapSender::libnetsnmp_handle = NULL;

/*!
 * \brief Functions in NET-SNMP client library.
 */
TNetSNMPFunctions TTrapSender::netSnmpFuncs;

 /*!
  * \brief SNMP session information.
  */
netsnmp_session TTrapSender::session;


/*!
 * \brief TTrapSender initialization.
 * \param snmp      [in] SNMP version.
 * \param pPeer     [in] Target of SNMP trap.
 * \param pCommName [in] Community name use for SNMP.
 * \param port      [in] Port used by SNMP trap.
 * \return true if succeeded.
 */
// Avoid deprecation warning of snmp_session::remote_port
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
bool TTrapSender::initialize(int snmp, char *pPeer, char *pCommName, int port) {
  /* If snmp target is illegal. */
  if (pPeer == NULL) {
    logger->printWarnMsg("Illegal SNMP target.");
    return false;
  }

  /* Get now date and set time as agent init-time. */
  struct timeval tv;
  gettimeofday(&tv, NULL);
  TTrapSender::initializeTime =
      (jlong)tv.tv_sec * 100 + (jlong)tv.tv_usec / 10000;

  /* Load functions from libnetsnmp */
  if (!getProcAddressFromNetSNMPLib()) {
    return false;
  }

  /* Initialize session. */
  {
    TMutexLocker locker(&senderMutex);

    memset(&session, 0, sizeof(netsnmp_session));
    netSnmpFuncs.snmp_sess_init(&session);
    session.version = snmp;
    session.peername = strdup(pPeer);
    session.remote_port = port;
    session.community = (u_char *)strdup(pCommName);
    session.community_len = (pCommName != NULL) ? strlen(pCommName) : 0;
  }

  return true;
}
#pragma GCC diagnostic pop

/*!
 * \brief TTrapSender global finalization.
 */
void TTrapSender::finalize(void) {
  /* Close and free SNMP session. */
  {
    TMutexLocker locker(&senderMutex);

    netSnmpFuncs.snmp_close(&session);
    free(session.peername);
    free(session.community);
  }

  /* Unload library */
  if (libnetsnmp_handle != NULL) {
    dlclose(libnetsnmp_handle);
  }
}

/*!
 * \brief TrapSender constructor.
 */
TTrapSender::TTrapSender() {
  TMutexLocker locker(&senderMutex);

  /* Disable NETSNMP logging. */
  netSnmpFuncs.netsnmp_register_loghandler(
                             NETSNMP_LOGHANDLER_NONE, LOG_EMERG);
  /* Make a PDU */
  pPdu = netSnmpFuncs.snmp_pdu_create(SNMP_MSG_TRAP2);
}

/*!
 * \brief TrapSender destructor.
 */
TTrapSender::~TTrapSender(void) {
  TMutexLocker locker(&senderMutex);

  /* Clear Allocated str. */
  clearValues();

  /* Free SNMP pdu. */
  if (pPdu != NULL) {
    netSnmpFuncs.snmp_free_pdu(pPdu);
  }
}

/*!
 * \brief Add agent running time from initialize to trap.
 */
void TTrapSender::setSysUpTime(void) {
  /* OID and buffer. */
  oid OID_SYSUPTIME[] = {SNMP_OID_SYSUPTIME, 0};
  char buff[255] = {0};

  /* Get agent uptime. */
  unsigned long int agentUpTime = 0;
  struct timeval tv;
  gettimeofday(&tv, NULL);
  agentUpTime = ((jlong)tv.tv_sec * 100 + (jlong)tv.tv_usec / 10000) -
                TTrapSender::initializeTime;

  /* Uptime to String. */
  sprintf(buff, "%lu", agentUpTime);

  /* Setting sysUpTime. */
  if (addValue(OID_SYSUPTIME, OID_LENGTH(OID_SYSUPTIME), buff,
               SNMP_VAR_TYPE_TIMETICK) == SNMP_PROC_FAILURE) {
    logger->printWarnMsg("Couldn't append SysUpTime.");
  }
}

/*!
 * \brief Add trapOID to send information by trap.
 * \param trapOID[] [in] Identifier of trap.
 */
void TTrapSender::setTrapOID(const char *trapOID) {
  /* Setting trapOID. */
  oid OID_TRAPOID[] = {SNMP_OID_TRAPOID, 0};

  /* Add snmpTrapOID. */
  if (addValue(OID_TRAPOID, OID_LENGTH(OID_TRAPOID), trapOID,
               SNMP_VAR_TYPE_OID) == SNMP_PROC_FAILURE) {
    logger->printWarnMsg("Couldn't append TrapOID.");
  }
}

/*!
 * \brief Add variable as send information by trap.
 * \param id[]   [in] Identifier of variable.
 * \param len    [in] Length of param "id".
 * \param pValue [in] Variable's data.
 * \param type   [in] Kind of a variable.
 * \return Return process result code.
 */
int TTrapSender::addValue(oid id[], int len, const char *pValue, char type) {
  /* Check param. */
  if (id == NULL || len <= 0 || pValue == NULL || type < 'A' || type > 'z' ||
      (type > 'Z' && type < 'a') || pPdu == NULL) {
    logger->printWarnMsg("Illegal SNMP trap parameter!");
    return SNMP_PROC_FAILURE;
  }

  /* Allocate string memory. */
  char *pStr = strdup(pValue);

  /* If failure allocate value string. */
  if (unlikely(pStr == NULL)) {
    logger->printWarnMsg("Couldn't allocate variable string memory!");
    return SNMP_PROC_FAILURE;
  }

  /* Append variable. */
  int error = netSnmpFuncs.snmp_add_var(pPdu, id, len, type, pStr);

  /* Failure append variables. */
  if (error) {
    free(pStr);
    logger->printWarnMsg("Couldn't append variable list!");
    return SNMP_PROC_FAILURE;
  }

  /* Insert allocated string set. */
  strSet.insert(pStr);

  /* Return success code. */
  return SNMP_PROC_SUCCESS;
}

/*!
 * \brief Add variable as send information by trap.
 * \return Return process result code.
 */
// Avoid deprecation warning of snmp_session::remote_port
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
int TTrapSender::sendTrap(void) {
  /* If snmp target is illegal. */
  if (pPdu == NULL) {
    logger->printWarnMsg("Illegal SNMP target.");
    return SNMP_PROC_FAILURE;
  }

  /* If failure lock to use in multi-thread. */
  if (unlikely(pthread_mutex_lock(&senderMutex) != 0)) {
    logger->printWarnMsg("Entering mutex failed!");
    return SNMP_PROC_FAILURE;
  }

  SOCK_STARTUP;

  /* Open session. */
#ifdef HAVE_NETSNMP_TRANSPORT_OPEN_CLIENT
  netsnmp_session *sess = netSnmpFuncs.snmp_add(
        &session, netSnmpFuncs.netsnmp_transport_open_client(
                                        "snmptrap", session.peername),
                                                                  NULL, NULL);
#else
  char target[256];
  snprintf(target, sizeof(target), "%s:%d", session.peername,
             session.remote_port);
  netsnmp_session *sess = netSnmpFuncs.snmp_add(
        &session, netSnmpFuncs.netsnmp_tdomain_transport(target, 0, "udp"),
                                                                  NULL, NULL);
#endif

  /* If failure open session. */
  if (sess == NULL) {
    logger->printWarnMsg("Failure open SNMP trap session.");
    SOCK_CLEANUP;

    /* Unlock to use in multi-thread. */
    pthread_mutex_unlock(&senderMutex);

    return SNMP_PROC_FAILURE;
  }

  /*
   * Send trap.
   * snmp_send() will free pPDU.
   */
  int success = netSnmpFuncs.snmp_send(sess, pPdu);

  /* Clean up after send trap. */
  netSnmpFuncs.snmp_close(sess);

  /* If failure send trap. */
  if (!success) {
    /* Free PDU. */
    netSnmpFuncs.snmp_free_pdu(pPdu);
    logger->printWarnMsg("Send SNMP trap failed!");
  }

  pPdu = NULL;

  /* Clean up. */
  SOCK_CLEANUP;

  /* Unlock to use in multi-thread. */
  pthread_mutex_unlock(&senderMutex);

  clearValues();

  return (success) ? SNMP_PROC_SUCCESS : SNMP_PROC_FAILURE;
}
#pragma GCC diagnostic pop

/*!
 * \brief Clear PDU and allocated strings.
 */
void TTrapSender::clearValues(void) {
  /* Free allocated strings. */
  std::set<char *>::iterator it = strSet.begin();
  while (it != strSet.end()) {
    free((*it));
    ++it;
  }
  strSet.clear();

  if (pPdu == NULL) {
    pPdu = netSnmpFuncs.snmp_pdu_create(SNMP_MSG_TRAP2);
  }
}

/*!
 * \brief Get function address from libnetsnmp.
 * \return true if succeeded.
 */
bool TTrapSender::getProcAddressFromNetSNMPLib(void) {
  libnetsnmp_handle = dlopen(conf->SnmpLibPath()->get(), RTLD_NOW);

  if (libnetsnmp_handle == NULL) {
    logger->printCritMsg("Could not load libnetsnmp: %s", dlerror());
    return false;
  }

  netSnmpFuncs.netsnmp_register_loghandler =
                   (Tnetsnmp_register_loghandler)dlsym(
                             libnetsnmp_handle, "netsnmp_register_loghandler");
  netSnmpFuncs.snmp_sess_init = (Tsnmp_sess_init)dlsym(
                                           libnetsnmp_handle, "snmp_sess_init");
  netSnmpFuncs.snmp_pdu_create = (Tsnmp_pdu_create)dlsym(
                                          libnetsnmp_handle, "snmp_pdu_create");
  netSnmpFuncs.snmp_free_pdu = (Tsnmp_free_pdu)dlsym(
                                            libnetsnmp_handle, "snmp_free_pdu");
  netSnmpFuncs.snmp_close = (Tsnmp_close)dlsym(libnetsnmp_handle, "snmp_close");
  netSnmpFuncs.snmp_add_var = (Tsnmp_add_var)dlsym(
                                             libnetsnmp_handle, "snmp_add_var");
  netSnmpFuncs.snmp_add = (Tsnmp_add)dlsym(libnetsnmp_handle, "snmp_add");
  netSnmpFuncs.netsnmp_transport_open_client =
                    (Tnetsnmp_transport_open_client)dlsym(
                            libnetsnmp_handle, "netsnmp_transport_open_client");
  netSnmpFuncs.netsnmp_tdomain_transport = (Tnetsnmp_tdomain_transport)dlsym(
                                libnetsnmp_handle, "netsnmp_tdomain_transport");
  netSnmpFuncs.snmp_send = (Tsnmp_send)dlsym(libnetsnmp_handle, "snmp_send");

  if ((netSnmpFuncs.netsnmp_register_loghandler == NULL) ||
      (netSnmpFuncs.snmp_sess_init == NULL) ||
      (netSnmpFuncs.snmp_pdu_create == NULL) ||
      (netSnmpFuncs.snmp_free_pdu == NULL) ||
      (netSnmpFuncs.snmp_close == NULL) ||
      (netSnmpFuncs.snmp_add_var == NULL) ||
      (netSnmpFuncs.snmp_add == NULL) ||
      (netSnmpFuncs.netsnmp_transport_open_client == NULL) ||
      (netSnmpFuncs.snmp_send == NULL)) {
    logger->printCritMsg("Could not load function(s) from libnetsnmp");
    return false;
  }

  return true;
}