From f2e922917ee5d159a33b898022d830059901ea50 Mon Sep 17 00:00:00 2001 From: Jim Klimov Date: Wed, 10 Jul 2024 20:04:39 +0200 Subject: [PATCH] tools/nut-scanner/*.c: refactor nutscan_free() to actively lt_dlclose() the loaded library modules [#2511] Avoid some memory leak reports Signed-off-by: Jim Klimov --- tools/nut-scanner/nut-scanner.c | 2 +- tools/nut-scanner/nutscan-init.c | 78 +++++++++++++++++++++++++------ tools/nut-scanner/scan_avahi.c | 25 ++++++++++ tools/nut-scanner/scan_ipmi.c | 25 ++++++++++ tools/nut-scanner/scan_nut.c | 19 ++++++++ tools/nut-scanner/scan_snmp.c | 34 +++++++++++++- tools/nut-scanner/scan_usb.c | 27 +++++++++++ tools/nut-scanner/scan_xml_http.c | 25 ++++++++++ 8 files changed, 219 insertions(+), 16 deletions(-) diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index eea548a650..a198f526ad 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -1886,8 +1886,8 @@ int main(int argc, char *argv[]) #endif upsdebugx(1, "SCANS DONE: free common scanner resources"); - nutscan_free(); nutscan_free_ip_ranges(&ip_ranges_list); + nutscan_free(); upsdebugx(1, "SCANS DONE: EXIT_SUCCESS"); return EXIT_SUCCESS; diff --git a/tools/nut-scanner/nutscan-init.c b/tools/nut-scanner/nutscan-init.c index d415299aff..bf458ec394 100644 --- a/tools/nut-scanner/nutscan-init.c +++ b/tools/nut-scanner/nutscan-init.c @@ -41,6 +41,11 @@ # if defined HAVE_WINSOCK2_H && HAVE_WINSOCK2_H # include # endif +#endif + +/* FIXME: We may want to (also?) use lt_dlopenext(), so + * that libtool would offer platform-specific extensions */ +#ifdef WIN32 # define SOEXT ".dll" #else # ifdef NUT_PLATFORM_APPLE_OSX @@ -61,12 +66,19 @@ int nutscan_avail_snmp = 0; int nutscan_avail_usb = 0; int nutscan_avail_xml_http = 0; +/* Methods defined in scan_*.c source files */ int nutscan_load_usb_library(const char *libname_path); +int nutscan_unload_usb_library(void); int nutscan_load_snmp_library(const char *libname_path); +int nutscan_unload_snmp_library(void); int nutscan_load_neon_library(const char *libname_path); +int nutscan_unload_neon_library(void); int nutscan_load_avahi_library(const char *libname_path); +int nutscan_unload_avahi_library(void); int nutscan_load_ipmi_library(const char *libname_path); +int nutscan_unload_ipmi_library(void); int nutscan_load_upsclient_library(const char *libname_path); +int nutscan_unload_upsclient_library(void); #ifdef HAVE_PTHREAD # ifdef HAVE_SEMAPHORE @@ -586,27 +598,67 @@ void nutscan_init(void) nutscan_avail_nut_simulation = 1; } -void nutscan_free(void) +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally to scan_* modules */ +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath) { - if (nutscan_avail_usb) { - lt_dlexit(); + int ret = -1; + + if (avail == NULL || pdl_handle == NULL) { + upsdebugx(1, "%s: called with bad inputs, no-op", __func__); + return -2; } - if (nutscan_avail_snmp) { - lt_dlexit(); + + /* never tried/already unloaded */ + if (*pdl_handle == NULL) { + goto end; } - if (nutscan_avail_xml_http) { - lt_dlexit(); + + /* if previous init failed */ + if (*pdl_handle == (void *)1) { + goto end; } - if (nutscan_avail_avahi) { - lt_dlexit(); + + if (*avail == 0) { + upsdebugx(1, "%s: Asked to unload a module %p " + "for %s but our flag says it is not loaded", + __func__, (void *)(*pdl_handle), + (libpath && *libpath && **libpath) + ? *libpath + : ""); } - if (nutscan_avail_ipmi) { - lt_dlexit(); + + /* init has already been done */ + if (libpath && *libpath && **libpath) { + upsdebugx(1, "%s: unloading module %s", + __func__, *libpath); } - if (nutscan_avail_nut) { - lt_dlexit(); + ret = lt_dlclose(*pdl_handle); + lt_dlexit(); + +end: + *pdl_handle = NULL; + *avail = 0; + + if (libpath && *libpath) { + free(*libpath); + *libpath = NULL; } + return ret; +} + +void nutscan_free(void) +{ + nutscan_unload_usb_library(); + nutscan_unload_snmp_library(); + nutscan_unload_neon_library(); + nutscan_unload_avahi_library(); + nutscan_unload_ipmi_library(); + nutscan_unload_upsclient_library(); + #ifdef HAVE_PTHREAD /* TOTHINK: See comments near mutex/semaphore init code above */ # ifdef HAVE_SEMAPHORE diff --git a/tools/nut-scanner/scan_avahi.c b/tools/nut-scanner/scan_avahi.c index d7813777a4..d83174167f 100644 --- a/tools/nut-scanner/scan_avahi.c +++ b/tools/nut-scanner/scan_avahi.c @@ -27,6 +27,9 @@ #include "common.h" #include "nut-scan.h" +/* externally visible to nutscan-init */ +int nutscan_unload_avahi_library(void); + #ifdef WITH_AVAHI #include @@ -48,6 +51,7 @@ /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +static char *dl_saved_libname = NULL; static AvahiClient* (*nut_avahi_service_browser_get_client)(AvahiServiceBrowser *); static int (*nut_avahi_simple_poll_loop)(AvahiSimplePoll *s); @@ -91,6 +95,15 @@ static int (*nut_avahi_service_browser_free)(AvahiServiceBrowser *); static char * (*nut_avahi_address_snprint)(char *ret_s, size_t length, const AvahiAddress *a); static const AvahiPoll* (*nut_avahi_simple_poll_get)(AvahiSimplePoll *s); +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally */ +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +int nutscan_unload_avahi_library(void) +{ + return nutscan_unload_library(&nutscan_avail_avahi, &dl_handle, &dl_saved_libname); +} + /* return 0 on error; visible externally */ int nutscan_load_avahi_library(const char *libname_path); int nutscan_load_avahi_library(const char *libname_path) @@ -210,6 +223,10 @@ int nutscan_load_avahi_library(const char *libname_path) goto err; } + if (dl_saved_libname) + free(dl_saved_libname); + dl_saved_libname = xstrdup(libname_path); + return 1; err: fprintf(stderr, @@ -217,6 +234,10 @@ int nutscan_load_avahi_library(const char *libname_path) libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); + if (dl_saved_libname) { + free(dl_saved_libname); + dl_saved_libname = NULL; + } return 0; } /* end of dynamic link library stuff */ @@ -600,4 +621,8 @@ nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) return NULL; } +int nutscan_unload_avahi_library(void) +{ + return 0; +} #endif /* WITH_AVAHI */ diff --git a/tools/nut-scanner/scan_ipmi.c b/tools/nut-scanner/scan_ipmi.c index eb00db95fe..867fd927ee 100644 --- a/tools/nut-scanner/scan_ipmi.c +++ b/tools/nut-scanner/scan_ipmi.c @@ -28,6 +28,9 @@ #include "nut-scan.h" #include "nut_stdint.h" +/* externally visible to nutscan-init */ +int nutscan_unload_ipmi_library(void); + #ifdef WITH_IPMI #include "upsclient.h" @@ -46,6 +49,7 @@ /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +static char *dl_saved_libname = NULL; #ifdef HAVE_FREEIPMI_11X_12X /* Functions symbols remapping */ @@ -134,6 +138,15 @@ typedef int bool_t; static nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t * sec); static void * nutscan_scan_ipmi_device_thready(void * arg_sec); +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally */ +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +int nutscan_unload_ipmi_library(void) +{ + return nutscan_unload_library(&nutscan_avail_ipmi, &dl_handle, &dl_saved_libname); +} + /* Return 0 on error; visible externally */ int nutscan_load_ipmi_library(const char *libname_path); int nutscan_load_ipmi_library(const char *libname_path) @@ -261,6 +274,10 @@ int nutscan_load_ipmi_library(const char *libname_path) goto err; } + if (dl_saved_libname) + free(dl_saved_libname); + dl_saved_libname = xstrdup(libname_path); + return 1; err: @@ -269,6 +286,10 @@ int nutscan_load_ipmi_library(const char *libname_path) libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); + if (dl_saved_libname) { + free(dl_saved_libname); + dl_saved_libname = NULL; + } return 0; } /* end of dynamic link library stuff */ @@ -1048,4 +1069,8 @@ nutscan_device_t * nutscan_scan_ip_range_ipmi(nutscan_ip_range_list_t * irl, nu return NULL; } +int nutscan_unload_ipmi_library(void) +{ + return 0; +} #endif /* WITH_IPMI */ diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index a29f39a576..b1625ad8c6 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -37,6 +37,7 @@ /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +static char *dl_saved_libname = NULL; static int (*nut_upscli_splitaddr)(const char *buf, char **hostname, uint16_t *port); static int (*nut_upscli_tryconnect)(UPSCONN_t *ups, const char *host, uint16_t port, @@ -68,6 +69,16 @@ struct scan_nut_arg { useconds_t timeout; }; +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally */ +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +int nutscan_unload_upsclient_library(void); +int nutscan_unload_upsclient_library(void) +{ + return nutscan_unload_library(&nutscan_avail_nut, &dl_handle, &dl_saved_libname); +} + /* return 0 on error; visible externally */ int nutscan_load_upsclient_library(const char *libname_path); int nutscan_load_upsclient_library(const char *libname_path) @@ -129,6 +140,10 @@ int nutscan_load_upsclient_library(const char *libname_path) goto err; } + if (dl_saved_libname) + free(dl_saved_libname); + dl_saved_libname = xstrdup(libname_path); + return 1; err: @@ -137,6 +152,10 @@ int nutscan_load_upsclient_library(const char *libname_path) libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); + if (dl_saved_libname) { + free(dl_saved_libname); + dl_saved_libname = NULL; + } return 0; } /* end of dynamic link library stuff */ diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 6f971f5c5c..57007e2d79 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -29,6 +29,9 @@ #include "nut-scan.h" #include "nut_stdint.h" +/* externally visible to nutscan-init */ +int nutscan_unload_snmp_library(void); + #ifdef WITH_SNMP #ifndef WIN32 @@ -116,6 +119,7 @@ typedef int bool_t; /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +static char *dl_saved_libname = NULL; #endif /* WITH_SNMP_STATIC */ static void (*nut_init_snmp)(const char *type); @@ -177,9 +181,23 @@ static oid *nut_usmHMAC256SHA384AuthProtocol; static oid *nut_usmHMAC384SHA512AuthProtocol; #endif +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally */ +#ifndef WITH_SNMP_STATIC +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +#endif +int nutscan_unload_snmp_library(void) +{ +#ifdef WITH_SNMP_STATIC + return 0; +#else + return nutscan_unload_library(&nutscan_avail_snmp, &dl_handle, &dl_saved_libname); +#endif +} + /* return 0 on error; visible externally */ int nutscan_load_snmp_library(const char *libname_path); - int nutscan_load_snmp_library(const char *libname_path) { #ifdef WITH_SNMP_STATIC @@ -453,7 +471,11 @@ int nutscan_load_snmp_library(const char *libname_path) } #endif /* NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol */ -#endif /* WITH_SNMP_STATIC */ + if (dl_saved_libname) + free(dl_saved_libname); + dl_saved_libname = xstrdup(libname_path); + +#endif /* not WITH_SNMP_STATIC */ return 1; @@ -464,6 +486,10 @@ int nutscan_load_snmp_library(const char *libname_path) libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); + if (dl_saved_libname) { + free(dl_saved_libname); + dl_saved_libname = NULL; + } return 0; #endif /* not WITH_SNMP_STATIC */ } @@ -1422,4 +1448,8 @@ nutscan_device_t * nutscan_scan_ip_range_snmp(nutscan_ip_range_list_t * irl, return NULL; } +int nutscan_unload_snmp_library(void) +{ + return 0; +} #endif /* WITH_SNMP */ diff --git a/tools/nut-scanner/scan_usb.c b/tools/nut-scanner/scan_usb.c index 164f6c7983..0b756991ca 100644 --- a/tools/nut-scanner/scan_usb.c +++ b/tools/nut-scanner/scan_usb.c @@ -26,6 +26,9 @@ #include "common.h" #include "nut-scan.h" +/* externally visible to nutscan-init */ +int nutscan_unload_usb_library(void); + #ifdef WITH_USB #include "upsclient.h" @@ -37,6 +40,8 @@ /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +static char *dl_saved_libname = NULL; + static int (*nut_usb_close)(libusb_device_handle *dev); static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index, char *buf, size_t buflen); @@ -76,6 +81,15 @@ static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index, static char * (*nut_usb_strerror)(void); #endif /* WITH_LIBUSB_1_0 */ +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally */ +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +int nutscan_unload_usb_library(void) +{ + return nutscan_unload_library(&nutscan_avail_usb, &dl_handle, &dl_saved_libname); +} + /* return 0 on error; visible externally */ int nutscan_load_usb_library(const char *libname_path); int nutscan_load_usb_library(const char *libname_path) @@ -219,6 +233,10 @@ int nutscan_load_usb_library(const char *libname_path) } #endif /* WITH_LIBUSB_1_0 */ + if (dl_saved_libname) + free(dl_saved_libname); + dl_saved_libname = xstrdup(libname_path); + return 1; err: @@ -227,6 +245,10 @@ int nutscan_load_usb_library(const char *libname_path) libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); + if (dl_saved_libname) { + free(dl_saved_libname); + dl_saved_libname = NULL; + } return 0; } /* end of dynamic link library stuff */ @@ -663,4 +685,9 @@ nutscan_device_t * nutscan_scan_usb(nutscan_usb_t * scanopts) return NULL; } + +int nutscan_unload_usb_library(void) +{ + return 0; +} #endif /* WITH_USB */ diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index 1e46191d1e..51a92cfed2 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -30,6 +30,9 @@ #include "nut-scan.h" #include "nut_stdint.h" +/* externally visible to nutscan-init */ +int nutscan_unload_neon_library(void); + #ifdef WITH_NEON #ifndef WIN32 @@ -60,6 +63,7 @@ /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; +static char *dl_saved_libname = NULL; static void (*nut_ne_xml_push_handler)(ne_xml_parser *p, ne_xml_startelm_cb *startelm, @@ -85,6 +89,15 @@ typedef enum ebool { FALSE = 0, TRUE } bool_t; typedef int bool_t; #endif +/* return 0 on success, -1 on error e.g. "was not loaded"; + * other values may be possible if lt_dlclose() errors set them; + * visible externally */ +int nutscan_unload_library(int *avail, lt_dlhandle *pdl_handle, char **libpath); +int nutscan_unload_neon_library(void) +{ + return nutscan_unload_library(&nutscan_avail_xml_http, &dl_handle, &dl_saved_libname); +} + /* return 0 on error; visible externally */ int nutscan_load_neon_library(const char *libname_path); int nutscan_load_neon_library(const char *libname_path) @@ -141,6 +154,10 @@ int nutscan_load_neon_library(const char *libname_path) goto err; } + if (dl_saved_libname) + free(dl_saved_libname); + dl_saved_libname = xstrdup(libname_path); + return 1; err: fprintf(stderr, @@ -148,6 +165,10 @@ int nutscan_load_neon_library(const char *libname_path) libname_path, dl_error); dl_handle = (void *)1; lt_dlexit(); + if (dl_saved_libname) { + free(dl_saved_libname); + dl_saved_libname = NULL; + } return 0; } @@ -845,4 +866,8 @@ nutscan_device_t * nutscan_scan_ip_range_xml_http(nutscan_ip_range_list_t * irl, return NULL; } +int nutscan_unload_neon_library(void) +{ + return 0; +} #endif /* WITH_NEON */