Skip to content

Commit

Permalink
tools/nut-scanner/scan_*: revise "thready" scanning methods [networku…
Browse files Browse the repository at this point in the history
…pstools#2511]

* Clearly name "thread-ready" scanning methods that can be used
  standalone or from pthread_create() constraints
* Settle on "thready" methods freeing caller's data (so we do not
  worry about doing it after the parallel loops, or at a wrong
  moment before)
* Unify code mark-up in the files to facilitate later comparisons
* Update some comments

Signed-off-by: Jim Klimov <[email protected]>
  • Loading branch information
jimklimov committed Jul 11, 2024
1 parent 00cf4ec commit 687dc63
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 36 deletions.
16 changes: 13 additions & 3 deletions tools/nut-scanner/scan_eaton_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,14 @@ static nutscan_device_t * nutscan_scan_eaton_serial_q1(const char* port_name)
return dev;
}

static void * nutscan_scan_eaton_serial_device(void * port_arg)
/* Wrap calls to actual implementations of nutscan_scan_eaton_serial_shut(),
* nutscan_scan_eaton_serial_xcp() and/or nutscan_scan_eaton_serial_q1()
* which implement the semantics of parallel-able scanning.
* Returns the device entry, updates global dev_ret when a scan is successful.
* DOES NOT FREE the caller's copy of "port_arg", unlike many similar methods
* in other scanners.
*/
static void * nutscan_scan_eaton_serial_device_thready(void * port_arg)
{
nutscan_device_t * dev = NULL;
char* port_name = (char*) port_arg;
Expand All @@ -396,6 +403,7 @@ static void * nutscan_scan_eaton_serial_device(void * port_arg)
}
/* Else try UTalk? */
}

return dev;
}

Expand Down Expand Up @@ -546,7 +554,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
current_port_name = serial_ports_list[current_port_nb];

#ifdef HAVE_PTHREAD
if (pthread_create(&thread, NULL, nutscan_scan_eaton_serial_device, (void*)current_port_name) == 0) {
if (pthread_create(&thread, NULL, nutscan_scan_eaton_serial_device_thready, (void*)current_port_name) == 0) {
nutscan_thread_t *new_thread_array;
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_lock(&threadcount_mutex);
Expand All @@ -571,8 +579,10 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
# endif /* HAVE_PTHREAD_TRYJOIN */
}
#else /* if not HAVE_PTHREAD */
nutscan_scan_eaton_serial_device(current_port_name);
nutscan_scan_eaton_serial_device_thready(current_port_name);
#endif /* if HAVE_PTHREAD */

/* Prepare the next iteration */
current_port_nb++;
} else { /* if not pass -- all slots busy */
#ifdef HAVE_PTHREAD
Expand Down
24 changes: 19 additions & 5 deletions tools/nut-scanner/scan_nut.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ typedef int bool_t;
#endif

struct scan_nut_arg {
/* String includes square brackets around host/IP
* address, and/or :port suffix (if customized so): */
char * hostname;
useconds_t timeout;
};
Expand Down Expand Up @@ -137,9 +139,14 @@ int nutscan_load_upsclient_library(const char *libname_path)
lt_dlexit();
return 0;
}
/* end of dynamic link library stuff */

/* FIXME: SSL support */
static void * list_nut_devices(void * arg)
/* Performs a (parallel-able) NUT protocol scan of one remote host:port.
* Returns NULL, updates global dev_ret when a scan is successful.
* FREES the caller's copy of "nut_arg" and "hostname" in it, if applicable.
*/
static void * list_nut_devices_thready(void * arg)
{
struct scan_nut_arg * nut_arg = (struct scan_nut_arg*)arg;
char *target_hostname = nut_arg->hostname;
Expand Down Expand Up @@ -192,6 +199,7 @@ static void * list_nut_devices(void * arg)
free(ups);
return NULL;
}

/* FIXME: check for duplication by getting driver.port and device.serial
* for comparison with other busses results */
/* FIXME:
Expand Down Expand Up @@ -256,6 +264,7 @@ static void * list_nut_devices(void * arg)
free(target_hostname);
free(nut_arg);
free(ups);

return NULL;
}

Expand Down Expand Up @@ -526,7 +535,7 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons
nut_arg->hostname = ip_dest;

#ifdef HAVE_PTHREAD
if (pthread_create(&thread, NULL, list_nut_devices, (void*)nut_arg) == 0) {
if (pthread_create(&thread, NULL, list_nut_devices_thready, (void*)nut_arg) == 0) {
nutscan_thread_t *new_thread_array;
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_lock(&threadcount_mutex);
Expand All @@ -550,11 +559,16 @@ nutscan_device_t * nutscan_scan_ip_range_nut(nutscan_ip_range_list_t * irl, cons
pthread_mutex_unlock(&threadcount_mutex);
# endif /* HAVE_PTHREAD_TRYJOIN */
}
#else /* not HAVE_PTHREAD */
list_nut_devices(nut_arg);
#else /* if not HAVE_PTHREAD */
list_nut_devices_thready(nut_arg);
#endif /* if HAVE_PTHREAD */

/* Prepare the next iteration */
/* Prepare the next iteration; note that
* nutscan_scan_ipmi_device_thready()
* takes care of freeing "tmp_sec" and its
* copy (note strdup!) of "ip_str" as
* hostname, possibly suffixed with a port.
*/
free(ip_str);
ip_str = nutscan_ip_ranges_iter_inc(&ip);
} else { /* if not pass -- all slots busy */
Expand Down
21 changes: 14 additions & 7 deletions tools/nut-scanner/scan_snmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,11 @@ static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec)
return 1;
}

static void * try_SysOID(void * arg)
/* Performs a (parallel-able) SNMP protocol scan of one remote host.
* Returns NULL, updates global dev_ret when a scan is successful.
* FREES the caller's copy of "sec" and "peername" in it, if applicable.
*/
static void * try_SysOID_thready(void * arg)
{
struct snmp_session snmp_sess;
void * handle;
Expand Down Expand Up @@ -1265,7 +1269,7 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl,
tmp_sec->peername = ip_str;

#ifdef HAVE_PTHREAD
if (pthread_create(&thread, NULL, try_SysOID, (void*)tmp_sec) == 0) {
if (pthread_create(&thread, NULL, try_SysOID_thready, (void*)tmp_sec) == 0) {
nutscan_thread_t *new_thread_array;
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_lock(&threadcount_mutex);
Expand All @@ -1289,14 +1293,17 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl,
pthread_mutex_unlock(&threadcount_mutex);
# endif /* HAVE_PTHREAD_TRYJOIN */
}
#else /* not HAVE_PTHREAD */
try_SysOID((void *)tmp_sec);
#else /* if not HAVE_PTHREAD */
try_SysOID_thready((void *)tmp_sec);
#endif /* if HAVE_PTHREAD */

/* Prepare the next iteration */
/* free(ip_str); */ /* Do not free() here - seems to cause a double-free instead */
/* Prepare the next iteration; note that
* try_SysOID_thready()
* takes care of freeing "tmp_sec" and its
* reference (NOT strdup!) to "ip_str" as
* peername.
*/
ip_str = nutscan_ip_ranges_iter_inc(&ip);
/* free(tmp_sec); */
} else { /* if not pass -- all slots busy */
#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
Expand Down
60 changes: 39 additions & 21 deletions tools/nut-scanner/scan_xml_http.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,11 @@ static int startelm_cb(void *userdata, int parent, const char *nspace, const cha
return result;
}

static void * nutscan_scan_xml_http_generic(void * arg)
/* Performs a (parallel-able) NetXML protocol scan of one remote host:port.
* Returns NULL, updates global dev_ret when a scan is successful.
* FREES the caller's copy of "arg" and "hostname" in it, if applicable.
*/
static void * nutscan_scan_xml_http_thready(void * arg)
{
nutscan_xml_t * sec = (nutscan_xml_t *)arg;
char *scanMsg = "<SCAN_REQUEST/>";
Expand Down Expand Up @@ -221,12 +225,12 @@ static void * nutscan_scan_xml_http_generic(void * arg)
usec_timeout = 5000000; /* Driver default : 5sec */

if (!nutscan_avail_xml_http) {
return NULL;
goto end_free;
}

if ((peerSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
fprintf(stderr, "Error creating socket\n");
return NULL;
goto end_free;
}

/* FIXME : Per http://stackoverflow.com/questions/683624/udp-broadcast-on-all-interfaces
Expand All @@ -237,7 +241,7 @@ static void * nutscan_scan_xml_http_generic(void * arg)
sockAddress_udp.sin_family = AF_INET;
if (ip == NULL) {
upsdebugx(2,
"nutscan_scan_xml_http_generic() : scanning connected network segment(s) "
"nutscan_scan_xml_http_thready() : scanning connected network segment(s) "
"with a broadcast, attempt %d of %d with a timeout of %" PRIdMAX " usec",
(i + 1), MAX_RETRIES, (uintmax_t)usec_timeout);
sockAddress_udp.sin_addr.s_addr = INADDR_BROADCAST;
Expand All @@ -246,7 +250,7 @@ static void * nutscan_scan_xml_http_generic(void * arg)
sizeof(sockopt_on));
} else {
upsdebugx(2,
"nutscan_scan_xml_http_generic() : scanning IP '%s' with a unicast, "
"nutscan_scan_xml_http_thready() : scanning IP '%s' with a unicast, "
"attempt %d of %d with a timeout of %" PRIdMAX " usec",
ip, (i + 1), MAX_RETRIES, (uintmax_t)usec_timeout);
inet_pton(AF_INET, ip, &(sockAddress_udp.sin_addr));
Expand All @@ -273,7 +277,7 @@ static void * nutscan_scan_xml_http_generic(void * arg)
timeout.tv_sec = usec_timeout / 1000000;
timeout.tv_usec = usec_timeout % 1000000;

upsdebugx(5, "nutscan_scan_xml_http_generic() : sent request to %s, "
upsdebugx(5, "nutscan_scan_xml_http_thready() : sent request to %s, "
"loop #%d/%d, waiting for responses",
(ip ? ip : "<broadcast>"), (i + 1), MAX_RETRIES);
while ((ret = select(peerSocket + 1, &fds, NULL, NULL,
Expand All @@ -283,7 +287,7 @@ static void * nutscan_scan_xml_http_generic(void * arg)
int parserFailed;

retNum ++;
upsdebugx(5, "nutscan_scan_xml_http_generic() : request to %s, "
upsdebugx(5, "nutscan_scan_xml_http_thready() : request to %s, "
"loop #%d/%d, response #%d",
(ip ? ip : "<broadcast>"), (i + 1), MAX_RETRIES, retNum);

Expand Down Expand Up @@ -353,7 +357,7 @@ static void * nutscan_scan_xml_http_generic(void * arg)
* Does our driver support the notation? */
nut_dev->port = strdup(buf);
upsdebugx(3,
"nutscan_scan_xml_http_generic(): "
"nutscan_scan_xml_http_thready(): "
"Adding configuration for driver='%s' port='%s'",
nut_dev->driver, nut_dev->port);
dev_ret = nutscan_add_device_to_device(
Expand Down Expand Up @@ -383,32 +387,43 @@ static void * nutscan_scan_xml_http_generic(void * arg)

if (ip != NULL) {
upsdebugx(2,
"nutscan_scan_xml_http_generic(): we collected one reply "
"nutscan_scan_xml_http_thready(): we collected one reply "
"to unicast for %s (repsponse from %s), done",
ip, string);
goto end;
}
} /* while select() responses */
if (ip == NULL && dev_ret != NULL) {
upsdebugx(2,
"nutscan_scan_xml_http_generic(): we collected one round of replies "
"nutscan_scan_xml_http_thready(): we collected one round of replies "
"to broadcast with no errors, done");
goto end;
}
}
}
upsdebugx(2,
"nutscan_scan_xml_http_generic(): no replies collected for %s, done",
"nutscan_scan_xml_http_thready(): no replies collected for %s, done",
(ip ? ip : "<broadcast>"));
goto end;

end_abort:
upsdebugx(1,
"Had to abort nutscan_scan_xml_http_generic() for %s, see fatal details above",
"Had to abort nutscan_scan_xml_http_thready() for %s, see fatal details above",
(ip ? ip : "<broadcast>"));

end:
if (ip != NULL) /* do not free "ip", it comes from caller */
if (ip != NULL)
close(peerSocket);

end_free:
/* free resources which come from the caller
* (in parallel runs, nobody else can reap them)
*/
if (ip != NULL)
free(ip);
if (sec != NULL)
free(sec);

return NULL;
}

Expand Down Expand Up @@ -652,7 +667,7 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl,
}

#ifdef HAVE_PTHREAD
if (pthread_create(&thread, NULL, nutscan_scan_xml_http_generic, (void *)tmp_sec) == 0) {
if (pthread_create(&thread, NULL, nutscan_scan_xml_http_thready, (void*)tmp_sec) == 0) {
nutscan_thread_t *new_thread_array;
# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_lock(&threadcount_mutex);
Expand All @@ -676,14 +691,17 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl,
pthread_mutex_unlock(&threadcount_mutex);
# endif /* HAVE_PTHREAD_TRYJOIN */
}
#else /* not HAVE_PTHREAD */
nutscan_scan_xml_http_generic((void *)tmp_sec);
#else /* if not HAVE_PTHREAD */
nutscan_scan_xml_http_thready(tmp_sec);
#endif /* if HAVE_PTHREAD */

/* Prepare the next iteration */
/* free(ip_str); */ /* One of these free()s seems to cause a double-free instead */
/* Prepare the next iteration; note that
* nutscan_scan_xml_http_thready()
* takes care of freeing "tmp_sec" and its
* reference (NOT strdup!) to "ip_str" as
* peername.
*/
ip_str = nutscan_ip_ranges_iter_inc(&ip);
/* free(tmp_sec); */
} else { /* if not pass -- all slots busy */
#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
Expand Down Expand Up @@ -797,10 +815,10 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl,
tmp_sec->usec_timeout = usec_timeout;
}

nutscan_scan_xml_http_generic(tmp_sec);
/* Note: the thready method releases the resources */
nutscan_scan_xml_http_thready(tmp_sec);
result = nutscan_rewind_device(dev_ret);
dev_ret = NULL;
free(tmp_sec);
return result;
}

Expand Down

0 comments on commit 687dc63

Please sign in to comment.