]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/utmp-wtmp.c
hashmap, set: remove unused functions
[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>
29#include <sys/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
95 zero(*store);
e537352b 96
871d7de4
LP
97 if (t <= 0)
98 t = now(CLOCK_REALTIME);
e537352b 99
871d7de4
LP
100 store->ut_tv.tv_sec = t / USEC_PER_SEC;
101 store->ut_tv.tv_usec = t % USEC_PER_SEC;
169c1bda
LP
102}
103
104static void init_entry(struct utmpx *store, usec_t t) {
b92bea5d 105 struct utsname uts = {};
169c1bda
LP
106
107 assert(store);
108
109 init_timestamp(store, t);
110
e537352b
LP
111 if (uname(&uts) >= 0)
112 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
113
114 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
115 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
116}
117
118static int write_entry_utmp(const struct utmpx *store) {
119 int r;
120
121 assert(store);
122
123 /* utmp is similar to wtmp, but there is only one entry for
124 * each entry type resp. user; i.e. basically a key/value
125 * table. */
126
127 if (utmpxname(_PATH_UTMPX) < 0)
128 return -errno;
129
130 setutxent();
131
132 if (!pututxline(store))
133 r = -errno;
134 else
135 r = 0;
136
137 endutxent();
138
139 return r;
140}
141
142static int write_entry_wtmp(const struct utmpx *store) {
143 assert(store);
144
145 /* wtmp is a simple append-only file where each entry is
146 simply appended to * the end; i.e. basically a log. */
147
148 errno = 0;
149 updwtmpx(_PATH_WTMPX, store);
150 return -errno;
151}
152
4743137a 153static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
e537352b
LP
154 int r, s;
155
4743137a
MS
156 r = write_entry_utmp(store_utmp);
157 s = write_entry_wtmp(store_wtmp);
e537352b
LP
158
159 if (r >= 0)
160 r = s;
161
162 /* If utmp/wtmp have been disabled, that's a good thing, hence
163 * ignore the errors */
164 if (r == -ENOENT)
165 r = 0;
166
167 return r;
168}
169
4743137a
MS
170static int write_entry_both(const struct utmpx *store) {
171 return write_utmp_wtmp(store, store);
172}
173
0ad26e09 174int utmp_put_shutdown(void) {
e537352b
LP
175 struct utmpx store;
176
0ad26e09 177 init_entry(&store, 0);
e537352b
LP
178
179 store.ut_type = RUN_LVL;
180 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
181
182 return write_entry_both(&store);
183}
184
871d7de4 185int utmp_put_reboot(usec_t t) {
e537352b
LP
186 struct utmpx store;
187
871d7de4 188 init_entry(&store, t);
e537352b
LP
189
190 store.ut_type = BOOT_TIME;
55e39f40 191 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
e537352b
LP
192
193 return write_entry_both(&store);
194}
195
44a6b1b6 196_pure_ static const char *sanitize_id(const char *id) {
169c1bda
LP
197 size_t l;
198
199 assert(id);
200 l = strlen(id);
201
202 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
203 return id;
204
205 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
206}
207
0ad26e09 208int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
169c1bda
LP
209 struct utmpx store;
210
211 assert(id);
212
0ad26e09 213 init_timestamp(&store, 0);
169c1bda
LP
214
215 store.ut_type = INIT_PROCESS;
216 store.ut_pid = pid;
217 store.ut_session = sid;
218
219 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
220
221 if (line)
2b6bf07d 222 strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
169c1bda
LP
223
224 return write_entry_both(&store);
225}
226
227int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
4743137a 228 struct utmpx lookup, store, store_wtmp, *found;
169c1bda
LP
229
230 assert(id);
231
232 setutxent();
233
234 zero(lookup);
235 lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
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) {
e537352b
LP
263 struct utmpx store;
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}