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