From 24b1513296fd61722166625ad46be1c56a5efc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20G=C3=B6ttsche?= Date: Sat, 30 Mar 2024 13:47:14 +0100 Subject: linux: use dlopen for libnl3 instead of dynamic linking Instead of the current behavior of dynamic linking against libnl3 and libnl-genl-3 when configured with --enable-delayacct, load the shared libraries on request, if any delay accounting related process field is active, via dlopen(3), similar to libsensors and libsystemd. Distribution, who currently build htop with --enable-delayacct, need to explicitly add libnl3 and libnl-genl-3 as runtime dependencies to continue supporting delay accounting out-of-the-box. --- README.md | 5 +- configure.ac | 38 +++++--------- linux/LibNl.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 151 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 7ace7cd1..40887254 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ List of additional build-time dependencies (based on feature flags): * `sensors` * `hwloc` * `libcap` (v2.21 or later) -* `libnl-3` +* `libnl-3` and `libnl-genl-3` Install these and other required packages for C development from your package manager. @@ -137,7 +137,7 @@ To install on the local system run `make install`. By default `make install` ins - default: *no* * `--enable-delayacct`: enable Linux delay accounting support - - dependencies: *pkg-config*(build-time), *libnl-3* and *libnl-genl-3* + - dependencies: *libnl-3-dev*(build-time) and *libnl-genl-3-dev*(build-time), at runtime *libnl-3* and *libnl-genl-3* are loaded via `dlopen(3)` if available and requested - default: *check* @@ -153,6 +153,7 @@ To install on the local system run `make install`. By default `make install` ins * `libcap`, user-space interfaces to POSIX 1003.1e capabilities, is always required when `--enable-capabilities` was used to configure `htop`. * `libsensors`, readout of temperatures and CPU speeds, is optional even when `--enable-sensors` was used to configure `htop`. * `libsystemd` is optional when `--enable-static` was not used to configure `htop`. If building statically and `libsystemd` is not found by `configure`, support for the systemd meter is disabled entirely. +* `libnl-3` and `libnl-genl-3`, if `htop` was configured with `--enable-delayacct` and delay accounting process fields are active. `htop` checks for the availability of the actual runtime libraries as `htop` runs. diff --git a/configure.ac b/configure.ac index b3137dec..0a90ca48 100644 --- a/configure.ac +++ b/configure.ac @@ -658,40 +658,26 @@ case "$enable_delayacct" in elif test "$enable_static" = yes; then enable_delayacct=no else - m4_ifdef([PKG_PROG_PKG_CONFIG], [ - enable_delayacct=yes - PKG_PROG_PKG_CONFIG() - PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [enable_delayacct=no]) - PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [enable_delayacct=no]) - if test "$enable_delayacct" = yes; then - AM_CFLAGS="$AM_CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" - LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" - AC_DEFINE([HAVE_DELAYACCT], [1], [Define if delay accounting support should be enabled.]) - fi - ], [ - enable_delayacct=no - AC_MSG_NOTICE([Linux delay accounting support can not be enabled, cause pkg-config is required for checking its availability]) - ]) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -I/usr/include/libnl3" + AC_CHECK_HEADERS([netlink/attr.h netlink/handlers.h netlink/msg.h], [enable_delayacct=yes], [enable_delayacct=no]) + CFLAGS="$old_CFLAGS" fi ;; yes) - m4_ifdef([PKG_PROG_PKG_CONFIG], [ - PKG_PROG_PKG_CONFIG() - PKG_CHECK_MODULES(LIBNL3, libnl-3.0, [], [AC_MSG_ERROR([can not find required library libnl3])]) - PKG_CHECK_MODULES(LIBNL3GENL, libnl-genl-3.0, [], [AC_MSG_ERROR([can not find required library libnl3genl])]) - AM_CFLAGS="$AM_CFLAGS $LIBNL3_CFLAGS $LIBNL3GENL_CFLAGS" - LIBS="$LIBS $LIBNL3_LIBS $LIBNL3GENL_LIBS" - AC_DEFINE([HAVE_DELAYACCT], [1], [Define if delay accounting support should be enabled.]) - ], [ - pkg_m4_absent=1 - m4_warning([configure is generated without pkg.m4. 'make dist' target will be disabled.]) - AC_MSG_ERROR([htop on Linux requires pkg-config for checking delayacct requirements. Please install pkg-config and run ./autogen.sh to rebuild the configure script.]) - ]) + old_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -I/usr/include/libnl3" + AC_CHECK_HEADERS([netlink/attr.h netlink/handlers.h netlink/msg.h], [], [AC_MSG_ERROR([can not find required header files netlink/attr.h, netlink/handlers.h, netlink/msg.h])]) + CFLAGS="$old_CFLAGS" ;; *) AC_MSG_ERROR([bad value '$enable_delayacct' for --enable-delayacct]) ;; esac +if test "$enable_delayacct" = yes; then + AC_DEFINE([HAVE_DELAYACCT], [1], [Define if delay accounting support should be enabled.]) + AM_CFLAGS="$AM_CFLAGS -I/usr/include/libnl3" +fi AM_CONDITIONAL([HAVE_DELAYACCT], [test "$enable_delayacct" = yes]) diff --git a/linux/LibNl.c b/linux/LibNl.c index e61b14f6..72465f22 100644 --- a/linux/LibNl.c +++ b/linux/LibNl.c @@ -13,36 +13,149 @@ in the source distribution for its full text. #include "linux/LibNl.h" +#include + #include #include #include #include #include -#include -#include -#include -#include +static void* libnlHandle; +static void* libnlGenlHandle; + +static void (*sym_nl_close)(struct nl_sock*); +static int (*sym_nl_connect)(struct nl_sock*, int); +static int (*sym_nl_recvmsgs_default)(struct nl_sock*); +static int (*sym_nl_send_sync)(struct nl_sock*, struct nl_msg*); +static struct nl_sock* (*sym_nl_socket_alloc)(void); +static void (*sym_nl_socket_free)(struct nl_sock*); +static int (*sym_nl_socket_modify_cb)(struct nl_sock*, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void*); +static void* (*sym_nla_data)(const struct nlattr*); +static struct nlattr* (*sym_nla_next)(const struct nlattr*, int*); +static int (*sym_nla_put_u32)(struct nl_msg*, int, uint32_t); +static struct nl_msg* (*sym_nlmsg_alloc)(void); +static struct nlmsghdr* (*sym_nlmsg_hdr)(struct nl_msg*); +static void (*sym_nlmsg_free)(struct nl_msg*); + +static int (*sym_genl_ctrl_resolve)(struct nl_sock*, const char*); +static int (*sym_genlmsg_parse)(struct nlmsghdr*, int, struct nlattr**, int, const struct nla_policy*); +static void* (*sym_genlmsg_put)(struct nl_msg*, uint32_t, uint32_t, int, int, int, uint8_t, uint8_t); + + +static void unload_libnl(void) { + sym_nl_close = NULL; + sym_nl_connect = NULL; + sym_nl_recvmsgs_default = NULL; + sym_nl_send_sync = NULL; + sym_nl_socket_alloc = NULL; + sym_nl_socket_free = NULL; + sym_nl_socket_modify_cb = NULL; + sym_nla_data = NULL; + sym_nla_next = NULL; + sym_nla_put_u32 = NULL; + sym_nlmsg_alloc = NULL; + sym_nlmsg_free = NULL; + sym_nlmsg_hdr = NULL; + + sym_genl_ctrl_resolve = NULL; + sym_genlmsg_parse = NULL; + sym_genlmsg_put = NULL; + + if (libnlGenlHandle) { + dlclose(libnlGenlHandle); + libnlGenlHandle = NULL; + } + if (libnlHandle) { + dlclose(libnlHandle); + libnlHandle = NULL; + } +} + +static int load_libnl(void) { + if (libnlHandle && libnlGenlHandle) + return 0; + + libnlHandle = dlopen("libnl-3.so", RTLD_LAZY); + if (!libnlHandle) { + libnlHandle = dlopen("libnl-3.so.200", RTLD_LAZY); + if (!libnlHandle) { + goto dlfailure; + } + } + + libnlGenlHandle = dlopen("libnl-genl-3.so", RTLD_LAZY); + if (!libnlGenlHandle) { + libnlGenlHandle = dlopen("libnl-genl-3.so.200", RTLD_LAZY); + if (!libnlGenlHandle) { + goto dlfailure; + } + } + + /* Clear any errors */ + dlerror(); + + #define resolve(handle, symbolname) do { \ + *(void **)(&sym_##symbolname) = dlsym(handle, #symbolname); \ + if (!sym_##symbolname || dlerror() != NULL) { \ + goto dlfailure; \ + } \ + } while(0) + + resolve(libnlHandle, nl_close); + resolve(libnlHandle, nl_connect); + resolve(libnlHandle, nl_recvmsgs_default); + resolve(libnlHandle, nl_send_sync); + resolve(libnlHandle, nl_socket_alloc); + resolve(libnlHandle, nl_socket_free); + resolve(libnlHandle, nl_socket_modify_cb); + resolve(libnlHandle, nla_data); + resolve(libnlHandle, nla_next); + resolve(libnlHandle, nla_put_u32); + resolve(libnlHandle, nlmsg_alloc); + resolve(libnlHandle, nlmsg_free); + resolve(libnlHandle, nlmsg_hdr); + + resolve(libnlGenlHandle, genl_ctrl_resolve); + resolve(libnlGenlHandle, genlmsg_parse); + resolve(libnlGenlHandle, genlmsg_put); + + #undef resolve + + return 0; + +dlfailure: + unload_libnl(); + return -1; +} + static void initNetlinkSocket(LinuxProcessTable* this) { - this->netlink_socket = nl_socket_alloc(); + if (load_libnl() < 0) { + return; + } + + this->netlink_socket = sym_nl_socket_alloc(); if (this->netlink_socket == NULL) { return; } - if (nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { + if (sym_nl_connect(this->netlink_socket, NETLINK_GENERIC) < 0) { return; } - this->netlink_family = genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); + this->netlink_family = sym_genl_ctrl_resolve(this->netlink_socket, TASKSTATS_GENL_NAME); } void LibNl_destroyNetlinkSocket(LinuxProcessTable* this) { - if (!this->netlink_socket) - return; + if (this->netlink_socket) { + assert(libnlHandle); + + sym_nl_close(this->netlink_socket); + sym_nl_socket_free(this->netlink_socket); + this->netlink_socket = NULL; + } - nl_close(this->netlink_socket); - nl_socket_free(this->netlink_socket); - this->netlink_socket = NULL; + unload_libnl(); } static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { @@ -53,14 +166,14 @@ static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) { int rem; LinuxProcess* lp = (LinuxProcess*) linuxProcess; - nlhdr = nlmsg_hdr(nlmsg); + nlhdr = sym_nlmsg_hdr(nlmsg); - if (genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { + if (sym_genlmsg_parse(nlhdr, 0, nlattrs, TASKSTATS_TYPE_MAX, NULL) < 0) { return NL_SKIP; } if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) { - memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats)); + memcpy(&stats, sym_nla_data(sym_nla_next(sym_nla_data(nlattr), &rem)), sizeof(stats)); assert(Process_getPid(&lp->super) == (pid_t)stats.ac_pid); // The xxx_delay_total values wrap around on overflow. @@ -90,27 +203,27 @@ void LibNl_readDelayAcctData(LinuxProcessTable* this, LinuxProcess* process) { } } - if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { + if (sym_nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) { goto delayacct_failure; } - if (! (msg = nlmsg_alloc())) { + if (! (msg = sym_nlmsg_alloc())) { goto delayacct_failure; } - if (! genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { - nlmsg_free(msg); + if (! sym_genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, this->netlink_family, 0, NLM_F_REQUEST, TASKSTATS_CMD_GET, TASKSTATS_VERSION)) { + sym_nlmsg_free(msg); } - if (nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { - nlmsg_free(msg); + if (sym_nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, Process_getPid(&process->super)) < 0) { + sym_nlmsg_free(msg); } - if (nl_send_sync(this->netlink_socket, msg) < 0) { + if (sym_nl_send_sync(this->netlink_socket, msg) < 0) { goto delayacct_failure; } - if (nl_recvmsgs_default(this->netlink_socket) < 0) { + if (sym_nl_recvmsgs_default(this->netlink_socket) < 0) { goto delayacct_failure; } -- cgit v1.2.3