summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Göttsche <cgzones@googlemail.com>2024-03-30 13:47:14 +0100
committercgzones <cgzones@googlemail.com>2024-04-05 19:27:07 +0200
commit24b1513296fd61722166625ad46be1c56a5efc44 (patch)
treea0137954bfd9252bef84f18c57d90c95696d9dfc
parenta782ef357067962f60580478067f4023facab6a0 (diff)
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.
-rw-r--r--README.md5
-rw-r--r--configure.ac38
-rw-r--r--linux/LibNl.c159
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 <dlfcn.h>
+
#include <linux/netlink.h>
#include <linux/taskstats.h>
#include <netlink/attr.h>
#include <netlink/handlers.h>
#include <netlink/msg.h>
-#include <netlink/netlink.h>
-#include <netlink/socket.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
+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;
}

© 2014-2024 Faster IT GmbH | imprint | privacy policy