diff --git a/NEWS.adoc b/NEWS.adoc index 82b507c397..231fabb543 100644 --- a/NEWS.adoc +++ b/NEWS.adoc @@ -140,6 +140,13 @@ https://github.com/networkupstools/nut/milestone/11 + NOTE: Currently both the long and short names for `libupsclient` should be installed. + * 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 + configured local network interfaces (currently not implemented for WIN32). + [issue #2244, PR #2509, PR #2513] + * bumped version of `libnutscan` to 2.5.2, it now includes a few more + methods and symbols from `libcommon`. [issue #2244, PR #2509] - common code: * introduced a `NUT_DEBUG_SYSLOG` environment variable to tweak activation diff --git a/docs/man/nut-scanner.txt b/docs/man/nut-scanner.txt index dca9b946e3..fe66ff113e 100644 --- a/docs/man/nut-scanner.txt +++ b/docs/man/nut-scanner.txt @@ -78,38 +78,59 @@ an 'end IP'. See specific SNMP OPTIONS for community and security settings. *-M* | *--xml_scan*:: Scan XML/HTTP devices. Can broadcast a network message on the current network -interfaces to retrieve XML/HTTP capable devices. No IP required in this mode. +interface(s) to retrieve XML/HTTP capable devices. No IP required in this mode. +If IP address ranges are specified, they would be scanned instead of a broadcast. *-O* | *--oldnut_scan*:: Scan NUT devices (i.e. upsd daemon) on IP ranging from 'start IP' to 'end IP'. *-n* | *--nut_simulation_scan*:: -Scan NUT simulated devices (.dev files in $CONFPATH). +Scan NUT simulated devices (`.dev` files in `$NUT_CONFPATH`). *-A* | *--avahi_scan*:: -Scan NUT servers using Avahi request on the current network interfaces. -No IP required. +Scan NUT servers using Avahi request on the current network interface(s). +No IP address options are required or used. *-I* | *--ipmi_scan*:: Scan NUT compatible power supplies available via IPMI on the current host, -or over the network. +or over the network if IP address ranges are specified. *-E* | *--eaton_serial* 'serial ports':: Scan Eaton devices (XCP and SHUT) available via serial bus on the current host. This option must be requested explicitly, even for a complete scan. 'serial ports' can be expressed in various forms: - ++ - 'auto' to scan all serial ports. - a single character indicating a port number ('0' (zero) for /dev/ttyS0 and -/dev/ttyUSB0 on Linux, '1' for COM1 on Windows, 'a' for /dev/ttya on Solaris...) + /dev/ttyUSB0 on Linux, '1' for COM1 on Windows, 'a' for /dev/ttya on Solaris...) - a range of N characters, hyphen separated, describing the range of -ports using 'X-Y', where X and Y are characters referring to the port number. + ports using 'X-Y', where X and Y are characters referring to the port number. - a single port name. - a list of ports name, coma separated, like '/dev/ttyS1,/dev/ttyS4'. NETWORK OPTIONS --------------- +NOTE: The networked buses (such as SNMP, NetXML, IPMI and "Old NUT") allow to +specify several IP (IPv4 or IPv6) address ranges, down to individual single +IP addresses. Normally a new range is specified by a set of one `-s` and one +`-e` options following each other (in any order). Lone or consecutive `-s` or +`-e` options present on the command line would translate to single-IP queries. +Also a `-m` option squashed between two `-s` and `-e` options would be a new +range, turning those two into single-IP queries. This feature does not by +itself recombine "neighboring" addresses into one range, nor even check for +duplicate or overlapping specifications. ++ +Also note that some buses require IP address(es) to scan, and others have a +different behavior when exactly no addresses are specified (it is not currently +possible to mix the two behaviors in one invocation of the `nut-scanner` tool). ++ +Finally note that currently even if multi-threaded support is available, each +range specification is a separate fan-out of queries constrained by the timeout. +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. + *-t* | *--timeout* 'timeout':: Set the network timeout in seconds. Default timeout is 5 seconds. @@ -124,7 +145,12 @@ If this parameter is omitted, only the 'start IP' is scanned. If 'end IP' is less than 'start IP', both parameters are internally permuted. *-m* | *--mask_cidr* 'IP address/mask':: -Set a range of IP using CIDR notation. +Set a range of IP addresses by using CIDR notation. ++ +A special form `-m auto` allows `nut-scanner` to detect local IP address(es) +and scan corresponding subnet(s) on supported platforms, and `-m auto4` or +`-m auto6` limits the selected addresses to IPv4 and IPv6 respectively. Only +the first "auto*" request would be honoured, others ignored with a warning. NUT DEVICE OPTION ----------------- diff --git a/docs/nut.dict b/docs/nut.dict index 57888a9c05..559bbc115d 100644 --- a/docs/nut.dict +++ b/docs/nut.dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 3177 utf-8 +personal_ws-1.1 en 3178 utf-8 AAC AAS ABI @@ -754,6 +754,7 @@ NetInvent NetPro NetServer NetUps +NetXML Netman NetworkUPSTools Neus diff --git a/drivers/libshut.c b/drivers/libshut.c index 83b0cbe7c4..8d8991613d 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -376,15 +376,10 @@ static int libshut_open( usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen)) { int ret, res; - /* Below we cast this buffer as sometimes containing entried of type - * "struct device_descriptor_s" or "struct my_hid_descriptor". - * Currently both of these are sized "2", and I don't see a way - * to require a "max()" of such sizes to align for generally. - */ usb_ctrl_char buf[20] __attribute__((aligned(4))); char string[MAX_STRING_SIZE]; - struct my_hid_descriptor *desc; - struct device_descriptor_s *dev_descriptor; + struct my_hid_descriptor desc_buf, *desc = &desc_buf; + struct device_descriptor_s dev_descriptor_buf, *dev_descriptor = &dev_descriptor_buf; /* report descriptor */ usb_ctrl_char rdbuf[MAX_REPORT_SIZE]; @@ -470,21 +465,7 @@ static int libshut_open( } /* Get DEVICE descriptor */ -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wcast-align" -#endif - dev_descriptor = (struct device_descriptor_s *)buf; -#ifdef __clang__ -# pragma clang diagnostic pop -#endif -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif + memcpy(dev_descriptor, buf, sizeof(struct device_descriptor_s)); res = shut_get_descriptor(*arg_upsfd, USB_DT_DEVICE, 0, buf, USB_DT_DEVICE_SIZE); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_DEVICE << 8) + 0, 0, buf, 0x9, SHUT_TIMEOUT); */ @@ -587,21 +568,7 @@ static int libshut_open( } /* Get HID descriptor */ -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wcast-align" -#endif - desc = (struct my_hid_descriptor *)buf; -#ifdef __clang__ -# pragma clang diagnostic pop -#endif -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif + memcpy(desc, buf, sizeof(struct my_hid_descriptor)); res = shut_get_descriptor(*arg_upsfd, USB_DT_HID, hid_desc_index, buf, 0x9); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_HID << 8) + 0, 0, buf, 0x9, SHUT_TIMEOUT); */ diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 014cb4e8bc..480c104762 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -83,7 +83,7 @@ endif HAVE_WINDOWS # object .so names would differ) # # libnutscan version information -libnutscan_la_LDFLAGS += -version-info 2:5:1 +libnutscan_la_LDFLAGS += -version-info 2:5:2 # libnutscan exported symbols regex # WARNING: Since the library includes parts of libcommon (as much as needed @@ -94,7 +94,7 @@ libnutscan_la_LDFLAGS += -version-info 2:5:1 # 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|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_upsdebugx|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 6a8a8f0ebb..0c3071d146 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -36,6 +36,31 @@ #include #include +/* Headers related to getifaddrs() for `-m auto` on different platforms */ +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#include +#ifndef WIN32 +# include +# include +# include +# include +# include +# include +#else +/* Those 2 files for support of getaddrinfo, getnameinfo and freeaddrinfo + on Windows 2000 and older versions */ +/* // TODO: complete "-m auto" support +# include +# include +# ifndef AI_NUMERICSERV +# define AI_NUMERICSERV NI_NUMERICSERV +# endif +# include "wincompat.h" +*/ +#endif + #ifdef HAVE_PTHREAD # include # ifdef HAVE_SEMAPHORE @@ -59,8 +84,6 @@ #include "nut-scan.h" -#define DEFAULT_TIMEOUT 5 - #define ERR_BAD_OPTION (-1) static const char optstring[] = "?ht:T:s:e:E:c:l:u:W:X:w:x:p:b:B:d:L:CUSMOAm:QnNPqIVaD"; @@ -110,12 +133,87 @@ static const struct option longopts[] = { static nutscan_device_t *dev[TYPE_END]; static useconds_t timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; /* in usec */ -static char * start_ip = NULL; -static char * end_ip = NULL; static char * port = NULL; static char * serial_ports = NULL; static int cli_link_detail_level = -1; +/* Track requested IP ranges (from CLI or auto-discovery) */ +typedef struct ip_range_s { + char * start_ip; + char * end_ip; + struct ip_range_s * next; +} ip_range_t; +static ip_range_t * ip_ranges = NULL; +static ip_range_t * ip_ranges_last = NULL; +static size_t ip_ranges_count = 0; + +static size_t add_ip_range(char * start_ip, char * end_ip) +{ + ip_range_t *p; + + if (!start_ip && !end_ip) { + upsdebugx(5, "%s: skip, no addresses were provided", __func__); + return ip_ranges_count; + } + + if (start_ip == NULL) { + upsdebugx(5, "%s: only end address was provided, setting start to same: %s", + __func__, end_ip); + start_ip = end_ip; + } + if (end_ip == NULL) { + upsdebugx(5, "%s: only start address was provided, setting end to same: %s", + __func__, start_ip); + end_ip = start_ip; + } + + p = xcalloc(1, sizeof(ip_range_t)); + + p->start_ip = start_ip; + p->end_ip = end_ip; + p->next = NULL; + + if (!ip_ranges) { + ip_ranges = p; + } + + if (ip_ranges_last) { + ip_ranges_last->next = p; + } + ip_ranges_last = p; + ip_ranges_count++; + + upsdebugx(1, "Recorded IP address range #%" PRIuSIZE ": [%s .. %s]", + ip_ranges_count, start_ip, end_ip); + + return ip_ranges_count; +} + +static void free_ip_ranges(void) +{ + ip_range_t *p = ip_ranges; + + while (p) { + ip_ranges = p->next; + + /* Only free the strings once, if they pointed to same */ + if (p->start_ip == p->end_ip && p->start_ip) { + free(p->start_ip); + } else { + if (p->start_ip) + free(p->start_ip); + if (p->end_ip) + free(p->end_ip); + } + + free(p); + p = ip_ranges; + } + + ip_ranges_last = NULL; + ip_ranges_count = 0; +} + #ifdef HAVE_PTHREAD static pthread_t thread[TYPE_END]; @@ -172,28 +270,88 @@ static void * run_usb(void *arg) dev[TYPE_USB] = nutscan_scan_usb(scanopts_ptr); return NULL; } +#endif /* HAVE_PTHREAD */ static void * run_snmp(void * arg) { nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; + nutscan_device_t * dev_ret; + ip_range_t *p = ip_ranges; + + upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", + __func__, ip_ranges_count); + + dev[TYPE_SNMP] = NULL; + while (p) { + dev_ret = nutscan_scan_snmp(p->start_ip, p->end_ip, timeout, sec); + if (!dev[TYPE_SNMP]) { + dev[TYPE_SNMP] = dev_ret; + } else { + dev[TYPE_SNMP] = nutscan_rewind_device( + nutscan_add_device_to_device(dev_ret, dev[TYPE_SNMP])); + } + p = p->next; + } - dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, sec); + upsdebugx(2, "Finished %s loop", __func__); return NULL; } static void * run_xml(void * arg) { nutscan_xml_t * sec = (nutscan_xml_t *)arg; + nutscan_device_t * dev_ret; + ip_range_t *p = ip_ranges; + + upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", + __func__, ip_ranges_count); - dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, sec); + if (!p) { + /* Probe broadcast */ + dev[TYPE_XML] = nutscan_scan_xml_http_range(NULL, NULL, timeout, sec); + + upsdebugx(2, "Finished %s query", __func__); + return NULL; + } + + dev[TYPE_XML] = NULL; + while (p) { + dev_ret = nutscan_scan_xml_http_range(p->start_ip, p->end_ip, timeout, sec); + if (!dev[TYPE_XML]) { + dev[TYPE_XML] = dev_ret; + } else { + dev[TYPE_XML] = nutscan_rewind_device( + nutscan_add_device_to_device(dev_ret, dev[TYPE_XML])); + } + p = p->next; + } + + upsdebugx(2, "Finished %s loop", __func__); return NULL; } static void * run_nut_old(void *arg) { + nutscan_device_t * dev_ret; + ip_range_t *p = ip_ranges; NUT_UNUSED_VARIABLE(arg); - dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout); + upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", + __func__, ip_ranges_count); + + dev[TYPE_NUT] = NULL; + while (p) { + dev_ret = nutscan_scan_nut(p->start_ip, p->end_ip, port, timeout); + if (!dev[TYPE_NUT]) { + dev[TYPE_NUT] = dev_ret; + } else { + dev[TYPE_NUT] = nutscan_rewind_device( + nutscan_add_device_to_device(dev_ret, dev[TYPE_NUT])); + } + p = p->next; + } + + upsdebugx(2, "Finished %s loop", __func__); return NULL; } @@ -216,19 +374,43 @@ static void * run_avahi(void *arg) static void * run_ipmi(void * arg) { nutscan_ipmi_t * sec = (nutscan_ipmi_t *)arg; + nutscan_device_t * dev_ret; + ip_range_t *p = ip_ranges; + + upsdebugx(2, "Entering %s for %" PRIuSIZE " IP address range(s)", + __func__, ip_ranges_count); + + if (!p) { + /* Probe local device */ + dev[TYPE_IPMI] = nutscan_scan_ipmi(NULL, NULL, sec); + + upsdebugx(2, "Finished %s query", __func__); + return NULL; + } - dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, sec); + dev[TYPE_IPMI] = NULL; + while (p) { + dev_ret = nutscan_scan_ipmi(p->start_ip, p->end_ip, sec); + if (!dev[TYPE_IPMI]) { + dev[TYPE_IPMI] = dev_ret; + } else { + dev[TYPE_IPMI] = nutscan_rewind_device( + nutscan_add_device_to_device(dev_ret, dev[TYPE_IPMI])); + } + p = p->next; + } + + upsdebugx(2, "Finished %s loop", __func__); return NULL; } static void * run_eaton_serial(void *arg) { - NUT_UNUSED_VARIABLE(arg); + char * arg_serial_ports = (char *)arg; - dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial(serial_ports); + dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial(arg_serial_ports); return NULL; } -#endif /* HAVE_PTHREAD */ static void show_usage(void) { @@ -286,6 +468,15 @@ static void show_usage(void) printf(" -s, --start_ip : First IP address to scan.\n"); printf(" -e, --end_ip : Last IP address to scan.\n"); printf(" -m, --mask_cidr : Give a range of IP using CIDR notation.\n"); + printf(" -m, --mask_cidr auto: Detect local IP address(es) and scan corresponding subnet(s).\n"); +#ifdef WIN32 + printf(" (Currently not implemented for this platform)\n"); +#endif + printf(" -m, --mask_cidr auto4/auto6: Likewise, limiting to IPv4 or IPv6 interfaces.\n"); + printf(" Only the first auto* request would be honoured.\n"); + printf("NOTE: IP address range specifications can be repeated, to scan several.\n"); + printf("Specifying a single first or last address before starting another range\n"); + printf("leads to scanning just that one address as the range.\n"); if (nutscan_avail_snmp) { printf("\nSNMP v1 specific options:\n"); @@ -414,7 +605,8 @@ int main(int argc, char *argv[]) nutscan_ipmi_t ipmi_sec; nutscan_xml_t xml_sec; int opt_ret; - char * cidr = NULL; + char *start_ip = NULL, *end_ip = NULL; + int auto_nets = 0; int allow_all = 0; int allow_usb = 0; int allow_snmp = 0; @@ -512,22 +704,196 @@ int main(int argc, char *argv[]) } break; case 's': + if (start_ip) { + /* Save whatever we have, either + * this one address or an earlier + * known range with its end */ + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } + start_ip = strdup(optarg); - if (end_ip == NULL) - end_ip = start_ip; + if (end_ip != NULL) { + /* Already we know two addresses, save them */ + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } break; case 'e': + if (end_ip) { + /* Save whatever we have, either + * this one address or an earlier + * known range with its start */ + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } + end_ip = strdup(optarg); - if (start_ip == NULL) - start_ip = end_ip; + if (start_ip != NULL) { + /* Already we know two addresses, save them */ + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } break; case 'E': serial_ports = strdup(optarg); allow_eaton_serial = 1; break; case 'm': - cidr = strdup(optarg); - upsdebugx(5, "Got CIDR net/mask: %s", cidr); + if (start_ip || end_ip) { + /* Save whatever we have, either + * this one address or an earlier + * known range with its start or end */ + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } + + if (!strcmp(optarg, "auto") || !strcmp(optarg, "auto4") || !strcmp(optarg, "auto6")) { + if (auto_nets) { + fprintf(stderr, "Duplicate request for connected subnet scan ignored\n"); + } else { +#ifndef WIN32 + /* TODO: Refactor into a method, reduce indentation? */ + /* Inspired by https://stackoverflow.com/a/63789267/4715872 */ + struct ifaddrs *ifap; +#endif + + if (!strcmp(optarg, "auto")) { + auto_nets = 46; + } else if (!strcmp(optarg, "auto4")) { + auto_nets = 4; + } else if (!strcmp(optarg, "auto6")) { + auto_nets = 6; + } + +#ifndef WIN32 + if (getifaddrs(&ifap) < 0) { + fatalx(EXIT_FAILURE, + "Failed to getifaddrs() for connected subnet scan: %s\n", + strerror(errno)); + } else { + struct ifaddrs *ifa; + char msg[LARGEBUF]; + /* Note: INET6_ADDRSTRLEN is large enough for IPv4 too, + * and is smaller than LARGEBUF to avoid snprintf() + * warnings that the result might not fit. */ + char addr[INET6_ADDRSTRLEN]; + char mask[INET6_ADDRSTRLEN]; + int masklen = 0; + + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr) { + memset(msg, 0, sizeof(msg)); + memset(addr, 0, sizeof(addr)); + memset(mask, 0, sizeof(mask)); + masklen = -1; + + if (ifa->ifa_addr->sa_family == AF_INET6) { + uint8_t i, j; + + /* Ensure proper alignment */ + struct sockaddr_in6 sm; + memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in6)); + + masklen = 0; + for (j = 0; j < 16; j++) { + i = sm.sin6_addr.s6_addr[j]; + while (i) { + masklen += i & 1; + i >>= 1; + } + } + + getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), addr, sizeof(addr), NULL, 0, NI_NUMERICHOST); + getnameinfo(ifa->ifa_netmask, sizeof(struct sockaddr_in6), mask, sizeof(mask), NULL, 0, NI_NUMERICHOST); + snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); + } else if (ifa->ifa_addr->sa_family == AF_INET) { + in_addr_t i; + + /* Ensure proper alignment */ + struct sockaddr_in sa, sm; + memcpy (&sa, ifa->ifa_addr, sizeof(struct sockaddr_in)); + memcpy (&sm, ifa->ifa_netmask, sizeof(struct sockaddr_in)); + snprintf(addr, sizeof(addr), "%s", inet_ntoa(sa.sin_addr)); + snprintf(mask, sizeof(mask), "%s", inet_ntoa(sm.sin_addr)); + + i = sm.sin_addr.s_addr; + masklen = 0; + while (i) { + masklen += i & 1; + i >>= 1; + } + snprintf(msg, sizeof(msg), "Interface: %s\tAddress: %s\tMask: %s (len: %i)\tFlags: %08" PRIxMAX, ifa->ifa_name, addr, mask, masklen, (uintmax_t)ifa->ifa_flags); +/* + } else { + snprintf(msg, sizeof(msg), "Addr family: %" PRIuMAX, (intmax_t)ifa->ifa_addr->sa_family); +*/ + } + + if (ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_INET) { + if (ifa->ifa_flags & IFF_LOOPBACK) + snprintfcat(msg, sizeof(msg), " IFF_LOOPBACK"); + if (ifa->ifa_flags & IFF_UP) + snprintfcat(msg, sizeof(msg), " IFF_UP"); + if (ifa->ifa_flags & IFF_RUNNING) + snprintfcat(msg, sizeof(msg), " IFF_RUNNING"); + if (ifa->ifa_flags & IFF_BROADCAST) + snprintfcat(msg, sizeof(msg), " IFF_BROADCAST(is assigned)"); + + upsdebugx(5, "Discovering getifaddrs(): %s", msg); + + /* TODO: also rule out "link-local" address ranges + * so we do not issue billions of worthless scans. + * FIXME: IPv6 may also be a problem, see + * https://github.com/networkupstools/nut/issues/2512 + */ + if (!(ifa->ifa_flags & IFF_LOOPBACK) + && (ifa->ifa_flags & IFF_UP) + && (ifa->ifa_flags & IFF_RUNNING) + && (ifa->ifa_flags & IFF_BROADCAST) + && (auto_nets == 46 + || (auto_nets == 4 && ifa->ifa_addr->sa_family == AF_INET) + || (auto_nets == 6 && ifa->ifa_addr->sa_family == AF_INET6) ) + ) { + char cidr[LARGEBUF]; + + if (snprintf(cidr, sizeof(cidr), "%s/%i", addr, masklen) < 0) { + fatalx(EXIT_FAILURE, "Could not construct a CIDR string from discovered address/mask"); + } + + upsdebugx(5, "Processing CIDR net/mask: %s", cidr); + nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); + upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } + } /* else AF_UNIX or a dozen other types we do not care about here */ + } + } + freeifaddrs(ifap); + } +#else /* WIN32 */ + /* https://stackoverflow.com/questions/122208/how-can-i-get-the-ip-address-of-a-local-computer */ + upsdebugx(0, "Local address detection feature is not completed on Windows, please call back later"); +#endif + } + } else { + /* not `-m auto` => is `-m cidr` */ + upsdebugx(5, "Processing CIDR net/mask: %s", optarg); + nutscan_cidr_to_ip(optarg, &start_ip, &end_ip); + upsdebugx(5, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; + } break; case 'D': /* nothing to do, here */ @@ -799,10 +1165,11 @@ int main(int argc, char *argv[]) # endif #endif /* HAVE_PTHREAD */ - if (cidr) { - upsdebugx(1, "Processing CIDR net/mask: %s", cidr); - nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); - upsdebugx(1, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); + if (start_ip != NULL || end_ip != NULL) { + /* Something did not cancel out above */ + add_ip_range(start_ip, end_ip); + start_ip = NULL; + end_ip = NULL; } if (!allow_usb && !allow_snmp && !allow_xml && !allow_oldnut && !allow_nut_simulation && @@ -839,6 +1206,7 @@ int main(int argc, char *argv[]) } #else upsdebugx(1, "USB SCAN: no pthread support, starting nutscan_scan_usb..."); + /* Not calling run_usb() here, as it re-processes the arg */ dev[TYPE_USB] = nutscan_scan_usb(&cli_link_detail_level); #endif /* HAVE_PTHREAD */ } else { @@ -846,8 +1214,8 @@ int main(int argc, char *argv[]) } if (allow_snmp && nutscan_avail_snmp) { - if (start_ip == NULL) { - upsdebugx(quiet, "No start IP, skipping SNMP"); + if (!ip_ranges_count) { + upsdebugx(quiet, "No IP range(s) requested, skipping SNMP"); nutscan_avail_snmp = 0; } else { @@ -860,7 +1228,8 @@ int main(int argc, char *argv[]) } #else upsdebugx(1, "SNMP SCAN: no pthread support, starting nutscan_scan_snmp..."); - dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, &snmp_sec); + /* dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, &snmp_sec); */ + run_snmp(&snmp_sec); #endif /* HAVE_PTHREAD */ } } else { @@ -868,6 +1237,11 @@ int main(int argc, char *argv[]) } if (allow_xml && nutscan_avail_xml_http) { + /* NOTE: No check for ip_ranges_count, + * NetXML default scan is broadcast + * so it just runs (if requested and + * supported). + */ upsdebugx(quiet, "Scanning XML/HTTP bus."); xml_sec.usec_timeout = timeout; #ifdef HAVE_PTHREAD @@ -878,15 +1252,17 @@ int main(int argc, char *argv[]) } #else upsdebugx(1, "XML/HTTP SCAN: no pthread support, starting nutscan_scan_xml_http_range()..."); - dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, &xml_sec); + /* dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, &xml_sec); */ + run_xml(&xml_sec); + } #endif /* HAVE_PTHREAD */ } else { upsdebugx(1, "XML/HTTP SCAN: not requested or supported, SKIPPED"); } if (allow_oldnut && nutscan_avail_nut) { - if (start_ip == NULL) { - upsdebugx(quiet, "No start IP, skipping NUT bus (old libupsclient connect method)"); + if (!ip_ranges_count) { + upsdebugx(quiet, "No IP range(s) requested, skipping NUT bus (old libupsclient connect method)"); nutscan_avail_nut = 0; } else { @@ -899,7 +1275,8 @@ int main(int argc, char *argv[]) } #else upsdebugx(1, "NUT bus (old) SCAN: no pthread support, starting nutscan_scan_nut..."); - dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout); + /*dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout);*/ + run_nut_old(NULL); #endif /* HAVE_PTHREAD */ } } else { @@ -915,8 +1292,9 @@ int main(int argc, char *argv[]) nutscan_avail_nut_simulation = 0; } #else - upsdebugx(1, "NUT simulation devices SCAN: no pthread support, starting nutscan_scan_nut_simulation..."); - dev[TYPE_NUT_SIMULATION] = nutscan_scan_nut_simulation(timeout); + upsdebugx(1, "NUT simulation devices SCAN: no pthread support, starting nutscan_scan_nut_simulation..."); + /* dev[TYPE_NUT_SIMULATION] = nutscan_scan_nut_simulation(); */ + run_nut_simulation(NULL); #endif /* HAVE_PTHREAD */ } else { upsdebugx(1, "NUT simulation devices SCAN: not requested or supported, SKIPPED"); @@ -932,13 +1310,19 @@ int main(int argc, char *argv[]) } #else upsdebugx(1, "NUT bus (avahi) SCAN: no pthread support, starting nutscan_scan_avahi..."); - dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout); + /* dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout); */ + run_avahi(NULL); #endif /* HAVE_PTHREAD */ } else { upsdebugx(1, "NUT bus (avahi) SCAN: not requested or supported, SKIPPED"); } if (allow_ipmi && nutscan_avail_ipmi) { + /* NOTE: No check for ip_ranges_count, + * IPMI default scan is local device + * so it just runs (if requested and + * supported). + */ upsdebugx(quiet, "Scanning IPMI bus."); #ifdef HAVE_PTHREAD upsdebugx(1, "IPMI SCAN: starting pthread_create with run_ipmi..."); @@ -948,7 +1332,8 @@ int main(int argc, char *argv[]) } #else upsdebugx(1, "IPMI SCAN: no pthread support, starting nutscan_scan_ipmi..."); - dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, &ipmi_sec); + /* dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, &ipmi_sec); */ + run_ipmi(&ipmi_sec); #endif /* HAVE_PTHREAD */ } else { upsdebugx(1, "IPMI SCAN: not requested or supported, SKIPPED"); @@ -965,7 +1350,8 @@ int main(int argc, char *argv[]) /* nutscan_avail_eaton_serial(?) = 0; */ #else upsdebugx(1, "SERIAL SCAN: no pthread support, starting nutscan_scan_eaton_serial..."); - dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial (serial_ports); + /* dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial (serial_ports); */ + run_eaton_serial(serial_ports); #endif /* HAVE_PTHREAD */ } else { upsdebugx(1, "SERIAL SCAN: not requested or supported, SKIPPED"); @@ -1056,6 +1442,7 @@ int main(int argc, char *argv[]) upsdebugx(1, "SCANS DONE: free common scanner resources"); nutscan_free(); + free_ip_ranges(); upsdebugx(1, "SCANS DONE: EXIT_SUCCESS"); return EXIT_SUCCESS; diff --git a/tools/nut-scanner/nutscan-device.c b/tools/nut-scanner/nutscan-device.c index c01e229b98..b5764f11b9 100644 --- a/tools/nut-scanner/nutscan-device.c +++ b/tools/nut-scanner/nutscan-device.c @@ -25,6 +25,7 @@ #include "config.h" /* must be the first header */ #include "nutscan-device.h" +#include "common.h" #include #include #include @@ -175,6 +176,11 @@ nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutsca nutscan_device_t * dev1 = NULL; nutscan_device_t * dev2 = NULL; + if (first == second) { + upsdebugx(5, "%s: skip: called to \"add\" same list pointers", __func__); + return first; + } + /* Get end of first device */ if (first != NULL) { dev1 = first; diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 4223bfa868..2bb6976dd4 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 - EATON + * Copyright (C) 2022 - 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 @@ -65,12 +66,12 @@ static void invert_IPv6(struct in6_addr * addr1, struct in6_addr * addr2) static int ntop(struct in_addr * ip, char * host, GETNAMEINFO_TYPE_ARG46 host_size) { - struct sockaddr_in in; - memset(&in, 0, sizeof(struct sockaddr_in)); - in.sin_addr = *ip; - in.sin_family = AF_INET; + struct sockaddr_in in4; + memset(&in4, 0, sizeof(struct sockaddr_in)); + in4.sin_addr = *ip; + in4.sin_family = AF_INET; return getnameinfo( - (struct sockaddr *)&in, + (struct sockaddr *)&in4, sizeof(struct sockaddr_in), host, host_size, NULL, 0, NI_NUMERICHOST); } @@ -94,10 +95,17 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const int i; struct addrinfo hints; struct addrinfo *res; - struct sockaddr_in * s_in; - struct sockaddr_in6 * s_in6; char host[SMALLBUF]; + /* Ensure proper alignment of IPvN structure fields: + * we receive a pointer to res from getaddrinfo() et al, + * so have no control about alignment of its further data. + * Make a copy of the bytes into an object allocated + * whichever way the system likes it. + */ + struct sockaddr_in s_in4buf, *s_in4 = &s_in4buf; + struct sockaddr_in6 s_in6buf, *s_in6 = &s_in6buf; + if (startIP == NULL) { return NULL; } @@ -120,31 +128,13 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const return NULL; } -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in6 = (struct sockaddr_in6 *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif + memcpy(s_in6, res->ai_addr, sizeof(struct sockaddr_in6)); memcpy(&ip->start6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); } else { -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in = (struct sockaddr_in *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif - ip->start = s_in->sin_addr; + memcpy(s_in4, res->ai_addr, sizeof(struct sockaddr_in)); + ip->start = s_in4->sin_addr; freeaddrinfo(res); } @@ -156,17 +146,8 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const return NULL; } -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in = (struct sockaddr_in *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif - ip->stop = s_in->sin_addr; + memcpy(s_in4, res->ai_addr, sizeof(struct sockaddr_in)); + ip->stop = s_in4->sin_addr; freeaddrinfo(res); } else { @@ -175,16 +156,7 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const fprintf(stderr, "Invalid address : %s\n", stopIP); return NULL; } -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in6 = (struct sockaddr_in6 *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif + memcpy(s_in6, res->ai_addr, sizeof(struct sockaddr_in6)); memcpy(&ip->stop6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); } @@ -267,15 +239,22 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) char * mask; char * saveptr = NULL; nutscan_ip_iter_t ip; - int mask_val; - int mask_byte; int ret; - uint32_t mask_bit; /* 32-bit IPv4 address bitmask */ + int mask_val; /* mask length in bits */ + int mask_byte; /* number of byte in 128-bit IPv6 address array (uint8_t[16]) for netmask */ + uint32_t mask_bit; /* 32-bit IPv4 address bitmask value */ char host[SMALLBUF]; struct addrinfo hints; struct addrinfo *res; - struct sockaddr_in * s_in; - struct sockaddr_in6 * s_in6; + + /* Ensure proper alignment of IPvN structure fields: + * we receive a pointer to res from getaddrinfo() et al, + * so have no control about alignment of its further data. + * Make a copy of the bytes into an object allocated + * whichever way the system likes it. + */ + struct sockaddr_in s_in4buf, *s_in4 = &s_in4buf; + struct sockaddr_in6 s_in6buf, *s_in6 = &s_in6buf; *start_ip = NULL; *stop_ip = NULL; @@ -302,8 +281,9 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) upsdebugx(5, "%s: parsed cidr=%s into first_ip=%s and mask=%s", __func__, cidr, first_ip, mask); + /* TODO: check if mask is also an IP address or a bit count */ mask_val = atoi(mask); - upsdebugx(5, "%s: parsed mask value %d", + upsdebugx(5, "%s: parsed mask into numeric value %d", __func__, mask_val); /* NOTE: Sanity-wise, some larger number also makes sense @@ -331,44 +311,26 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) if ((ret = getaddrinfo(first_ip, NULL, &hints, &res)) != 0) { /* EAI_ADDRFAMILY? */ - upsdebugx(5, "%s: getaddrinfo() failed for AF_INET (IPv4): %d", - __func__, ret); + upsdebugx(5, "%s: getaddrinfo() failed for AF_INET (IPv4, will retry with IPv6): %d: %s", + __func__, ret, gai_strerror(ret)); /* Try IPv6 detection */ ip.type = IPv6; hints.ai_family = AF_INET6; if ((ret = getaddrinfo(first_ip, NULL, &hints, &res)) != 0) { - upsdebugx(5, "%s: getaddrinfo() failed for AF_INET6 (IPv6): %d", - __func__, ret); + upsdebugx(5, "%s: getaddrinfo() failed for AF_INET6 (IPv6): %d: %s", + __func__, ret, gai_strerror(ret)); free(first_ip); return 0; } -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in6 = (struct sockaddr_in6 *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif + memcpy(s_in6, res->ai_addr, sizeof(struct sockaddr_in6)); memcpy(&ip.start6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); } else { -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in = (struct sockaddr_in *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif - ip.start = s_in->sin_addr; + memcpy(s_in4, res->ai_addr, sizeof(struct sockaddr_in)); + ip.start = s_in4->sin_addr; freeaddrinfo(res); } @@ -404,20 +366,12 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) free(first_ip); return 1; } - else { + else { /* ip.type == IPv6 */ if (getaddrinfo(first_ip, NULL, &hints, &res) != 0) { return 0; } -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wcast-align" -#endif - /* Note: we receive a pointer to res above, so have - * no control about alignment of its further data */ - s_in6 = (struct sockaddr_in6 *)res->ai_addr; -#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) -# pragma GCC diagnostic pop -#endif + + memcpy(s_in6, res->ai_addr, sizeof(struct sockaddr_in6)); memcpy(&ip.stop6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); diff --git a/tools/nut-scanner/scan_ipmi.c b/tools/nut-scanner/scan_ipmi.c index ba4e50776f..13ec34680e 100644 --- a/tools/nut-scanner/scan_ipmi.c +++ b/tools/nut-scanner/scan_ipmi.c @@ -406,6 +406,8 @@ nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t /* Are we scanning locally, or over the network? */ if (IPaddr == NULL) { + upsdebugx(2, "Entering %s for local device scan", __func__); + /* FIXME: we need root right to access local IPMI! if (!ipmi_is_root ()) { fprintf(stderr, "IPMI scan: %s\n", ipmi_ctx_strerror (IPMI_ERR_PERMISSION)); @@ -433,6 +435,8 @@ nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t } else { + upsdebugx(2, "Entering %s for %s", __func__, IPaddr); + #if 0 if (ipmi_sec->ipmi_version == IPMI_2_0) { @@ -608,14 +612,20 @@ nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip return NULL; } - /* Are we scanning locally, or through the network? */ if (start_ip == NULL) { - /* Local PSU scan */ + upsdebugx(1, "%s: Local PSU scan", __func__); current_nut_dev = nutscan_scan_ipmi_device(NULL, NULL); } else { + if (start_ip == stop_ip || !stop_ip) { + upsdebugx(1, "%s: Scanning remote PSU for single IP address: %s", + __func__, start_ip); + } else { + upsdebugx(1, "%s: Scanning remote PSU for IP address range: %s .. %s", + __func__, start_ip, stop_ip); + } ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); while (ip_str != NULL) { diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index cfff369f8e..9b5aa97fad 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -156,6 +156,8 @@ static void * list_nut_devices(void * arg) query[0] = "UPS"; numq = 1; + upsdebugx(2, "Entering %s for %s", __func__, target_hostname); + if ((*nut_upscli_splitaddr)(target_hostname, &hostname, &port) != 0) { free(target_hostname); free(nut_arg); @@ -301,6 +303,16 @@ nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, con return NULL; } + if (!startIP) { + upsdebugx(1, "%s: no starting IP address specified", __func__); + } else if (startIP == stopIP || !stopIP) { + upsdebugx(1, "%s: Scanning \"Old NUT\" bus for single IP address: %s", + __func__, startIP); + } else { + upsdebugx(1, "%s: Scanning \"Old NUT\" bus for IP address range: %s .. %s", + __func__, startIP, stopIP); + } + #ifndef WIN32 /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ if (sigaction(SIGPIPE, NULL, &oldact) == 0) { diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index bd1ee0a0a3..7c28a85235 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -1085,6 +1085,16 @@ nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip return NULL; } + if (!start_ip) { + upsdebugx(1, "%s: no starting IP address specified", __func__); + } else if (start_ip == stop_ip || !stop_ip) { + upsdebugx(1, "%s: Scanning SNMP for single IP address: %s", + __func__, start_ip); + } else { + upsdebugx(1, "%s: Scanning SNMP for IP address range: %s .. %s", + __func__, start_ip, stop_ip); + } + g_usec_timeout = usec_timeout; /* Force numeric OIDs resolution (ie, do not resolve to textual names) diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index 0aa9ccc897..ed635aba33 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -429,10 +429,11 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char } if (start_ip == NULL) { - upsdebugx(1, "Scanning XML/HTTP bus using broadcast."); + upsdebugx(1, "%s: Scanning XML/HTTP bus using broadcast.", __func__); } else { if ((start_ip == end_ip) || (end_ip == NULL) || (0 == strncmp(start_ip, end_ip, 128))) { - upsdebugx(1, "Scanning XML/HTTP bus for single IP (%s).", start_ip); + upsdebugx(1, "%s: Scanning XML/HTTP bus for single IP address: %s", + __func__, start_ip); } else { /* Iterate the range of IPs to scan */ nutscan_ip_iter_t ip; @@ -449,7 +450,12 @@ nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char # if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) size_t max_threads_scantype = max_threads_netxml; # endif +#endif + + upsdebugx(1, "%s: Scanning XML/HTTP bus for IP address range: %s .. %s", + __func__, start_ip, end_ip); +#ifdef HAVE_PTHREAD pthread_mutex_init(&dev_mutex, NULL); # ifdef HAVE_SEMAPHORE