From 8b2d2193164236c725f4afce79103c83cf80bff8 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 8 Sep 2023 18:44:44 +0800 Subject: [PATCH 1/4] utmp-wtmp: remove unneeded include --- src/shared/utmp-wtmp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index c90e07d4d1..eec3c35447 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#include -#include #include #include #include @@ -13,14 +11,11 @@ #include "alloc-util.h" #include "errno-util.h" -#include "fd-util.h" #include "hostname-util.h" -#include "io-util.h" #include "macro.h" #include "memory-util.h" #include "path-util.h" #include "string-util.h" -#include "terminal-util.h" #include "time-util.h" #include "user-util.h" #include "utmp-wtmp.h" From 056aff96b929803705ff946618810986e13ff49a Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 8 Sep 2023 19:16:46 +0800 Subject: [PATCH 2/4] shared/wall: several cleanups Follow-up for 53c0397b1dbc95f144d9a551c2086d132933e8ce * Split do_wall into wall_utmp and wall_logind * Don't pass unused arguments * Add missing asserts * Modernize error handling * Don't do anything if neither utmp nor logind is enabled --- src/shared/wall.c | 106 +++++++++++++++++++++++++--------------------- src/shared/wall.h | 16 +++++++ 2 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/shared/wall.c b/src/shared/wall.c index e2efd96d37..9fdde59eda 100644 --- a/src/shared/wall.c +++ b/src/shared/wall.c @@ -17,6 +17,8 @@ #include "utmp-wtmp.h" #include "wall.h" +#if ENABLE_UTMP || ENABLE_LOGIND + #define TIMEOUT_USEC (50 * USEC_PER_MSEC) static int write_to_terminal(const char *tty, const char *message) { @@ -35,95 +37,90 @@ static int write_to_terminal(const char *tty, const char *message) { } #if ENABLE_UTMP -static int do_wall( +static int wall_utmp( const char *message, - const char *username, - const char *origin_tty, bool (*match_tty)(const char *tty, bool is_local, void *userdata), void *userdata) { _unused_ _cleanup_(utxent_cleanup) bool utmpx = false; struct utmpx *u; - int r; + int r = 0; + + assert(message); utmpx = utxent_start(); - r = 0; - while ((u = getutxent())) { - _cleanup_free_ char *buf = NULL; - const char *path; - int q; + _cleanup_free_ char *p = NULL; + const char *tty_path; + bool is_local; - if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) + if (u->ut_type != USER_PROCESS || isempty(u->ut_user)) continue; /* This access is fine, because strlen("/dev/") < 32 (UT_LINESIZE) */ if (path_startswith(u->ut_line, "/dev/")) - path = u->ut_line; + tty_path = u->ut_line; else { - if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) + if (asprintf(&p, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0) return -ENOMEM; - path = buf; + + tty_path = p; } - /* It seems that the address field is always set for remote logins. - * For local logins and other local entries, we get [0,0,0,0]. */ - bool is_local = memeqzero(u->ut_addr_v6, sizeof(u->ut_addr_v6)); + /* It seems that the address field is always set for remote logins. For local logins and + * other local entries, we get [0,0,0,0]. */ + is_local = eqzero(u->ut_addr_v6); - if (!match_tty || match_tty(path, is_local, userdata)) { - q = write_to_terminal(path, message); - if (q < 0) - r = q; - } + if (!match_tty || match_tty(tty_path, is_local, userdata)) + RET_GATHER(r, write_to_terminal(tty_path, message)); } return r; } +#endif -#else - -static int do_wall( +#if ENABLE_LOGIND +static int wall_logind( const char *message, - const char *username, - const char *origin_tty, bool (*match_tty)(const char *tty, bool is_local, void *userdata), void *userdata) { - int r; _cleanup_strv_free_ char **sessions = NULL; + int r; + + assert(message); r = sd_get_sessions(&sessions); - if (r < 0) + if (r <= 0) return r; + r = 0; + STRV_FOREACH(s, sessions) { - _cleanup_free_ char *path = NULL, *tty = NULL, *rhost = NULL; + _cleanup_free_ char *tty_path = NULL, *tty = NULL, *rhost = NULL; + bool is_local; int q; q = sd_session_get_tty(*s, &tty); - if (q < 0) { - if (q != -ENXIO && q != -ENODATA) - r = q; + if (IN_SET(q, -ENXIO, -ENODATA)) continue; - } + if (q < 0) + return RET_GATHER(r, q); - path = strjoin("/dev/", tty); - if (!path) + tty_path = strjoin("/dev/", tty); + if (!tty_path) return -ENOMEM; (void) sd_session_get_remote_host(*s, &rhost); - bool is_local = !rhost; + is_local = !rhost; - if (!match_tty || match_tty(path, is_local, userdata)) { - q = write_to_terminal(path, message); - if (q < 0) - r = q; - } + if (!match_tty || match_tty(tty_path, is_local, userdata)) + RET_GATHER(r, write_to_terminal(tty_path, message)); } + return r; } - #endif int wall( @@ -133,15 +130,20 @@ int wall( bool (*match_tty)(const char *tty, bool is_local, void *userdata), void *userdata) { - _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL; + _cleanup_free_ char *text = NULL, *hostname = NULL, *username_alloc = NULL, *stdin_tty = NULL; - hn = gethostname_malloc(); - if (!hn) + assert(message); + + hostname = gethostname_malloc(); + if (!hostname) return -ENOMEM; + if (!username) { - un = getlogname_malloc(); - if (!un) + username_alloc = getlogname_malloc(); + if (!username_alloc) return -ENOMEM; + + username = username_alloc; } if (!origin_tty) { @@ -153,11 +155,17 @@ int wall( "\r\n" "Broadcast message from %s@%s%s%s (%s):\r\n\r\n" "%s\r\n\r\n", - un ?: username, hn, + username, hostname, origin_tty ? " on " : "", strempty(origin_tty), FORMAT_TIMESTAMP(now(CLOCK_REALTIME)), message) < 0) return -ENOMEM; - return do_wall(text, username, origin_tty, match_tty, userdata); +#if ENABLE_UTMP + return wall_utmp(text, match_tty, userdata); +#elif ENABLE_LOGIND + return wall_logind(text, match_tty, userdata); +#endif } + +#endif diff --git a/src/shared/wall.h b/src/shared/wall.h index 4423c39404..29642778c7 100644 --- a/src/shared/wall.h +++ b/src/shared/wall.h @@ -3,9 +3,25 @@ #include +#if ENABLE_UTMP || ENABLE_LOGIND + int wall( const char *message, const char *username, const char *origin_tty, bool (*match_tty)(const char *tty, bool is_local, void *userdata), void *userdata); + +#else + +static inline int wall( + const char *message, + const char *username, + const char *origin_tty, + bool (*match_tty)(const char *tty, bool is_local, void *userdata), + void *userdata) { + + return 0; +} + +#endif From 560f15fc8f338beb7c8a69af631db6f30507fc46 Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Sat, 9 Sep 2023 02:01:57 +0800 Subject: [PATCH 3/4] shared/wall: fall back to logind if utmpx database doesn't exist --- src/shared/wall.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/shared/wall.c b/src/shared/wall.c index 9fdde59eda..d5900ef019 100644 --- a/src/shared/wall.c +++ b/src/shared/wall.c @@ -36,18 +36,27 @@ static int write_to_terminal(const char *tty, const char *message) { return loop_write_full(fd, message, SIZE_MAX, TIMEOUT_USEC); } -#if ENABLE_UTMP static int wall_utmp( const char *message, bool (*match_tty)(const char *tty, bool is_local, void *userdata), void *userdata) { +#if ENABLE_UTMP _unused_ _cleanup_(utxent_cleanup) bool utmpx = false; struct utmpx *u; int r = 0; assert(message); + /* libc's setutxent() unfortunately doesn't inform us about success, i.e. whether /var/run/utmp + * exists. Hence we have to check manually first. */ + if (access(_PATH_UTMPX, F_OK) < 0) { + if (errno == ENOENT) + return -ENOPROTOOPT; + + return -errno; + } + utmpx = utxent_start(); while ((u = getutxent())) { @@ -77,15 +86,18 @@ static int wall_utmp( } return r; -} -#endif -#if ENABLE_LOGIND +#else + return -ENOPROTOOPT; +#endif +} + static int wall_logind( const char *message, bool (*match_tty)(const char *tty, bool is_local, void *userdata), void *userdata) { +#if ENABLE_LOGIND _cleanup_strv_free_ char **sessions = NULL; int r; @@ -120,8 +132,11 @@ static int wall_logind( } return r; -} + +#else + return -ENOPROTOOPT; #endif +} int wall( const char *message, @@ -131,6 +146,7 @@ int wall( void *userdata) { _cleanup_free_ char *text = NULL, *hostname = NULL, *username_alloc = NULL, *stdin_tty = NULL; + int r; assert(message); @@ -161,11 +177,11 @@ int wall( message) < 0) return -ENOMEM; -#if ENABLE_UTMP - return wall_utmp(text, match_tty, userdata); -#elif ENABLE_LOGIND - return wall_logind(text, match_tty, userdata); -#endif + r = wall_utmp(text, match_tty, userdata); + if (r == -ENOPROTOOPT) + r = wall_logind(text, match_tty, userdata); + + return r == -ENOPROTOOPT ? 0 : r; } #endif From 7af442cf63acb35006e36d4878ee3e83139dac4f Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Fri, 8 Sep 2023 19:50:02 +0800 Subject: [PATCH 4/4] TODO: switch the default wall implementation to sd-login --- TODO | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO b/TODO index 7e8d2a93e4..3e66361eeb 100644 --- a/TODO +++ b/TODO @@ -2466,3 +2466,7 @@ Features: - some servers don't do rapid commit without a filled in IA_NA, verify this behavior - RouteTable= ? + +* shared/wall: Once more programs are taught to prefer sd-login over utmp, + switch the default wall implementation to wall_logind + (https://github.com/systemd/systemd/pull/29051#issuecomment-1704917074)