1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/inotify.h>
29 #include <sys/socket.h>
30 #include <sys/sysmacros.h>
33 #include <linux/tiocl.h>
37 #include <sys/ioctl.h>
38 #include <sys/types.h>
42 #include "alloc-util.h"
50 #include "parse-util.h"
51 #include "process-util.h"
52 #include "socket-util.h"
53 #include "stat-util.h"
54 #include "string-util.h"
56 #include "terminal-util.h"
57 #include "time-util.h"
60 /// Additional includes needed by elogind
61 #include "path-util.h"
63 static volatile unsigned cached_columns = 0;
64 static volatile unsigned cached_lines = 0;
66 static volatile int cached_on_tty = -1;
67 static volatile int cached_colors_enabled = -1;
68 static volatile int cached_underline_enabled = -1;
71 _cleanup_close_ int fd;
73 /* Switch to the specified vt number. If the VT is specified <= 0 switch to the VT the kernel log messages go,
74 * if that's configured. */
76 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
82 TIOCL_GETKMSGREDIRECT,
86 if (ioctl(fd, TIOCLINUX, tiocl) < 0)
89 vt = tiocl[0] <= 0 ? 1 : tiocl[0];
92 if (ioctl(fd, VT_ACTIVATE, vt) < 0)
98 #if 0 /// UNNEEDED by elogind
99 int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
100 struct termios old_termios, new_termios;
101 char c, line[LINE_MAX];
106 if (tcgetattr(fileno(f), &old_termios) >= 0) {
107 new_termios = old_termios;
109 new_termios.c_lflag &= ~ICANON;
110 new_termios.c_cc[VMIN] = 1;
111 new_termios.c_cc[VTIME] = 0;
113 if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
116 if (t != USEC_INFINITY) {
117 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
118 tcsetattr(fileno(f), TCSADRAIN, &old_termios);
123 k = fread(&c, 1, 1, f);
125 tcsetattr(fileno(f), TCSADRAIN, &old_termios);
131 *need_nl = c != '\n';
138 if (t != USEC_INFINITY) {
139 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
144 if (!fgets(line, sizeof(line), f))
145 return errno > 0 ? -errno : -EIO;
149 if (strlen(line) != 1)
159 #define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC)
161 int ask_char(char *ret, const char *replies, const char *fmt, ...) {
173 if (colors_enabled())
174 fputs(ANSI_HIGHLIGHT, stdout);
182 if (colors_enabled())
183 fputs(ANSI_NORMAL, stdout);
187 r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, &need_nl);
194 puts("Bad input, please try again.");
205 if (strchr(replies, c)) {
210 puts("Read unexpected character, please try again.");
214 int ask_string(char **ret, const char *text, ...) {
222 if (colors_enabled())
223 fputs(ANSI_HIGHLIGHT, stdout);
229 if (colors_enabled())
230 fputs(ANSI_NORMAL, stdout);
235 if (!fgets(line, sizeof(line), stdin))
236 return errno > 0 ? -errno : -EIO;
238 if (!endswith(line, "\n"))
257 int reset_terminal_fd(int fd, bool switch_to_text) {
258 struct termios termios;
261 /* Set terminal to some sane defaults */
265 /* We leave locked terminal attributes untouched, so that
266 * Plymouth may set whatever it wants to set, and we don't
267 * interfere with that. */
269 /* Disable exclusive mode, just in case */
270 (void) ioctl(fd, TIOCNXCL);
272 /* Switch to text mode */
274 (void) ioctl(fd, KDSETMODE, KD_TEXT);
276 /* Set default keyboard mode */
277 (void) vt_reset_keyboard(fd);
279 if (tcgetattr(fd, &termios) < 0) {
284 /* We only reset the stuff that matters to the software. How
285 * hardware is set up we don't touch assuming that somebody
286 * else will do that for us */
288 termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
289 termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
290 termios.c_oflag |= ONLCR;
291 termios.c_cflag |= CREAD;
292 termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
294 termios.c_cc[VINTR] = 03; /* ^C */
295 termios.c_cc[VQUIT] = 034; /* ^\ */
296 termios.c_cc[VERASE] = 0177;
297 termios.c_cc[VKILL] = 025; /* ^X */
298 termios.c_cc[VEOF] = 04; /* ^D */
299 termios.c_cc[VSTART] = 021; /* ^Q */
300 termios.c_cc[VSTOP] = 023; /* ^S */
301 termios.c_cc[VSUSP] = 032; /* ^Z */
302 termios.c_cc[VLNEXT] = 026; /* ^V */
303 termios.c_cc[VWERASE] = 027; /* ^W */
304 termios.c_cc[VREPRINT] = 022; /* ^R */
305 termios.c_cc[VEOL] = 0;
306 termios.c_cc[VEOL2] = 0;
308 termios.c_cc[VTIME] = 0;
309 termios.c_cc[VMIN] = 1;
311 if (tcsetattr(fd, TCSANOW, &termios) < 0)
315 /* Just in case, flush all crap out */
316 (void) tcflush(fd, TCIOFLUSH);
321 int reset_terminal(const char *name) {
322 _cleanup_close_ int fd = -1;
324 /* We open the terminal with O_NONBLOCK here, to ensure we
325 * don't block on carrier if this is a terminal with carrier
328 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
332 return reset_terminal_fd(fd, true);
336 int open_terminal(const char *name, int mode) {
341 * If a TTY is in the process of being closed opening it might
342 * cause EIO. This is horribly awful, but unlikely to be
343 * changed in the kernel. Hence we work around this problem by
344 * retrying a couple of times.
346 * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
353 fd = open(name, mode, 0);
360 /* Max 1s in total */
364 usleep(50 * USEC_PER_MSEC);
368 if (isatty(fd) <= 0) {
376 #if 0 /// UNNEEDED by elogind
377 int acquire_terminal(
379 AcquireTerminalFlags flags,
382 _cleanup_close_ int notify = -1, fd = -1;
383 usec_t ts = USEC_INFINITY;
387 assert(IN_SET(flags & ~ACQUIRE_TERMINAL_PERMISSIVE, ACQUIRE_TERMINAL_TRY, ACQUIRE_TERMINAL_FORCE, ACQUIRE_TERMINAL_WAIT));
389 /* We use inotify to be notified when the tty is closed. We create the watch before checking if we can actually
390 * acquire it, so that we don't lose any event.
392 * Note: strictly speaking this actually watches for the device being closed, it does *not* really watch
393 * whether a tty loses its controlling process. However, unless some rogue process uses TIOCNOTTY on /dev/tty
394 * *after* closing its tty otherwise this will not become a problem. As long as the administrator makes sure
395 * not configure any service on the same tty as an untrusted user this should not be a problem. (Which he
396 * probably should not do anyway.) */
398 if ((flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_WAIT) {
399 notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
403 wd = inotify_add_watch(notify, name, IN_CLOSE);
407 if (timeout != USEC_INFINITY)
408 ts = now(CLOCK_MONOTONIC);
412 struct sigaction sa_old, sa_new = {
413 .sa_handler = SIG_IGN,
414 .sa_flags = SA_RESTART,
418 r = flush_fd(notify);
423 /* We pass here O_NOCTTY only so that we can check the return value TIOCSCTTY and have a reliable way
424 * to figure out if we successfully became the controlling process of the tty */
425 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
429 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed if we already own the tty. */
430 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
432 /* First, try to get the tty */
433 r = ioctl(fd, TIOCSCTTY,
434 (flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_FORCE) < 0 ? -errno : 0;
436 /* Reset signal handler to old value */
437 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
439 /* Success? Exit the loop now! */
443 /* Any failure besides -EPERM? Fail, regardless of the mode. */
447 if (flags & ACQUIRE_TERMINAL_PERMISSIVE) /* If we are in permissive mode, then EPERM is fine, turn this
448 * into a success. Note that EPERM is also returned if we
449 * already are the owner of the TTY. */
452 if (flags != ACQUIRE_TERMINAL_WAIT) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
459 union inotify_event_buffer buffer;
460 struct inotify_event *e;
463 if (timeout != USEC_INFINITY) {
466 assert(ts != USEC_INFINITY);
468 n = now(CLOCK_MONOTONIC);
469 if (ts + timeout < n)
472 r = fd_wait_for_event(notify, POLLIN, ts + timeout - n);
479 l = read(notify, &buffer, sizeof(buffer));
481 if (IN_SET(errno, EINTR, EAGAIN))
487 FOREACH_INOTIFY_EVENT(e, buffer, l) {
488 if (e->mask & IN_Q_OVERFLOW) /* If we hit an inotify queue overflow, simply check if the terminal is up for grabs now. */
491 if (e->wd != wd || !(e->mask & IN_CLOSE)) /* Safety checks */
498 /* We close the tty fd here since if the old session ended our handle will be dead. It's important that
499 * we do this after sleeping, so that we don't enter an endless loop. */
510 #if 0 /// UNNEEDED by elogind
511 int release_terminal(void) {
512 static const struct sigaction sa_new = {
513 .sa_handler = SIG_IGN,
514 .sa_flags = SA_RESTART,
517 _cleanup_close_ int fd = -1;
518 struct sigaction sa_old;
521 fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
525 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
526 * by our own TIOCNOTTY */
527 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
529 r = ioctl(fd, TIOCNOTTY) < 0 ? -errno : 0;
531 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
536 int terminal_vhangup_fd(int fd) {
539 if (ioctl(fd, TIOCVHANGUP) < 0)
545 int terminal_vhangup(const char *name) {
546 _cleanup_close_ int fd;
548 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
552 return terminal_vhangup_fd(fd);
555 int vt_disallocate(const char *name) {
556 _cleanup_close_ int fd = -1;
561 /* Deallocate the VT if possible. If not possible
562 * (i.e. because it is the active one), at least clear it
563 * entirely (including the scrollback buffer) */
565 e = path_startswith(name, "/dev/");
569 if (!tty_is_vc(name)) {
570 /* So this is not a VT. I guess we cannot deallocate
571 * it then. But let's at least clear the screen */
573 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
578 "\033[r" /* clear scrolling region */
579 "\033[H" /* move home */
580 "\033[2J", /* clear screen */
585 n = startswith(e, "tty");
589 r = safe_atou(n, &u);
596 /* Try to deallocate */
597 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
601 r = ioctl(fd, VT_DISALLOCATE, u);
610 /* Couldn't deallocate, so let's clear it fully with
612 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
617 "\033[r" /* clear scrolling region */
618 "\033[H" /* move home */
619 "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
624 int make_console_stdio(void) {
627 /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
629 fd = acquire_terminal("/dev/console", ACQUIRE_TERMINAL_FORCE|ACQUIRE_TERMINAL_PERMISSIVE, USEC_INFINITY);
631 return log_error_errno(fd, "Failed to acquire terminal: %m");
633 r = reset_terminal_fd(fd, true);
635 log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
639 return log_error_errno(r, "Failed to duplicate terminal fd: %m");
641 reset_terminal_feature_caches();
647 bool tty_is_vc(const char *tty) {
650 return vtnr_from_tty(tty) >= 0;
653 bool tty_is_console(const char *tty) {
656 return streq(skip_dev_prefix(tty), "console");
659 int vtnr_from_tty(const char *tty) {
664 tty = skip_dev_prefix(tty);
666 if (!startswith(tty, "tty") )
669 if (tty[3] < '0' || tty[3] > '9')
672 r = safe_atoi(tty+3, &i);
682 #if 0 /// UNNEEDED by elogind
683 char *resolve_dev_console(char **active) {
686 /* Resolve where /dev/console is pointing to, if /sys is actually ours
687 * (i.e. not read-only-mounted which is a sign for container setups) */
689 if (path_is_read_only_fs("/sys") > 0)
692 if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
695 /* If multiple log outputs are configured the last one is what
696 * /dev/console points to */
697 tty = strrchr(*active, ' ');
703 if (streq(tty, "tty0")) {
706 /* Get the active VC (e.g. tty1) */
707 if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) {
716 int get_kernel_consoles(char ***consoles) {
717 _cleanup_strv_free_ char **con = NULL;
718 _cleanup_free_ char *line = NULL;
724 r = read_one_line_file("/sys/class/tty/console/active", &line);
730 _cleanup_free_ char *tty = NULL;
733 r = extract_first_word(&active, &tty, NULL, 0);
739 if (streq(tty, "tty0")) {
741 r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
746 path = strappend("/dev/", tty);
750 if (access(path, F_OK) < 0) {
751 log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
756 r = strv_consume(&con, path);
761 if (strv_isempty(con)) {
762 log_debug("No devices found for system console");
764 r = strv_extend(&con, "/dev/console");
774 bool tty_is_vc_resolve(const char *tty) {
775 _cleanup_free_ char *active = NULL;
779 tty = skip_dev_prefix(tty);
781 if (streq(tty, "console")) {
782 tty = resolve_dev_console(&active);
787 return tty_is_vc(tty);
790 const char *default_term_for_tty(const char *tty) {
791 return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220";
795 int fd_columns(int fd) {
796 struct winsize ws = {};
798 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
807 unsigned columns(void) {
811 if (cached_columns > 0)
812 return cached_columns;
815 e = getenv("COLUMNS");
817 (void) safe_atoi(e, &c);
820 c = fd_columns(STDOUT_FILENO);
826 return cached_columns;
829 int fd_lines(int fd) {
830 struct winsize ws = {};
832 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
841 unsigned lines(void) {
845 if (cached_lines > 0)
851 (void) safe_atoi(e, &l);
854 l = fd_lines(STDOUT_FILENO);
863 /* intended to be used as a SIGWINCH sighandler */
864 #if 0 /// UNNEEDED by elogind
865 void columns_lines_cache_reset(int signum) {
871 void reset_terminal_feature_caches(void) {
875 cached_colors_enabled = -1;
876 cached_underline_enabled = -1;
881 if (cached_on_tty < 0)
882 cached_on_tty = isatty(STDOUT_FILENO) > 0;
884 return cached_on_tty;
887 int make_stdio(int fd) {
892 if (dup2(fd, STDIN_FILENO) < 0)
894 if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0)
896 if (dup2(fd, STDERR_FILENO) < 0 && r >= 0)
902 /* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */
903 stdio_unset_cloexec();
908 int make_null_stdio(void) {
911 null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC);
915 r = make_stdio(null_fd);
917 reset_terminal_feature_caches();
922 int getttyname_malloc(int fd, char **ret) {
932 r = ttyname_r(fd, path, sizeof(path));
936 c = strdup(skip_dev_prefix(path));
953 int getttyname_harder(int fd, char **r) {
957 k = getttyname_malloc(fd, &s);
961 if (streq(s, "tty")) {
963 return get_ctty(0, NULL, r);
970 int get_ctty_devnr(pid_t pid, dev_t *d) {
972 _cleanup_free_ char *line = NULL;
978 p = procfs_file_alloca(pid, "stat");
979 r = read_one_line_file(p, &line);
983 p = strrchr(line, ')');
998 if (major(ttynr) == 0 && minor(ttynr) == 0)
1007 int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
1008 char fn[STRLEN("/dev/char/") + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
1009 _cleanup_free_ char *s = NULL;
1016 k = get_ctty_devnr(pid, &devnr);
1020 sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
1022 k = readlink_malloc(fn, &s);
1028 /* This is an ugly hack */
1029 if (major(devnr) == 136) {
1030 if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
1033 /* Probably something like the ptys which have no
1034 * symlink in /dev/char. Let's return something
1035 * vaguely useful. */
1042 if (startswith(s, "/dev/"))
1044 else if (startswith(s, "../"))
1061 #if 0 /// UNNEEDED by elogind
1062 int ptsname_malloc(int fd, char **ret) {
1075 if (ptsname_r(fd, c, l) == 0) {
1079 if (errno != ERANGE) {
1089 int ptsname_namespace(int pty, char **ret) {
1092 /* Like ptsname(), but doesn't assume that the path is
1093 * accessible in the local namespace. */
1095 r = ioctl(pty, TIOCGPTN, &no);
1102 if (asprintf(ret, "/dev/pts/%i", no) < 0)
1108 int openpt_in_namespace(pid_t pid, int flags) {
1109 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1110 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1116 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1120 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1123 r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
1129 pair[0] = safe_close(pair[0]);
1131 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1133 _exit(EXIT_FAILURE);
1135 master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
1137 _exit(EXIT_FAILURE);
1139 if (unlockpt(master) < 0)
1140 _exit(EXIT_FAILURE);
1142 if (send_one_fd(pair[1], master, 0) < 0)
1143 _exit(EXIT_FAILURE);
1145 _exit(EXIT_SUCCESS);
1148 pair[1] = safe_close(pair[1]);
1150 r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
1153 if (r != EXIT_SUCCESS)
1156 return receive_one_fd(pair[0], 0);
1159 int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
1160 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1161 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1165 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1169 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1172 r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
1178 pair[0] = safe_close(pair[0]);
1180 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1182 _exit(EXIT_FAILURE);
1184 master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
1186 _exit(EXIT_FAILURE);
1188 if (send_one_fd(pair[1], master, 0) < 0)
1189 _exit(EXIT_FAILURE);
1191 _exit(EXIT_SUCCESS);
1194 pair[1] = safe_close(pair[1]);
1196 r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
1199 if (r != EXIT_SUCCESS)
1202 return receive_one_fd(pair[0], 0);
1206 static bool getenv_terminal_is_dumb(void) {
1213 return streq(e, "dumb");
1216 bool terminal_is_dumb(void) {
1220 return getenv_terminal_is_dumb();
1223 bool colors_enabled(void) {
1225 /* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
1226 * (which is the explicit way to turn off/on colors). If that didn't work we turn off colors unless we are on a
1227 * TTY. And if we are on a TTY we turn it off if $TERM is set to "dumb". There's one special tweak though: if
1228 * we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open
1229 * continously due to fear of SAK, and hence things are a bit weird. */
1231 if (cached_colors_enabled < 0) {
1232 #if 0 /// elogind does not allow such forcing, and we are never init!
1235 val = getenv_bool("SYSTEMD_COLORS");
1237 cached_colors_enabled = val;
1238 else if (getpid_cached() == 1)
1239 /* PID1 outputs to the console without holding it open all the time */
1240 cached_colors_enabled = !getenv_terminal_is_dumb();
1243 cached_colors_enabled = !terminal_is_dumb();
1246 return cached_colors_enabled;
1249 bool underline_enabled(void) {
1251 if (cached_underline_enabled < 0) {
1253 /* The Linux console doesn't support underlining, turn it off, but only there. */
1255 if (colors_enabled())
1256 cached_underline_enabled = !streq_ptr(getenv("TERM"), "linux");
1258 cached_underline_enabled = false;
1261 return cached_underline_enabled;
1264 int vt_default_utf8(void) {
1265 _cleanup_free_ char *b = NULL;
1268 /* Read the default VT UTF8 setting from the kernel */
1270 r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b);
1274 return parse_boolean(b);
1277 int vt_reset_keyboard(int fd) {
1280 /* If we can't read the default, then default to unicode. It's 2017 after all. */
1281 kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
1283 if (ioctl(fd, KDSKBMODE, kb) < 0)