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) 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" diff --git a/src/shared/wall.c b/src/shared/wall.c index e2efd96d37..d5900ef019 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) { @@ -34,97 +36,107 @@ 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 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) { +#if ENABLE_UTMP _unused_ _cleanup_(utxent_cleanup) bool utmpx = false; struct utmpx *u; - int r; + 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(); - 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; -} #else + return -ENOPROTOOPT; +#endif +} -static int do_wall( +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; +#if ENABLE_LOGIND _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; -} + return r; + +#else + return -ENOPROTOOPT; #endif +} int wall( const char *message, @@ -133,15 +145,21 @@ 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; + int r; - 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 +171,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); + r = wall_utmp(text, match_tty, userdata); + if (r == -ENOPROTOOPT) + r = wall_logind(text, match_tty, userdata); + + return r == -ENOPROTOOPT ? 0 : r; } + +#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