Inconsistency in sd_session_get_tty()
systemd version the issue has been seen with
systemd-257.6-1.fc42.x86_64
Used distribution
Fedora 42
Linux kernel version used
6.14.11-300.fc42.x86_64
CPU architectures issue was seen on
x86_64
Component
other
Expected behaviour you didn't see
By analysing an error in the write command, which is able to send messages to users which are directly connected to the system (e.g. tty1), but not able to send messages to users, which are connected via ssh, I came across the following anomaly
root@training2:~# loginctl
SESSION UID USER SEAT LEADER CLASS TTY IDLE SINCE
1 1077 mcj - 1053 user - no -
10 0 root - 7191 manager-early - no -
11 1077 mcj - 7285 user **pts/1** yes 2h 5min ago
12 0 root - 7388 user **pts/0** no -
14 0 root seat0 60565 user-early **tty1** yes 2h 33min ago
2 1077 mcj - 1094 manager - no -
and
root@training2:~# utmpdump /run/utmp
Utmp dump of /run/utmp
[2] [00000] [~~ ] [reboot ] [~ ] [6.14.11-300.fc42.x86_64] [0.0.0.0 ] [2025-06-17T18:21:14,080339+00:00]
[1] [00051] [~~ ] [runlevel] [~ ] [6.14.11-300.fc42.x86_64] [0.0.0.0 ] [2025-06-17T18:21:18,002586+00:00]
[7] [60565] [**tty1**] [root ] [**tty1** ] [ ] [0.0.0.0 ] [2025-06-18T08:53:38,998951+00:00]
[7] [07388] [**ts/0**] [root ] [**pts/0** ] [2001:db8::1000] [2001:db8::1000] [2025-06-18T07:12:58,218188+00:00]
[7] [07285] [**ts/1**] [mcj ] [**pts/1** ] [2001:db8::1000] [2001:db8::1000] [2025-06-18T07:09:24,347860+00:00]
[8] [03912] [ts/2] [ ] [pts/2 ] [2001:db8:19ee:7d84:53a9:bd44] [2001:db8:19ee:7d84:53a9:bd44] [2025-06-17T19:13:26,651383+00:00]
[8] [03917] [ts/3] [ ] [pts/3 ] [2001:db8:19ee:7d84:53a9:bd44] [2001:db8:19ee:7d84:53a9:bd44] [2025-06-17T19:13:26,643437+00:00]
[8] [04230] [423 ] [ ] [web console ] [ ] [0.0.0.0 ] [2025-06-17T20:40:47,024294+00:00]
In utmpdump the third column is crippled to ts/0 where it should read pts/0 This resulted in a failure when calling sd_session_get_tty() in write.c giving you ENODEV
157 if ((r=sd_session_get_tty(sessions_list[i], &tty)) < 0) {
158 printf("Debug: TTY error for %s - %s\n", name, strerror(-r));
159 free(name);
160 continue;
161 }
Unexpected behaviour you saw
see above
Steps to reproduce the problem
Double check:
# chgrp tty /usr/bin/write
# chmod g+s /usr/bin/write
SessionA: One user log in via ssh; will result in e.g. pts/0 SessionB: another user log in via ssh, doing a write userA pts/0
Additional program output to the terminal or log subsystem illustrating the issue
In utmpdump the third column is crippled to ts/0 where it should read pts/0
Yes, it definitely is, see utmpx(5) man page. The third column is the ut_id[] field of struct utmp, and on Linux that's sized to 4. Hence we must truncate. And this is even documented: "Terminal name suffix" according to man page.
sd_session_get_tty() doesn't bother with utmp though. hence not sure i grok what your report precisely is about?
note that the venerable write tool does not interface with logind though, i.e. it does not care about sd_session_get_tty().
most likely your ssh ttys are just 0600 or so, while the other ttys are 0622 or so. Which is entirely unrelated to systemd...
Perms and SGID are ok:
# ll /usr/local/bin/write
-rwxr-sr-x. 1 root tty 59120 Jun 18 11:11 /usr/local/bin/write
Code from util-linux/*/write.c pls look at line 156
131 /*
132 * check_utmp - checks that the given user is actually logged in on
133 * the given tty
134 */
135 static int check_utmp(const struct write_control *ctl)
136 {
137 struct utmpx *u;
138 int res = 1;
139 #if defined(USE_SYSTEMD) && HAVE_DECL_SD_SESSION_GET_USERNAME == 1
140 if (sd_booted() > 0) {
141 char **sessions_list;
142 int sessions = sd_get_sessions(&sessions_list);
143 if (sessions < 0)
144 errx(EXIT_FAILURE, _("error getting sessions: %s"),
145 strerror(-sessions));
146
147 for (int i = 0; i < sessions; i++) {
148
149 char *name, *tty;
150 int r;
151 printf("Debug: Checking session %d\n", i);
152 if ((r = sd_session_get_username(sessions_list[i], &name)) < 0)
153 errx(EXIT_FAILURE, _("get user name failed: %s"), strerror (-r));
154 printf("Debug: Username: %s\n", name);
155 if ((r=sd_session_get_tty(sessions_list[i], &tty)) < 0) {
156 printf("Debug: TTY error for %s - %s\n", name, strerror(-r));
157 free(name);
158 continue;
159 }
160 printf("Debug: TTY: %s, Expected: %s\n", tty, ctl->dst_tty_name);
161 if (strcmp(ctl->dst_login, name) == 0 &&
162 strcmp(ctl->dst_tty_name, tty) == 0) {
163 free(name);
164 free(tty);
165 res = 0;
166 break;
167 }
168 free(name);
169 free(tty);
170 }
171 for (int i = 0; i < sessions; i++)
172 free(sessions_list[i]);
173 free(sessions_list);
174 } else {
175 #endif
root is logged in twice. on tty1 and via ssh on pts/0 giving me with the code above
$ /usr/local/bin/write root pts/0
Debug: Checking session 0
Debug: Username: root
Debug: TTY: tty1, Expected: pts/0
Debug: Checking session 1
Debug: Username: root
Debug: TTY error for root - No data available
Debug: Checking session 2
Debug: Username: mcj
Debug: TTY error for mcj - No data available
Debug: Checking session 3
Debug: Username: root
Debug: TTY error for root - No data available
Debug: Checking session 4
Debug: Username: mcj
Debug: TTY error for mcj - No data available
Debug: Checking session 5
Debug: Username: mcj
Debug: TTY error for mcj - No data available
write: root is not logged in on pts/0