nut/tools/nut-scanner/scan_snmp.c

1195 lines
34 KiB
C
Raw Permalink Normal View History

2016-07-18 00:11:41 +00:00
/*
* Copyright (C) 2011 - EATON
2022-06-29 10:37:36 +00:00
* Copyright (C) 2016-2021 - EATON - Various threads-related improvements
2011-09-29 18:14:46 +00:00
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2016-07-18 00:11:41 +00:00
/*! \file scan_snmp.c
\brief detect NUT supported SNMP devices
2022-06-29 10:37:36 +00:00
\author Frederic Bohe <FredericBohe@Eaton.com>
\author Arnaud Quette <ArnaudQuette@Eaton.com>
\author Jim Klimov <EvgenyKlimov@eaton.com>
2016-07-18 00:11:41 +00:00
*/
2011-09-29 18:14:46 +00:00
#include "common.h"
2012-01-24 10:22:33 +00:00
#include "nut-scan.h"
2011-09-29 18:14:46 +00:00
2012-01-24 10:22:33 +00:00
#ifdef WITH_SNMP
2011-09-29 18:14:46 +00:00
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
2012-01-24 10:22:33 +00:00
#include <ltdl.h>
2011-09-29 18:14:46 +00:00
/* workaround for buggy Net-SNMP config
* from drivers/snmp-ups.h */
#ifdef PACKAGE_BUGREPORT
#undef PACKAGE_BUGREPORT
#endif
#ifdef PACKAGE_NAME
#undef PACKAGE_NAME
#endif
#ifdef PACKAGE_VERSION
#undef PACKAGE_VERSION
#endif
#ifdef PACKAGE_STRING
#undef PACKAGE_STRING
#endif
#ifdef PACKAGE_TARNAME
#undef PACKAGE_TARNAME
#endif
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include "nutscan-snmp.h"
2012-06-01 13:55:19 +00:00
/* Address API change */
2022-06-29 10:37:36 +00:00
#if ( ! NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol ) && ( ! defined usmAESPrivProtocol )
2012-06-01 13:55:19 +00:00
#define USMAESPRIVPROTOCOL "usmAES128PrivProtocol"
#else
#define USMAESPRIVPROTOCOL "usmAESPrivProtocol"
#endif
2011-09-29 18:14:46 +00:00
#define SysOID ".1.3.6.1.2.1.1.2.0"
2022-06-29 10:37:36 +00:00
/* use explicit booleans */
#ifndef FALSE
typedef enum ebool { FALSE = 0, TRUE } bool_t;
#else
typedef int bool_t;
#endif
2011-09-29 18:14:46 +00:00
static nutscan_device_t * dev_ret = NULL;
#ifdef HAVE_PTHREAD
static pthread_mutex_t dev_mutex;
#endif
2022-06-29 10:37:36 +00:00
static useconds_t g_usec_timeout ;
2011-09-29 18:14:46 +00:00
2012-01-24 10:22:33 +00:00
/* dynamic link library stuff */
static lt_dlhandle dl_handle = NULL;
static const char *dl_error = NULL;
static void (*nut_init_snmp)(const char *type);
static void (*nut_snmp_sess_init)(netsnmp_session * session);
static void * (*nut_snmp_sess_open)(struct snmp_session *session);
static int (*nut_snmp_sess_close)(void *handle);
2022-06-29 10:37:36 +00:00
static struct snmp_session * (*nut_snmp_sess_session)(void *handle);
2012-01-24 10:22:33 +00:00
static void * (*nut_snmp_parse_oid)(const char *input, oid *objid,
size_t *objidlen);
2022-06-29 10:37:36 +00:00
static struct snmp_pdu * (*nut_snmp_pdu_create) (int command);
static netsnmp_variable_list * (*nut_snmp_add_null_var)(netsnmp_pdu *pdu,
2012-01-24 10:22:33 +00:00
const oid *objid, size_t objidlen);
static int (*nut_snmp_sess_synch_response) (void *sessp, netsnmp_pdu *pdu,
netsnmp_pdu **response);
static int (*nut_snmp_oid_compare) (const oid *in_name1, size_t len1,
const oid *in_name2, size_t len2);
static void (*nut_snmp_free_pdu) (netsnmp_pdu *pdu);
2022-06-29 10:37:36 +00:00
/* NOTE: Net-SNMP headers just are weird like that, in the same release:
net-snmp/types.h: size_t securityAuthProtoLen;
net-snmp/library/keytools.h: int generate_Ku(const oid * hashtype, u_int hashtype_len, ...
* Should we match in configure like for "getnameinfo()" arg types?
* Currently we cast one to another below (detecting target type could help).
*/
2012-01-24 10:22:33 +00:00
static int (*nut_generate_Ku)(const oid * hashtype, u_int hashtype_len,
2022-06-29 10:37:36 +00:00
unsigned char * P, size_t pplen, unsigned char * Ku, size_t * kulen);
static char* (*nut_snmp_out_toggle_options)(char *options);
2012-01-24 10:22:33 +00:00
static const char * (*nut_snmp_api_errstring) (int snmp_errnumber);
2022-06-29 10:37:36 +00:00
/* Variables (not methods) exported by libnet-snmp: */
static int *nut_snmp_errno;
#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol
static oid *nut_usmAESPrivProtocol; /* might be usmAES128PrivProtocol on some systems */
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol
static oid *nut_usmHMACMD5AuthProtocol;
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol
static oid *nut_usmHMACSHA1AuthProtocol;
#endif
#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol
static oid *nut_usmDESPrivProtocol;
#endif
#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04
# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol
static oid *nut_usmAES192PrivProtocol;
# endif
# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol
static oid *nut_usmAES256PrivProtocol;
# endif
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol
static oid *nut_usmHMAC192SHA256AuthProtocol;
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol
static oid *nut_usmHMAC256SHA384AuthProtocol;
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol
static oid *nut_usmHMAC384SHA512AuthProtocol;
#endif
/* return 0 on error; visible externally */
int nutscan_load_snmp_library(const char *libname_path);
2016-07-18 00:11:41 +00:00
int nutscan_load_snmp_library(const char *libname_path)
2012-01-24 10:22:33 +00:00
{
2022-06-29 10:37:36 +00:00
if (dl_handle != NULL) {
2012-01-24 10:22:33 +00:00
/* if previous init failed */
2022-06-29 10:37:36 +00:00
if (dl_handle == (void *)1) {
2012-01-24 10:22:33 +00:00
return 0;
}
/* init has already been done */
return 1;
}
2016-07-18 00:11:41 +00:00
if (libname_path == NULL) {
2022-06-29 10:37:36 +00:00
upsdebugx(1, "SNMP library not found. SNMP search disabled");
2016-07-18 00:11:41 +00:00
return 0;
}
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
if (lt_dlinit() != 0) {
upsdebugx(1, "Error initializing lt_init");
2016-07-18 00:11:41 +00:00
return 0;
}
dl_handle = lt_dlopen(libname_path);
2012-01-24 10:22:33 +00:00
if (!dl_handle) {
dl_error = lt_dlerror();
goto err;
}
lt_dlerror(); /* Clear any existing error */
*(void **) (&nut_init_snmp) = lt_dlsym(dl_handle, "init_snmp");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_sess_init) = lt_dlsym(dl_handle,
"snmp_sess_init");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_sess_open) = lt_dlsym(dl_handle,
"snmp_sess_open");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_sess_close) = lt_dlsym(dl_handle,
"snmp_sess_close");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_sess_session) = lt_dlsym(dl_handle,
"snmp_sess_session");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_parse_oid) = lt_dlsym(dl_handle,
"snmp_parse_oid");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_pdu_create) = lt_dlsym(dl_handle,
"snmp_pdu_create");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_add_null_var) = lt_dlsym(dl_handle,
"snmp_add_null_var");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_sess_synch_response) = lt_dlsym(dl_handle,
"snmp_sess_synch_response");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_oid_compare) = lt_dlsym(dl_handle,
"snmp_oid_compare");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
*(void **) (&nut_snmp_free_pdu) = lt_dlsym(dl_handle, "snmp_free_pdu");
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_generate_Ku) = lt_dlsym(dl_handle, "generate_Ku");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
*(void **) (&nut_snmp_out_toggle_options) = lt_dlsym(dl_handle,
"snmp_out_toggle_options");
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_api_errstring) = lt_dlsym(dl_handle,
"snmp_api_errstring");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
*(void **) (&nut_snmp_errno) = lt_dlsym(dl_handle, "snmp_errno");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol
2012-01-24 10:22:33 +00:00
*(void **) (&nut_usmAESPrivProtocol) = lt_dlsym(dl_handle,
2012-06-01 13:55:19 +00:00
USMAESPRIVPROTOCOL);
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
#endif /* NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol */
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol
2012-01-24 10:22:33 +00:00
*(void **) (&nut_usmHMACMD5AuthProtocol) = lt_dlsym(dl_handle,
"usmHMACMD5AuthProtocol");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
#endif /* NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol */
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol
2012-01-24 10:22:33 +00:00
*(void **) (&nut_usmHMACSHA1AuthProtocol) = lt_dlsym(dl_handle,
"usmHMACSHA1AuthProtocol");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
#endif /* NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol */
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol
2012-01-24 10:22:33 +00:00
*(void **) (&nut_usmDESPrivProtocol) = lt_dlsym(dl_handle,
"usmDESPrivProtocol");
2022-06-29 10:37:36 +00:00
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
#endif /* NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol */
#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04
# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol
*(void **) (&nut_usmAES192PrivProtocol) = lt_dlsym(dl_handle,
"usmAES192PrivProtocol");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
# endif /* NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol */
# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol
*(void **) (&nut_usmAES256PrivProtocol) = lt_dlsym(dl_handle,
"usmAES256PrivProtocol");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
# endif /* NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol */
#endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */
#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol
*(void **) (&nut_usmHMAC192SHA256AuthProtocol) = lt_dlsym(dl_handle,
"usmHMAC192SHA256AuthProtocol");
if ((dl_error = lt_dlerror()) != NULL) {
2012-01-24 10:22:33 +00:00
goto err;
}
2022-06-29 10:37:36 +00:00
#endif /* NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol */
#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol
*(void **) (&nut_usmHMAC256SHA384AuthProtocol) = lt_dlsym(dl_handle,
"usmHMAC256SHA384AuthProtocol");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
#endif /* NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol */
#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol
*(void **) (&nut_usmHMAC384SHA512AuthProtocol) = lt_dlsym(dl_handle,
"usmHMAC384SHA512AuthProtocol");
if ((dl_error = lt_dlerror()) != NULL) {
goto err;
}
#endif /* NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol */
2012-01-24 10:22:33 +00:00
return 1;
2022-06-29 10:37:36 +00:00
2012-01-24 10:22:33 +00:00
err:
2022-06-29 10:37:36 +00:00
fprintf(stderr, "Cannot load SNMP library (%s) : %s. SNMP search disabled.\n",
libname_path, dl_error);
2012-01-24 10:22:33 +00:00
dl_handle = (void *)1;
2012-06-01 13:55:19 +00:00
lt_dlexit();
2012-01-24 10:22:33 +00:00
return 0;
}
/* end of dynamic link library stuff */
2022-06-29 10:37:36 +00:00
static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response, char * mib)
2011-09-29 18:14:46 +00:00
{
nutscan_device_t * dev = NULL;
struct snmp_session * session;
char * buf;
2012-01-24 10:22:33 +00:00
session = (*nut_snmp_sess_session)(sec->handle);
2022-06-29 10:37:36 +00:00
if (session == NULL) {
2012-01-24 10:22:33 +00:00
return;
}
2011-09-29 18:14:46 +00:00
/* SNMP device found */
dev = nutscan_new_device();
dev->type = TYPE_SNMP;
dev->driver = strdup("snmp-ups");
dev->port = strdup(session->peername);
2022-06-29 10:37:36 +00:00
if (response != NULL) {
buf = malloc (response->variables->val_len + 1);
if (buf) {
memcpy(buf, response->variables->val.string,
response->variables->val_len);
buf[response->variables->val_len] = 0;
nutscan_add_option_to_device(dev, "desc", buf);
free(buf);
}
}
nutscan_add_option_to_device(dev, "mibs", mib);
2011-09-29 18:14:46 +00:00
/* SNMP v3 */
2022-06-29 10:37:36 +00:00
if (session->community == NULL || session->community[0] == 0) {
nutscan_add_option_to_device(dev, "snmp_version", "v3");
if (sec->secLevel) {
nutscan_add_option_to_device(dev, "secLevel",
sec->secLevel);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (sec->secName) {
nutscan_add_option_to_device(dev, "secName",
sec->secName);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (sec->authPassword) {
nutscan_add_option_to_device(dev, "authPassword",
sec->authPassword);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (sec->privPassword) {
nutscan_add_option_to_device(dev, "privPassword",
sec->privPassword);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (sec->authProtocol) {
nutscan_add_option_to_device(dev, "authProtocol",
sec->authProtocol);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
if (sec->privProtocol) {
nutscan_add_option_to_device(dev, "privProtocol",
sec->privProtocol);
2011-09-29 18:14:46 +00:00
}
}
else {
2022-06-29 10:37:36 +00:00
buf = malloc (session->community_len + 1);
if (buf) {
memcpy(buf, session->community,
2011-09-29 18:14:46 +00:00
session->community_len);
2022-06-29 10:37:36 +00:00
buf[session->community_len] = 0;
nutscan_add_option_to_device(dev, "community", buf);
2011-09-29 18:14:46 +00:00
free(buf);
}
}
#ifdef HAVE_PTHREAD
pthread_mutex_lock(&dev_mutex);
#endif
2022-06-29 10:37:36 +00:00
dev_ret = nutscan_add_device_to_device(dev_ret, dev);
2011-09-29 18:14:46 +00:00
#ifdef HAVE_PTHREAD
pthread_mutex_unlock(&dev_mutex);
#endif
}
2022-06-29 10:37:36 +00:00
static struct snmp_pdu * scan_snmp_get_oid(char* oid_str, void* handle)
2011-09-29 18:14:46 +00:00
{
2022-06-29 10:37:36 +00:00
size_t name_len;
oid name[MAX_OID_LEN];
2011-09-29 18:14:46 +00:00
struct snmp_pdu *pdu, *response = NULL;
int status;
int index = 0;
/* create and send request. */
name_len = MAX_OID_LEN;
2012-01-24 10:22:33 +00:00
if (!(*nut_snmp_parse_oid)(oid_str, name, &name_len)) {
2011-09-29 18:14:46 +00:00
index++;
return NULL;
}
2012-01-24 10:22:33 +00:00
pdu = (*nut_snmp_pdu_create)(SNMP_MSG_GET);
2011-09-29 18:14:46 +00:00
if (pdu == NULL) {
index++;
return NULL;
}
2012-01-24 10:22:33 +00:00
(*nut_snmp_add_null_var)(pdu, name, name_len);
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
status = (*nut_snmp_sess_synch_response)(handle, pdu, &response);
if (response == NULL) {
2011-09-29 18:14:46 +00:00
index++;
return NULL;
}
2022-06-29 10:37:36 +00:00
if (status != STAT_SUCCESS
|| response->errstat != SNMP_ERR_NOERROR
|| response->variables == NULL
|| response->variables->name == NULL
|| ((*nut_snmp_oid_compare)(response->variables->name,
response->variables->name_length,
name, name_len) != 0)
|| response->variables->val.string == NULL
) {
2012-01-24 10:22:33 +00:00
(*nut_snmp_free_pdu)(response);
2011-09-29 18:14:46 +00:00
index++;
return NULL;
}
return response;
}
2022-06-29 10:37:36 +00:00
static void try_all_oid(void * arg, const char * mib_found)
2011-09-29 18:14:46 +00:00
{
struct snmp_pdu *response = NULL;
int index = 0;
nutscan_snmp_t * sec = (nutscan_snmp_t *)arg;
2022-06-29 10:37:36 +00:00
upsdebugx(2, "Entering %s for %s", __func__, sec->peername);
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
while (snmp_device_table[index].mib != NULL) {
if (snmp_device_table[index].oid == NULL
|| snmp_device_table[index].oid[0] == '\0'
) {
2011-09-29 18:14:46 +00:00
index++;
continue;
}
2022-06-29 10:37:36 +00:00
response = scan_snmp_get_oid(snmp_device_table[index].oid, sec->handle);
if (response == NULL) {
index++;
continue;
}
/* add device only if not yet detected with the same mib */
if (mib_found == NULL || (strcmp(mib_found, snmp_device_table[index].mib) != 0)) {
scan_snmp_add_device(sec, response, snmp_device_table[index].mib);
upsdebugx(3, "Found another match for device with MIB '%s'",
snmp_device_table[index].mib);
}
else {
upsdebugx(3, "Skip duplicated device %s", snmp_device_table[index].mib);
}
2011-09-29 18:14:46 +00:00
2012-01-24 10:22:33 +00:00
(*nut_snmp_free_pdu)(response);
2011-09-29 18:14:46 +00:00
response = NULL;
index++;
}
}
static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec)
{
2012-01-24 10:22:33 +00:00
(*nut_snmp_sess_init)(snmp_sess);
2011-09-29 18:14:46 +00:00
snmp_sess->peername = sec->peername;
2022-06-29 10:37:36 +00:00
if (sec->community != NULL || sec->secLevel == NULL) {
2011-09-29 18:14:46 +00:00
snmp_sess->version = SNMP_VERSION_1;
2022-06-29 10:37:36 +00:00
if (sec->community != NULL) {
2011-09-29 18:14:46 +00:00
snmp_sess->community = (unsigned char *)sec->community;
snmp_sess->community_len = strlen(sec->community);
}
else {
snmp_sess->community = (unsigned char *)"public";
snmp_sess->community_len = strlen("public");
}
}
2022-06-29 10:37:36 +00:00
else { /* SNMP v3 */
2011-09-29 18:14:46 +00:00
snmp_sess->version = SNMP_VERSION_3;
/* Security level */
if (strcmp(sec->secLevel, "noAuthNoPriv") == 0)
snmp_sess->securityLevel = SNMP_SEC_LEVEL_NOAUTH;
else if (strcmp(sec->secLevel, "authNoPriv") == 0)
snmp_sess->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
else if (strcmp(sec->secLevel, "authPriv") == 0)
snmp_sess->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
else {
2022-06-29 10:37:36 +00:00
fprintf(stderr,
"Bad SNMPv3 securityLevel: %s\n",
sec->secLevel);
2011-09-29 18:14:46 +00:00
return 0;
}
/* Security name */
2022-06-29 10:37:36 +00:00
if (sec->secName == NULL) {
fprintf(stderr, "securityName is required for SNMPv3\n");
2011-09-29 18:14:46 +00:00
return 0;
}
snmp_sess->securityName = strdup(sec->secName);
snmp_sess->securityNameLen = strlen(snmp_sess->securityName);
/* Everything is ready for NOAUTH */
2022-06-29 10:37:36 +00:00
if (snmp_sess->securityLevel == SNMP_SEC_LEVEL_NOAUTH) {
2011-09-29 18:14:46 +00:00
return 1;
}
/* Process mandatory fields, based on the security level */
2022-06-29 10:37:36 +00:00
switch (snmp_sess->securityLevel) {
case SNMP_SEC_LEVEL_AUTHNOPRIV:
if (sec->authPassword == NULL) {
fprintf(stderr,
"authPassword is required "
"for SNMPv3 in %s mode\n",
2011-09-29 18:14:46 +00:00
sec->secLevel);
return 0;
}
break;
2022-06-29 10:37:36 +00:00
case SNMP_SEC_LEVEL_AUTHPRIV:
if ((sec->authPassword == NULL) ||
2011-09-29 18:14:46 +00:00
(sec->privPassword == NULL)) {
2022-06-29 10:37:36 +00:00
fprintf(stderr,
"authPassword and privPassword are "
"required for SNMPv3 in %s mode\n",
2011-09-29 18:14:46 +00:00
sec->secLevel);
return 0;
}
break;
2022-06-29 10:37:36 +00:00
default:
/* nothing else needed */
break;
}
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
/* Process authentication protocol and key */
snmp_sess->securityAuthKeyLen = USM_AUTH_KU_LEN;
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol
2011-09-29 18:14:46 +00:00
/* default to MD5 */
2022-06-29 10:37:36 +00:00
snmp_sess->securityAuthProto = nut_usmHMACMD5AuthProtocol;
2012-01-24 10:22:33 +00:00
snmp_sess->securityAuthProtoLen =
2022-06-29 10:37:36 +00:00
sizeof(usmHMACMD5AuthProtocol)/
sizeof(oid);
#endif
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
if (sec->authProtocol) {
#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol
2011-09-29 18:14:46 +00:00
if (strcmp(sec->authProtocol, "SHA") == 0) {
2022-06-29 10:37:36 +00:00
snmp_sess->securityAuthProto = nut_usmHMACSHA1AuthProtocol;
2011-09-29 18:14:46 +00:00
snmp_sess->securityAuthProtoLen =
2022-06-29 10:37:36 +00:00
sizeof(usmHMACSHA1AuthProtocol)/
2011-09-29 18:14:46 +00:00
sizeof(oid);
}
2022-06-29 10:37:36 +00:00
else
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol
if (strcmp(sec->authProtocol, "SHA256") == 0) {
snmp_sess->securityAuthProto = nut_usmHMAC192SHA256AuthProtocol;
snmp_sess->securityAuthProtoLen =
sizeof(usmHMAC192SHA256AuthProtocol)/
sizeof(oid);
}
else
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol
if (strcmp(sec->authProtocol, "SHA384") == 0) {
snmp_sess->securityAuthProto = nut_usmHMAC256SHA384AuthProtocol;
snmp_sess->securityAuthProtoLen =
sizeof(usmHMAC256SHA384AuthProtocol)/
sizeof(oid);
}
else
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol
if (strcmp(sec->authProtocol, "SHA512") == 0) {
snmp_sess->securityAuthProto = nut_usmHMAC384SHA512AuthProtocol;
snmp_sess->securityAuthProtoLen =
sizeof(usmHMAC384SHA512AuthProtocol)/
sizeof(oid);
}
else
#endif
#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol
if (strcmp(sec->authProtocol, "MD5") != 0) {
#else
{
#endif
fprintf(stderr,
"Bad SNMPv3 authProtocol: %s\n",
sec->authProtocol);
return 0;
2011-09-29 18:14:46 +00:00
}
}
/* set the authentication key to a MD5/SHA1 hashed version of
* our passphrase (must be at least 8 characters long) */
2022-06-29 10:37:36 +00:00
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) )
# pragma GCC diagnostic push
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE
# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
if ((uintmax_t)snmp_sess->securityAuthProtoLen > UINT_MAX) {
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) )
# pragma GCC diagnostic pop
#endif
fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %zu",
snmp_sess->securityAuthProtoLen);
return 0;
}
2012-01-24 10:22:33 +00:00
if ((*nut_generate_Ku)(snmp_sess->securityAuthProto,
2022-06-29 10:37:36 +00:00
(u_int)snmp_sess->securityAuthProtoLen,
(unsigned char *) sec->authPassword,
2011-09-29 18:14:46 +00:00
strlen(sec->authPassword),
snmp_sess->securityAuthKey,
&snmp_sess->securityAuthKeyLen)
2022-06-29 10:37:36 +00:00
!= SNMPERR_SUCCESS
) {
fprintf(stderr,
"Error generating Ku from "
"authentication pass phrase\n");
return 0;
2011-09-29 18:14:46 +00:00
}
/* Everything is ready for AUTHNOPRIV */
2022-06-29 10:37:36 +00:00
if (snmp_sess->securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV) {
2011-09-29 18:14:46 +00:00
return 1;
}
2022-06-29 10:37:36 +00:00
#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol
2011-09-29 18:14:46 +00:00
/* default to DES */
2022-06-29 10:37:36 +00:00
snmp_sess->securityPrivProto = nut_usmDESPrivProtocol;
2011-09-29 18:14:46 +00:00
snmp_sess->securityPrivProtoLen =
2022-06-29 10:37:36 +00:00
sizeof(usmDESPrivProtocol)/sizeof(oid);
#endif
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
if (sec->privProtocol) {
#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol
2011-09-29 18:14:46 +00:00
if (strcmp(sec->privProtocol, "AES") == 0) {
2022-06-29 10:37:36 +00:00
snmp_sess->securityPrivProto = nut_usmAESPrivProtocol;
snmp_sess->securityPrivProtoLen =
sizeof(usmAESPrivProtocol)/
2012-01-24 10:22:33 +00:00
sizeof(oid);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
else
#endif
#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04
# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol
if (strcmp(sec->privProtocol, "AES192") == 0) {
snmp_sess->securityPrivProto = nut_usmAES192PrivProtocol;
snmp_sess->securityPrivProtoLen =
sizeof(usmAES192PrivProtocol)/
sizeof(oid);
}
else
# endif
# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol
if (strcmp(sec->privProtocol, "AES256") == 0) {
snmp_sess->securityPrivProto = nut_usmAES256PrivProtocol;
snmp_sess->securityPrivProtoLen =
sizeof(usmAES256PrivProtocol)/
sizeof(oid);
}
else
# endif
#endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */
#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol
if (strcmp(sec->privProtocol, "DES") != 0) {
#else
{
#endif
fprintf(stderr,
"Bad SNMPv3 privProtocol: %s\n",
sec->privProtocol);
2011-09-29 18:14:46 +00:00
return 0;
}
}
/* set the private key to a MD5/SHA hashed version of
* our passphrase (must be at least 8 characters long) */
snmp_sess->securityPrivKeyLen = USM_PRIV_KU_LEN;
2022-06-29 10:37:36 +00:00
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) )
# pragma GCC diagnostic push
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE
# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
if ((uintmax_t)snmp_sess->securityAuthProtoLen > UINT_MAX) {
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) )
# pragma GCC diagnostic pop
#endif
fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %zu",
snmp_sess->securityAuthProtoLen);
return 0;
}
2012-01-24 10:22:33 +00:00
if ((*nut_generate_Ku)(snmp_sess->securityAuthProto,
2022-06-29 10:37:36 +00:00
(u_int)snmp_sess->securityAuthProtoLen,
(unsigned char *) sec->privPassword,
2011-09-29 18:14:46 +00:00
strlen(sec->privPassword),
snmp_sess->securityPrivKey,
&snmp_sess->securityPrivKeyLen)
2022-06-29 10:37:36 +00:00
!= SNMPERR_SUCCESS
) {
fprintf(stderr,
"Error generating Ku from "
"private pass phrase\n");
return 0;
2011-09-29 18:14:46 +00:00
}
}
return 1;
}
static void * try_SysOID(void * arg)
{
struct snmp_session snmp_sess;
void * handle;
2012-01-24 10:22:33 +00:00
struct snmp_pdu *pdu, *response = NULL, *resp = NULL;
2022-06-29 10:37:36 +00:00
oid name[MAX_OID_LEN];
size_t name_len = MAX_OID_LEN;
2011-09-29 18:14:46 +00:00
nutscan_snmp_t * sec = (nutscan_snmp_t *)arg;
int index = 0;
2022-06-29 10:37:36 +00:00
char *mib_found = NULL;
upsdebugx(2, "Entering %s for %s", __func__, sec->peername);
2011-09-29 18:14:46 +00:00
/* Initialize session */
2022-06-29 10:37:36 +00:00
if (!init_session(&snmp_sess, sec)) {
2011-09-29 18:14:46 +00:00
goto try_SysOID_free;
}
2022-06-29 10:37:36 +00:00
2011-09-29 18:14:46 +00:00
snmp_sess.retries = 0;
2022-06-29 10:37:36 +00:00
/* netsnmp timeout is accounted in uS, but typed as long
* and not useconds_t (which is at most long per POSIX)
*/
snmp_sess.timeout = (long)g_usec_timeout;
2011-09-29 18:14:46 +00:00
/* Open the session */
2012-01-24 10:22:33 +00:00
handle = (*nut_snmp_sess_open)(&snmp_sess); /* establish the session */
2011-09-29 18:14:46 +00:00
if (handle == NULL) {
2022-06-29 10:37:36 +00:00
upsdebugx(2,
"Failed to open SNMP session for %s",
2011-09-29 18:14:46 +00:00
sec->peername);
goto try_SysOID_free;
}
/* create and send request. */
2012-01-24 10:22:33 +00:00
if (!(*nut_snmp_parse_oid)(SysOID, name, &name_len)) {
2022-06-29 10:37:36 +00:00
upsdebugx(2,
"SNMP errors for %s: %s",
sec->peername,
(*nut_snmp_api_errstring)((*nut_snmp_errno)));
2012-01-24 10:22:33 +00:00
(*nut_snmp_sess_close)(handle);
2011-09-29 18:14:46 +00:00
goto try_SysOID_free;
}
2012-01-24 10:22:33 +00:00
pdu = (*nut_snmp_pdu_create)(SNMP_MSG_GET);
2011-09-29 18:14:46 +00:00
if (pdu == NULL) {
2022-06-29 10:37:36 +00:00
fprintf(stderr, "Not enough memory\n");
2012-01-24 10:22:33 +00:00
(*nut_snmp_sess_close)(handle);
2011-09-29 18:14:46 +00:00
goto try_SysOID_free;
}
2012-01-24 10:22:33 +00:00
(*nut_snmp_add_null_var)(pdu, name, name_len);
2011-09-29 18:14:46 +00:00
2012-01-24 10:22:33 +00:00
(*nut_snmp_sess_synch_response)(handle,
2011-09-29 18:14:46 +00:00
pdu, &response);
if (response) {
sec->handle = handle;
/* SNMP device found */
/* SysOID is supposed to give the required MIB. */
/* Check if the received OID match with a known sysOID */
2022-06-29 10:37:36 +00:00
if (response->variables != NULL &&
response->variables->val.objid != NULL
) {
while (snmp_device_table[index].mib != NULL) {
if (snmp_device_table[index].sysoid == NULL) {
2011-09-29 18:14:46 +00:00
index++;
continue;
}
name_len = MAX_OID_LEN;
2022-06-29 10:37:36 +00:00
2012-01-24 10:22:33 +00:00
if (!(*nut_snmp_parse_oid)(
2011-09-29 18:14:46 +00:00
snmp_device_table[index].sysoid,
2022-06-29 10:37:36 +00:00
name, &name_len)
) {
2011-09-29 18:14:46 +00:00
index++;
continue;
}
2022-06-29 10:37:36 +00:00
if ((*nut_snmp_oid_compare)(
2011-09-29 18:14:46 +00:00
response->variables->val.objid,
response->variables->val_len/sizeof(oid),
2022-06-29 10:37:36 +00:00
name, name_len) == 0
) {
/* we have found a relevant sysoid */
/* add mib if no complementary oid is present */
/* FIXME: No desc defined when add device */
if (snmp_device_table[index].oid == NULL
|| snmp_device_table[index].oid[0] == '\0'
) {
scan_snmp_add_device(sec, NULL, snmp_device_table[index].mib);
mib_found = snmp_device_table[index].sysoid;
}
/* else test complementary oid before adding mib */
else {
resp = scan_snmp_get_oid(
snmp_device_table[index].oid,
handle);
if (resp != NULL) {
scan_snmp_add_device(sec, resp, snmp_device_table[index].mib);
mib_found = snmp_device_table[index].mib;
(*nut_snmp_free_pdu)(resp);
}
2012-01-24 10:22:33 +00:00
}
2011-09-29 18:14:46 +00:00
}
index++;
}
}
2022-06-29 10:37:36 +00:00
/* try a list of known OID, if no device was found otherwise */
if (mib_found == NULL)
try_all_oid(sec, mib_found);
2011-09-29 18:14:46 +00:00
2012-01-24 10:22:33 +00:00
(*nut_snmp_free_pdu)(response);
2011-09-29 18:14:46 +00:00
response = NULL;
}
2012-01-24 10:22:33 +00:00
(*nut_snmp_sess_close)(handle);
2011-09-29 18:14:46 +00:00
try_SysOID_free:
2022-06-29 10:37:36 +00:00
if (sec->peername) {
2011-09-29 18:14:46 +00:00
free(sec->peername);
}
free(sec);
return NULL;
}
2022-06-29 10:37:36 +00:00
nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip,
useconds_t usec_timeout, nutscan_snmp_t * sec)
2011-09-29 18:14:46 +00:00
{
2022-06-29 10:37:36 +00:00
bool_t pass = TRUE; /* Track that we may spawn a scanning thread */
2011-09-29 18:14:46 +00:00
nutscan_snmp_t * tmp_sec;
nutscan_ip_iter_t ip;
char * ip_str = NULL;
#ifdef HAVE_PTHREAD
2022-06-29 10:37:36 +00:00
# ifdef HAVE_SEMAPHORE
sem_t * semaphore = nutscan_semaphore();
sem_t semaphore_scantype_inst;
sem_t * semaphore_scantype = &semaphore_scantype_inst;
# endif /* HAVE_SEMAPHORE */
2011-09-29 18:14:46 +00:00
pthread_t thread;
2022-06-29 10:37:36 +00:00
nutscan_thread_t * thread_array = NULL;
size_t thread_count = 0, i;
# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE)
size_t max_threads_scantype = max_threads_netsnmp;
# endif
pthread_mutex_init(&dev_mutex, NULL);
# ifdef HAVE_SEMAPHORE
if (max_threads_scantype > 0) {
if (SIZE_MAX > UINT_MAX && max_threads_scantype > UINT_MAX) {
upsdebugx(1,
"WARNING: %s: Limiting max_threads_scantype to range acceptable for sem_init()",
__func__);
max_threads_scantype = UINT_MAX - 1;
}
sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype);
}
# endif /* HAVE_SEMAPHORE */
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
#endif /* HAVE_PTHREAD */
2012-01-24 10:22:33 +00:00
2022-06-29 10:37:36 +00:00
if (!nutscan_avail_snmp) {
return NULL;
}
2012-01-24 10:22:33 +00:00
2011-09-29 18:14:46 +00:00
g_usec_timeout = usec_timeout;
2022-06-29 10:37:36 +00:00
/* Force numeric OIDs resolution (ie, do not resolve to textual names)
* This is mostly for the convenience of debug output */
if (nut_snmp_out_toggle_options("n") != NULL) {
upsdebugx(1, "Failed to enable numeric OIDs resolution");
}
2011-09-29 18:14:46 +00:00
/* Initialize the SNMP library */
2012-01-24 10:22:33 +00:00
(*nut_init_snmp)("nut-scanner");
2011-09-29 18:14:46 +00:00
ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip);
2022-06-29 10:37:36 +00:00
while (ip_str != NULL) {
2011-09-29 18:14:46 +00:00
#ifdef HAVE_PTHREAD
2022-06-29 10:37:36 +00:00
/* NOTE: With many enough targets to scan, this can crash
* by spawning too many children; add a limit and loop to
* "reap" some already done with their work. And probably
* account them in thread_array[] as something to not wait
* for below in pthread_join()...
*/
# ifdef HAVE_SEMAPHORE
/* Just wait for someone to free a semaphored slot,
* if none are available, and then/otherwise grab one
*/
if (thread_array == NULL) {
/* Starting point, or after a wait to complete
* all earlier runners */
if (max_threads_scantype > 0)
sem_wait(semaphore_scantype);
sem_wait(semaphore);
pass = TRUE;
} else {
pass = ((max_threads_scantype == 0 || sem_trywait(semaphore_scantype) == 0) &&
sem_trywait(semaphore) == 0);
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
# else
# ifdef HAVE_PTHREAD_TRYJOIN
/* A somewhat naive and brute-force solution for
* systems without a semaphore.h. This may suffer
* some off-by-one errors, using a few more threads
* than intended (if we race a bit at the wrong time,
* probably up to one per enabled scanner routine).
*/
/* TOTHINK: Should there be a threadcount_mutex when
* we just read the value in if() and while() below?
* At worst we would overflow the limit a bit due to
* other protocol scanners...
*/
if (curr_threads >= max_threads
|| (curr_threads >= max_threads_scantype && max_threads_scantype > 0)
) {
upsdebugx(2, "%s: already running %zu scanning threads "
"(launched overall: %zu), "
"waiting until some would finish",
__func__, curr_threads, thread_count);
while (curr_threads >= max_threads
|| (curr_threads >= max_threads_scantype && max_threads_scantype > 0)
) {
for (i = 0; i < thread_count ; i++) {
int ret;
if (!thread_array[i].active) continue;
pthread_mutex_lock(&threadcount_mutex);
upsdebugx(3, "%s: Trying to join thread #%i...", __func__, i);
ret = pthread_tryjoin_np(thread_array[i].thread, NULL);
switch (ret) {
case ESRCH: /* No thread with the ID thread could be found - already "joined"? */
upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i);
break;
case 0: /* thread exited */
if (curr_threads > 0) {
curr_threads --;
upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i);
} else {
/* threadcount_mutex fault? */
upsdebugx(0, "WARNING: %s: Accounting of thread count "
"says we are already at 0", __func__);
}
thread_array[i].active = FALSE;
break;
case EBUSY: /* actively running */
upsdebugx(6, "%s: thread #%zu still busy (%i)",
__func__, i, ret);
break;
case EDEADLK: /* Errors with thread interactions... bail out? */
case EINVAL: /* Errors with thread interactions... bail out? */
default: /* new pthreads abilities? */
upsdebugx(5, "%s: thread #%zu reported code %i",
__func__, i, ret);
break;
}
pthread_mutex_unlock(&threadcount_mutex);
}
if (curr_threads >= max_threads
|| (curr_threads >= max_threads_scantype && max_threads_scantype > 0)
) {
usleep (10000); /* microSec's, so 0.01s here */
}
}
upsdebugx(2, "%s: proceeding with scan", __func__);
}
/* NOTE: No change to default "pass" in this ifdef:
* if we got to this line, we have a slot to use */
# endif /* HAVE_PTHREAD_TRYJOIN */
# endif /* HAVE_SEMAPHORE */
#endif /* HAVE_PTHREAD */
if (pass) {
tmp_sec = malloc(sizeof(nutscan_snmp_t));
memcpy(tmp_sec, sec, sizeof(nutscan_snmp_t));
tmp_sec->peername = ip_str;
#ifdef HAVE_PTHREAD
if (pthread_create(&thread, NULL, try_SysOID, (void*)tmp_sec) == 0) {
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_lock(&threadcount_mutex);
curr_threads++;
# endif /* HAVE_PTHREAD_TRYJOIN */
thread_count++;
nutscan_thread_t *new_thread_array = realloc(thread_array,
thread_count * sizeof(nutscan_thread_t));
if (new_thread_array == NULL) {
upsdebugx(1, "%s: Failed to realloc thread array", __func__);
break;
}
else {
thread_array = new_thread_array;
}
thread_array[thread_count - 1].thread = thread;
thread_array[thread_count - 1].active = TRUE;
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_unlock(&threadcount_mutex);
# endif /* HAVE_PTHREAD_TRYJOIN */
}
#else /* not HAVE_PTHREAD */
try_SysOID((void *)tmp_sec);
#endif /* if HAVE_PTHREAD */
/* free(ip_str); */ /* Do not free() here - seems to cause a double-free instead */
ip_str = nutscan_ip_iter_inc(&ip);
/* free(tmp_sec); */
} else { /* if not pass -- all slots busy */
#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
/* Wait for all current scans to complete */
if (thread_array != NULL) {
upsdebugx (2, "%s: Running too many scanning threads, "
"waiting until older ones would finish",
__func__);
for (i = 0; i < thread_count ; i++) {
int ret;
if (!thread_array[i].active) {
/* Probably should not get here,
* but handle it just in case */
upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active",
__func__, i);
sem_post(semaphore);
if (max_threads_scantype > 0)
sem_post(semaphore_scantype);
continue;
}
thread_array[i].active = FALSE;
ret = pthread_join(thread_array[i].thread, NULL);
if (ret != 0) {
upsdebugx(0, "WARNING: %s: Midway clean-up: pthread_join() returned code %i",
__func__, ret);
}
sem_post(semaphore);
if (max_threads_scantype > 0)
sem_post(semaphore_scantype);
}
thread_count = 0;
free(thread_array);
thread_array = NULL;
}
# else
# ifdef HAVE_PTHREAD_TRYJOIN
/* TODO: Move the wait-loop for TRYJOIN here? */
# endif /* HAVE_PTHREAD_TRYJOIN */
# endif /* HAVE_SEMAPHORE */
#endif /* HAVE_PTHREAD */
} /* if: could we "pass" or not? */
} /* while */
2011-09-29 18:14:46 +00:00
#ifdef HAVE_PTHREAD
2022-06-29 10:37:36 +00:00
if (thread_array != NULL) {
upsdebugx(2, "%s: all planned scans launched, waiting for threads to complete", __func__);
for (i = 0; i < thread_count; i++) {
int ret;
if (!thread_array[i].active) continue;
ret = pthread_join(thread_array[i].thread, NULL);
if (ret != 0) {
upsdebugx(0, "WARNING: %s: Clean-up: pthread_join() returned code %i",
__func__, ret);
}
thread_array[i].active = FALSE;
# ifdef HAVE_SEMAPHORE
sem_post(semaphore);
if (max_threads_scantype > 0)
sem_post(semaphore_scantype);
# else
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_lock(&threadcount_mutex);
if (curr_threads > 0) {
curr_threads --;
upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu",
__func__, i);
} else {
upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count "
"says we are already at 0", __func__);
}
pthread_mutex_unlock(&threadcount_mutex);
# endif /* HAVE_PTHREAD_TRYJOIN */
# endif /* HAVE_SEMAPHORE */
}
free(thread_array);
upsdebugx(2, "%s: all threads freed", __func__);
2011-09-29 18:14:46 +00:00
}
pthread_mutex_destroy(&dev_mutex);
2022-06-29 10:37:36 +00:00
# ifdef HAVE_SEMAPHORE
if (max_threads_scantype > 0)
sem_destroy(semaphore_scantype);
# endif /* HAVE_SEMAPHORE */
#endif /* HAVE_PTHREAD */
2016-07-18 00:11:41 +00:00
nutscan_device_t * result = nutscan_rewind_device(dev_ret);
dev_ret = NULL;
return result;
2011-09-29 18:14:46 +00:00
}
2022-06-29 10:37:36 +00:00
2012-01-24 10:22:33 +00:00
#else /* WITH_SNMP */
2022-06-29 10:37:36 +00:00
nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip,
useconds_t usec_timeout, nutscan_snmp_t * sec)
2012-01-24 10:22:33 +00:00
{
2022-06-29 10:37:36 +00:00
NUT_UNUSED_VARIABLE(start_ip);
NUT_UNUSED_VARIABLE(stop_ip);
NUT_UNUSED_VARIABLE(usec_timeout);
NUT_UNUSED_VARIABLE(sec);
2012-01-24 10:22:33 +00:00
return NULL;
}
2011-09-29 18:14:46 +00:00
2022-06-29 10:37:36 +00:00
#endif /* WITH_SNMP */