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 e = getenv("RUNLEVEL");
54 /* $PREVLEVEL seems to be an Upstart thing */
56 e = getenv("PREVLEVEL");
66 if (utmpxname(_PATH_UTMPX) < 0)
71 found = getutxid(&lookup);
77 a = found->ut_pid & 0xFF;
78 b = (found->ut_pid >> 8) & 0xFF;
92 static void init_timestamp(struct utmpx *store, usec_t t) {
98 t = now(CLOCK_REALTIME);
100 store->ut_tv.tv_sec = t / USEC_PER_SEC;
101 store->ut_tv.tv_usec = t % USEC_PER_SEC;
104 static void init_entry(struct utmpx *store, usec_t t) {
105 struct utsname uts = {};
109 init_timestamp(store, t);
111 if (uname(&uts) >= 0)
112 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
114 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
115 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
118 static int write_entry_utmp(const struct utmpx *store) {
123 /* utmp is similar to wtmp, but there is only one entry for
124 * each entry type resp. user; i.e. basically a key/value
127 if (utmpxname(_PATH_UTMPX) < 0)
132 if (!pututxline(store))
142 static int write_entry_wtmp(const struct utmpx *store) {
145 /* wtmp is a simple append-only file where each entry is
146 simply appended to * the end; i.e. basically a log. */
149 updwtmpx(_PATH_WTMPX, store);
153 static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
156 r = write_entry_utmp(store_utmp);
157 s = write_entry_wtmp(store_wtmp);
162 /* If utmp/wtmp have been disabled, that's a good thing, hence
163 * ignore the errors */
170 static int write_entry_both(const struct utmpx *store) {
171 return write_utmp_wtmp(store, store);
174 int utmp_put_shutdown(void) {
177 init_entry(&store, 0);
179 store.ut_type = RUN_LVL;
180 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
182 return write_entry_both(&store);
185 int utmp_put_reboot(usec_t t) {
188 init_entry(&store, t);
190 store.ut_type = BOOT_TIME;
191 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
193 return write_entry_both(&store);
196 _pure_ static const char *sanitize_id(const char *id) {
202 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
205 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
208 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
213 init_timestamp(&store, 0);
215 store.ut_type = INIT_PROCESS;
217 store.ut_session = sid;
219 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
222 strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
224 return write_entry_both(&store);
227 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
228 struct utmpx lookup, store, store_wtmp, *found;
235 lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
236 strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
238 found = getutxid(&lookup);
242 if (found->ut_pid != pid)
245 memcpy(&store, found, sizeof(store));
246 store.ut_type = DEAD_PROCESS;
247 store.ut_exit.e_termination = code;
248 store.ut_exit.e_exit = status;
254 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
255 /* wtmp wants the current time */
256 init_timestamp(&store_wtmp, 0);
258 return write_utmp_wtmp(&store, &store_wtmp);
262 int utmp_put_runlevel(int runlevel, int previous) {
266 assert(runlevel > 0);
269 /* Find the old runlevel automatically */
271 r = utmp_get_runlevel(&previous, NULL);
280 if (previous == runlevel)
283 init_entry(&store, 0);
285 store.ut_type = RUN_LVL;
286 store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
287 strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
289 return write_entry_both(&store);
292 #define TIMEOUT_MSEC 50
294 static int write_to_terminal(const char *tty, const char *message) {
295 _cleanup_close_ int fd = -1;
303 fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC);
304 if (fd < 0 || !isatty(fd))
308 left = strlen(message);
310 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
314 struct pollfd pollfd = {
321 t = now(CLOCK_MONOTONIC);
326 k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC);
333 n = write(fd, p, left);
341 assert((size_t) n <= left);
350 int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
351 _cleanup_free_ char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
352 char date[FORMAT_TIMESTAMP_MAX];
356 hn = gethostname_malloc();
357 un = getlogname_malloc();
361 getttyname_harder(STDIN_FILENO, &tty);
365 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
368 tty ? " on " : "", strempty(tty),
369 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
377 while ((u = getutxent())) {
378 _cleanup_free_ char *buf = NULL;
382 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
385 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
386 if (path_startswith(u->ut_line, "/dev/"))
389 if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
395 if (!match_tty || match_tty(path)) {
396 q = write_to_terminal(path, text);