diff --git a/lib/readutmp.c b/lib/readutmp.c index 6e43b5e956..29dc98fc15 100644 --- a/lib/readutmp.c +++ b/lib/readutmp.c @@ -38,7 +38,16 @@ #endif #if READUTMP_USE_SYSTEMD # include +# include # include +# if HAVE_SYSTEMD_SD_DLOPEN_H +# include + +SD_ELF_NOTE_DLOPEN ("systemd", + "Support for systemd sessions", + SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED, + "libsystemd.so.0"); +# endif #endif #if HAVE_SYS_SYSCTL_H && !(defined __GLIBC__ && defined __linux__) && !defined __minix @@ -61,12 +70,150 @@ #include "stat-time.h" #include "xalloc.h" +#include "gettext.h" +#define _(msgid) dgettext (GNULIB_TEXT_DOMAIN, msgid) + /* Each of the FILE streams in this file is only used in a single thread. */ #include "unlocked-io.h" /* Some helper functions. */ #include "boot-time-aux.h" +#if READUTMP_USE_SYSTEMD + +struct systemd_operations +{ + __typeof__ (&sd_get_sessions) sd_get_sessions; + __typeof__ (&sd_session_get_start_time) sd_session_get_start_time; + __typeof__ (&sd_session_get_seat) sd_session_get_seat; + __typeof__ (&sd_session_get_tty) sd_session_get_tty; + __typeof__ (&sd_session_get_type) sd_session_get_type; + __typeof__ (&sd_session_get_service) sd_session_get_service; + __typeof__ (&sd_session_get_uid) sd_session_get_uid; + __typeof__ (&sd_session_get_username) sd_session_get_username; + __typeof__ (&sd_session_get_leader) sd_session_get_leader; + __typeof__ (&sd_session_get_class) sd_session_get_class; + __typeof__ (&sd_session_get_remote_host) sd_session_get_remote_host; + __typeof__ (&sd_session_get_display) sd_session_get_display; + __typeof__ (&sd_session_get_remote_user) sd_session_get_remote_user; +}; + +static struct systemd_operations systemd_ops; + +/* Return a function pointer in HANDLE for SYMBOL. */ +static void * +systemd_symbol_address (void *handle, char const *symbol) +{ + void *address = dlsym (handle, symbol); + if (!address) + errno = ENOSYS; + return address; +} + +/* Load libsystemd and resolve all required symbols. Cache the result. + Return 0 on success. Return -1 with errno set to ENOSYS on failure. */ +static int +load_libsystemd (void) +{ + static int status = 0; + static void *handle = NULL; + int flags = RTLD_LAZY | RTLD_LOCAL; + + if (status) + { + if (status < 0) + errno = ENOSYS; + return status < 0 ? -1 : 0; + } + +# ifdef RTLD_NODELETE + flags |= RTLD_NODELETE; +# endif + handle = dlopen ("libsystemd.so.0", flags); + if (!handle) + goto fail; + + systemd_ops.sd_get_sessions = + systemd_symbol_address (handle, "sd_get_sessions"); + if (!systemd_ops.sd_get_sessions) + goto fail; + systemd_ops.sd_session_get_start_time = + systemd_symbol_address (handle, "sd_session_get_start_time"); + if (!systemd_ops.sd_session_get_start_time) + goto fail; + systemd_ops.sd_session_get_seat = + systemd_symbol_address (handle, "sd_session_get_seat"); + if (!systemd_ops.sd_session_get_seat) + goto fail; + systemd_ops.sd_session_get_tty = + systemd_symbol_address (handle, "sd_session_get_tty"); + if (!systemd_ops.sd_session_get_tty) + goto fail; + systemd_ops.sd_session_get_type = + systemd_symbol_address (handle, "sd_session_get_type"); + if (!systemd_ops.sd_session_get_type) + goto fail; + systemd_ops.sd_session_get_service = + systemd_symbol_address (handle, "sd_session_get_service"); + if (!systemd_ops.sd_session_get_service) + goto fail; + systemd_ops.sd_session_get_uid = + systemd_symbol_address (handle, "sd_session_get_uid"); + if (!systemd_ops.sd_session_get_uid) + goto fail; + systemd_ops.sd_session_get_username = + systemd_symbol_address (handle, "sd_session_get_username"); + if (!systemd_ops.sd_session_get_username) + goto fail; + systemd_ops.sd_session_get_leader = + systemd_symbol_address (handle, "sd_session_get_leader"); + if (!systemd_ops.sd_session_get_leader) + goto fail; + systemd_ops.sd_session_get_class = + systemd_symbol_address (handle, "sd_session_get_class"); + if (!systemd_ops.sd_session_get_class) + goto fail; + systemd_ops.sd_session_get_remote_host = + systemd_symbol_address (handle, "sd_session_get_remote_host"); + if (!systemd_ops.sd_session_get_remote_host) + goto fail; + systemd_ops.sd_session_get_display = + systemd_symbol_address (handle, "sd_session_get_display"); + if (!systemd_ops.sd_session_get_display) + goto fail; + systemd_ops.sd_session_get_remote_user = + systemd_symbol_address (handle, "sd_session_get_remote_user"); + if (!systemd_ops.sd_session_get_remote_user) + goto fail; + + status = 1; + return 0; + + fail: + if (handle) + dlclose (handle); + handle = NULL; + status = -1; + errno = ENOSYS; + return -1; +} + +# define sd_get_sessions systemd_ops.sd_get_sessions +# define sd_session_get_start_time systemd_ops.sd_session_get_start_time +# define sd_session_get_seat systemd_ops.sd_session_get_seat +# define sd_session_get_tty systemd_ops.sd_session_get_tty +# define sd_session_get_type systemd_ops.sd_session_get_type +# define sd_session_get_service systemd_ops.sd_session_get_service +# define sd_session_get_uid systemd_ops.sd_session_get_uid +# define sd_session_get_username systemd_ops.sd_session_get_username +# define sd_session_get_leader systemd_ops.sd_session_get_leader +# define sd_session_get_class systemd_ops.sd_session_get_class +# define sd_session_get_remote_host systemd_ops.sd_session_get_remote_host +# define sd_session_get_display systemd_ops.sd_session_get_display +# define sd_session_get_remote_user systemd_ops.sd_session_get_remote_user + +#endif + /* The following macros describe the 'struct UTMP_STRUCT_NAME', *not* 'struct gl_utmp'. */ #undef UT_USER @@ -797,6 +944,14 @@ guess_pty_name (uid_t uid, const struct timespec at) static int read_utmp_from_systemd (idx_t *n_entries, STRUCT_UTMP **utmp_buf, int options) { + if (load_libsystemd () < 0) + { + fprintf (stderr, "%s\n", + _("error: functionality provided by libsystemd is not " + "available, as libsystemd could not be dynamically loaded")); + return -1; + } + /* Fill entries, simulating what a utmp file would contain. */ struct utmp_alloc a = { NULL, 0, 0, 0 }; diff --git a/m4/readutmp.m4 b/m4/readutmp.m4 index 3eeec0d081..445f00601d 100644 --- a/m4/readutmp.m4 +++ b/m4/readutmp.m4 @@ -1,5 +1,5 @@ # readutmp.m4 -# serial 32 +# serial 34 dnl Copyright (C) 2002-2026 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -9,11 +9,10 @@ dnl This file is offered as-is, without any warranty. AC_DEFUN([gl_READUTMP], [ AC_REQUIRE([gl_SYSTEMD_CHOICE]) + AC_REQUIRE([gl_LIBDL]) - dnl Set READUTMP_LIB to '-lsystemd' or '', depending on whether use of - dnl systemd APIs is possible and desired (only the systemd login API, here). - dnl AC_LIB_LINKFLAGS_BODY would be overkill here, since few people install - dnl libsystemd in non-system directories. + dnl Set READUTMP_LIB to LIBDL or '', depending on whether use of systemd + dnl APIs is possible and desired (only the systemd login API, here). READUTMP_LIB= if test "$SYSTEMD_CHOICE" = yes; then AC_CHECK_HEADER([systemd/sd-login.h]) @@ -38,7 +37,8 @@ AC_DEFUN([gl_READUTMP], if test $gl_cv_lib_readutmp_systemd = yes; then AC_DEFINE([READUTMP_USE_SYSTEMD], [1], [Define if the readutmp module should use the systemd login API.]) - READUTMP_LIB='-lsystemd' + AC_CHECK_HEADERS([systemd/sd-dlopen.h]) + READUTMP_LIB=$LIBDL fi fi fi diff --git a/modules/readutmp b/modules/readutmp index f74c533f78..afbc9fea48 100644 --- a/modules/readutmp +++ b/modules/readutmp @@ -5,6 +5,7 @@ Files: lib/readutmp.h lib/readutmp.c lib/boot-time-aux.h +m4/libdl.m4 m4/readutmp.m4 m4/systemd.m4 @@ -14,6 +15,8 @@ idx stat-time gettimeofday bool +gettext-h +gnulib-i18n stdcountof-h stdint-h memeq