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) {
96 t = now(CLOCK_REALTIME);
98 store->ut_tv.tv_sec = t / USEC_PER_SEC;
99 store->ut_tv.tv_usec = t % USEC_PER_SEC;
102 static void init_entry(struct utmpx *store, usec_t t) {
103 struct utsname uts = {};
107 init_timestamp(store, t);
109 if (uname(&uts) >= 0)
110 strncpy(store->ut_host, uts.release, sizeof(store->ut_host));
112 strncpy(store->ut_line, "~", sizeof(store->ut_line)); /* or ~~ ? */
113 strncpy(store->ut_id, "~~", sizeof(store->ut_id));
116 static int write_entry_utmp(const struct utmpx *store) {
121 /* utmp is similar to wtmp, but there is only one entry for
122 * each entry type resp. user; i.e. basically a key/value
125 if (utmpxname(_PATH_UTMPX) < 0)
130 if (!pututxline(store))
140 static int write_entry_wtmp(const struct utmpx *store) {
143 /* wtmp is a simple append-only file where each entry is
144 simply appended to the end; i.e. basically a log. */
147 updwtmpx(_PATH_WTMPX, store);
151 static int write_utmp_wtmp(const struct utmpx *store_utmp, const struct utmpx *store_wtmp) {
154 r = write_entry_utmp(store_utmp);
155 s = write_entry_wtmp(store_wtmp);
160 /* If utmp/wtmp have been disabled, that's a good thing, hence
161 * ignore the errors */
168 static int write_entry_both(const struct utmpx *store) {
169 return write_utmp_wtmp(store, store);
172 int utmp_put_shutdown(void) {
173 struct utmpx store = {};
175 init_entry(&store, 0);
177 store.ut_type = RUN_LVL;
178 strncpy(store.ut_user, "shutdown", sizeof(store.ut_user));
180 return write_entry_both(&store);
183 int utmp_put_reboot(usec_t t) {
184 struct utmpx store = {};
186 init_entry(&store, t);
188 store.ut_type = BOOT_TIME;
189 strncpy(store.ut_user, "reboot", sizeof(store.ut_user));
191 return write_entry_both(&store);
194 _pure_ static const char *sanitize_id(const char *id) {
200 if (l <= sizeof(((struct utmpx*) NULL)->ut_id))
203 return id + l - sizeof(((struct utmpx*) NULL)->ut_id);
206 int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) {
207 struct utmpx store = {
208 .ut_type = INIT_PROCESS,
215 init_timestamp(&store, 0);
217 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
218 strncpy(store.ut_id, sanitize_id(id), sizeof(store.ut_id));
221 strncpy(store.ut_line, basename(line), sizeof(store.ut_line));
223 return write_entry_both(&store);
226 int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) {
227 struct utmpx lookup = {
228 .ut_type = INIT_PROCESS /* looks for DEAD_PROCESS, LOGIN_PROCESS, USER_PROCESS, too */
229 }, store, store_wtmp, *found;
235 /* ut_id needs only be nul-terminated if it is shorter than sizeof(ut_id) */
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) {
263 struct utmpx store = {};
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, const char *username, 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();
360 un = getlogname_malloc();
365 getttyname_harder(STDIN_FILENO, &tty);
369 "Broadcast message from %s@%s%s%s (%s):\r\n\r\n"
372 tty ? " on " : "", strempty(tty),
373 format_timestamp(date, sizeof(date), now(CLOCK_REALTIME)),
381 while ((u = getutxent())) {
382 _cleanup_free_ char *buf = NULL;
386 if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0)
389 /* this access is fine, because strlen("/dev/") << 32 (UT_LINESIZE) */
390 if (path_startswith(u->ut_line, "/dev/"))
393 if (asprintf(&buf, "/dev/%.*s", (int) sizeof(u->ut_line), u->ut_line) < 0)
399 if (!match_tty || match_tty(path)) {
400 q = write_to_terminal(path, text);