Skip to content

Commit

Permalink
tools/nut-scanner/*: add ability to use named semaphores [networkupst…
Browse files Browse the repository at this point in the history
…ools#2522]

Signed-off-by: Jim Klimov <[email protected]>
  • Loading branch information
jimklimov committed Jul 17, 2024
1 parent 2fe558a commit 88e99ed
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 114 deletions.
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -796,7 +796,7 @@ sem_destroy(&semaphore);
* normally check for non-zero meaning to look in errno */
]
)],
[AC_DEFINE([HAVE_SEMAPHORE], [1],
[AC_DEFINE([HAVE_SEMAPHORE_UNNAMED], [1],
[Define to 1 if you have <sys/semaphore.h> with usable sem_t, sem_init() and sem_destroy() for unnamed semaphores.])
AC_MSG_RESULT([ok])
Expand Down
22 changes: 19 additions & 3 deletions tools/nut-scanner/nut-scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
#ifdef HAVE_PTHREAD
# include <pthread.h>

# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
# include <semaphore.h>
# endif
#endif
Expand All @@ -82,10 +82,25 @@ extern "C" {
#endif

#ifdef HAVE_PTHREAD
# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE)
# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
extern size_t max_threads, curr_threads, max_threads_netxml, max_threads_oldnut, max_threads_netsnmp, max_threads_ipmi;
# endif

# if defined HAVE_SEMAPHORE_UNNAMED
# define REPORT_SEM_INIT_METHOD "sem_init"
# elif defined HAVE_SEMAPHORE_NAMED
# define REPORT_SEM_INIT_METHOD "sem_open"
# endif

# ifdef HAVE_SEMAPHORE_NAMED
# define SEMNAME_TOPLEVEL "/libnutscan-toplevel-bus-scans"
# define SEMNAME_NETXML "/libnutscan-netxml"
# define SEMNAME_UPSCLIENT "/libnutscan-upsclient"
# define SEMNAME_EATON_SERIAL "/libnutscan-eaton-serial"
# define SEMNAME_SNMP "/libnutscan-snmp"
# define SEMNAME_IPMI "/libnutscan-ipmi"
# endif

# ifdef HAVE_PTHREAD_TRYJOIN
extern pthread_mutex_t threadcount_mutex;
# endif
Expand Down Expand Up @@ -185,10 +200,11 @@ nutscan_device_t * nutscan_scan_ip_range_ipmi(nutscan_ip_range_list_t * irl, nut
nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_list);

#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
/* Expose shared libnutscan semaphore for overall thread count
* limited across different scanning methods (protocols/media): */
sem_t * nutscan_semaphore(void);
void nutscan_semaphore_set(sem_t *s);
# endif
#endif

Expand Down
73 changes: 55 additions & 18 deletions tools/nut-scanner/nut-scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@

#ifdef HAVE_PTHREAD
# include <pthread.h>
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
# include <semaphore.h>
# endif
# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE)
# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
# include "nut_stdint.h"
# ifdef HAVE_SYS_RESOURCE_H
# include <sys/resource.h> /* for getrlimit() and struct rlimit */
Expand All @@ -81,7 +81,7 @@
*/
# define RESERVE_FD_COUNT 3
# endif /* HAVE_SYS_RESOURCE_H */
# endif /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE */
# endif /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */
#endif /* HAVE_PTHREAD */

#include "nut-scan.h"
Expand Down Expand Up @@ -997,7 +997,7 @@ static void show_usage(void)

printf(" -E, --eaton_serial <serial ports list>: Scan serial Eaton devices (XCP, SHUT and Q1).\n");

#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) )
#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED) )
printf(" -T, --thread <max number of threads>: Limit the amount of scanning threads running simultaneously (default: %" PRIuSIZE ").\n", max_threads);
#else
printf(" -T, --thread <max number of threads>: Limit the amount of scanning threads running simultaneously (not implemented in this build: no pthread support)\n");
Expand Down Expand Up @@ -1164,11 +1164,11 @@ int main(int argc, char *argv[])
void (*display_func)(nutscan_device_t * device);
int ret_code = EXIT_SUCCESS;
#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
sem_t *current_sem;
# endif
#endif
#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) ) && (defined HAVE_SYS_RESOURCE_H)
#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED) ) && (defined HAVE_SYS_RESOURCE_H)
struct rlimit nofile_limit;

/* Limit the max scanning thread count by the amount of allowed open
Expand Down Expand Up @@ -1197,7 +1197,7 @@ int main(int argc, char *argv[])
}
}
}
#endif /* HAVE_PTHREAD && ( HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE ) && HAVE_SYS_RESOURCE_H */
#endif /* HAVE_PTHREAD && ( HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED ) && HAVE_SYS_RESOURCE_H */

memset(&snmp_sec, 0, sizeof(snmp_sec));
memset(&ipmi_sec, 0, sizeof(ipmi_sec));
Expand Down Expand Up @@ -1428,7 +1428,7 @@ int main(int argc, char *argv[])
port = strdup(optarg);
break;
case 'T': {
#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) )
#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED) )
char* endptr;
long val = strtol(optarg, &endptr, 10);
/* With endptr we check that no chars were left in optarg
Expand Down Expand Up @@ -1569,21 +1569,40 @@ int main(int argc, char *argv[])
}

#ifdef HAVE_PTHREAD
# if (defined HAVE_PTHREAD_TRYJOIN) && (defined HAVE_SEMAPHORE)
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && HAVE_PTHREAD_TRYJOIN && HAVE_SEMAPHORE");
{ /* scoping for the string */
# if defined HAVE_SEMAPHORE_UNNAMED
char * semsuf = "_UNNAMED";
# elif defined HAVE_SEMAPHORE_NAMED
char * semsuf = "_NAMED";
# else
char * semsuf = "*";
# endif
NUT_UNUSED_VARIABLE(semsuf); /* just in case */

# if (defined HAVE_PTHREAD_TRYJOIN) && ((defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED))
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && HAVE_PTHREAD_TRYJOIN && HAVE_SEMAPHORE%s", semsuf);
# elif (defined HAVE_PTHREAD_TRYJOIN)
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && HAVE_PTHREAD_TRYJOIN && !HAVE_SEMAPHORE");
# elif (defined HAVE_SEMAPHORE)
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && !HAVE_PTHREAD_TRYJOIN && HAVE_SEMAPHORE");
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && HAVE_PTHREAD_TRYJOIN && !HAVE_SEMAPHORE*");
# elif (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && !HAVE_PTHREAD_TRYJOIN && HAVE_SEMAPHORE%s", semsuf);
# else
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && !HAVE_PTHREAD_TRYJOIN && !HAVE_SEMAPHORE");
upsdebugx(1, "Parallel scan support: HAVE_PTHREAD && !HAVE_PTHREAD_TRYJOIN && !HAVE_SEMAPHORE*");
# endif
# ifdef HAVE_SEMAPHORE
}

# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
/* FIXME: Currently sem_init already done on nutscan-init for lib need.
We need to destroy it before re-init. We currently can't change "sem value"
on lib (need to be thread safe). */
current_sem = nutscan_semaphore();
# ifdef HAVE_SEMAPHORE_UNNAMED
sem_destroy(current_sem);
# elif defined HAVE_SEMAPHORE_NAMED
if (current_sem) {
sem_unlink(SEMNAME_TOPLEVEL);
sem_close(current_sem);
}
# endif
#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE
#pragma GCC diagnostic push
#endif
Expand All @@ -1603,15 +1622,27 @@ int main(int argc, char *argv[])
#pragma GCC diagnostic pop
#endif
fprintf(stderr, "\n\n"
"WARNING: Limiting max_threads to range acceptable for sem_init()\n\n");
"WARNING: Limiting max_threads to range acceptable for "
REPORT_SEM_INIT_METHOD "()\n\n");
max_threads = UINT_MAX - 1;
}

upsdebugx(1, "Parallel scan support: max_threads=%" PRIuSIZE, max_threads);
# ifdef HAVE_SEMAPHORE_UNNAMED
if (sem_init(current_sem, 0, (unsigned int)max_threads)) {
/* Show this one to end-users so they know */
upsdebug_with_errno(0, "Parallel scan support: sem_init() failed");
upsdebug_with_errno(0, "Parallel scan support: " REPORT_SEM_INIT_METHOD "() failed");
}
# elif defined HAVE_SEMAPHORE_NAMED
/* FIXME: Do we need O_EXCL here? */
if (SEM_FAILED == (current_sem = sem_open(SEMNAME_TOPLEVEL, O_CREAT, 0644, (unsigned int)max_threads))) {
/* Show this one to end-users so they know */
upsdebug_with_errno(0, "Parallel scan support: " REPORT_SEM_INIT_METHOD "() failed");
current_sem = NULL;
}
nutscan_semaphore_set(current_sem);
# endif

# endif
#else
upsdebugx(1, "Parallel scan support: !HAVE_PTHREAD");
Expand Down Expand Up @@ -1887,8 +1918,14 @@ int main(int argc, char *argv[])
nutscan_free_device(dev[TYPE_EATON_SERIAL]);

#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
# ifdef HAVE_SEMAPHORE_UNNAMED
sem_destroy(nutscan_semaphore());
# elif defined HAVE_SEMAPHORE_NAMED
if (nutscan_semaphore()) {
sem_unlink(SEMNAME_TOPLEVEL);
sem_close(nutscan_semaphore());
nutscan_semaphore_set(NULL);
}
# endif
#endif

Expand Down
50 changes: 42 additions & 8 deletions tools/nut-scanner/nutscan-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,41 @@ int nutscan_load_upsclient_library(const char *libname_path);
int nutscan_unload_upsclient_library(void);

#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
# ifdef HAVE_SEMAPHORE_UNNAMED
/* Shared by library consumers, exposed by nutscan_semaphore() below */
static sem_t semaphore;

sem_t * nutscan_semaphore(void)
{
return &semaphore;
}
# endif /* HAVE_SEMAPHORE */

void nutscan_semaphore_set(sem_t *s)
{
NUT_UNUSED_VARIABLE(s);
}
# elif defined HAVE_SEMAPHORE_NAMED
/* Shared by library consumers, exposed by nutscan_semaphore() below.
* Methods like sem_open() return the pointer and sem_close() frees its data.
*/
static sem_t *semaphore = NULL; /* TOTHINK: maybe SEM_FAILED? */

sem_t * nutscan_semaphore(void)
{
return semaphore;
}

void nutscan_semaphore_set(sem_t *s)
{
semaphore = s;
}
# endif /* HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */

# ifdef HAVE_PTHREAD_TRYJOIN
pthread_mutex_t threadcount_mutex;
# endif

# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE)
# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
/* We have 3 networked scan types: nut, snmp, xml,
* and users typically give their /24 subnet as "-m" arg.
* With some systems having a 1024 default (u)limit to
Expand All @@ -113,7 +133,7 @@ size_t max_threads_netsnmp = 0; /* 10240; */
*/
size_t max_threads_ipmi = 0; /* limits not yet known */

# endif /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE */
# endif /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */

#endif /* HAVE_PTHREAD */

Expand Down Expand Up @@ -148,7 +168,7 @@ void nutscan_init(void)
* and the more naive but portable methods be an
* if-else proposition? At least when initializing?
*/
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
/* NOTE: This semaphore may get re-initialized in nut-scanner program
* after parsing command-line arguments. It calls nutscan_init() before
* parsing CLI, to know about available libs and to set defaults below.
Expand All @@ -172,16 +192,24 @@ void nutscan_init(void)
#pragma GCC diagnostic pop
#endif
upsdebugx(1,
"WARNING: %s: Limiting max_threads to range acceptable for sem_init()",
"WARNING: %s: Limiting max_threads to range acceptable for " REPORT_SEM_INIT_METHOD "()",
__func__);
max_threads = UINT_MAX - 1;
}

upsdebugx(1, "%s: Parallel scan support: max_threads=%" PRIuSIZE,
__func__, max_threads);
# ifdef HAVE_SEMAPHORE_UNNAMED
if (sem_init(&semaphore, 0, (unsigned int)max_threads)) {
upsdebug_with_errno(4, "%s: Parallel scan support: sem_init() failed", __func__);
upsdebug_with_errno(4, "%s: Parallel scan support: " REPORT_SEM_INIT_METHOD "() failed", __func__);
}
# elif defined HAVE_SEMAPHORE_NAMED
/* FIXME: Do we need O_EXCL here? */
if (SEM_FAILED == (semaphore = sem_open(SEMNAME_TOPLEVEL, O_CREAT, 0644, (unsigned int)max_threads))) {
upsdebug_with_errno(4, "%s: Parallel scan support: " REPORT_SEM_INIT_METHOD "() failed", __func__);
semaphore = NULL;
}
# endif
# endif

# ifdef HAVE_PTHREAD_TRYJOIN
Expand Down Expand Up @@ -674,8 +702,14 @@ void nutscan_free(void)

#ifdef HAVE_PTHREAD
/* TOTHINK: See comments near mutex/semaphore init code above */
# ifdef HAVE_SEMAPHORE
# ifdef HAVE_SEMAPHORE_UNNAMED
sem_destroy(nutscan_semaphore());
# elif defined HAVE_SEMAPHORE_NAMED
if (nutscan_semaphore()) {
sem_unlink(SEMNAME_TOPLEVEL);
sem_close(nutscan_semaphore());
nutscan_semaphore_set(NULL);
}
# endif

# ifdef HAVE_PTHREAD_TRYJOIN
Expand Down
16 changes: 8 additions & 8 deletions tools/nut-scanner/scan_eaton_serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -419,13 +419,13 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
int current_port_nb;

#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
sem_t * semaphore = nutscan_semaphore();
/* TODO? Port semaphore_scantype / max_threads_scantype
* from sibling sources? We do not have that many serial
* ports to care much, usually... right?
*/
# endif /* HAVE_SEMAPHORE */
# endif /* HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */
pthread_t thread;
nutscan_thread_t * thread_array = NULL;
size_t thread_count = 0, i;
Expand Down Expand Up @@ -467,7 +467,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
* for below in pthread_join()...
*/

# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
/* Just wait for someone to free a semaphored slot,
* if none are available, and then/otherwise grab one
*/
Expand Down Expand Up @@ -547,7 +547,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
/* NOTE: No change to default "pass" in this ifdef:
* if we got to this line, we have a slot to use */
# endif /* HAVE_PTHREAD_TRYJOIN */
# endif /* HAVE_SEMAPHORE */
# endif /* HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */
#endif /* HAVE_PTHREAD */

if (pass) {
Expand Down Expand Up @@ -586,7 +586,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
current_port_nb++;
} else { /* if not pass -- all slots busy */
#ifdef HAVE_PTHREAD
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
/* Wait for all current scans to complete */
if (thread_array != NULL) {
upsdebugx (2, "%s: Running too many scanning threads (%"
Expand Down Expand Up @@ -619,7 +619,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
# ifdef HAVE_PTHREAD_TRYJOIN
/* TODO: Move the wait-loop for TRYJOIN here? */
# endif /* HAVE_PTHREAD_TRYJOIN */
# endif /* HAVE_SEMAPHORE */
# endif /* HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */
#endif /* HAVE_PTHREAD */
} /* if: could we "pass" or not? */
} /* while */
Expand All @@ -638,7 +638,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
__func__, ret);
}
thread_array[i].active = FALSE;
# ifdef HAVE_SEMAPHORE
# if (defined HAVE_SEMAPHORE_UNNAMED) || (defined HAVE_SEMAPHORE_NAMED)
sem_post(semaphore);
# else
# ifdef HAVE_PTHREAD_TRYJOIN
Expand All @@ -653,7 +653,7 @@ nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range)
}
pthread_mutex_unlock(&threadcount_mutex);
# endif /* HAVE_PTHREAD_TRYJOIN */
# endif /* HAVE_SEMAPHORE */
# endif /* HAVE_SEMAPHORE_UNNAMED || HAVE_SEMAPHORE_NAMED */
}
free(thread_array);
upsdebugx(2, "%s: all threads freed", __func__);
Expand Down
Loading

0 comments on commit 88e99ed

Please sign in to comment.