]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/utmp-wtmp.c
network: warn if Address= is specified without prefixlen
[thirdparty/systemd.git] / src / shared / utmp-wtmp.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e537352b 2
e537352b 3#include <errno.h>
07630cea
LP
4#include <fcntl.h>
5#include <poll.h>
a8fbdf54
TA
6#include <stddef.h>
7#include <stdio.h>
8#include <stdlib.h>
e537352b 9#include <string.h>
a8fbdf54 10#include <sys/time.h>
e537352b 11#include <sys/utsname.h>
ef2f1067 12#include <unistd.h>
07630cea 13#include <utmpx.h>
e537352b 14
b5efdb8a 15#include "alloc-util.h"
3ffd4af2 16#include "fd-util.h"
07630cea 17#include "hostname-util.h"
e537352b 18#include "macro.h"
9eb977db 19#include "path-util.h"
07630cea 20#include "string-util.h"
288a74cc 21#include "terminal-util.h"
a8fbdf54 22#include "time-util.h"
b1d4f8e1 23#include "user-util.h"
a8fbdf54 24#include "util.h"
958b66ea 25#include "utmp-wtmp.h"
e537352b
LP
26
27int utmp_get_runlevel(int *runlevel, int *previous) {
b92bea5d 28 struct utmpx *found, lookup = { .ut_type = RUN_LVL };
e537352b
LP
29 int r;
30 const char *e;
31
32 assert(runlevel);
33
34 /* If these values are set in the environment this takes
35 * precedence. Presumably, sysvinit does this to work around a
36 * race condition that would otherwise exist where we'd always
37 * go to disk and hence might read runlevel data that might be
38 * very new and does not apply to the current script being
39 * executed. */
40
e7fb33ff
LP
41 e = getenv("RUNLEVEL");
42 if (e && e[0] > 0) {
e537352b
LP
43 *runlevel = e[0];
44
45 if (previous) {
46 /* $PREVLEVEL seems to be an Upstart thing */
47
e7fb33ff
LP
48 e = getenv("PREVLEVEL");
49 if (e && e[0] > 0)
e537352b
LP
50 *previous = e[0];
51 else
52 *previous = 0;
53 }
54
55 return 0;
56 }
57
58 if (utmpxname(_PATH_UTMPX) < 0)
59 return -errno;
60
61 setutxent();
62
e7fb33ff
LP
63 found = getutxid(&lookup);
64 if (!found)
e537352b
LP
65 r = -errno;
66 else {
67 int a, b;
68
69 a = found->ut_pid & 0xFF;
70 b = (found->ut_pid >> 8) & 0xFF;
71
4dd1de72
ZJS
72 *runlevel = a;
73 if (previous)
74 *previous = b;
75
76 r = 0;
e537352b
LP
77 }
78
79 endutxent();
80
81 return r;
82}
83
169c1bda 84static void init_timestamp(struct utmpx *store, usec_t t) {
e537352b
LP
85 assert(store);
86
871d7de4
LP
87 if (t <= 0)
88 t = now(CLOCK_REALTIME);
e537352b 89
871d7de4
LP
90 store->ut_tv.tv_sec = t / USEC_PER_SEC;
91 store->ut_tv.tv_usec = t % USEC_PER_SEC;
169c1bda
LP
92}
93
94static void init_entry(struct utmpx *store, usec_t t) {
b92bea5d 95 struct utsname uts = {};
169c1bda
LP
96
97 assert(store);
98
99 init_timestamp(store, t);
100
e537352b
LP
101 if (uname(&uts) >= 0)
102 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
103
104 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
105 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
106}
107
108static int write_entry_utmp(const struct utmpx *store) {
109 int r;
110
111 assert(store);
112
113 /* utmp is similar to wtmp, but there is only one entry for
114 * each entry type resp. user; i.e. basically a key/value
115 * table. */
116
117 if (utmpxname(_PATH_UTMPX) < 0)
118 return -errno;
119
120 setutxent();
121
122 if (!pututxline(store))
123 r = -errno;
124 else
125 r = 0;
126
127 endutxent();
128
129 return r;
130}
131
132static int write_entry_wtmp(const struct utmpx *store) {
133 assert(store);
134
135 /* wtmp is a simple append-only file where each entry is
863f3ce0 136 simply appended to the end; i.e. basically a log. */
e537352b
LP
137
138 errno = 0;
139 updwtmpx(_PATH_WTMPX, store);
140 return -errno;
141}
142
4743137a 143static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
e537352b
LP
144 int r, s;
145
4743137a
MS
146 r = write_entry_utmp(store_utmp);
147 s = write_entry_wtmp(store_wtmp);
e537352b
LP
148
149 if (r >= 0)
150 r = s;
151
152 /* If utmp/wtmp have been disabled, that's a good thing, hence
153 * ignore the errors */
154 if (r == -ENOENT)
155 r = 0;
156
157 return r;
158}
159
4743137a
MS
160static int write_entry_both(const struct utmpx *store) {
161 return write_utmp_wtmp(store, store);
162}
163
0ad26e09 164int utmp_put_shutdown(void) {
863f3ce0 165 struct utmpx store = {};
e537352b 166
0ad26e09 167 init_entry(&store, 0);
e537352b
LP
168
169 store.ut_type = RUN_LVL;
170 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
171
172 return write_entry_both(&store);
173}
174
871d7de4 175int utmp_put_reboot(usec_t t) {
863f3ce0 176 struct utmpx store = {};
e537352b 177
871d7de4 178 init_entry(&store, t);
e537352b
LP
179
180 store.ut_type = BOOT_TIME;
55e39f40 181 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
e537352b
LP
182
183 return write_entry_both(&store);
184}
185
44a6b1b6 186_pure_ static const char *sanitize_id(const char *id) {
169c1bda
LP
187 size_t l;
188
189 assert(id);
190 l = strlen(id);
191
192 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
193 return id;
194
195 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
196}
197
023a4f67 198int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) {
863f3ce0
TG
199 struct utmpx store = {
200 .ut_type = INIT_PROCESS,
201 .ut_pid = pid,
202 .ut_session = sid,
203 };
023a4f67 204 int r;
169c1bda
LP
205
206 assert(id);
207
0ad26e09 208 init_timestamp(&store, 0);
169c1bda 209
863f3ce0 210 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
169c1bda
LP
211 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
212
213 if (line)
2b6bf07d 214 strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
169c1bda 215
023a4f67
LP
216 r = write_entry_both(&store);
217 if (r < 0)
218 return r;
219
3742095b 220 if (IN_SET(ut_type, LOGIN_PROCESS, USER_PROCESS)) {
023a4f67
LP
221 store.ut_type = LOGIN_PROCESS;
222 r = write_entry_both(&store);
223 if (r < 0)
224 return r;
225 }
226
227 if (ut_type == USER_PROCESS) {
228 store.ut_type = USER_PROCESS;
229 strncpy(store.ut_user, user, sizeof(store.ut_user)-1);
230 r = write_entry_both(&store);
231 if (r < 0)
232 return r;
233 }
234
235 return 0;
169c1bda
LP
236}
237
238int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
863f3ce0
TG
239 struct utmpx lookup = {
240 .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
241 }, store, store_wtmp, *found;
169c1bda
LP
242
243 assert(id);
244
245 setutxent();
246
863f3ce0 247 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
169c1bda
LP
248 strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
249
e7fb33ff
LP
250 found = getutxid(&lookup);
251 if (!found)
169c1bda
LP
252 return 0;
253
254 if (found->ut_pid != pid)
255 return 0;
256
fa4ad7ce 257 memcpy(&store, found, sizeof(store));
169c1bda
LP
258 store.ut_type = DEAD_PROCESS;
259 store.ut_exit.e_termination = code;
260 store.ut_exit.e_exit = status;
261
262 zero(store.ut_user);
263 zero(store.ut_host);
264 zero(store.ut_tv);
265
4743137a
MS
266 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
267 /* wtmp wants the current time */
268 init_timestamp(&store_wtmp, 0);
269
270 return write_utmp_wtmp(&store, &store_wtmp);
169c1bda
LP
271}
272
0ad26e09 273int utmp_put_runlevel(int runlevel, int previous) {
863f3ce0 274 struct utmpx store = {};
e537352b
LP
275 int r;
276
277 assert(runlevel > 0);
278
279 if (previous <= 0) {
280 /* Find the old runlevel automatically */
281
e7fb33ff
LP
282 r = utmp_get_runlevel(&previous, NULL);
283 if (r < 0) {
d7fc909d
LP
284 if (r != -ESRCH)
285 return r;
286
287 previous = 0;
288 }
e537352b
LP
289 }
290
4927fcae
LP
291 if (previous == runlevel)
292 return 0;
293
0ad26e09 294 init_entry(&store, 0);
e537352b
LP
295
296 store.ut_type = RUN_LVL;
297 store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
298 strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
299
300 return write_entry_both(&store);
301}
ef2f1067
LP
302
303#define TIMEOUT_MSEC 50
304
305static int write_to_terminal(const char *tty, const char *message) {
7fd1b19b 306 _cleanup_close_ int fd = -1;
ef2f1067
LP
307 const char *p;
308 size_t left;
309 usec_t end;
310
311 assert(tty);
312 assert(message);
313
db4a47e9 314 fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
e62d8c39 315 if (fd < 0 || !isatty(fd))
ef2f1067
LP
316 return -errno;
317
ef2f1067
LP
318 p = message;
319 left = strlen(message);
320
321 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
322
323 while (left > 0) {
324 ssize_t n;
b92bea5d
ZJS
325 struct pollfd pollfd = {
326 .fd = fd,
327 .events = POLLOUT,
328 };
ef2f1067
LP
329 usec_t t;
330 int k;
331
332 t = now(CLOCK_MONOTONIC);
333
e62d8c39
ZJS
334 if (t >= end)
335 return -ETIME;
ef2f1067 336
e62d8c39
ZJS
337 k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
338 if (k < 0)
ef2f1067
LP
339 return -errno;
340
e62d8c39
ZJS
341 if (k == 0)
342 return -ETIME;
ef2f1067 343
e62d8c39
ZJS
344 n = write(fd, p, left);
345 if (n < 0) {
ef2f1067
LP
346 if (errno == EAGAIN)
347 continue;
348
e62d8c39 349 return -errno;
ef2f1067
LP
350 }
351
352 assert((size_t) n <= left);
353
354 p += n;
355 left -= n;
356 }
357
e62d8c39 358 return 0;
ef2f1067
LP
359}
360
99f710dd
DM
361int utmp_wall(
362 const char *message,
363 const char *username,
364 const char *origin_tty,
365 bool (*match_tty)(const char *tty, void *userdata),
366 void *userdata) {
367
368 _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *stdin_tty = NULL;
11620592 369 char date[FORMAT_TIMESTAMP_MAX];
e7fb33ff 370 struct utmpx *u;
ef2f1067 371 int r;
ef2f1067 372
e7fb33ff 373 hn = gethostname_malloc();
9003d9b0 374 if (!hn)
e7fb33ff 375 return -ENOMEM;
9003d9b0
ST
376 if (!username) {
377 un = getlogname_malloc();
378 if (!un)
379 return -ENOMEM;
380 }
ef2f1067 381
99f710dd
DM
382 if (!origin_tty) {
383 getttyname_harder(STDIN_FILENO, &stdin_tty);
384 origin_tty = stdin_tty;
385 }
ef2f1067
LP
386
387 if (asprintf(&text,
388 "\a\r\n"
11620592 389 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
ef2f1067 390 "%s\r\n\r\n",
9003d9b0 391 un ?: username, hn,
99f710dd 392 origin_tty ? " on " : "", strempty(origin_tty),
11620592 393 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
e7fb33ff
LP
394 message) < 0)
395 return -ENOMEM;
ef2f1067
LP
396
397 setutxent();
398
399 r = 0;
400
401 while ((u = getutxent())) {
e7fb33ff 402 _cleanup_free_ char *buf = NULL;
ef2f1067 403 const char *path;
e7fb33ff 404 int q;
ef2f1067
LP
405
406 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
407 continue;
408
fbd0b64f 409 /* this access is fine, because STRLEN("/dev/") << 32 (UT_LINESIZE) */
ef2f1067
LP
410 if (path_startswith(u->ut_line, "/dev/"))
411 path = u->ut_line;
412 else {
e7fb33ff
LP
413 if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
414 return -ENOMEM;
ef2f1067
LP
415
416 path = buf;
417 }
418
99f710dd 419 if (!match_tty || match_tty(path, userdata)) {
e7fb33ff
LP
420 q = write_to_terminal(path, text);
421 if (q < 0)
7af53310 422 r = q;
e7fb33ff 423 }
ef2f1067
LP
424 }
425
ef2f1067
LP
426 return r;
427}