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/>.
25 #include <sys/utsname.h>
31 #include "path-util.h"
32 #include "utmp-wtmp.h"
34 int utmp_get_runlevel(int *runlevel, int *previous) {
35 struct utmpx *found, lookup = { .ut_type = RUN_LVL };
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
48 e = getenv("RUNLEVEL");
53 /* $PREVLEVEL seems to be an Upstart thing */
55 e = getenv("PREVLEVEL");
65 if (utmpxname(_PATH_UTMPX) < 0)
70 found = getutxid(&lookup);
76 a = found->ut_pid & 0xFF;
77 b = (found->ut_pid >> 8) & 0xFF;
91 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) {
172 struct utmpx store = {};
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) {
183 struct utmpx store = {};
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) {
206 struct utmpx store = {
207 .ut_type = INIT_PROCESS,
214 init_timestamp(&store, 0);
216 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
217 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
220 strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
222 return write_entry_both(&store);
225 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
226 struct utmpx lookup = {
227 .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
228 }, store, store_wtmp, *found;
234 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
235 strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
237 found = getutxid(&lookup);
241 if (found->ut_pid != pid)
244 memcpy(&store, found, sizeof(store));
245 store.ut_type = DEAD_PROCESS;
246 store.ut_exit.e_termination = code;
247 store.ut_exit.e_exit = status;
253 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
254 /* wtmp wants the current time */
255 init_timestamp(&store_wtmp, 0);
257 return write_utmp_wtmp(&store, &store_wtmp);
261 int utmp_put_runlevel(int runlevel, int previous) {
262 struct utmpx store = {};
265 assert(runlevel > 0);
268 /* Find the old runlevel automatically */
270 r = utmp_get_runlevel(&previous, NULL);
279 if (previous == runlevel)
282 init_entry(&store, 0);
284 store.ut_type = RUN_LVL;
285 store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
286 strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
288 return write_entry_both(&store);
291 #define TIMEOUT_MSEC 50
293 static int write_to_terminal(const char *tty, const char *message) {
294 _cleanup_close_ int fd = -1;
302 fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC);
303 if (fd < 0 || !isatty(fd))
307 left = strlen(message);
309 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
313 struct pollfd pollfd = {
320 t = now(CLOCK_MONOTONIC);
325 k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
332 n = write(fd, p, left);
340 assert((size_t) n <= left);
349 int utmp_wall(const char *message, const char *username, bool (*match_tty)(const char *tty)) {
350 _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
351 char date[FORMAT_TIMESTAMP_MAX];
355 hn = gethostname_malloc();
359 un = getlogname_malloc();
364 getttyname_harder(STDIN_FILENO, &tty);
368 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
371 tty ? " on " : "", strempty(tty),
372 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
380 while ((u = getutxent())) {
381 _cleanup_free_ char *buf = NULL;
385 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
388 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
389 if (path_startswith(u->ut_line, "/dev/"))
392 if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
398 if (!match_tty || match_tty(path)) {
399 q = write_to_terminal(path, text);