diff options
author | Christian Göttsche <cgzones@googlemail.com> | 2024-03-30 13:47:14 +0100 |
---|---|---|
committer | cgzones <cgzones@googlemail.com> | 2024-04-05 19:27:07 +0200 |
commit | 24b1513296fd61722166625ad46be1c56a5efc44 (patch) | |
tree | a0137954bfd9252bef84f18c57d90c95696d9dfc /linux | |
parent | a782ef357067962f60580478067f4023facab6a0 (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.
Diffstat (limited to 'linux')
-rw-r--r-- | linux/LibNl.c | 159 |
1 files changed, 136 insertions, 23 deletions
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; } |