]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/utmp-wtmp.c
build-sys: remove papersize option from sphinx
[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) {
36 struct utmpx lookup, *found;
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
49 if ((e = getenv("RUNLEVEL")) && e[0] > 0) {
50 *runlevel = e[0];
51
52 if (previous) {
53 /* $PREVLEVEL seems to be an Upstart thing */
54
55 if ((e = getenv("PREVLEVEL")) && e[0] > 0)
56 *previous = e[0];
57 else
58 *previous = 0;
59 }
60
61 return 0;
62 }
63
64 if (utmpxname(_PATH_UTMPX) < 0)
65 return -errno;
66
67 setutxent();
68
69 zero(lookup);
70 lookup.ut_type = RUN_LVL;
71
72 if (!(found = getutxid(&lookup)))
73 r = -errno;
74 else {
75 int a, b;
76
77 a = found->ut_pid & 0xFF;
78 b = (found->ut_pid >> 8) & 0xFF;
79
80 if (a < 0 || b < 0)
81 r = -EIO;
82 else {
83 *runlevel = a;
84
85 if (previous)
86 *previous = b;
87 r = 0;
88 }
89 }
90
91 endutxent();
92
93 return r;
94}
95
169c1bda 96static void init_timestamp(struct utmpx *store, usec_t t) {
e537352b
LP
97 assert(store);
98
99 zero(*store);
e537352b 100
871d7de4
LP
101 if (t <= 0)
102 t = now(CLOCK_REALTIME);
e537352b 103
871d7de4
LP
104 store->ut_tv.tv_sec = t / USEC_PER_SEC;
105 store->ut_tv.tv_usec = t % USEC_PER_SEC;
169c1bda
LP
106}
107
108static void init_entry(struct utmpx *store, usec_t t) {
109 struct utsname uts;
110
111 assert(store);
112
113 init_timestamp(store, t);
114
115 zero(uts);
e537352b
LP
116
117 if (uname(&uts) >= 0)
118 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
119
120 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
121 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
122}
123
124static int write_entry_utmp(const struct utmpx *store) {
125 int r;
126
127 assert(store);
128
129 /* utmp is similar to wtmp, but there is only one entry for
130 * each entry type resp. user; i.e. basically a key/value
131 * table. */
132
133 if (utmpxname(_PATH_UTMPX) < 0)
134 return -errno;
135
136 setutxent();
137
138 if (!pututxline(store))
139 r = -errno;
140 else
141 r = 0;
142
143 endutxent();
144
145 return r;
146}
147
148static int write_entry_wtmp(const struct utmpx *store) {
149 assert(store);
150
151 /* wtmp is a simple append-only file where each entry is
152 simply appended to * the end; i.e. basically a log. */
153
154 errno = 0;
155 updwtmpx(_PATH_WTMPX, store);
156 return -errno;
157}
158
4743137a 159static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
e537352b
LP
160 int r, s;
161
4743137a
MS
162 r = write_entry_utmp(store_utmp);
163 s = write_entry_wtmp(store_wtmp);
e537352b
LP
164
165 if (r >= 0)
166 r = s;
167
168 /* If utmp/wtmp have been disabled, that's a good thing, hence
169 * ignore the errors */
170 if (r == -ENOENT)
171 r = 0;
172
173 return r;
174}
175
4743137a
MS
176static int write_entry_both(const struct utmpx *store) {
177 return write_utmp_wtmp(store, store);
178}
179
0ad26e09 180int utmp_put_shutdown(void) {
e537352b
LP
181 struct utmpx store;
182
0ad26e09 183 init_entry(&store, 0);
e537352b
LP
184
185 store.ut_type = RUN_LVL;
186 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
187
188 return write_entry_both(&store);
189}
190
871d7de4 191int utmp_put_reboot(usec_t t) {
e537352b
LP
192 struct utmpx store;
193
871d7de4 194 init_entry(&store, t);
e537352b
LP
195
196 store.ut_type = BOOT_TIME;
55e39f40 197 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
e537352b
LP
198
199 return write_entry_both(&store);
200}
201
169c1bda
LP
202static const char *sanitize_id(const char *id) {
203 size_t l;
204
205 assert(id);
206 l = strlen(id);
207
208 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
209 return id;
210
211 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
212}
213
0ad26e09 214int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
169c1bda
LP
215 struct utmpx store;
216
217 assert(id);
218
0ad26e09 219 init_timestamp(&store, 0);
169c1bda
LP
220
221 store.ut_type = INIT_PROCESS;
222 store.ut_pid = pid;
223 store.ut_session = sid;
224
225 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
226
227 if (line)
9eb977db 228 strncpy(store.ut_line, path_get_file_name(line), sizeof(store.ut_line));
169c1bda
LP
229
230 return write_entry_both(&store);
231}
232
233int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
4743137a 234 struct utmpx lookup, store, store_wtmp, *found;
169c1bda
LP
235
236 assert(id);
237
238 setutxent();
239
240 zero(lookup);
241 lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
242 strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
243
244 if (!(found = getutxid(&lookup)))
245 return 0;
246
247 if (found->ut_pid != pid)
248 return 0;
249
fa4ad7ce 250 memcpy(&store, found, sizeof(store));
169c1bda
LP
251 store.ut_type = DEAD_PROCESS;
252 store.ut_exit.e_termination = code;
253 store.ut_exit.e_exit = status;
254
255 zero(store.ut_user);
256 zero(store.ut_host);
257 zero(store.ut_tv);
258
4743137a
MS
259 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
260 /* wtmp wants the current time */
261 init_timestamp(&store_wtmp, 0);
262
263 return write_utmp_wtmp(&store, &store_wtmp);
169c1bda
LP
264}
265
266
0ad26e09 267int utmp_put_runlevel(int runlevel, int previous) {
e537352b
LP
268 struct utmpx store;
269 int r;
270
271 assert(runlevel > 0);
272
273 if (previous <= 0) {
274 /* Find the old runlevel automatically */
275
d7fc909d
LP
276 if ((r = utmp_get_runlevel(&previous, NULL)) < 0) {
277 if (r != -ESRCH)
278 return r;
279
280 previous = 0;
281 }
e537352b
LP
282 }
283
4927fcae
LP
284 if (previous == runlevel)
285 return 0;
286
0ad26e09 287 init_entry(&store, 0);
e537352b
LP
288
289 store.ut_type = RUN_LVL;
290 store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
291 strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
292
293 return write_entry_both(&store);
294}
ef2f1067
LP
295
296#define TIMEOUT_MSEC 50
297
298static int write_to_terminal(const char *tty, const char *message) {
299 int fd, r;
300 const char *p;
301 size_t left;
302 usec_t end;
303
304 assert(tty);
305 assert(message);
306
307 if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0)
308 return -errno;
309
310 if (!isatty(fd)) {
311 r = -errno;
312 goto finish;
313 }
314
315 p = message;
316 left = strlen(message);
317
318 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
319
320 while (left > 0) {
321 ssize_t n;
322 struct pollfd pollfd;
323 usec_t t;
324 int k;
325
326 t = now(CLOCK_MONOTONIC);
327
328 if (t >= end) {
329 r = -ETIME;
330 goto finish;
331 }
332
333 zero(pollfd);
334 pollfd.fd = fd;
335 pollfd.events = POLLOUT;
336
337 if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0)
338 return -errno;
339
340 if (k <= 0) {
341 r = -ETIME;
342 goto finish;
343 }
344
345 if ((n = write(fd, p, left)) < 0) {
346
347 if (errno == EAGAIN)
348 continue;
349
350 r = -errno;
351 goto finish;
352 }
353
354 assert((size_t) n <= left);
355
356 p += n;
357 left -= n;
358 }
359
360 r = 0;
361
362finish:
363 close_nointr_nofail(fd);
364
365 return r;
366}
367
7af53310 368int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
ef2f1067 369 struct utmpx *u;
11620592 370 char date[FORMAT_TIMESTAMP_MAX];
629c210d 371 char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
ef2f1067 372 int r;
ef2f1067
LP
373
374 if (!(hn = gethostname_malloc()) ||
8c6db833 375 !(un = getlogname_malloc())) {
ef2f1067
LP
376 r = -ENOMEM;
377 goto finish;
378 }
379
fc116c6a 380 getttyname_harder(STDIN_FILENO, &tty);
ef2f1067
LP
381
382 if (asprintf(&text,
383 "\a\r\n"
11620592 384 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
ef2f1067 385 "%s\r\n\r\n",
11620592
LP
386 un, hn,
387 tty ? " on " : "", strempty(tty),
388 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
389 message) < 0) {
ef2f1067
LP
390 r = -ENOMEM;
391 goto finish;
392 }
393
394 setutxent();
395
396 r = 0;
397
398 while ((u = getutxent())) {
399 int q;
400 const char *path;
401 char *buf = NULL;
402
403 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
404 continue;
405
406 if (path_startswith(u->ut_line, "/dev/"))
407 path = u->ut_line;
408 else {
409 if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) {
410 r = -ENOMEM;
411 goto finish;
412 }
413
414 path = buf;
415 }
416
7af53310
LP
417 if (!match_tty || match_tty(path))
418 if ((q = write_to_terminal(path, text)) < 0)
419 r = q;
ef2f1067
LP
420
421 free(buf);
422 }
423
424finish:
425 free(hn);
426 free(un);
427 free(tty);
428 free(text);
429
430 return r;
431}