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 lookup, *found;
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)
70 lookup.ut_type = RUN_LVL;
72 if (!(found = getutxid(&lookup)))
77 a = found->ut_pid & 0xFF;
78 b = (found->ut_pid >> 8) & 0xFF;
96 static void init_timestamp(struct utmpx *store, usec_t t) {
102 t = now(CLOCK_REALTIME);
104 store->ut_tv.tv_sec = t / USEC_PER_SEC;
105 store->ut_tv.tv_usec = t % USEC_PER_SEC;
108 static void init_entry(struct utmpx *store, usec_t t) {
113 init_timestamp(store, t);
117 if (uname(&uts) >= 0)
118 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
120 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
121 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
124 static int write_entry_utmp(const struct utmpx *store) {
129 /* utmp is similar to wtmp, but there is only one entry for
130 * each entry type resp. user; i.e. basically a key/value
133 if (utmpxname(_PATH_UTMPX) < 0)
138 if (!pututxline(store))
148 static int write_entry_wtmp(const struct utmpx *store) {
151 /* wtmp is a simple append-only file where each entry is
152 simply appended to * the end; i.e. basically a log. */
155 updwtmpx(_PATH_WTMPX, store);
159 static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
162 r = write_entry_utmp(store_utmp);
163 s = write_entry_wtmp(store_wtmp);
168 /* If utmp/wtmp have been disabled, that's a good thing, hence
169 * ignore the errors */
176 static int write_entry_both(const struct utmpx *store) {
177 return write_utmp_wtmp(store, store);
180 int utmp_put_shutdown(void) {
183 init_entry(&store, 0);
185 store.ut_type = RUN_LVL;
186 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
188 return write_entry_both(&store);
191 int utmp_put_reboot(usec_t t) {
194 init_entry(&store, t);
196 store.ut_type = BOOT_TIME;
197 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
199 return write_entry_both(&store);
202 static const char *sanitize_id(const char *id) {
208 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
211 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
214 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
219 init_timestamp(&store, 0);
221 store.ut_type = INIT_PROCESS;
223 store.ut_session = sid;
225 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
228 strncpy(store.ut_line, path_get_file_name(line), sizeof(store.ut_line));
230 return write_entry_both(&store);
233 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
234 struct utmpx lookup, store, store_wtmp, *found;
241 lookup.ut_type = INIT_PROCESS; /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
242 strncpy(lookup.ut_id, sanitize_id(id), sizeof(lookup.ut_id));
244 if (!(found = getutxid(&lookup)))
247 if (found->ut_pid != pid)
250 memcpy(&store, found, sizeof(store));
251 store.ut_type = DEAD_PROCESS;
252 store.ut_exit.e_termination = code;
253 store.ut_exit.e_exit = status;
259 memcpy(&store_wtmp, &store, sizeof(store_wtmp));
260 /* wtmp wants the current time */
261 init_timestamp(&store_wtmp, 0);
263 return write_utmp_wtmp(&store, &store_wtmp);
267 int utmp_put_runlevel(int runlevel, int previous) {
271 assert(runlevel > 0);
274 /* Find the old runlevel automatically */
276 if ((r = utmp_get_runlevel(&previous, NULL)) < 0) {
284 if (previous == runlevel)
287 init_entry(&store, 0);
289 store.ut_type = RUN_LVL;
290 store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
291 strncpy(store.ut_user, "runlevel", sizeof(store.ut_user));
293 return write_entry_both(&store);
296 #define TIMEOUT_MSEC 50
298 static int write_to_terminal(const char *tty, const char *message) {
307 if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0)
316 left = strlen(message);
318 end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC;
322 struct pollfd pollfd;
326 t = now(CLOCK_MONOTONIC);
335 pollfd.events = POLLOUT;
337 if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0)
345 if ((n = write(fd, p, left)) < 0) {
354 assert((size_t) n <= left);
363 close_nointr_nofail(fd);
368 int utmp_wall(const char *message, bool (*match_tty)(const char *tty)) {
370 char date[FORMAT_TIMESTAMP_MAX];
371 char *text = NULL, *hn = NULL, *un = NULL, *tty = NULL;
374 if (!(hn = gethostname_malloc()) ||
375 !(un = getlogname_malloc())) {
380 getttyname_harder(STDIN_FILENO, &tty);
384 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
387 tty ? " on " : "", strempty(tty),
388 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
398 while ((u = getutxent())) {
403 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
406 if (path_startswith(u->ut_line, "/dev/"))
409 if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) {
417 if (!match_tty || match_tty(path))
418 if ((q = write_to_terminal(path, text)) < 0)