1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
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
11 (at your option) any later version.
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
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include <sys/utsname.h>
32 #include "path-util.h"
33 #include "utmp-wtmp.h"
35 int utmp_get_runlevel(int *runlevel, int *previous) {
36 struct utmpx *found, lookup = { .ut_type = RUN_LVL };
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
49 if ((e = getenv("RUNLEVEL")) && e[0] > 0) {
53 /* $PREVLEVEL seems to be an Upstart thing */
55 if ((e = getenv("PREVLEVEL")) && e[0] > 0)
64 if (utmpxname(_PATH_UTMPX) < 0)
69 if (!(found = getutxid(&lookup)))
74 a = found->ut_pid & 0xFF;
75 b = (found->ut_pid >> 8) & 0xFF;
89 static void init_timestamp(struct utmpx *store, usec_t t) {
95 t = now(CLOCK_REALTIME);
97 store->ut_tv.tv_sec = t / USEC_PER_SEC;
98 store->ut_tv.tv_usec = t % USEC_PER_SEC;
101 static void init_entry(struct utmpx *store, usec_t t) {
102 struct utsname uts = {};
106 init_timestamp(store, t);
108 if (uname(&uts) >= 0)
109 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
111 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
112 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
115 static int write_entry_utmp(const struct utmpx *store) {
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
124 if (utmpxname(_PATH_UTMPX) < 0)
129 if (!pututxline(store))
139 static int write_entry_wtmp(const struct utmpx *store) {
142 /* wtmp is a simple append-only file where each entry is
143 simply appended to * the end; i.e. basically a log. */
146 updwtmpx(_PATH_WTMPX, store);
150 static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
153 r = write_entry_utmp(store_utmp);
154 s = write_entry_wtmp(store_wtmp);
159 /* If utmp/wtmp have been disabled, that's a good thing, hence
160 * ignore the errors */
167 static int write_entry_both(const struct utmpx *store) {
168 return write_utmp_wtmp(store, store);
171 int utmp_put_shutdown(void) {
174 init_entry(&store, 0);
176 store.ut_type = RUN_LVL;
177 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
179 return write_entry_both(&store);
182 int utmp_put_reboot(usec_t t) {
185 init_entry(&store, t);
187 store.ut_type = BOOT_TIME;
188 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
190 return write_entry_both(&store);
193 _pure_ static const char *sanitize_id(const char *id) {
199 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
202 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
205 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
210 init_timestamp(&store, 0);
212 store.ut_type = INIT_PROCESS;
214 store.ut_session = sid;
216 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
219 strncpy(store.ut_line, path_get_file_name(line), sizeof(store.ut_line));
221 return write_entry_both(&store);
224 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
225 struct utmpx lookup, store, store_wtmp, *found;
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));
235 if (!(found = getutxid(&lookup)))
238 if (found->ut_pid != pid)
241 memcpy(&store, found, sizeof(store));
242 store.ut_type = DEAD_PROCESS;
243 store.ut_exit.e_termination = code;
244 store.ut_exit.e_exit = status;
250 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
251 /* wtmp wants the current time */
252 init_timestamp(&store_wtmp, 0);
254 return write_utmp_wtmp(&store, &store_wtmp);
258 int utmp_put_runlevel(int runlevel, int previous) {
262 assert(runlevel > 0);
265 /* Find the old runlevel automatically */
267 if ((r = utmp_get_runlevel(&previous, NULL)) < 0) {
275 if (previous == runlevel)
278 init_entry(&store, 0);
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));
284 return write_entry_both(&store);
287 #define TIMEOUT_MSEC 50
289 static int write_to_terminal(const char *tty, const char *message) {
290 _cleanup_close_ int fd = -1;
298 fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC);
299 if (fd < 0 || !isatty(fd))
303 left = strlen(message);
305 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
309 struct pollfd pollfd = {
316 t = now(CLOCK_MONOTONIC);
321 k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
328 n = write(fd, p, left);
336 assert((size_t) n <= left);
345 int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
347 char date[FORMAT_TIMESTAMP_MAX];
348 char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
351 if (!(hn = gethostname_malloc()) ||
352 !(un = getlogname_malloc())) {
357 getttyname_harder(STDIN_FILENO, &tty);
361 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
364 tty ? " on " : "", strempty(tty),
365 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
375 while ((u = getutxent())) {
380 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
383 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
384 if (path_startswith(u->ut_line, "/dev/"))
387 if (asprintf(&buf, "/dev/%.*s",
388 (int) sizeof(u->ut_line), u->ut_line) < 0) {
396 if (!match_tty || match_tty(path))
397 if ((q = write_to_terminal(path, text)) < 0)