From 0af497c6688b53a3a176176bfbcdca821bd856ec Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Mon, 3 Jun 2024 14:32:18 +0200
Subject: [PATCH] wall: always use utmp as fallback

Wall(1) can be compiled with systemd support to read the names of ttys
from the systemd session list. However, this may not work on all systems.
In case of failure, the best option is to use the traditional
utmp method as a fallback.

This commit uses strv (string vector) to collect tty names from both
sources (systemd and utmp) and then sends a message to all ttys.

Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=2283049
Signed-off-by: Karel Zak <kzak@redhat.com>
---
 term-utils/wall.c | 57 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 18 deletions(-)

diff --git a/term-utils/wall.c b/term-utils/wall.c
index 125fde438..22c3918bb 100644
--- a/term-utils/wall.c
+++ b/term-utils/wall.c
@@ -78,6 +78,7 @@
 #include "closestream.h"
 #include "timeutils.h"
 #include "pwdutils.h"
+#include "strv.h"
 
 #define	TERM_WIDTH	79
 #define	WRITE_TIME_OUT	300		/* in seconds */
@@ -190,19 +191,30 @@ static int is_gr_member(const char *login, const struct group_workspace *buf)
 	return 0;
 }
 
+static int has_tty(char **ttys, char *name)
+{
+	char **str;
+
+	STRV_FOREACH(str, ttys) {
+		if (strcmp(*str, name) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
 int main(int argc, char **argv)
 {
 	int ch;
 	struct iovec iov;
 	struct utmpx *utmpptr;
-	char *p;
 	char line[sizeof(utmpptr->ut_line) + 1];
 	int print_banner = TRUE;
 	struct group_workspace *group_buf = NULL;
 	char *mbuf, *fname = NULL;
 	size_t mbufsize;
 	unsigned timeout = WRITE_TIME_OUT;
-	char **mvec = NULL;
+	char **mvec = NULL, **ttys = NULL, **str;
 	int mvecsz = 0;
 
 	static const struct option longopts[] = {
@@ -265,30 +277,30 @@ int main(int argc, char **argv)
 		int sessions;
 
 		sessions = sd_get_sessions(&sessions_list);
-		if (sessions < 0)
-			errx(EXIT_FAILURE, _("error getting sessions: %s"),
-				strerror(-sessions));
+		if (sessions < 0) {
+			warnx(_("error getting sessions: %s"), strerror(-sessions));
+			goto utmp;
+		}
 
 		for (int i = 0; i < sessions; i++) {
 			char *name, *tty;
 			int r;
 
-			if ((r = sd_session_get_username(sessions_list[i], &name)) < 0)
-				errx(EXIT_FAILURE, _("get user name failed: %s"), strerror (-r));
-
-			if (!(group_buf && !is_gr_member(name, group_buf))) {
-				if (sd_session_get_tty(sessions_list[i], &tty) >= 0) {
-					if ((p = ttymsg(&iov, 1, tty, timeout)) != NULL)
-						warnx("%s", p);
-
-					free(tty);
-				}
+			if ((r = sd_session_get_username(sessions_list[i], &name)) < 0) {
+				warnx(_("get user name failed: %s"), strerror (-r));
+				goto utmp;
 			}
+			if (!(group_buf && !is_gr_member(name, group_buf))
+			    && sd_session_get_tty(sessions_list[i], &tty) >= 0
+			    && strv_consume(&ttys, tty) < 0)
+				err(EXIT_FAILURE, _("failed to allocate lines list"));
+
 			free(name);
 			free(sessions_list[i]);
 		}
 		free(sessions_list);
-	} else
+	}
+utmp:
 #endif
 	{
 		while ((utmpptr = getutxent())) {
@@ -310,12 +322,21 @@ int main(int argc, char **argv)
 				continue;
 
 			mem2strcpy(line, utmpptr->ut_line, sizeof(utmpptr->ut_line), sizeof(line));
-			if ((p = ttymsg(&iov, 1, line, timeout)) != NULL)
-				warnx("%s", p);
+			if (has_tty(ttys, line))
+				continue;
+			if (strv_extend(&ttys, line) < 0)
+				err(EXIT_FAILURE, _("failed to allocate lines list"));
 		}
 		endutxent();
 	}
 
+	STRV_FOREACH(str, ttys) {
+		char *er = ttymsg(&iov, 1, *str, timeout);
+		if (er)
+			 warnx("%s", er);
+	}
+
+	strv_free(ttys);
 	free(mbuf);
 	free_group_workspace(group_buf);
 	exit(EXIT_SUCCESS);
-- 
2.48.1

