1 /*
2  *  Copyright (C) 2011 - 2012  Arnaud Quette <arnaud.quette@free.fr>
3  *  Copyright (C) 2016 Michal Vyskocil <MichalVyskocil@eaton.com>
4  *  Copyright (C) 2016 - 2021 Jim Klimov <EvgenyKlimov@eaton.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 
21 /*! \file nut-scanner.c
22     \brief A tool to detect NUT supported devices
23     \author Arnaud Quette <arnaud.quette@free.fr>
24     \author Michal Vyskocil <MichalVyskocil@eaton.com>
25     \author Jim Klimov <EvgenyKlimov@eaton.com>
26 */
27 
28 #include "common.h"	/* Must be first include to pull "config.h" */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include "nut_version.h"
34 #include <unistd.h>
35 #include <string.h>
36 
37 #ifdef HAVE_PTHREAD
38 # include <pthread.h>
39 # ifdef HAVE_SEMAPHORE
40 #  include <semaphore.h>
41 # endif
42 # if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE)
43 #  include "nut_stdint.h"
44 #  ifdef HAVE_SYS_RESOURCE_H
45 #   include <sys/resource.h> /* for getrlimit() and struct rlimit */
46 #   include <errno.h>
47 
48 /* 3 is reserved for known overhead (for NetXML at least)
49  * following practical investigation summarized at
50  *   https://github.com/networkupstools/nut/pull/1158
51  * and probably means the usual stdin/stdout/stderr triplet
52  */
53 #   define RESERVE_FD_COUNT 3
54 #  endif /* HAVE_SYS_RESOURCE_H */
55 # endif  /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE */
56 #endif   /* HAVE_PTHREAD */
57 
58 #include "nut-scan.h"
59 
60 #define DEFAULT_TIMEOUT 5
61 
62 #define ERR_BAD_OPTION	(-1)
63 
64 static const char optstring[] = "?ht:T:s:e:E:c:l:u:W:X:w:x:p:b:B:d:L:CUSMOAm:NPqIVaD";
65 
66 #ifdef HAVE_GETOPT_LONG
67 static const struct option longopts[] = {
68 	{ "timeout", required_argument, NULL, 't' },
69 	{ "thread", required_argument, NULL, 'T' },
70 	{ "start_ip", required_argument, NULL, 's' },
71 	{ "end_ip", required_argument, NULL, 'e' },
72 	{ "eaton_serial", required_argument, NULL, 'E' },
73 	{ "mask_cidr", required_argument, NULL, 'm' },
74 	{ "community", required_argument, NULL, 'c' },
75 	{ "secLevel", required_argument, NULL, 'l' },
76 	{ "secName", required_argument, NULL, 'u' },
77 	{ "authPassword", required_argument, NULL, 'W' },
78 	{ "privPassword", required_argument, NULL, 'X' },
79 	{ "authProtocol", required_argument, NULL, 'w' },
80 	{ "privProtocol", required_argument, NULL, 'x' },
81 	{ "username", required_argument, NULL, 'b' },
82 	{ "password", required_argument, NULL, 'B' },
83 	{ "authType", required_argument, NULL, 'd' },
84 	{ "cipher_suite_id", required_argument, NULL, 'L' },
85 	{ "port", required_argument, NULL, 'p' },
86 	{ "complete_scan", no_argument, NULL, 'C' },
87 	{ "usb_scan", no_argument, NULL, 'U' },
88 	{ "snmp_scan", no_argument, NULL, 'S' },
89 	{ "xml_scan", no_argument, NULL, 'M' },
90 	{ "oldnut_scan", no_argument, NULL, 'O' },
91 	{ "avahi_scan", no_argument, NULL, 'A' },
92 	{ "ipmi_scan", no_argument, NULL, 'I' },
93 	{ "disp_nut_conf", no_argument, NULL, 'N' },
94 	{ "disp_parsable", no_argument, NULL, 'P' },
95 	{ "quiet", no_argument, NULL, 'q' },
96 	{ "help", no_argument, NULL, 'h' },
97 	{ "version", no_argument, NULL, 'V' },
98 	{ "available", no_argument, NULL, 'a' },
99 	{ "nut_debug_level", no_argument, NULL, 'D' },
100 	{ NULL, 0, NULL, 0 }
101 };
102 #else
103 #define getopt_long(a,b,c,d,e)	getopt(a,b,c)
104 #endif /* HAVE_GETOPT_LONG */
105 
106 static nutscan_device_t *dev[TYPE_END];
107 
108 static useconds_t timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; /* in usec */
109 static char * start_ip = NULL;
110 static char * end_ip = NULL;
111 static char * port = NULL;
112 static char * serial_ports = NULL;
113 
114 #ifdef HAVE_PTHREAD
115 static pthread_t thread[TYPE_END];
116 
run_usb(void * arg)117 static void * run_usb(void *arg)
118 {
119 	NUT_UNUSED_VARIABLE(arg);
120 
121 	dev[TYPE_USB] = nutscan_scan_usb();
122 	return NULL;
123 }
124 
run_snmp(void * arg)125 static void * run_snmp(void * arg)
126 {
127 	nutscan_snmp_t * sec = (nutscan_snmp_t *)arg;
128 
129 	dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, sec);
130 	return NULL;
131 }
132 
run_xml(void * arg)133 static void * run_xml(void * arg)
134 {
135 	nutscan_xml_t * sec = (nutscan_xml_t *)arg;
136 
137 	dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, sec);
138 	return NULL;
139 }
140 
run_nut_old(void * arg)141 static void * run_nut_old(void *arg)
142 {
143 	NUT_UNUSED_VARIABLE(arg);
144 
145 	dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout);
146 	return NULL;
147 }
148 
run_avahi(void * arg)149 static void * run_avahi(void *arg)
150 {
151 	NUT_UNUSED_VARIABLE(arg);
152 
153 	dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout);
154 	return NULL;
155 }
156 
run_ipmi(void * arg)157 static void * run_ipmi(void * arg)
158 {
159 	nutscan_ipmi_t * sec = (nutscan_ipmi_t *)arg;
160 
161 	dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, sec);
162 	return NULL;
163 }
164 
run_eaton_serial(void * arg)165 static void * run_eaton_serial(void *arg)
166 {
167 	NUT_UNUSED_VARIABLE(arg);
168 
169 	dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial(serial_ports);
170 	return NULL;
171 }
172 
173 #endif /* HAVE_PTHREAD */
174 
show_usage()175 static void show_usage()
176 {
177 /* NOTE: This code uses `nutscan_avail_*` global vars from nutscan-init.c */
178 	puts("nut-scanner : utility for detection of available power devices.\n");
179 	puts("OPTIONS:");
180 	printf("  -C, --complete_scan: Scan all available devices except serial ports (default).\n");
181 	if (nutscan_avail_usb) {
182 		printf("  -U, --usb_scan: Scan USB devices.\n");
183 	}
184 	if (nutscan_avail_snmp) {
185 		printf("  -S, --snmp_scan: Scan SNMP devices using built-in mapping definitions.\n");
186 	}
187 	if (nutscan_avail_xml_http) {
188 		printf("  -M, --xml_scan: Scan XML/HTTP devices.\n");
189 	}
190 	printf("  -O, --oldnut_scan: Scan NUT devices (old method).\n");
191 	if (nutscan_avail_avahi) {
192 		printf("  -A, --avahi_scan: Scan NUT devices (avahi method).\n");
193 	}
194 	if (nutscan_avail_ipmi) {
195 		printf("  -I, --ipmi_scan: Scan IPMI devices.\n");
196 	}
197 
198 	printf("  -E, --eaton_serial <serial ports list>: Scan serial Eaton devices (XCP, SHUT and Q1).\n");
199 
200 #if (defined HAVE_PTHREAD) && (defined HAVE_PTHREAD_TRYJOIN)
201 	printf("  -T, --thread <max number of threads>: Limit the amount of scanning threads running simultaneously (default: %zu).\n", max_threads);
202 #else
203 	printf("  -T, --thread <max number of threads>: Limit the amount of scanning threads running simultaneously (not implemented in this build: no pthread support)");
204 #endif
205 
206 	printf("\nNetwork specific options:\n");
207 	printf("  -t, --timeout <timeout in seconds>: network operation timeout (default %d).\n", DEFAULT_NETWORK_TIMEOUT);
208 	printf("  -s, --start_ip <IP address>: First IP address to scan.\n");
209 	printf("  -e, --end_ip <IP address>: Last IP address to scan.\n");
210 	printf("  -m, --mask_cidr <IP address/mask>: Give a range of IP using CIDR notation.\n");
211 
212 	if (nutscan_avail_snmp) {
213 		printf("\nSNMP v1 specific options:\n");
214 		printf("  -c, --community <community name>: Set SNMP v1 community name (default = public)\n");
215 
216 		printf("\nSNMP v3 specific options:\n");
217 		printf("  -l, --secLevel <security level>: Set the securityLevel used for SNMPv3 messages (allowed values: noAuthNoPriv, authNoPriv, authPriv)\n");
218 		printf("  -u, --secName <security name>: Set the securityName used for authenticated SNMPv3 messages (mandatory if you set secLevel. No default)\n");
219 
220 		/* Construct help for AUTHPROTO */
221 		{ int comma = 0;
222 		NUT_UNUSED_VARIABLE(comma); // potentially, if no protocols are available
223 		printf("  -w, --authProtocol <authentication protocol>: Set the authentication protocol (");
224 #if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol
225 		printf("%s%s",
226 			(comma++ ? ", " : ""),
227 			"MD5"
228 			);
229 #endif
230 #if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol
231 		printf("%s%s",
232 			(comma++ ? ", " : ""),
233 			"SHA"
234 			);
235 #endif
236 #if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol
237 		printf("%s%s",
238 			(comma++ ? ", " : ""),
239 			"SHA256"
240 			);
241 #endif
242 #if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol
243 		printf("%s%s",
244 			(comma++ ? ", " : ""),
245 			"SHA384"
246 			);
247 #endif
248 #if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol
249 		printf("%s%s",
250 			(comma++ ? ", " : ""),
251 			"SHA512"
252 			);
253 #endif
254 		printf("%s%s",
255 			(comma ? "" : "none supported"),
256 			") used for authenticated SNMPv3 messages (default=MD5 if available)\n"
257 			);
258 		} /* Construct help for AUTHPROTO */
259 
260 		printf("  -W, --authPassword <authentication pass phrase>: Set the authentication pass phrase used for authenticated SNMPv3 messages (mandatory if you set secLevel to authNoPriv or authPriv)\n");
261 
262 		/* Construct help for PRIVPROTO */
263 		{ int comma = 0;
264 		NUT_UNUSED_VARIABLE(comma); // potentially, if no protocols are available
265 		printf("  -x, --privProtocol <privacy protocol>: Set the privacy protocol (");
266 #if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol
267 		printf("%s%s",
268 			(comma++ ? ", " : ""),
269 			"DES"
270 			);
271 #endif
272 #if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol
273 		printf("%s%s",
274 			(comma++ ? ", " : ""),
275 			"AES"
276 			);
277 #endif
278 #if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04
279 # if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol
280 		printf("%s%s",
281 			(comma++ ? ", " : ""),
282 			"AES192"
283 			);
284 # endif
285 # if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol
286 		printf("%s%s",
287 			(comma++ ? ", " : ""),
288 			"AES256"
289 			);
290 # endif
291 #endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */
292 		printf("%s%s",
293 			(comma ? "" : "none supported"),
294 			") used for encrypted SNMPv3 messages (default=DES if available)\n"
295 			);
296 		} /* Construct help for PRIVPROTO */
297 
298 		printf("  -X, --privPassword <privacy pass phrase>: Set the privacy pass phrase used for encrypted SNMPv3 messages (mandatory if you set secLevel to authPriv)\n");
299 	}
300 
301 	if (nutscan_avail_ipmi) {
302 		printf("\nIPMI over LAN specific options:\n");
303 		printf("  -b, --username <username>: Set the username used for authenticating IPMI over LAN connections (mandatory for IPMI over LAN. No default)\n");
304 		/* Specify  the  username  to  use  when authenticating with the remote host.  If not specified, a null (i.e. anonymous) username is assumed. The user must have
305 		 * at least ADMIN privileges in order for this tool to operate fully. */
306 		printf("  -B, --password <password>: Specify the password to use when authenticationg with the remote host (mandatory for IPMI over LAN. No default)\n");
307 		/* Specify the password to use when authenticationg with the remote host.  If not specified, a null password is assumed. Maximum password length is 16 for IPMI
308 		 * 1.5 and 20 for IPMI 2.0. */
309 		printf("  -d, --authType <authentication type>: Specify the IPMI 1.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, MD2, and MD5) with the remote host (default=MD5)\n");
310 		printf("  -L, --cipher_suite_id <cipher suite id>: Specify the IPMI 2.0 cipher suite ID to use, for authentication, integrity, and confidentiality (default=3)\n");
311 	}
312 
313 	printf("\nNUT specific options:\n");
314 	printf("  -p, --port <port number>: Port number of remote NUT upsd\n");
315 	printf("\ndisplay specific options:\n");
316 	printf("  -N, --disp_nut_conf: Display result in the ups.conf format\n");
317 	printf("  -P, --disp_parsable: Display result in a parsable format\n");
318 	printf("\nMiscellaneous options:\n");
319 	printf("  -V, --version: Display NUT version\n");
320 	printf("  -a, --available: Display available bus that can be scanned\n");
321 	printf("  -q, --quiet: Display only scan result. No information on currently scanned bus is displayed.\n");
322 	printf("  -D, --nut_debug_level: Raise the debugging level.  Use this multiple times to see more details.\n");
323 }
324 
main(int argc,char * argv[])325 int main(int argc, char *argv[])
326 {
327 	nutscan_snmp_t snmp_sec;
328 	nutscan_ipmi_t ipmi_sec;
329 	nutscan_xml_t  xml_sec;
330 	int opt_ret;
331 	char *	cidr = NULL;
332 	int allow_all = 0;
333 	int allow_usb = 0;
334 	int allow_snmp = 0;
335 	int allow_xml = 0;
336 	int allow_oldnut = 0;
337 	int allow_avahi = 0;
338 	int allow_ipmi = 0;
339 	int allow_eaton_serial = 0; /* MUST be requested explicitly! */
340 	int quiet = 0; /* The debugging level for certain upsdebugx() progress messages; 0 = print always, quiet==1 is to require at least one -D */
341 	void (*display_func)(nutscan_device_t * device);
342 	int ret_code = EXIT_SUCCESS;
343 #if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) ) && (defined HAVE_SYS_RESOURCE_H)
344 	struct rlimit nofile_limit;
345 
346 	/* Limit the max scanning thread count by the amount of allowed open
347 	 * file descriptors (which caller can change with `ulimit -n NUM`),
348 	 * following practical investigation summarized at
349 	 *   https://github.com/networkupstools/nut/pull/1158
350 	 * Resource-Limit code inspired by example from:
351 	 *   https://stackoverflow.com/questions/4076848/how-to-do-the-equivalent-of-ulimit-n-400-from-within-c/4077000#4077000
352 	 */
353 
354 	/* Get max number of files. */
355 	if (getrlimit(RLIMIT_NOFILE, &nofile_limit) != 0) {
356 		/* Report error, keep hardcoded default */
357 		fprintf(stderr, "getrlimit() failed with errno=%d, keeping default job limits\n", errno);
358 		nofile_limit.rlim_cur = 0;
359 		nofile_limit.rlim_max = 0;
360 	} else {
361 		if (nofile_limit.rlim_cur > 0
362 		&&  nofile_limit.rlim_cur > RESERVE_FD_COUNT
363 		&&  (uintmax_t)max_threads > (uintmax_t)(nofile_limit.rlim_cur - RESERVE_FD_COUNT)
364 		&&  (uintmax_t)(nofile_limit.rlim_cur) < (uintmax_t)SIZE_MAX
365 		) {
366 			max_threads = (size_t)nofile_limit.rlim_cur;
367 			if (max_threads > (RESERVE_FD_COUNT + 1)) {
368 				max_threads -= RESERVE_FD_COUNT;
369 			}
370 		}
371 	}
372 #endif /* HAVE_PTHREAD && ( HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE ) && HAVE_SYS_RESOURCE_H */
373 
374 	memset(&snmp_sec, 0, sizeof(snmp_sec));
375 	memset(&ipmi_sec, 0, sizeof(ipmi_sec));
376 	memset(&xml_sec, 0, sizeof(xml_sec));
377 
378 	/* Set the default values for IPMI */
379 	ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD5;
380 	ipmi_sec.ipmi_version = IPMI_1_5; /* default to IPMI 1.5, if not otherwise specified */
381 	ipmi_sec.cipher_suite_id = 3; /* default to HMAC-SHA1; HMAC-SHA1-96; AES-CBC-128 */
382 	ipmi_sec.privilege_level = IPMI_PRIVILEGE_LEVEL_ADMIN; /* should be sufficient */
383 
384 	/* Set the default values for XML HTTP (run_xml()) */
385 	xml_sec.port_http = 80;
386 	xml_sec.port_udp = 4679;
387 	xml_sec.usec_timeout = 0; /* Override with the "timeout" common setting later */
388 	xml_sec.peername = NULL;
389 
390 	/* Parse command line options -- First loop: only get debug level */
391 	/* Suppress error messages, for now -- leave them to the second loop. */
392 	opterr = 0;
393 	while ((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) {
394 		if (opt_ret == 'D')
395 			nut_debug_level++;
396 	}
397 
398 	nutscan_init();
399 
400 	display_func = nutscan_display_ups_conf;
401 
402 	/* Parse command line options -- Second loop: everything else */
403 	/* Restore error messages... */
404 	opterr = 1;
405 	/* ...and index of the item to be processed by getopt(). */
406 	optind = 1;
407 	/* Note: the getopts print an error message about unknown arguments
408 	 * or arguments which need a second token and that is missing now */
409 	while ((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) {
410 
411 		switch(opt_ret) {
412 			case 't':
413 				timeout = (useconds_t)atol(optarg) * 1000 * 1000; /*in usec*/
414 				if (timeout <= 0) {
415 					fprintf(stderr,
416 						"Illegal timeout value, using default %ds\n",
417 						DEFAULT_NETWORK_TIMEOUT);
418 					timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000;
419 				}
420 				break;
421 			case 's':
422 				start_ip = strdup(optarg);
423 				if (end_ip == NULL)
424 					end_ip = start_ip;
425 				break;
426 			case 'e':
427 				end_ip = strdup(optarg);
428 				if (start_ip == NULL)
429 					start_ip = end_ip;
430 				break;
431 			case 'E':
432 				serial_ports = strdup(optarg);
433 				allow_eaton_serial = 1;
434 				break;
435 			case 'm':
436 				cidr = strdup(optarg);
437 				upsdebugx(5, "Got CIDR net/mask: %s", cidr);
438 				break;
439 			case 'D':
440 				/* nothing to do, here */
441 				break;
442 			case 'c':
443 				if (!nutscan_avail_snmp) {
444 					goto display_help;
445 				}
446 				snmp_sec.community = strdup(optarg);
447 				break;
448 			case 'l':
449 				if (!nutscan_avail_snmp) {
450 					goto display_help;
451 				}
452 				snmp_sec.secLevel = strdup(optarg);
453 				break;
454 			case 'u':
455 				if (!nutscan_avail_snmp) {
456 					goto display_help;
457 				}
458 				snmp_sec.secName = strdup(optarg);
459 				break;
460 			case 'W':
461 				if (!nutscan_avail_snmp) {
462 					goto display_help;
463 				}
464 				snmp_sec.authPassword = strdup(optarg);
465 				break;
466 			case 'X':
467 				if (!nutscan_avail_snmp) {
468 					goto display_help;
469 				}
470 				snmp_sec.privPassword = strdup(optarg);
471 				break;
472 			case 'w':
473 				if (!nutscan_avail_snmp) {
474 					goto display_help;
475 				}
476 				snmp_sec.authProtocol = strdup(optarg);
477 				break;
478 			case 'x':
479 				if (!nutscan_avail_snmp) {
480 					goto display_help;
481 				}
482 				snmp_sec.privProtocol = strdup(optarg);
483 				break;
484 			case 'S':
485 				if (!nutscan_avail_snmp) {
486 					goto display_help;
487 				}
488 				allow_snmp = 1;
489 				break;
490 			case 'b':
491 				if (!nutscan_avail_ipmi) {
492 					goto display_help;
493 				}
494 				ipmi_sec.username = strdup(optarg);
495 				break;
496 			case 'B':
497 				if (!nutscan_avail_ipmi) {
498 					goto display_help;
499 				}
500 				ipmi_sec.password = strdup(optarg);
501 				break;
502 			case 'd':
503 				if (!nutscan_avail_ipmi) {
504 					goto display_help;
505 				}
506 				if (!strcmp(optarg, "NONE")) {
507 					ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_NONE;
508 				}
509 				else if (!strcmp(optarg, "STRAIGHT_PASSWORD_KEY")) {
510 					ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY;
511 				}
512 				else if (!strncmp(optarg, "MD2", 3)) {
513 					ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD2;
514 				}
515 				else if (!strncmp(optarg, "MD5", 3)) {
516 					ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD5;
517 				}
518 				else {
519 					fprintf(stderr,
520 						"Unknown authentication type (%s). Defaulting to MD5\n",
521 						optarg);
522 				}
523 				break;
524 			case 'L':
525 				if (!nutscan_avail_ipmi) {
526 					goto display_help;
527 				}
528 				ipmi_sec.cipher_suite_id = atoi(optarg);
529 				/* Force IPMI 2.0! */
530 				ipmi_sec.ipmi_version = IPMI_2_0;
531 				break;
532 			case 'p':
533 				port = strdup(optarg);
534 				break;
535 			case 'T': {
536 #if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) )
537 				char* endptr;
538 				long val = strtol(optarg, &endptr, 10);
539 				/* With endptr we check that no chars were left in optarg
540 				 * (that is, pointed-to char -- if reported -- is '\0')
541 				 */
542 				if ((!endptr || !*endptr)
543 				&& val > 0
544 				&& (uintmax_t)val < (uintmax_t)SIZE_MAX
545 				) {
546 # ifdef HAVE_SYS_RESOURCE_H
547 					if (nofile_limit.rlim_cur > 0
548 					&&  nofile_limit.rlim_cur > RESERVE_FD_COUNT
549 					&& (uintmax_t)nofile_limit.rlim_cur < (uintmax_t)SIZE_MAX
550 					&& (uintmax_t)val > (uintmax_t)(nofile_limit.rlim_cur - RESERVE_FD_COUNT)
551 					) {
552 						upsdebugx(1, "Detected soft limit for "
553 							"file descriptor count is %ju",
554 							(uintmax_t)nofile_limit.rlim_cur);
555 						upsdebugx(1, "Detected hard limit for "
556 							"file descriptor count is %ju",
557 							(uintmax_t)nofile_limit.rlim_max);
558 
559 						max_threads = (size_t)nofile_limit.rlim_cur;
560 						if (max_threads > (RESERVE_FD_COUNT + 1)) {
561 							max_threads -= RESERVE_FD_COUNT;
562 						}
563 
564 						fprintf(stderr,
565 							"WARNING: Requested max scanning "
566 							"thread count %s (%ld) exceeds the "
567 							"current file descriptor count limit "
568 							"(minus reservation), constraining "
569 							"to %zu\n",
570 							optarg, val, max_threads);
571 					} else
572 # endif /* HAVE_SYS_RESOURCE_H */
573 						max_threads = (size_t)val;
574 				} else {
575 					fprintf(stderr,
576 						"WARNING: Requested max scanning "
577 						"thread count %s (%ld) is out of range, "
578 						"using default %zu\n",
579 						optarg, val, max_threads);
580 				}
581 #else
582 				fprintf(stderr,
583 					"WARNING: Max scanning thread count option "
584 					"is not supported in this build, ignored\n");
585 #endif /* HAVE_PTHREAD && ways to limit the thread count */
586 				}
587 				break;
588 			case 'C':
589 				allow_all = 1;
590 				break;
591 			case 'U':
592 				if (!nutscan_avail_usb) {
593 					goto display_help;
594 				}
595 				allow_usb = 1;
596 				break;
597 			case 'M':
598 				if (!nutscan_avail_xml_http) {
599 					goto display_help;
600 				}
601 				allow_xml = 1;
602 				break;
603 			case 'O':
604 				allow_oldnut = 1;
605 				break;
606 			case 'A':
607 				if (!nutscan_avail_avahi) {
608 					goto display_help;
609 				}
610 				allow_avahi = 1;
611 				break;
612 			case 'I':
613 				if (!nutscan_avail_ipmi) {
614 					goto display_help;
615 				}
616 				allow_ipmi = 1;
617 				break;
618 			case 'N':
619 				display_func = nutscan_display_ups_conf;
620 				break;
621 			case 'P':
622 				display_func = nutscan_display_parsable;
623 				break;
624 			case 'q':
625 				quiet = 1;
626 				break;
627 			case 'V':
628 				printf("Network UPS Tools - %s\n", NUT_VERSION_MACRO);
629 				exit(EXIT_SUCCESS);
630 			case 'a':
631 				printf("OLDNUT\n");
632 				if (nutscan_avail_usb) {
633 					printf("USB\n");
634 				}
635 				if (nutscan_avail_snmp) {
636 					printf("SNMP\n");
637 				}
638 				if (nutscan_avail_xml_http) {
639 					printf("XML\n");
640 				}
641 				if (nutscan_avail_avahi) {
642 					printf("AVAHI\n");
643 				}
644 				if (nutscan_avail_ipmi) {
645 					printf("IPMI\n");
646 				}
647 				printf("EATON_SERIAL\n");
648 				exit(EXIT_SUCCESS);
649 			case '?':
650 				ret_code = ERR_BAD_OPTION;
651 				goto display_help;
652 				/* Fall through to usage and error exit */
653 			case 'h':
654 			default:
655 display_help:
656 				show_usage();
657 				if ((opt_ret != 'h') || (ret_code != EXIT_SUCCESS))
658 					fprintf(stderr, "\n\n"
659 						"WARNING: Some error has occurred while processing 'nut-scanner' command-line\n"
660 						"arguments, see more details above the usage help text.\n\n");
661 				return ret_code;
662 		}
663 	}
664 
665 #ifdef HAVE_PTHREAD
666 # ifdef HAVE_SEMAPHORE
667 	/* FIXME: Currently sem_init already done on nutscan-init for lib need.
668 	   We need to destroy it before re-init. We currently can't change "sem value"
669 	   on lib (need to be thread safe). */
670 	sem_t *current_sem = nutscan_semaphore();
671 	sem_destroy(current_sem);
672 	if (SIZE_MAX > UINT_MAX && max_threads > UINT_MAX) {
673 		fprintf(stderr, "\n\n"
674 			"WARNING: Limiting max_threads to range acceptable for sem_init()\n\n");
675 		max_threads = UINT_MAX - 1;
676 	}
677 	sem_init(current_sem, 0, (unsigned int)max_threads);
678 # endif
679 #endif /* HAVE_PTHREAD */
680 
681 	if (cidr) {
682 		upsdebugx(1, "Processing CIDR net/mask: %s", cidr);
683 		nutscan_cidr_to_ip(cidr, &start_ip, &end_ip);
684 		upsdebugx(1, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip);
685 	}
686 
687 	if (!allow_usb && !allow_snmp && !allow_xml && !allow_oldnut &&
688 		!allow_avahi && !allow_ipmi && !allow_eaton_serial
689 	) {
690 		allow_all = 1;
691 	}
692 
693 	if (allow_all) {
694 		allow_usb = 1;
695 		allow_snmp = 1;
696 		allow_xml = 1;
697 		allow_oldnut = 1;
698 		allow_avahi = 1;
699 		allow_ipmi = 1;
700 		/* BEWARE: allow_all does not include allow_eaton_serial! */
701 	}
702 
703 /* TODO/discuss : Should the #else...#endif code below for lack of pthreads
704  * during build also serve as a fallback for pthread failure at runtime?
705  */
706 	if (allow_usb && nutscan_avail_usb) {
707 		upsdebugx(quiet, "Scanning USB bus.");
708 #ifdef HAVE_PTHREAD
709 		if (pthread_create(&thread[TYPE_USB], NULL, run_usb, NULL)) {
710 			upsdebugx(1, "pthread_create returned an error; disabling this scan mode");
711 			nutscan_avail_usb = 0;
712 		}
713 #else
714 		upsdebugx(1, "USB SCAN: no pthread support, starting nutscan_scan_usb...");
715 		dev[TYPE_USB] = nutscan_scan_usb();
716 #endif /* HAVE_PTHREAD */
717 	} else {
718 		upsdebugx(1, "USB SCAN: not requested, SKIPPED");
719 	}
720 
721 	if (allow_snmp && nutscan_avail_snmp) {
722 		if (start_ip == NULL) {
723 			upsdebugx(quiet, "No start IP, skipping SNMP");
724 			nutscan_avail_snmp = 0;
725 		}
726 		else {
727 			upsdebugx(quiet, "Scanning SNMP bus.");
728 #ifdef HAVE_PTHREAD
729 			upsdebugx(1, "SNMP SCAN: starting pthread_create with run_snmp...");
730 			if (pthread_create(&thread[TYPE_SNMP], NULL, run_snmp, &snmp_sec)) {
731 				upsdebugx(1, "pthread_create returned an error; disabling this scan mode");
732 				nutscan_avail_snmp = 0;
733 			}
734 #else
735 			upsdebugx(1, "SNMP SCAN: no pthread support, starting nutscan_scan_snmp...");
736 			dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, &snmp_sec);
737 #endif /* HAVE_PTHREAD */
738 		}
739 	} else {
740 		upsdebugx(1, "SNMP SCAN: not requested, SKIPPED");
741 	}
742 
743 	if (allow_xml && nutscan_avail_xml_http) {
744 		upsdebugx(quiet, "Scanning XML/HTTP bus.");
745 		xml_sec.usec_timeout = timeout;
746 #ifdef HAVE_PTHREAD
747 		upsdebugx(1, "XML/HTTP SCAN: starting pthread_create with run_xml...");
748 		if (pthread_create(&thread[TYPE_XML], NULL, run_xml, &xml_sec)) {
749 			upsdebugx(1, "pthread_create returned an error; disabling this scan mode");
750 			nutscan_avail_xml_http = 0;
751 		}
752 #else
753 		upsdebugx(1, "XML/HTTP SCAN: no pthread support, starting nutscan_scan_xml_http_range()...");
754 		dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, &xml_sec);
755 #endif /* HAVE_PTHREAD */
756 	} else {
757 		upsdebugx(1, "XML/HTTP SCAN: not requested, SKIPPED");
758 	}
759 
760 	if (allow_oldnut && nutscan_avail_nut) {
761 		if (start_ip == NULL) {
762 			upsdebugx(quiet, "No start IP, skipping NUT bus (old connect method)");
763 			nutscan_avail_nut = 0;
764 		}
765 		else {
766 			upsdebugx(quiet, "Scanning NUT bus (old connect method).");
767 #ifdef HAVE_PTHREAD
768 			upsdebugx(1, "NUT bus (old) SCAN: starting pthread_create with run_nut_old...");
769 			if (pthread_create(&thread[TYPE_NUT], NULL, run_nut_old, NULL)) {
770 				upsdebugx(1, "pthread_create returned an error; disabling this scan mode");
771 				nutscan_avail_nut = 0;
772 			}
773 #else
774 			upsdebugx(1, "NUT bus (old) SCAN: no pthread support, starting nutscan_scan_nut...");
775 			dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout);
776 #endif /* HAVE_PTHREAD */
777 		}
778 	} else {
779 		upsdebugx(1, "NUT bus (old) SCAN: not requested, SKIPPED");
780 	}
781 
782 	if (allow_avahi && nutscan_avail_avahi) {
783 		upsdebugx(quiet, "Scanning NUT bus (avahi method).");
784 #ifdef HAVE_PTHREAD
785 		upsdebugx(1, "NUT bus (avahi) SCAN: starting pthread_create with run_avahi...");
786 		if (pthread_create(&thread[TYPE_AVAHI], NULL, run_avahi, NULL)) {
787 			upsdebugx(1, "pthread_create returned an error; disabling this scan mode");
788 			nutscan_avail_avahi = 0;
789 		}
790 #else
791 		upsdebugx(1, "NUT bus (avahi) SCAN: no pthread support, starting nutscan_scan_avahi...");
792 		dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout);
793 #endif /* HAVE_PTHREAD */
794 	} else {
795 		upsdebugx(1, "NUT bus (avahi) SCAN: not requested, SKIPPED");
796 	}
797 
798 	if (allow_ipmi && nutscan_avail_ipmi) {
799 		upsdebugx(quiet, "Scanning IPMI bus.");
800 #ifdef HAVE_PTHREAD
801 		upsdebugx(1, "IPMI SCAN: starting pthread_create with run_ipmi...");
802 		if (pthread_create(&thread[TYPE_IPMI], NULL, run_ipmi, &ipmi_sec)) {
803 			upsdebugx(1, "pthread_create returned an error; disabling this scan mode");
804 			nutscan_avail_ipmi = 0;
805 		}
806 #else
807 		upsdebugx(1, "IPMI SCAN: no pthread support, starting nutscan_scan_ipmi...");
808 		dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, &ipmi_sec);
809 #endif /* HAVE_PTHREAD */
810 	} else {
811 		upsdebugx(1, "IPMI SCAN: not requested, SKIPPED");
812 	}
813 
814 	/* Eaton serial scan */
815 	if (allow_eaton_serial) {
816 		upsdebugx(quiet, "Scanning serial bus for Eaton devices.");
817 #ifdef HAVE_PTHREAD
818 		upsdebugx(1, "SERIAL SCAN: starting pthread_create with run_eaton_serial (return not checked!)...");
819 		pthread_create(&thread[TYPE_EATON_SERIAL], NULL, run_eaton_serial, serial_ports);
820 		/* FIXME: check return code */
821 		/* upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); */
822 		/* nutscan_avail_eaton_serial(?) = 0; */
823 #else
824 		upsdebugx(1, "SERIAL SCAN: no pthread support, starting nutscan_scan_eaton_serial...");
825 		dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial (serial_ports);
826 #endif /* HAVE_PTHREAD */
827 	} else {
828 		upsdebugx(1, "SERIAL SCAN: not requested, SKIPPED");
829 	}
830 
831 #ifdef HAVE_PTHREAD
832 	if (allow_usb && nutscan_avail_usb && thread[TYPE_USB]) {
833 		upsdebugx(1, "USB SCAN: join back the pthread");
834 		pthread_join(thread[TYPE_USB], NULL);
835 	}
836 	if (allow_snmp && nutscan_avail_snmp && thread[TYPE_SNMP]) {
837 		upsdebugx(1, "SNMP SCAN: join back the pthread");
838 		pthread_join(thread[TYPE_SNMP], NULL);
839 	}
840 	if (allow_xml && nutscan_avail_xml_http && thread[TYPE_XML]) {
841 		upsdebugx(1, "XML/HTTP SCAN: join back the pthread");
842 		pthread_join(thread[TYPE_XML], NULL);
843 	}
844 	if (allow_oldnut && nutscan_avail_nut && thread[TYPE_NUT]) {
845 		upsdebugx(1, "NUT bus (old) SCAN: join back the pthread");
846 		pthread_join(thread[TYPE_NUT], NULL);
847 	}
848 	if (allow_avahi && nutscan_avail_avahi && thread[TYPE_AVAHI]) {
849 		upsdebugx(1, "NUT bus (avahi) SCAN: join back the pthread");
850 		pthread_join(thread[TYPE_AVAHI], NULL);
851 	}
852 	if (allow_ipmi && nutscan_avail_ipmi && thread[TYPE_IPMI]) {
853 		upsdebugx(1, "IPMI SCAN: join back the pthread");
854 		pthread_join(thread[TYPE_IPMI], NULL);
855 	}
856 	if (allow_eaton_serial && thread[TYPE_EATON_SERIAL]) {
857 		upsdebugx(1, "SERIAL SCAN: join back the pthread");
858 		pthread_join(thread[TYPE_EATON_SERIAL], NULL);
859 	}
860 #endif /* HAVE_PTHREAD */
861 
862 	upsdebugx(1, "SCANS DONE: display results");
863 
864 	upsdebugx(1, "SCANS DONE: display results: USB");
865 	display_func(dev[TYPE_USB]);
866 	upsdebugx(1, "SCANS DONE: free resources: USB");
867 	nutscan_free_device(dev[TYPE_USB]);
868 
869 	upsdebugx(1, "SCANS DONE: display results: SNMP");
870 	display_func(dev[TYPE_SNMP]);
871 	upsdebugx(1, "SCANS DONE: free resources: SNMP");
872 	nutscan_free_device(dev[TYPE_SNMP]);
873 
874 	upsdebugx(1, "SCANS DONE: display results: XML/HTTP");
875 	display_func(dev[TYPE_XML]);
876 	upsdebugx(1, "SCANS DONE: free resources: XML/HTTP");
877 	nutscan_free_device(dev[TYPE_XML]);
878 
879 	upsdebugx(1, "SCANS DONE: display results: NUT bus (old)");
880 	display_func(dev[TYPE_NUT]);
881 	upsdebugx(1, "SCANS DONE: free resources: NUT bus (old)");
882 	nutscan_free_device(dev[TYPE_NUT]);
883 
884 	upsdebugx(1, "SCANS DONE: display results: NUT bus (avahi)");
885 	display_func(dev[TYPE_AVAHI]);
886 	upsdebugx(1, "SCANS DONE: free resources: NUT bus (avahi)");
887 	nutscan_free_device(dev[TYPE_AVAHI]);
888 
889 	upsdebugx(1, "SCANS DONE: display results: IPMI");
890 	display_func(dev[TYPE_IPMI]);
891 	upsdebugx(1, "SCANS DONE: free resources: IPMI");
892 	nutscan_free_device(dev[TYPE_IPMI]);
893 
894 	upsdebugx(1, "SCANS DONE: display results: SERIAL");
895 	display_func(dev[TYPE_EATON_SERIAL]);
896 	upsdebugx(1, "SCANS DONE: free resources: SERIAL");
897 	nutscan_free_device(dev[TYPE_EATON_SERIAL]);
898 
899 #ifdef HAVE_PTHREAD
900 # ifdef HAVE_SEMAPHORE
901 	sem_destroy(nutscan_semaphore());
902 # endif
903 #endif
904 
905 	upsdebugx(1, "SCANS DONE: free common scanner resources");
906 	nutscan_free();
907 
908 	upsdebugx(1, "SCANS DONE: EXIT_SUCCESS");
909 	return EXIT_SUCCESS;
910 }
911