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