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