From 0b71ff37a7cb0ce546975796f9366eee25be5fb7 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Tue, 1 Aug 2023 21:44:07 +0200 Subject: [PATCH] readutmp: For year-2038 safety on Linux/{x86,arm}, use systemd APIs. Suggested by Thorsten Kukuk in and . * m4/systemd.m4: New file. * m4/readutmp.m4 (gl_READUTMP): Require gl_SYSTEMD_CHOICE. Set READUTMP_LIB. Conditionally define READUTMP_USE_SYSTEMD. * lib/readutmp.h: For READUTMP_USE_SYSTEMD, include and . (struct gl_utmp): New type. (UTMP_STRUCT_NAME, UT_TIME_MEMBER, UT_EXIT_E_TERMINATION, UT_EXIT_E_EXIT, UT_USER, HAVE_STRUCT_XTMP_UT_EXIT, HAVE_STRUCT_XTMP_UT_ID, HAVE_STRUCT_XTMP_UT_PID): Define differently for READUTMP_USE_SYSTEMD. (UT_USER_SIZE): Don't define for READUTMP_USE_SYSTEMD. (UT_TYPE_EQ, UT_TYPE_NOT_DEFINED, READ_UTMP_SUPPORTED): Define also for READUTMP_USE_SYSTEMD. (free_utmp): New declaration. * lib/readutmp.c: Add new includes for READUTMP_USE_SYSTEMD. (extract_trimmed_name): Adapt to READUTMP_USE_SYSTEMD. (get_boot_time_uncached, get_boot_time, guess_pty_name): New functions. (read_utmp): New implementation for READUTMP_USE_SYSTEMD. (free_utmp): New function. * tests/test-readutmp.c (main): At the end, invoke free_utmp. * modules/readutmp (Files): Add m4/systemd.m4. (Link): New section. * modules/readutmp-tests (Makefile.am): Link test-readutmp with READUTMP_LIB. * NEWS: Mention the free_utmp function and the READUTMP_LIB link requirement. --- ChangeLog | 33 ++++ NEWS | 4 + lib/readutmp.c | 350 ++++++++++++++++++++++++++++++++++++++++- lib/readutmp.h | 75 +++++++-- m4/readutmp.m4 | 36 +++++ m4/systemd.m4 | 23 +++ modules/readutmp | 4 + modules/readutmp-tests | 1 + tests/test-readutmp.c | 2 + 9 files changed, 511 insertions(+), 17 deletions(-) create mode 100644 m4/systemd.m4 diff --git a/ChangeLog b/ChangeLog index a96252f2a0..16bf75a66d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,36 @@ +2023-08-01 Bruno Haible + + readutmp: For year-2038 safety on Linux/{x86,arm}, use systemd APIs. + Suggested by Thorsten Kukuk in + and + . + * m4/systemd.m4: New file. + * m4/readutmp.m4 (gl_READUTMP): Require gl_SYSTEMD_CHOICE. Set + READUTMP_LIB. Conditionally define READUTMP_USE_SYSTEMD. + * lib/readutmp.h: For READUTMP_USE_SYSTEMD, include and + . + (struct gl_utmp): New type. + (UTMP_STRUCT_NAME, UT_TIME_MEMBER, UT_EXIT_E_TERMINATION, + UT_EXIT_E_EXIT, UT_USER, HAVE_STRUCT_XTMP_UT_EXIT, + HAVE_STRUCT_XTMP_UT_ID, HAVE_STRUCT_XTMP_UT_PID): Define differently for + READUTMP_USE_SYSTEMD. + (UT_USER_SIZE): Don't define for READUTMP_USE_SYSTEMD. + (UT_TYPE_EQ, UT_TYPE_NOT_DEFINED, READ_UTMP_SUPPORTED): Define also for + READUTMP_USE_SYSTEMD. + (free_utmp): New declaration. + * lib/readutmp.c: Add new includes for READUTMP_USE_SYSTEMD. + (extract_trimmed_name): Adapt to READUTMP_USE_SYSTEMD. + (get_boot_time_uncached, get_boot_time, guess_pty_name): New functions. + (read_utmp): New implementation for READUTMP_USE_SYSTEMD. + (free_utmp): New function. + * tests/test-readutmp.c (main): At the end, invoke free_utmp. + * modules/readutmp (Files): Add m4/systemd.m4. + (Link): New section. + * modules/readutmp-tests (Makefile.am): Link test-readutmp with + READUTMP_LIB. + * NEWS: Mention the free_utmp function and the READUTMP_LIB link + requirement. + 2023-08-01 Bruno Haible readutmp: Trivial simplification. diff --git a/NEWS b/NEWS index 9aa243182c..87d49ffbe2 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,10 @@ User visible incompatible changes Date Modules Changes +2023-08-01 readutmp After processing the read_utmp() results, call + free_utmp() in order to avoid a memory leak. + Link additionally with $(READUTMP_LIB). + 2023-07-10 dfa The signature of the function case_folded_counterparts, declared in localeinfo.h, has changed. diff --git a/lib/readutmp.c b/lib/readutmp.c index 34b96d999e..ba4b0eadb1 100644 --- a/lib/readutmp.c +++ b/lib/readutmp.c @@ -31,6 +31,13 @@ #include #include +#if READUTMP_USE_SYSTEMD +# include +# include +# include +# include +#endif + #include "xalloc.h" /* Each of the FILE streams in this file is only used in a single thread. */ @@ -48,16 +55,20 @@ extract_trimmed_name (const STRUCT_UTMP *ut) { char *p, *trimmed_name; +#if READUTMP_USE_SYSTEMD + trimmed_name = xstrdup (UT_USER (ut)); +#else trimmed_name = xmalloc (UT_USER_SIZE + 1); strncpy (trimmed_name, UT_USER (ut), UT_USER_SIZE); /* Append a trailing NUL. Some systems pad names shorter than the - maximum with spaces, others pad with NULs. Remove any trailing - spaces. */ + maximum with spaces, others pad with NULs. */ trimmed_name[UT_USER_SIZE] = '\0'; +#endif + /* Remove any trailing spaces. */ for (p = trimmed_name + strlen (trimmed_name); trimmed_name < p && p[-1] == ' '; *--p = '\0') - continue; + ; return trimmed_name; } @@ -85,7 +96,322 @@ desirable_utmp_entry (STRUCT_UTMP const *ut, int options) return true; } -# if defined UTMP_NAME_FUNCTION +# if READUTMP_USE_SYSTEMD +/* Use systemd and Linux /proc and kernel APIs. */ + +static struct timeval +get_boot_time_uncached () +{ + /* /proc/uptime contains the uptime with a resolution of 0.01 sec. */ + FILE *fp = fopen ("/proc/uptime", "re"); + if (fp != NULL) + { + char buf[32 + 1]; + size_t n = fread (buf, 1, sizeof (buf) - 1, fp); + fclose (fp); + if (n > 0) + { + buf[n] = '\0'; + /* buf now contains two values: the uptime and the idle time. */ + char *endptr; + double uptime = strtod (buf, &endptr); + if (endptr > buf) + { + struct timeval result; + if (gettimeofday (&result, NULL) >= 0) + { + long uptime_sec = (long) uptime; + int uptime_usec = + (int) ((uptime - (double) uptime_sec) * 1000000.0 + 0.5); + if (result.tv_usec < uptime_usec) + { + result.tv_usec += 1000000; + result.tv_sec -= 1; + } + result.tv_sec -= uptime_sec; + result.tv_usec -= uptime_usec; + return result; + } + } + } + } + + /* The sysinfo call returns the uptime with a resolution of 1 sec only. */ + struct sysinfo info; + if (sysinfo (&info) >= 0) + { + struct timeval result; + if (gettimeofday (&result, NULL) >= 0) + { + result.tv_sec -= info.uptime; + return result; + } + } + + /* We shouldn't get here. */ + return (struct timeval) { .tv_sec = 0, .tv_usec = 0 }; +} + +static struct timeval +get_boot_time () +{ + static int cached; + static struct timeval boot_time; + + if (!cached) + { + boot_time = get_boot_time_uncached (); + cached = 1; + } + return boot_time; +} + +/* Guess the pty name that was opened for the given user right after + the given time AT. */ +static char * +guess_pty_name (uid_t uid, const struct timespec at) +{ + /* Traverse the entries of the /dev/pts/ directory, looking for devices + which are owned by UID and whose ctime is shortly after AT. */ + DIR *dirp = opendir ("/dev/pts"); + if (dirp != NULL) + { + /* Buffer containing /dev/pts/N. */ + char name_buf[9 + 10 + 1]; + memcpy (name_buf, "/dev/pts/", 9); + + char best_name[9 + 10 + 1]; + struct timespec best_time = { .tv_sec = 0, .tv_nsec = 0 }; + + for (;;) + { + struct dirent *dp = readdir (dirp); + if (dp == NULL) + break; + if (dp->d_name[0] != '.' && strlen (dp->d_name) <= 10) + { + /* Compose the absolute file name /dev/pts/N. */ + strcpy (name_buf + 9, dp->d_name); + + /* Find its owner and ctime. */ + struct stat st; + if (stat (name_buf, &st) >= 0 + && st.st_uid == uid + && (st.st_ctim.tv_sec > at.tv_sec + || (st.st_ctim.tv_sec == at.tv_sec + && st.st_ctim.tv_nsec >= at.tv_nsec))) + { + /* This entry has the owner UID and a ctime >= AT. */ + /* Is this entry the best one so far? */ + if ((best_time.tv_sec == 0 && best_time.tv_nsec == 0) + || (st.st_ctim.tv_sec < best_time.tv_sec + || (st.st_ctim.tv_sec == best_time.tv_sec + && st.st_ctim.tv_nsec < best_time.tv_nsec))) + { + strcpy (best_name, name_buf); + best_time = st.st_ctim; + } + } + } + } + + closedir (dirp); + + /* Did we find an entry owned by ID, and is it at most 5 seconds + after AT? */ + if (!(best_time.tv_sec == 0 && best_time.tv_nsec == 0) + && (best_time.tv_sec < at.tv_sec + 5 + || (best_time.tv_sec == at.tv_sec + 5 + && best_time.tv_nsec <= at.tv_nsec))) + return xstrdup (best_name + 5); + } + + return NULL; +} + +int +read_utmp (char const *file, size_t *n_entries, STRUCT_UTMP **utmp_buf, + int options) +{ + /* Fill entries, simulating what an utmp file would contain. */ + idx_t n_filled = 0; + idx_t n_alloc = 0; + STRUCT_UTMP *utmp = NULL; + + /* Synthesize a BOOT_TIME entry. */ + if (!(options & READ_UTMP_USER_PROCESS)) + { + if (n_filled == n_alloc) + utmp = xpalloc (utmp, &n_alloc, 1, -1, sizeof (STRUCT_UTMP)); + STRUCT_UTMP *ut = &utmp[n_filled]; + ut->ut_user = xstrdup ("reboot"); + ut->ut_id = xstrdup (""); + ut->ut_line = xstrdup ("~"); + ut->ut_pid = 0; + ut->ut_type = BOOT_TIME; + ut->ut_tv = get_boot_time (); + ut->ut_host = xstrdup (""); + ut->ut_session = 0; + if (desirable_utmp_entry (ut, options)) + n_filled++; + else + free_utmp (1, ut); + } + + /* Synthesize USER_PROCESS entries. */ + char **sessions; + int num_sessions = sd_get_sessions (&sessions); + if (num_sessions >= 0) + { + char **session_ptr; + for (session_ptr = sessions; *session_ptr != NULL; session_ptr++) + { + char *session = *session_ptr; + + uint64_t start_usec; + if (sd_session_get_start_time (session, &start_usec) < 0) + start_usec = 0; + struct timeval start_tv; + start_tv.tv_sec = start_usec / 1000000; + start_tv.tv_usec = start_usec % 1000000; + + char *seat; + if (sd_session_get_seat (session, &seat) < 0) + seat = NULL; + + char *tty; + if (sd_session_get_tty (session, &tty) < 0) + { + tty = NULL; + /* Try harder to get a sensible value for the tty. */ + char *type; + if (sd_session_get_type (session, &type) >= 0) + { + if (strcmp (type, "tty") == 0) + { + char *service; + if (sd_session_get_service (session, &service) < 0) + service = NULL; + + char *pty; + uid_t uid; + if (sd_session_get_uid (session, &uid) >= 0) + { + struct timespec start_ts = + { + .tv_sec = start_tv.tv_sec, + .tv_nsec = start_tv.tv_usec * 1000 + }; + pty = guess_pty_name (uid, start_ts); + } + else + pty = NULL; + + if (service != NULL && pty != NULL) + { + tty = xmalloc (strlen (service) + 1 + strlen (pty) + 1); + stpcpy (stpcpy (stpcpy (tty, service), " "), pty); + } + else if (service != NULL) + tty = xstrdup (service); + else if (pty != NULL) + tty = xstrdup (pty); + + free (pty); + free (service); + } + free (type); + } + } + + /* Create up to two USER_PROCESS entries: one for the seat, + one for the tty. */ + if (seat != NULL || tty != NULL) + { + char *user; + if (sd_session_get_username (session, &user) < 0) + user = xstrdup (""); + + pid_t leader_pid; + if (sd_session_get_leader (session, &leader_pid) < 0) + leader_pid = 0; + + char *remote_host; + if (sd_session_get_remote_host (session, &remote_host) < 0) + remote_host = NULL; + char *remote_user; + if (sd_session_get_remote_user (session, &remote_user) < 0) + remote_user = NULL; + char *host; + if (remote_host != NULL) + { + if (remote_user != NULL) + { + host = xmalloc (strlen (remote_user) + 1 + strlen (remote_host) + 1); + stpcpy (stpcpy (stpcpy (host, remote_user), "@"), remote_host); + } + else + host = xstrdup (remote_host); + } + else + host = xstrdup (""); + + size_t n_filled_after = n_filled + (seat != NULL) + (tty != NULL); + if (n_filled_after > n_alloc) + utmp = xpalloc (utmp, &n_alloc, n_filled_after - n_alloc, -1, + sizeof (STRUCT_UTMP)); + if (seat != NULL) + { + STRUCT_UTMP *ut = &utmp[n_filled]; + ut->ut_user = xstrdup (user); + ut->ut_id = xstrdup (session); + ut->ut_line = xstrdup (seat); + ut->ut_pid = leader_pid; /* this is the best we have */ + ut->ut_type = USER_PROCESS; + ut->ut_tv = start_tv; + ut->ut_host = xstrdup (host); + ut->ut_session = leader_pid; + if (desirable_utmp_entry (ut, options)) + n_filled++; + else + free_utmp (1, ut); + } + if (tty != NULL) + { + STRUCT_UTMP *ut = &utmp[n_filled]; + ut->ut_user = xstrdup (user); + ut->ut_id = xstrdup (session); + ut->ut_line = xstrdup (tty); + ut->ut_pid = leader_pid; /* this is the best we have */ + ut->ut_type = USER_PROCESS; + ut->ut_tv = start_tv; + ut->ut_host = xstrdup (host); + ut->ut_session = leader_pid; + if (desirable_utmp_entry (ut, options)) + n_filled++; + else + free_utmp (1, ut); + } + + free (host); + free (remote_user); + free (remote_host); + free (user); + } + free (tty); + free (seat); + free (session); + } + free (sessions); + } + + *n_entries = n_filled; + *utmp_buf = utmp; + + return 0; +} + +# elif defined UTMP_NAME_FUNCTION static void copy_utmp_entry (STRUCT_UTMP *dst, STRUCT_UTMP *src) @@ -219,3 +545,19 @@ read_utmp (char const *file, size_t *n_entries, STRUCT_UTMP **utmp_buf, } #endif + +void +free_utmp (size_t n_entries, STRUCT_UTMP *entries) +{ +#if READUTMP_USE_SYSTEMD + size_t i; + for (i = 0; i < n_entries; i++) + { + STRUCT_UTMP *ut = &entries[i]; + free (ut->ut_user); + free (ut->ut_id); + free (ut->ut_line); + free (ut->ut_host); + } +#endif +} diff --git a/lib/readutmp.h b/lib/readutmp.h index 271dd4921a..e03e0b0a32 100644 --- a/lib/readutmp.h +++ b/lib/readutmp.h @@ -37,7 +37,34 @@ # undef HAVE_UTMPX_H # endif -# if HAVE_UTMPX_H +# if READUTMP_USE_SYSTEMD + +/* Get 'struct timeval'. */ +# include + +/* Type for the entries returned by read_utmp. */ +struct gl_utmp +{ + /* All 'char *' here are of arbitrary length and malloc-allocated. */ + char *ut_user; /* User name */ + char *ut_id; /* Session ID */ + char *ut_line; /* seat / device */ + pid_t ut_pid; /* process ID of ? */ + short ut_type; /* BOOT_TIME or USER_PROCESS */ + struct timeval ut_tv; /* time */ + char *ut_host; /* for remote sessions: user@host or host */ + long ut_session; /* process ID of session leader */ +}; + +/* Get values for ut_type: BOOT_TIME, USER_PROCESS. */ +# include + +# define UTMP_STRUCT_NAME gl_utmp +# define UT_TIME_MEMBER(UT) ((UT)->ut_tv.tv_sec) +# define UT_EXIT_E_TERMINATION(UT) 0 +# define UT_EXIT_E_EXIT(UT) 0 + +# elif HAVE_UTMPX_H /* defines 'struct utmpx' with the following fields: @@ -171,7 +198,11 @@ struct gl_utmp # endif /* Accessor macro for the member named ut_user or ut_name. */ -# if HAVE_UTMPX_H +# if READUTMP_USE_SYSTEMD + +# define UT_USER(UT) ((UT)->ut_user) + +# elif HAVE_UTMPX_H # if HAVE_STRUCT_UTMPX_UT_USER # define UT_USER(UT) ((UT)->ut_user) @@ -197,23 +228,37 @@ struct gl_utmp # endif -# define HAVE_STRUCT_XTMP_UT_EXIT \ - (HAVE_STRUCT_UTMP_UT_EXIT \ - || HAVE_STRUCT_UTMPX_UT_EXIT) +# if READUTMP_USE_SYSTEMD +# define HAVE_STRUCT_XTMP_UT_EXIT 0 +# else +# define HAVE_STRUCT_XTMP_UT_EXIT \ + (HAVE_STRUCT_UTMP_UT_EXIT \ + || HAVE_STRUCT_UTMPX_UT_EXIT) +# endif -# define HAVE_STRUCT_XTMP_UT_ID \ - (HAVE_STRUCT_UTMP_UT_ID \ - || HAVE_STRUCT_UTMPX_UT_ID) +# if READUTMP_USE_SYSTEMD +# define HAVE_STRUCT_XTMP_UT_ID 1 +# else +# define HAVE_STRUCT_XTMP_UT_ID \ + (HAVE_STRUCT_UTMP_UT_ID \ + || HAVE_STRUCT_UTMPX_UT_ID) +# endif -# define HAVE_STRUCT_XTMP_UT_PID \ - (HAVE_STRUCT_UTMP_UT_PID \ - || HAVE_STRUCT_UTMPX_UT_PID) +# if READUTMP_USE_SYSTEMD +# define HAVE_STRUCT_XTMP_UT_PID 1 +# else +# define HAVE_STRUCT_XTMP_UT_PID \ + (HAVE_STRUCT_UTMP_UT_PID \ + || HAVE_STRUCT_UTMPX_UT_PID) +# endif /* Type of entry returned by read_utmp(). */ typedef struct UTMP_STRUCT_NAME STRUCT_UTMP; /* Size of the UT_USER (ut) member. */ +# if !READUTMP_USE_SYSTEMD enum { UT_USER_SIZE = sizeof UT_USER ((STRUCT_UTMP *) 0) }; +# endif /* Definition of UTMP_FILE and WTMP_FILE. */ @@ -252,7 +297,7 @@ enum { UT_USER_SIZE = sizeof UT_USER ((STRUCT_UTMP *) 0) }; /* Accessor macros for the member named ut_type. */ -# if HAVE_STRUCT_UTMP_UT_TYPE || HAVE_STRUCT_UTMPX_UT_TYPE +# if READUTMP_USE_SYSTEMD || HAVE_STRUCT_UTMP_UT_TYPE || HAVE_STRUCT_UTMPX_UT_TYPE # define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V)) # define UT_TYPE_NOT_DEFINED 0 # else @@ -279,7 +324,7 @@ enum { UT_USER_SIZE = sizeof UT_USER ((STRUCT_UTMP *) 0) }; || (UT_TYPE_NOT_DEFINED && UT_TIME_MEMBER (UT) != 0))) /* Define if read_utmp is not just a dummy. */ -# if HAVE_UTMPX_H || HAVE_UTMP_H +# if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H # define READ_UTMP_SUPPORTED 1 # endif @@ -309,4 +354,8 @@ char *extract_trimmed_name (const STRUCT_UTMP *ut) int read_utmp (char const *file, size_t *n_entries, STRUCT_UTMP **utmp_buf, int options); +/* Free the memory allocated by the N_ENTRIES utmp entries, starting + at ENTRIES. */ +void free_utmp (size_t n_entries, STRUCT_UTMP *entries); + #endif /* __READUTMP_H__ */ diff --git a/m4/readutmp.m4 b/m4/readutmp.m4 index 44fcc6151b..2dfd3fe8d0 100644 --- a/m4/readutmp.m4 +++ b/m4/readutmp.m4 @@ -9,6 +9,42 @@ AC_DEFUN([gl_READUTMP], dnl Persuade utmpx.h to declare utmpxname AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + AC_REQUIRE([gl_SYSTEMD_CHOICE]) + + 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. + READUTMP_LIB= + if test "$SYSTEMD_CHOICE" = yes; then + AC_CHECK_HEADER([systemd/sd-login.h]) + if test $ac_cv_header_systemd_sd_login_h = yes; then + AC_CACHE_CHECK([for libsystemd version >= 254], + [gl_cv_lib_readutmp_systemd], + [gl_save_LIBS="$LIBS" + LIBS="$LIBS -lsystemd" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + uint64_t st; + sd_session_get_start_time ("1", &st); + ]]) + ], + [gl_cv_lib_readutmp_systemd=yes], + [gl_cv_lib_readutmp_systemd=no]) + LIBS="$gl_save_LIBS" + ]) + 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' + fi + fi + fi + AC_SUBST([READUTMP_LIB]) + AC_CHECK_HEADERS_ONCE([utmp.h utmpx.h]) if test $ac_cv_header_utmp_h = yes || test $ac_cv_header_utmpx_h = yes; then dnl Prerequisites of lib/readutmp.h and lib/readutmp.c. diff --git a/m4/systemd.m4 b/m4/systemd.m4 new file mode 100644 index 0000000000..0c919385b2 --- /dev/null +++ b/m4/systemd.m4 @@ -0,0 +1,23 @@ +# systemd.m4 serial 1 +dnl Copyright (C) 2023 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Sets SYSTEMD_CHOICE to 'yes' or 'no', depending on the preferred use of +# systemd APIs. +AC_DEFUN([gl_SYSTEMD_CHOICE], +[ + AC_MSG_CHECKING([whether to use systemd APIs]) + AC_ARG_ENABLE([systemd], + [ --disable-systemd do not use systemd APIs], + [SYSTEMD_CHOICE="$enableval"], + [SYSTEMD_CHOICE=yes]) + AC_MSG_RESULT([$SYSTEMD_CHOICE]) + AC_SUBST([SYSTEMD_CHOICE]) +]) + +# Pre-built package name for the libsystemd library: +# - On Debian and Debian-based systems: libsystemd-dev +# - On Red Hat distributions: systemd-devel +# - Other: https://repology.org/project/systemd/versions diff --git a/modules/readutmp b/modules/readutmp index 4b2de331dc..04893a4487 100644 --- a/modules/readutmp +++ b/modules/readutmp @@ -5,6 +5,7 @@ Files: lib/readutmp.h lib/readutmp.c m4/readutmp.m4 +m4/systemd.m4 Depends-on: extensions @@ -24,6 +25,9 @@ lib_SOURCES += readutmp.c Include: "readutmp.h" +Link: +$(READUTMP_LIB) + License: GPL diff --git a/modules/readutmp-tests b/modules/readutmp-tests index 792a80e792..06f06a7c3c 100644 --- a/modules/readutmp-tests +++ b/modules/readutmp-tests @@ -11,3 +11,4 @@ configure.ac: Makefile.am: TESTS += test-readutmp check_PROGRAMS += test-readutmp +test_readutmp_LDADD = $(LDADD) @READUTMP_LIB@ diff --git a/tests/test-readutmp.c b/tests/test-readutmp.c index b806b47116..ae17087a81 100644 --- a/tests/test-readutmp.c +++ b/tests/test-readutmp.c @@ -138,6 +138,8 @@ main (int argc, char *argv[]) time_t now = time (NULL); ASSERT (first >= now - 157680000); ASSERT (last <= now + 604800); + + free_utmp (num_entries, entries); } return 0; -- 2.39.5