diff --git a/NEWS.adoc b/NEWS.adoc index 93f2909509..e674d73787 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -140,6 +140,9 @@ https://github.com/networkupstools/nut/milestone/11 + NOTE: Currently both the long and short names for `libupsclient` should be installed. + * fixed support for IPv6 addresses (passed in square brackets) for both + `-s` start/`-e` end command-line options, and for `-m cidr/mask` option. + [issue #2512, PR #2518] * newly added support to scan several IP addresses (single or ranges) with the same call, by repeating command-line options; also `-m auto{,4,6}` can be specified (once) to select IP (all, IPv4, IPv6) address ranges of diff --git a/clients/upsc.c b/clients/upsc.c index 71715f4731..80c9bd6591 100644 --- a/clients/upsc.c +++ b/clients/upsc.c @@ -2,6 +2,7 @@ Copyright (C) 1999 Russell Kroll Copyright (C) 2012 Arnaud Quette + Copyright (C) 2020-2024 Jim Klimov 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 @@ -217,10 +218,22 @@ static void clean_exit(void) int main(int argc, char **argv) { - int i; + int i = 0; uint16_t port; int varlist = 0, clientlist = 0, verbose = 0; const char *prog = xbasename(argv[0]); + char *s = NULL; + + /* NOTE: Caller must `export NUT_DEBUG_LEVEL` to see debugs for upsc + * and NUT methods called from it. This line aims to just initialize + * the subsystem, and set initial timestamp. Debugging the client is + * primarily of use to developers, so is not exposed via `-D` args. + */ + s = getenv("NUT_DEBUG_LEVEL"); + if (s && str_to_int(s, &i, 10) && i > 0) { + nut_debug_level = i; + } + upsdebugx(1, "Starting NUT client: %s", prog); while ((i = getopt(argc, argv, "+hlLcV")) != -1) { @@ -267,6 +280,8 @@ int main(int argc, char **argv) fatalx(EXIT_FAILURE, "Error: invalid UPS definition.\nRequired format: upsname[@hostname[:port]]"); } } + upsdebugx(1, "upsname='%s' hostname='%s' port='%" PRIu16 "'", + NUT_STRARG(upsname), NUT_STRARG(hostname), port); ups = xmalloc(sizeof(*ups)); @@ -275,18 +290,22 @@ int main(int argc, char **argv) } if (varlist) { + upsdebugx(1, "Calling list_upses()"); list_upses(verbose); exit(EXIT_SUCCESS); } if (clientlist) { + upsdebugx(1, "Calling list_clients()"); list_clients(upsname); exit(EXIT_SUCCESS); } if (argc > 1) { + upsdebugx(1, "Calling printvar(%s)", argv[1]); printvar(argv[1]); } else { + upsdebugx(1, "Calling list_vars()"); list_vars(); } diff --git a/clients/upsclient.c b/clients/upsclient.c index 74290cd26f..9c4f0424fd 100644 --- a/clients/upsclient.c +++ b/clients/upsclient.c @@ -3,6 +3,7 @@ Copyright (C) 2002 Russell Kroll 2008 Arjen de Korte + 2020 - 2024 Jim Klimov 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 @@ -1024,6 +1025,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags ups->fd = -1; if (!host) { + upslogx(LOG_WARNING, "%s: Host not found: '%s'", __func__, NUT_STRARG(host)); ups->upserror = UPSCLI_ERR_NOSUCHHOST; return -1; } @@ -1049,6 +1051,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags case EAI_AGAIN: continue; case EAI_NONAME: + upslogx(LOG_WARNING, "%s: Host not found: '%s'", __func__, NUT_STRARG(host)); ups->upserror = UPSCLI_ERR_NOSUCHHOST; return -1; case EAI_MEMORY: @@ -1207,7 +1210,7 @@ int upscli_tryconnect(UPSCONN_t *ups, const char *host, uint16_t port, int flags } else if (tryssl && ret == 0) { if (certverify != 0) { upslogx(LOG_NOTICE, "Can not connect to NUT server %s in SSL and " - "certificate is needed, disconnect", host); + "certificate is needed, disconnect", host); upscli_disconnect(ups); return -1; } @@ -1686,7 +1689,21 @@ int upscli_splitaddr(const char *buf, char **hostname, uint16_t *port) return -1; } + s = strchr(tmp, '@'); + + /* someone passed a "@hostname" string? */ + if (s) { + fprintf(stderr, "upscli_splitaddr: wrong call? " + "Got upsname@hostname[:port] string where " + "only hostname[:port] was expected: %s\n", buf); + /* let it pass, but probably fail later */ + } + if (*tmp == '[') { + /* NOTE: Brackets are required for colon-separated IPv6 + * addresses, to differentiate from a port number. For + * example, `[1234:5678]:3493` would seem right. + */ if (strchr(tmp, ']') == NULL) { fprintf(stderr, "upscli_splitaddr: missing closing bracket in [domain literal]\n"); return -1; diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index 400552c0fd..075589c152 100644 --- a/docs/man/nut-scanner.txt +++ b/docs/man/nut-scanner.txt @@ -136,6 +136,8 @@ Requests to scan many single IP addresses will take a while to complete, much longer than if they were a single range. This will be hopefully fixed in later releases. +NOTE: Colon-separated IPv6 addresses must be passed in square brackets. + *-t* | *--timeout* 'timeout':: Set the network timeout in seconds. Default timeout is 5 seconds. diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index aa7965236c..a445acc3d1 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -96,7 +96,7 @@ libnutscan_la_LDFLAGS += -version-info 2:5:2 # copies of "nut_debug_level" making fun of our debug-logging attempts. # One solution to tackle if needed for those cases would be to make some # dynamic/shared libnutcommon (etc.) -libnutscan_la_LDFLAGS += -export-symbols-regex '^(nutscan_|nut_debug_level|s_upsdebugx|fatalx|xcalloc|snprintfcat|max_threads|curr_threads|nut_report_config_flags|upsdebugx_report_search_paths|nut_prepare_search_paths)' +libnutscan_la_LDFLAGS += -export-symbols-regex '^(nutscan_|nut_debug_level|s_upsdebug|fatalx|xcalloc|snprintfcat|max_threads|curr_threads|nut_report_config_flags|upsdebugx_report_search_paths|nut_prepare_search_paths)' libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include \ $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 9628171ea8..2e664f6ce1 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -990,7 +990,7 @@ static void show_usage(void) printf(" -E, --eaton_serial : Scan serial Eaton devices (XCP, SHUT and Q1).\n"); -#if (defined HAVE_PTHREAD) && (defined HAVE_PTHREAD_TRYJOIN) +#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) ) printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (default: %" PRIuSIZE ").\n", max_threads); #else printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (not implemented in this build: no pthread support)\n"); @@ -1267,7 +1267,13 @@ int main(int argc, char *argv[]) end_ip = NULL; } - start_ip = strdup(optarg); + if (optarg[0] == '[' && optarg[strlen(optarg) - 1] == ']') { + start_ip = strdup(optarg + 1); + start_ip[strlen(start_ip) - 1] = '\0'; + } else { + start_ip = strdup(optarg); + } + if (end_ip != NULL) { /* Already we know two addresses, save them */ nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); @@ -1285,7 +1291,13 @@ int main(int argc, char *argv[]) end_ip = NULL; } - end_ip = strdup(optarg); + if (optarg[0] == '[' && optarg[strlen(optarg) - 1] == ']') { + end_ip = strdup(optarg + 1); + end_ip[strlen(end_ip) - 1] = '\0'; + } else { + end_ip = strdup(optarg); + } + if (start_ip != NULL) { /* Already we know two addresses, save them */ nutscan_add_ip_range(&ip_ranges_list, start_ip, end_ip); @@ -1549,6 +1561,15 @@ int main(int argc, char *argv[]) } #ifdef HAVE_PTHREAD +# if (defined HAVE_PTHREAD_TRYJOIN) && (defined HAVE_SEMAPHORE) + upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && HAVE_PTHREAD_TRYJOIN && HAVE_SEMAPHORE"); +# elif (defined HAVE_PTHREAD_TRYJOIN) + upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && HAVE_PTHREAD_TRYJOIN && !HAVE_SEMAPHORE"); +# elif (defined HAVE_SEMAPHORE) + upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && !HAVE_PTHREAD_TRYJOIN && HAVE_SEMAPHORE"); +# else + upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && !HAVE_PTHREAD_TRYJOIN && !HAVE_SEMAPHORE"); +# endif # ifdef HAVE_SEMAPHORE /* FIXME: Currently sem_init already done on nutscan-init for lib need. We need to destroy it before re-init. We currently can't change "sem value" @@ -1577,8 +1598,15 @@ int main(int argc, char *argv[]) "WARNING: Limiting max_threads to range acceptable for sem_init()\n\n"); max_threads = UINT_MAX - 1; } - sem_init(current_sem, 0, (unsigned int)max_threads); + + upsdebugx(1, "Parallel scan support: max_threads=%" PRIuSIZE, max_threads); + if (sem_init(current_sem, 0, (unsigned int)max_threads)) { + /* Show this one to end-users so they know */ + upsdebug_with_errno(0, "Parallel scan support: sem_init() failed"); + } # endif +#else + upsdebugx(1, "Parallel scan support: !HAVE_PTHREAD"); #endif /* HAVE_PTHREAD */ if (start_ip != NULL || end_ip != NULL) { diff --git a/tools/nut-scanner/nutscan-init.c b/tools/nut-scanner/nutscan-init.c index 940e04186d..b45aa42fdf 100644 --- a/tools/nut-scanner/nutscan-init.c +++ b/tools/nut-scanner/nutscan-init.c @@ -35,6 +35,7 @@ #include #include "nut-scan.h" #include "nut_platform.h" +#include "nut_stdint.h" #ifdef WIN32 # if defined HAVE_WINSOCK2_H && HAVE_WINSOCK2_H @@ -169,7 +170,12 @@ void nutscan_init(void) __func__); max_threads = UINT_MAX - 1; } - sem_init(&semaphore, 0, (unsigned int)max_threads); + + upsdebugx(1, "%s: Parallel scan support: max_threads=%" PRIuSIZE, + __func__, max_threads); + if (sem_init(&semaphore, 0, (unsigned int)max_threads)) { + upsdebug_with_errno(4, "%s: Parallel scan support: sem_init() failed", __func__); + } # endif # ifdef HAVE_PTHREAD_TRYJOIN diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 0df42ba33b..0f89e430e2 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -422,6 +422,7 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const return strdup(host); } else { /* IPv6 */ + size_t hlen; for (i = 0; i < 16; i++) { if (ip->start6.s6_addr[i] !=ip->stop6.s6_addr[i]) { if (ip->start6.s6_addr[i] > ip->stop6.s6_addr[i]) { @@ -431,9 +432,17 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const } } - if (ntop6(&ip->start6, host, sizeof(host)) != 0) { + /* IPv6 addresses must be in square brackets, + * for upsclient et al to differentiate them + * from the port number. + */ + host[0] = '['; + if (ntop6(&ip->start6, host + 1, sizeof(host) - 2) != 0) { return NULL; } + hlen = strlen(host); + host[hlen] = ']'; + host[hlen + 1] = '\0'; return strdup(host); } @@ -463,6 +472,8 @@ char * nutscan_ip_iter_inc(nutscan_ip_iter_t * ip) return strdup(host); } else { + size_t hlen; + /* Check if this is the last address to scan */ if (memcmp(&ip->start6.s6_addr, &ip->stop6.s6_addr, sizeof(ip->start6.s6_addr)) == 0) { @@ -470,9 +481,18 @@ char * nutscan_ip_iter_inc(nutscan_ip_iter_t * ip) } increment_IPv6(&ip->start6); - if (ntop6(&ip->start6, host, sizeof(host)) != 0) { + + /* IPv6 addresses must be in square brackets, + * for upsclient et al to differentiate them + * from the port number. + */ + host[0] = '['; + if (ntop6(&ip->start6, host + 1, sizeof(host) - 2) != 0) { return NULL; } + hlen = strlen(host); + host[hlen] = ']'; + host[hlen + 1] = '\0'; return strdup(host); } @@ -505,6 +525,12 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) *start_ip = NULL; *stop_ip = NULL; + if (!cidr) { + upsdebugx(0, "WARNING: %s: null cidr pointer was provided", + __func__); + return 0; + } + cidr_tok = strdup(cidr); first_ip = strdup(strtok_r(cidr_tok, "/", &saveptr)); if (first_ip == NULL) { @@ -517,10 +543,18 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) if (mask == NULL) { upsdebugx(0, "WARNING: %s failed to parse mask from cidr=%s (first_ip=%s)", __func__, cidr, first_ip); - free (first_ip); + free(first_ip); free(cidr_tok); return 0; } + + if (first_ip[0] == '[' && first_ip[strlen(first_ip) - 1] == ']') { + char *s = strdup(first_ip + 1); + s[strlen(s) - 1] = '\0'; + free(first_ip); + first_ip = s; + } + upsdebugx(5, "%s: parsed cidr=%s into first_ip=%s and mask=%s", __func__, cidr, first_ip, mask); diff --git a/tools/nut-scanner/scan_eaton_serial.c b/tools/nut-scanner/scan_eaton_serial.c index fad55f3e11..92997bdbb6 100644 --- a/tools/nut-scanner/scan_eaton_serial.c +++ b/tools/nut-scanner/scan_eaton_serial.c @@ -408,10 +408,15 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) char *current_port_name = NULL; char **serial_ports_list; int current_port_nb; + #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE sem_t * semaphore = nutscan_semaphore(); -# endif + /* TODO? Port semaphore_scantype / max_threads_scantype + * from sibling sources? We do not have that many serial + * ports to care much, usually... right? + */ +# endif /* HAVE_SEMAPHORE */ pthread_t thread; nutscan_thread_t * thread_array = NULL; size_t thread_count = 0, i; @@ -484,6 +489,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); + while (curr_threads >= max_threads) { for (i = 0; i < thread_count ; i++) { int ret; @@ -528,6 +534,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) } 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 */ diff --git a/tools/nut-scanner/scan_ipmi.c b/tools/nut-scanner/scan_ipmi.c index 14ca3576fc..2f60a25700 100644 --- a/tools/nut-scanner/scan_ipmi.c +++ b/tools/nut-scanner/scan_ipmi.c @@ -573,6 +573,8 @@ nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t } else { /* FIXME: also check against "localhost" and its IPv{4,6} */ + /* FIXME: Should the IPv6 address here be bracketed? + * Does our driver support the notation? */ sprintf(port_id, "id%x@%s", ipmi_id, IPaddr); } nut_dev->port = strdup(port_id); @@ -647,6 +649,10 @@ nutscan_device_t * nutscan_scan_ip_range_ipmi(nutscan_ip_range_list_t * irl, nut current_nut_dev = nutscan_scan_ipmi_device(NULL, NULL); } else { + /* TODO: Port HAVE_PTHREAD_TRYJOIN etc. from other files? + * Notably, the scans below currently are only sequential + * and so very slow (5 sec per IP timeout by default). + */ if (irl->ip_ranges_count == 1 && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index 4efc162ccf..1d77f05abd 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -199,8 +199,11 @@ static void * list_nut_devices(void * arg) /* NOTE: There is no driver by such name, in practice it could * be a dummy-ups relay, a clone driver, or part of upsmon config */ dev->driver = strdup(SCAN_NUT_DRIVERNAME); - /* +1+1 is for '@' character and terminating 0 */ - buf_size = strlen(answer[1]) + strlen(hostname) + 1 + 1; + /* +1+1 is for '@' character and terminating 0, + * and the other +1+1 is for possible '[' and ']' + * around the host name: + */ + buf_size = strlen(answer[1]) + strlen(hostname) + 1 + 1 + 1 + 1; if (port != PORT) { /* colon and up to 5 digits */ buf_size += 6; @@ -209,13 +212,31 @@ static void * list_nut_devices(void * arg) dev->port = malloc(buf_size); if (dev->port) { + /* Check if IPv6 and needs brackets */ + char *hostname_colon = strchr(hostname, ':'); + + if (hostname_colon && *hostname_colon == '\0') + hostname_colon = NULL; + if (*hostname == '[') + hostname_colon = NULL; + if (port != PORT) { - snprintf(dev->port, buf_size, "%s@%s:%" PRIu16, - answer[1], hostname, port); + if (hostname_colon) { + snprintf(dev->port, buf_size, "%s@[%s]:%" PRIu16, + answer[1], hostname, port); + } else { + snprintf(dev->port, buf_size, "%s@%s:%" PRIu16, + answer[1], hostname, port); + } } else { /* Standard port, not suffixed */ - snprintf(dev->port, buf_size, "%s@%s", - answer[1], hostname); + if (hostname_colon) { + snprintf(dev->port, buf_size, "%s@[%s]", + answer[1], hostname); + } else { + snprintf(dev->port, buf_size, "%s@%s", + answer[1], hostname); + } } #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); @@ -278,7 +299,15 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons # if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) size_t max_threads_scantype = max_threads_oldnut; # endif +#endif /* HAVE_PTHREAD */ +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif + +#ifdef HAVE_PTHREAD pthread_mutex_init(&dev_mutex, NULL); # ifdef HAVE_SEMAPHORE @@ -306,7 +335,11 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons __func__); max_threads_scantype = UINT_MAX - 1; } - sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype); + + upsdebugx(4, "%s: sem_init() for %" PRIuSIZE " threads", __func__, max_threads_scantype); + if (sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype)) { + upsdebug_with_errno(4, "%s: sem_init() failed", __func__); + } } # endif /* HAVE_SEMAPHORE */ @@ -373,8 +406,21 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons sem_wait(semaphore); pass = TRUE; } else { - pass = ((max_threads_scantype == 0 || sem_trywait(semaphore_scantype) == 0) && - sem_trywait(semaphore) == 0); + /* If successful (the lock was acquired), + * sem_wait() and sem_trywait() will return 0. + * Otherwise, -1 is returned and errno is set, + * and the state of the semaphore is unchanged. + */ + int stwST = sem_trywait(semaphore_scantype), stwS = sem_trywait(semaphore); + pass = ((max_threads_scantype == 0 || stwST == 0) && stwS == 0); + upsdebugx(4, "%s: max_threads_scantype=%" PRIuSIZE + " curr_threads=%" PRIuSIZE + " thread_count=%" PRIuSIZE + " stwST=%d stwS=%d pass=%d", + __func__, max_threads_scantype, + curr_threads, thread_count, + stwST, stwS, pass + ); } # else # ifdef HAVE_PTHREAD_TRYJOIN @@ -397,6 +443,7 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); + while (curr_threads >= max_threads || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) ) { @@ -445,6 +492,7 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons } 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 */ @@ -509,9 +557,10 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons # ifdef HAVE_SEMAPHORE /* Wait for all current scans to complete */ if (thread_array != NULL) { - upsdebugx (2, "%s: Running too many scanning threads, " + upsdebugx (2, "%s: Running too many scanning threads (%" + PRIuSIZE "), " "waiting until older ones would finish", - __func__); + __func__, thread_count); for (i = 0; i < thread_count ; i++) { int ret; if (!thread_array[i].active) { @@ -540,7 +589,7 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons } # else # ifdef HAVE_PTHREAD_TRYJOIN - /* TODO: Move the wait-loop for TRYJOIN here? */ + /* TODO: Move the wait-loop for TRYJOIN here? */ # endif /* HAVE_PTHREAD_TRYJOIN */ # endif /* HAVE_SEMAPHORE */ #endif /* HAVE_PTHREAD */ diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 4c1673639c..466e141fd0 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -480,6 +480,8 @@ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response dev = nutscan_new_device(); dev->type = TYPE_SNMP; dev->driver = strdup("snmp-ups"); + /* FIXME: Should the IPv6 address here be bracketed? + * Does our driver support the notation? */ dev->port = strdup(session->peername); if (response != NULL) { buf = malloc (response->variables->val_len + 1); @@ -1047,20 +1049,28 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, nutscan_snmp_t * tmp_sec; nutscan_ip_range_list_iter_t ip; char * ip_str = NULL; + #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE sem_t * semaphore = nutscan_semaphore(); sem_t semaphore_scantype_inst; sem_t * semaphore_scantype = &semaphore_scantype_inst; # endif /* HAVE_SEMAPHORE */ - - pthread_t thread; + pthread_t thread; 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 +#endif /* HAVE_PTHREAD */ + +#ifdef WIN32 + WSADATA WSAdata; + WSAStartup(2,&WSAdata); + atexit((void(*)(void))WSACleanup); +#endif +#ifdef HAVE_PTHREAD pthread_mutex_init(&dev_mutex, NULL); # ifdef HAVE_SEMAPHORE @@ -1088,7 +1098,11 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, __func__); max_threads_scantype = UINT_MAX - 1; } - sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype); + + upsdebugx(4, "%s: sem_init() for %" PRIuSIZE " threads", __func__, max_threads_scantype); + if (sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype)) { + upsdebug_with_errno(4, "%s: sem_init() failed", __func__); + } } # endif /* HAVE_SEMAPHORE */ @@ -1149,8 +1163,21 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, sem_wait(semaphore); pass = TRUE; } else { - pass = ((max_threads_scantype == 0 || sem_trywait(semaphore_scantype) == 0) && - sem_trywait(semaphore) == 0); + /* If successful (the lock was acquired), + * sem_wait() and sem_trywait() will return 0. + * Otherwise, -1 is returned and errno is set, + * and the state of the semaphore is unchanged. + */ + int stwST = sem_trywait(semaphore_scantype), stwS = sem_trywait(semaphore); + pass = ((max_threads_scantype == 0 || stwST == 0) && stwS == 0); + upsdebugx(4, "%s: max_threads_scantype=%" PRIuSIZE + " curr_threads=%" PRIuSIZE + " thread_count=%" PRIuSIZE + " stwST=%d stwS=%d pass=%d", + __func__, max_threads_scantype, + curr_threads, thread_count, + stwST, stwS, pass + ); } # else # ifdef HAVE_PTHREAD_TRYJOIN @@ -1173,6 +1200,7 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, "(launched overall: %" PRIuSIZE "), " "waiting until some would finish", __func__, curr_threads, thread_count); + while (curr_threads >= max_threads || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) ) { @@ -1221,6 +1249,7 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, } 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 */ @@ -1268,9 +1297,10 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, # ifdef HAVE_SEMAPHORE /* Wait for all current scans to complete */ if (thread_array != NULL) { - upsdebugx (2, "%s: Running too many scanning threads, " + upsdebugx (2, "%s: Running too many scanning threads (%" + PRIuSIZE "), " "waiting until older ones would finish", - __func__); + __func__, thread_count); for (i = 0; i < thread_count ; i++) { int ret; if (!thread_array[i].active) { @@ -1299,7 +1329,7 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, } # else # ifdef HAVE_PTHREAD_TRYJOIN - /* TODO: Move the wait-loop for TRYJOIN here? */ + /* TODO: Move the wait-loop for TRYJOIN here? */ # endif /* HAVE_PTHREAD_TRYJOIN */ # endif /* HAVE_SEMAPHORE */ #endif /* HAVE_PTHREAD */ diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index 3212c3b590..0705b2df3b 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -345,6 +345,8 @@ static void * nutscan_scan_xml_http_generic(void * arg) if (parserFailed == 0) { nut_dev->driver = strdup("netxml-ups"); sprintf(buf, "http://%s", string); + /* FIXME: Should the IPv6 address here be bracketed? + * Does our driver support the notation? */ nut_dev->port = strdup(buf); upsdebugx(3, "nutscan_scan_xml_http_generic(): " @@ -441,41 +443,47 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, return NULL; } - if (irl == NULL || irl->ip_ranges == NULL) { + /* We assume the list is maintained by our methods, so should not have + * null addresses. But just in case - check for it a little tiny once. + */ + if (irl == NULL || irl->ip_ranges == NULL + || irl->ip_ranges->start_ip == NULL || irl->ip_ranges->end_ip == NULL + ) { upsdebugx(1, "%s: Scanning XML/HTTP bus using broadcast.", __func__); } else { - if (irl->ip_ranges_count == 1 - && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip - || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) - )) { - upsdebugx(1, "%s: Scanning XML/HTTP bus for single IP address: %s", - __func__, irl->ip_ranges->start_ip); - } else { - /* Iterate the range of IPs to scan */ - nutscan_ip_range_list_iter_t ip; - char * ip_str = NULL; + /* Iterate the one or a range of IPs to scan */ + nutscan_ip_range_list_iter_t ip; + char * ip_str = NULL; #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE - sem_t * semaphore = nutscan_semaphore(); - sem_t semaphore_scantype_inst; - sem_t * semaphore_scantype = &semaphore_scantype_inst; + sem_t * semaphore = nutscan_semaphore(); + sem_t semaphore_scantype_inst; + sem_t * semaphore_scantype = &semaphore_scantype_inst; # endif /* HAVE_SEMAPHORE */ - pthread_t thread; - nutscan_thread_t * thread_array = NULL; - size_t thread_count = 0, i; + pthread_t thread; + 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_netxml; + size_t max_threads_scantype = max_threads_netxml; # endif #endif + if (irl->ip_ranges_count == 1 + && (irl->ip_ranges->start_ip == irl->ip_ranges->end_ip + || !strcmp(irl->ip_ranges->start_ip, irl->ip_ranges->end_ip) + )) { + upsdebugx(1, "%s: Scanning XML/HTTP bus for single IP address: %s", + __func__, irl->ip_ranges->start_ip); + } else { upsdebugx(1, "%s: Scanning XML/HTTP bus for IP address range(s): %s", __func__, nutscan_stringify_ip_ranges(irl)); + } #ifdef HAVE_PTHREAD - pthread_mutex_init(&dev_mutex, NULL); + pthread_mutex_init(&dev_mutex, NULL); # ifdef HAVE_SEMAPHORE - if (max_threads_scantype > 0) { + if (max_threads_scantype > 0) { #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic push #endif @@ -486,264 +494,284 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" #endif - /* Different platforms, different sizes, none fits all... */ - if (SIZE_MAX > UINT_MAX && max_threads_scantype > UINT_MAX) { + /* Different platforms, different sizes, none fits all... */ + if (SIZE_MAX > UINT_MAX && max_threads_scantype > UINT_MAX) { #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic pop #endif - 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); + upsdebugx(1, + "WARNING: %s: Limiting max_threads_scantype to range acceptable for sem_init()", + __func__); + max_threads_scantype = UINT_MAX - 1; } + + upsdebugx(4, "%s: sem_init() for %" PRIuSIZE " threads", __func__, max_threads_scantype); + if (sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype)) { + upsdebug_with_errno(4, "%s: sem_init() failed", __func__); + } + } # endif /* HAVE_SEMAPHORE */ #endif /* HAVE_PTHREAD */ - ip_str = nutscan_ip_ranges_iter_init(&ip, irl); + ip_str = nutscan_ip_ranges_iter_init(&ip, irl); - while (ip_str != NULL) { + while (ip_str != NULL) { #ifdef HAVE_PTHREAD - /* 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()... - */ + /* 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 + /* 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 { + /* If successful (the lock was acquired), + * sem_wait() and sem_trywait() will return 0. + * Otherwise, -1 is returned and errno is set, + * and the state of the semaphore is unchanged. */ - 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); - } + int stwST = sem_trywait(semaphore_scantype), stwS = sem_trywait(semaphore); + pass = ((max_threads_scantype == 0 || stwST == 0) && stwS == 0); + upsdebugx(4, "%s: max_threads_scantype=%" PRIuSIZE + " curr_threads=%" PRIuSIZE + " thread_count=%" PRIuSIZE + " stwST=%d stwS=%d pass=%d", + __func__, max_threads_scantype, + curr_threads, thread_count, + stwST, stwS, pass + ); + } # 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 %" PRIuSIZE " scanning threads " - "(launched overall: %" PRIuSIZE "), " - "waiting until some would finish", - __func__, curr_threads, thread_count); - while (curr_threads >= max_threads - || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + /* 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) ) { - 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 #%" PRIuSIZE " joined earlier?", __func__, i); - break; - case 0: /* thread exited */ - if (curr_threads > 0) { - curr_threads --; - upsdebugx(4, "%s: Joined a finished thread #%" PRIuSIZE, __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 #%" PRIuSIZE " 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 #%" PRIuSIZE " reported code %i", - __func__, i, ret); - break; - } - pthread_mutex_unlock(&threadcount_mutex); - } + upsdebugx(2, "%s: already running %" PRIuSIZE " scanning threads " + "(launched overall: %" PRIuSIZE "), " + "waiting until some would finish", + __func__, curr_threads, thread_count); - if (curr_threads >= max_threads - || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + while (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) ) { - usleep (10000); /* microSec's, so 0.01s here */ + 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 #%" PRIuSIZE " joined earlier?", __func__, i); + break; + case 0: /* thread exited */ + if (curr_threads > 0) { + curr_threads --; + upsdebugx(4, "%s: Joined a finished thread #%" PRIuSIZE, __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 #%" PRIuSIZE " 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 #%" PRIuSIZE " 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 */ + 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_xml_t)); - if (tmp_sec == NULL) { - fprintf(stderr, - "Memory allocation error\n"); - return NULL; - } - memcpy(tmp_sec, sec, sizeof(nutscan_xml_t)); - tmp_sec->peername = ip_str; - if (tmp_sec->usec_timeout <= 0) { - tmp_sec->usec_timeout = usec_timeout; - } + if (pass) { + tmp_sec = malloc(sizeof(nutscan_xml_t)); + if (tmp_sec == NULL) { + fprintf(stderr, + "Memory allocation error\n"); + return NULL; + } + memcpy(tmp_sec, sec, sizeof(nutscan_xml_t)); + tmp_sec->peername = ip_str; + if (tmp_sec->usec_timeout <= 0) { + tmp_sec->usec_timeout = usec_timeout; + } #ifdef HAVE_PTHREAD - if (pthread_create(&thread, NULL, nutscan_scan_xml_http_generic, (void *)tmp_sec) == 0) { - nutscan_thread_t *new_thread_array; + if (pthread_create(&thread, NULL, nutscan_scan_xml_http_generic, (void *)tmp_sec) == 0) { + nutscan_thread_t *new_thread_array; # ifdef HAVE_PTHREAD_TRYJOIN - pthread_mutex_lock(&threadcount_mutex); - curr_threads++; + pthread_mutex_lock(&threadcount_mutex); + curr_threads++; # endif /* HAVE_PTHREAD_TRYJOIN */ - thread_count++; - 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; + thread_count++; + 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); + pthread_mutex_unlock(&threadcount_mutex); # endif /* HAVE_PTHREAD_TRYJOIN */ - } + } #else /* not HAVE_PTHREAD */ - nutscan_scan_xml_http_generic((void *)tmp_sec); + nutscan_scan_xml_http_generic((void *)tmp_sec); #endif /* if HAVE_PTHREAD */ -/* free(ip_str); */ /* One of these free()s seems to cause a double-free instead */ - ip_str = nutscan_ip_ranges_iter_inc(&ip); -/* free(tmp_sec); */ - } else { /* if not pass -- all slots busy */ +/* free(ip_str); */ /* One of these free()s seems to cause a double-free instead */ + ip_str = nutscan_ip_ranges_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 %" PRIuSIZE " 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); - } + /* Wait for all current scans to complete */ + if (thread_array != NULL) { + upsdebugx (2, "%s: Running too many scanning threads (%" + PRIuSIZE "), " + "waiting until older ones would finish", + __func__, thread_count); + 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 %" PRIuSIZE " to be not active", + __func__, i); sem_post(semaphore); if (max_threads_scantype > 0) sem_post(semaphore_scantype); - } - thread_count = 0; - free(thread_array); - thread_array = NULL; + 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 */ + } /* if: could we "pass" or not? */ + } /* while */ #ifdef HAVE_PTHREAD - 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 != 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; + 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; + 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); + 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 #%" PRIuSIZE, - __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); + pthread_mutex_lock(&threadcount_mutex); + if (curr_threads > 0) { + curr_threads --; + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%" PRIuSIZE, + __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__); } - pthread_mutex_destroy(&dev_mutex); + free(thread_array); + upsdebugx(2, "%s: all threads freed", __func__); + } + pthread_mutex_destroy(&dev_mutex); # ifdef HAVE_SEMAPHORE - if (max_threads_scantype > 0) - sem_destroy(semaphore_scantype); + if (max_threads_scantype > 0) + sem_destroy(semaphore_scantype); # endif /* HAVE_SEMAPHORE */ #endif /* HAVE_PTHREAD */ - result = nutscan_rewind_device(dev_ret); - dev_ret = NULL; - return result; - } - } + result = nutscan_rewind_device(dev_ret); + dev_ret = NULL; + return result; + } /* end of: scan range of 1+ IP address(es), maybe in parallel */ + /* both start_ip == end_ip == NULL, scan broadcast */ tmp_sec = malloc(sizeof(nutscan_xml_t)); if (tmp_sec == NULL) { fprintf(stderr, @@ -755,6 +783,7 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, if (irl == NULL || irl->ip_ranges == NULL || irl->ip_ranges->start_ip == NULL) { tmp_sec->peername = NULL; } else { + /* Legacy code path for single-IP scan; should not get here */ tmp_sec->peername = strdup(irl->ip_ranges->start_ip); }