1 /*
2  *  Copyright (C) 2011-2021 - EATON
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 /*! \file nutscan-init.c
20     \brief init functions for nut scanner library
21     \author Frederic Bohe <fredericbohe@eaton.com>
22 */
23 
24 #include "common.h"
25 #include "nutscan-init.h"
26 #include <ltdl.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "nut-scan.h"
31 
32 int nutscan_avail_avahi = 0;
33 int nutscan_avail_ipmi = 0;
34 int nutscan_avail_nut = 0;
35 int nutscan_avail_snmp = 0;
36 int nutscan_avail_usb = 0;
37 int nutscan_avail_xml_http = 0;
38 
39 int nutscan_load_usb_library(const char *libname_path);
40 int nutscan_load_snmp_library(const char *libname_path);
41 int nutscan_load_neon_library(const char *libname_path);
42 int nutscan_load_avahi_library(const char *libname_path);
43 int nutscan_load_ipmi_library(const char *libname_path);
44 int nutscan_load_upsclient_library(const char *libname_path);
45 
46 #ifdef HAVE_PTHREAD
47 # ifdef HAVE_SEMAPHORE
48 /* Shared by library consumers, exposed by nutscan_semaphore() below */
49 static sem_t semaphore;
50 
nutscan_semaphore(void)51 sem_t * nutscan_semaphore(void)
52 {
53 	return &semaphore;
54 }
55 # endif /* HAVE_SEMAPHORE */
56 
57 # ifdef HAVE_PTHREAD_TRYJOIN
58 pthread_mutex_t threadcount_mutex;
59 # endif
60 
61 # if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE)
62 /* We have 3 networked scan types: nut, snmp, xml,
63  * and users typically give their /24 subnet as "-m" arg.
64  * With some systems having a 1024 default (u)limit to
65  * file descriptors, this should fit if those are involved.
66  * On some systems tested, a large amount of not-joined
67  * pthreads did cause various crashes; also RAM is limited.
68  * Note that each scan may be time consuming to query an
69  * IP address and wait for (no) reply, so while these threads
70  * are usually not resource-intensive (nor computationally),
71  * they spend much wallclock time each so parallelism helps.
72  */
73 size_t max_threads = DEFAULT_THREAD;
74 size_t curr_threads = 0;
75 
76 size_t max_threads_netxml = 1021; /* experimental finding, see PR#1158 */
77 size_t max_threads_oldnut = 1021;
78 size_t max_threads_netsnmp = 0; // 10240;
79 	/* per reports in PR#1158, some versions of net-snmp could be limited
80 	 * to 1024 threads in the past; this was not found in practice.
81 	 * Still, some practical limit can be useful (configurable?)
82 	 * Here 0 means to not apply any special limit (beside max_threads).
83 	 */
84 
85 # endif  /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE */
86 
87 #endif /* HAVE_PTHREAD */
88 
nutscan_init(void)89 void nutscan_init(void)
90 {
91 #ifdef HAVE_PTHREAD
92 /* TOTHINK: Should semaphores to limit thread count
93  * and the more naive but portable methods be an
94  * if-else proposition? At least when initializing?
95  */
96 # ifdef HAVE_SEMAPHORE
97 	/* NOTE: This semaphore may get re-initialized in nut-scanner program
98 	 * after parsing command-line arguments. It calls nutscan_init() before
99 	 * parsing CLI, to know about available libs and to set defaults below.
100 	 */
101 	if (SIZE_MAX > UINT_MAX && max_threads > UINT_MAX) {
102 		upsdebugx(1,
103 			"WARNING: %s: Limiting max_threads to range acceptable for sem_init()",
104 			__func__);
105 		max_threads = UINT_MAX - 1;
106 	}
107 	sem_init(&semaphore, 0, (unsigned int)max_threads);
108 # endif
109 
110 # ifdef HAVE_PTHREAD_TRYJOIN
111 	pthread_mutex_init(&threadcount_mutex, NULL);
112 # endif
113 #endif /* HAVE_PTHREAD */
114 
115 	char *libname = NULL;
116 #ifdef WITH_USB
117 	libname = get_libname("libusb-0.1.so");
118 	if (!libname) {
119 		/* We can also use libusb-compat from newer libusb-1.0 releases */
120 		libname = get_libname("libusb.so");
121 	}
122 	if (libname) {
123 		nutscan_avail_usb = nutscan_load_usb_library(libname);
124 		free(libname);
125 	}
126 #endif
127 #ifdef WITH_SNMP
128 	libname = get_libname("libnetsnmp.so");
129 	if (libname) {
130 		nutscan_avail_snmp = nutscan_load_snmp_library(libname);
131 		free(libname);
132 	}
133 #endif
134 #ifdef WITH_NEON
135 	libname = get_libname("libneon.so");
136 	if (!libname) {
137 		libname = get_libname("libneon-gnutls.so");
138 	}
139 	if (libname) {
140 		nutscan_avail_xml_http = nutscan_load_neon_library(libname);
141 		free(libname);
142 	}
143 #endif
144 #ifdef WITH_AVAHI
145 	libname = get_libname("libavahi-client.so");
146 	if (libname) {
147 		nutscan_avail_avahi = nutscan_load_avahi_library(libname);
148 		free(libname);
149 	}
150 #endif
151 #ifdef WITH_FREEIPMI
152 	libname = get_libname("libfreeipmi.so");
153 	if (libname) {
154 		nutscan_avail_ipmi = nutscan_load_ipmi_library(libname);
155 		free(libname);
156 	}
157 #endif
158 	libname = get_libname("libupsclient.so");
159 	if (libname) {
160 		nutscan_avail_nut = nutscan_load_upsclient_library(libname);
161 		free(libname);
162 	}
163 }
164 
nutscan_free(void)165 void nutscan_free(void)
166 {
167 	if (nutscan_avail_usb) {
168 		lt_dlexit();
169 	}
170 	if (nutscan_avail_snmp) {
171 		lt_dlexit();
172 	}
173 	if (nutscan_avail_xml_http) {
174 		lt_dlexit();
175 	}
176 	if (nutscan_avail_avahi) {
177 		lt_dlexit();
178 	}
179 	if (nutscan_avail_ipmi) {
180 		lt_dlexit();
181 	}
182 	if (nutscan_avail_nut) {
183 		lt_dlexit();
184 	}
185 
186 #ifdef HAVE_PTHREAD
187 /* TOTHINK: See comments near mutex/semaphore init code above */
188 # ifdef HAVE_SEMAPHORE
189 	sem_destroy(nutscan_semaphore());
190 # endif
191 
192 # ifdef HAVE_PTHREAD_TRYJOIN
193 	pthread_mutex_destroy(&threadcount_mutex);
194 # endif
195 #endif
196 
197 }
198