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 "path-util.h"
52 //#include "proc-cmdline.h"
53 #include "process-util.h"
54 #include "socket-util.h"
55 #include "stat-util.h"
56 #include "string-util.h"
58 #include "terminal-util.h"
59 #include "time-util.h"
62 /// Additional includes needed by elogind
63 #include "path-util.h"
65 static volatile unsigned cached_columns = 0;
66 static volatile unsigned cached_lines = 0;
68 static volatile int cached_on_tty = -1;
69 static volatile int cached_colors_enabled = -1;
70 static volatile int cached_underline_enabled = -1;
73 _cleanup_close_ int fd;
75 /* Switch to the specified vt number. If the VT is specified <= 0 switch to the VT the kernel log messages go,
76 * if that's configured. */
78 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
84 TIOCL_GETKMSGREDIRECT,
88 if (ioctl(fd, TIOCLINUX, tiocl) < 0)
91 vt = tiocl[0] <= 0 ? 1 : tiocl[0];
94 if (ioctl(fd, VT_ACTIVATE, vt) < 0)
100 #if 0 /// UNNEEDED by elogind
101 int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
102 struct termios old_termios, new_termios;
103 char c, line[LINE_MAX];
108 if (tcgetattr(fileno(f), &old_termios) >= 0) {
109 new_termios = old_termios;
111 new_termios.c_lflag &= ~ICANON;
112 new_termios.c_cc[VMIN] = 1;
113 new_termios.c_cc[VTIME] = 0;
115 if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
118 if (t != USEC_INFINITY) {
119 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
120 tcsetattr(fileno(f), TCSADRAIN, &old_termios);
125 k = fread(&c, 1, 1, f);
127 tcsetattr(fileno(f), TCSADRAIN, &old_termios);
133 *need_nl = c != '\n';
140 if (t != USEC_INFINITY) {
141 if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
146 if (!fgets(line, sizeof(line), f))
147 return errno > 0 ? -errno : -EIO;
151 if (strlen(line) != 1)
161 #define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC)
163 int ask_char(char *ret, const char *replies, const char *fmt, ...) {
175 if (colors_enabled())
176 fputs(ANSI_HIGHLIGHT, stdout);
184 if (colors_enabled())
185 fputs(ANSI_NORMAL, stdout);
189 r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, &need_nl);
196 puts("Bad input, please try again.");
207 if (strchr(replies, c)) {
212 puts("Read unexpected character, please try again.");
216 int ask_string(char **ret, const char *text, ...) {
224 if (colors_enabled())
225 fputs(ANSI_HIGHLIGHT, stdout);
231 if (colors_enabled())
232 fputs(ANSI_NORMAL, stdout);
237 if (!fgets(line, sizeof(line), stdin))
238 return errno > 0 ? -errno : -EIO;
240 if (!endswith(line, "\n"))
259 int reset_terminal_fd(int fd, bool switch_to_text) {
260 struct termios termios;
263 /* Set terminal to some sane defaults */
267 /* We leave locked terminal attributes untouched, so that
268 * Plymouth may set whatever it wants to set, and we don't
269 * interfere with that. */
271 /* Disable exclusive mode, just in case */
272 (void) ioctl(fd, TIOCNXCL);
274 /* Switch to text mode */
276 (void) ioctl(fd, KDSETMODE, KD_TEXT);
278 /* Set default keyboard mode */
279 (void) vt_reset_keyboard(fd);
281 if (tcgetattr(fd, &termios) < 0) {
286 /* We only reset the stuff that matters to the software. How
287 * hardware is set up we don't touch assuming that somebody
288 * else will do that for us */
290 termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
291 termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
292 termios.c_oflag |= ONLCR;
293 termios.c_cflag |= CREAD;
294 termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE;
296 termios.c_cc[VINTR] = 03; /* ^C */
297 termios.c_cc[VQUIT] = 034; /* ^\ */
298 termios.c_cc[VERASE] = 0177;
299 termios.c_cc[VKILL] = 025; /* ^X */
300 termios.c_cc[VEOF] = 04; /* ^D */
301 termios.c_cc[VSTART] = 021; /* ^Q */
302 termios.c_cc[VSTOP] = 023; /* ^S */
303 termios.c_cc[VSUSP] = 032; /* ^Z */
304 termios.c_cc[VLNEXT] = 026; /* ^V */
305 termios.c_cc[VWERASE] = 027; /* ^W */
306 termios.c_cc[VREPRINT] = 022; /* ^R */
307 termios.c_cc[VEOL] = 0;
308 termios.c_cc[VEOL2] = 0;
310 termios.c_cc[VTIME] = 0;
311 termios.c_cc[VMIN] = 1;
313 if (tcsetattr(fd, TCSANOW, &termios) < 0)
317 /* Just in case, flush all crap out */
318 (void) tcflush(fd, TCIOFLUSH);
323 int reset_terminal(const char *name) {
324 _cleanup_close_ int fd = -1;
326 /* We open the terminal with O_NONBLOCK here, to ensure we
327 * don't block on carrier if this is a terminal with carrier
330 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
334 return reset_terminal_fd(fd, true);
338 int open_terminal(const char *name, int mode) {
343 * If a TTY is in the process of being closed opening it might
344 * cause EIO. This is horribly awful, but unlikely to be
345 * changed in the kernel. Hence we work around this problem by
346 * retrying a couple of times.
348 * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
355 fd = open(name, mode, 0);
362 /* Max 1s in total */
366 usleep(50 * USEC_PER_MSEC);
370 if (isatty(fd) <= 0) {
378 #if 0 /// UNNEEDED by elogind
379 int acquire_terminal(
381 AcquireTerminalFlags flags,
384 _cleanup_close_ int notify = -1, fd = -1;
385 usec_t ts = USEC_INFINITY;
389 assert(IN_SET(flags & ~ACQUIRE_TERMINAL_PERMISSIVE, ACQUIRE_TERMINAL_TRY, ACQUIRE_TERMINAL_FORCE, ACQUIRE_TERMINAL_WAIT));
391 /* We use inotify to be notified when the tty is closed. We create the watch before checking if we can actually
392 * acquire it, so that we don't lose any event.
394 * Note: strictly speaking this actually watches for the device being closed, it does *not* really watch
395 * whether a tty loses its controlling process. However, unless some rogue process uses TIOCNOTTY on /dev/tty
396 * *after* closing its tty otherwise this will not become a problem. As long as the administrator makes sure to
397 * not configure any service on the same tty as an untrusted user this should not be a problem. (Which they
398 * probably should not do anyway.) */
400 if ((flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_WAIT) {
401 notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0));
405 wd = inotify_add_watch(notify, name, IN_CLOSE);
409 if (timeout != USEC_INFINITY)
410 ts = now(CLOCK_MONOTONIC);
414 struct sigaction sa_old, sa_new = {
415 .sa_handler = SIG_IGN,
416 .sa_flags = SA_RESTART,
420 r = flush_fd(notify);
425 /* We pass here O_NOCTTY only so that we can check the return value TIOCSCTTY and have a reliable way
426 * to figure out if we successfully became the controlling process of the tty */
427 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
431 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed if we already own the tty. */
432 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
434 /* First, try to get the tty */
435 r = ioctl(fd, TIOCSCTTY,
436 (flags & ~ACQUIRE_TERMINAL_PERMISSIVE) == ACQUIRE_TERMINAL_FORCE) < 0 ? -errno : 0;
438 /* Reset signal handler to old value */
439 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
441 /* Success? Exit the loop now! */
445 /* Any failure besides -EPERM? Fail, regardless of the mode. */
449 if (flags & ACQUIRE_TERMINAL_PERMISSIVE) /* If we are in permissive mode, then EPERM is fine, turn this
450 * into a success. Note that EPERM is also returned if we
451 * already are the owner of the TTY. */
454 if (flags != ACQUIRE_TERMINAL_WAIT) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
461 union inotify_event_buffer buffer;
462 struct inotify_event *e;
465 if (timeout != USEC_INFINITY) {
468 assert(ts != USEC_INFINITY);
470 n = now(CLOCK_MONOTONIC);
471 if (ts + timeout < n)
474 r = fd_wait_for_event(notify, POLLIN, ts + timeout - n);
481 l = read(notify, &buffer, sizeof(buffer));
483 if (IN_SET(errno, EINTR, EAGAIN))
489 FOREACH_INOTIFY_EVENT(e, buffer, l) {
490 if (e->mask & IN_Q_OVERFLOW) /* If we hit an inotify queue overflow, simply check if the terminal is up for grabs now. */
493 if (e->wd != wd || !(e->mask & IN_CLOSE)) /* Safety checks */
500 /* We close the tty fd here since if the old session ended our handle will be dead. It's important that
501 * we do this after sleeping, so that we don't enter an endless loop. */
512 #if 0 /// UNNEEDED by elogind
513 int release_terminal(void) {
514 static const struct sigaction sa_new = {
515 .sa_handler = SIG_IGN,
516 .sa_flags = SA_RESTART,
519 _cleanup_close_ int fd = -1;
520 struct sigaction sa_old;
523 fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
527 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
528 * by our own TIOCNOTTY */
529 assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
531 r = ioctl(fd, TIOCNOTTY) < 0 ? -errno : 0;
533 assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
538 int terminal_vhangup_fd(int fd) {
541 if (ioctl(fd, TIOCVHANGUP) < 0)
547 int terminal_vhangup(const char *name) {
548 _cleanup_close_ int fd;
550 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
554 return terminal_vhangup_fd(fd);
557 int vt_disallocate(const char *name) {
558 _cleanup_close_ int fd = -1;
563 /* Deallocate the VT if possible. If not possible
564 * (i.e. because it is the active one), at least clear it
565 * entirely (including the scrollback buffer) */
567 e = path_startswith(name, "/dev/");
571 if (!tty_is_vc(name)) {
572 /* So this is not a VT. I guess we cannot deallocate
573 * it then. But let's at least clear the screen */
575 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
580 "\033[r" /* clear scrolling region */
581 "\033[H" /* move home */
582 "\033[2J", /* clear screen */
587 n = startswith(e, "tty");
591 r = safe_atou(n, &u);
598 /* Try to deallocate */
599 fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
603 r = ioctl(fd, VT_DISALLOCATE, u);
612 /* Couldn't deallocate, so let's clear it fully with
614 fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
619 "\033[r" /* clear scrolling region */
620 "\033[H" /* move home */
621 "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
626 int make_console_stdio(void) {
629 /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
631 fd = acquire_terminal("/dev/console", ACQUIRE_TERMINAL_FORCE|ACQUIRE_TERMINAL_PERMISSIVE, USEC_INFINITY);
633 return log_error_errno(fd, "Failed to acquire terminal: %m");
635 r = reset_terminal_fd(fd, true);
637 log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
639 r = rearrange_stdio(fd, fd, fd); /* This invalidates 'fd' both on success and on failure. */
641 return log_error_errno(r, "Failed to make terminal stdin/stdout/stderr: %m");
643 reset_terminal_feature_caches();
649 bool tty_is_vc(const char *tty) {
652 return vtnr_from_tty(tty) >= 0;
655 bool tty_is_console(const char *tty) {
658 return streq(skip_dev_prefix(tty), "console");
661 int vtnr_from_tty(const char *tty) {
666 tty = skip_dev_prefix(tty);
668 if (!startswith(tty, "tty") )
671 if (tty[3] < '0' || tty[3] > '9')
674 r = safe_atoi(tty+3, &i);
684 #if 0 /// UNNEEDED by elogind
685 int resolve_dev_console(char **ret) {
686 _cleanup_free_ char *active = NULL;
692 /* Resolve where /dev/console is pointing to, if /sys is actually ours (i.e. not read-only-mounted which is a
693 * sign for container setups) */
695 if (path_is_read_only_fs("/sys") > 0)
698 r = read_one_line_file("/sys/class/tty/console/active", &active);
702 /* If multiple log outputs are configured the last one is what /dev/console points to */
703 tty = strrchr(active, ' ');
709 if (streq(tty, "tty0")) {
710 active = mfree(active);
712 /* Get the active VC (e.g. tty1) */
713 r = read_one_line_file("/sys/class/tty/tty0/active", &active);
736 int get_kernel_consoles(char ***ret) {
737 _cleanup_strv_free_ char **l = NULL;
738 _cleanup_free_ char *line = NULL;
744 /* If /sys is mounted read-only this means we are running in some kind of container environment. In that
745 * case /sys would reflect the host system, not us, hence ignore the data we can read from it. */
746 if (path_is_read_only_fs("/sys") > 0)
749 r = read_one_line_file("/sys/class/tty/console/active", &line);
755 _cleanup_free_ char *tty = NULL;
758 r = extract_first_word(&p, &tty, NULL, 0);
764 if (streq(tty, "tty0")) {
766 r = read_one_line_file("/sys/class/tty/tty0/active", &tty);
771 path = strappend("/dev/", tty);
775 if (access(path, F_OK) < 0) {
776 log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
781 r = strv_consume(&l, path);
786 if (strv_isempty(l)) {
787 log_debug("No devices found for system console");
797 r = strv_extend(&l, "/dev/console");
807 bool tty_is_vc_resolve(const char *tty) {
808 _cleanup_free_ char *resolved = NULL;
812 tty = skip_dev_prefix(tty);
814 if (streq(tty, "console")) {
815 if (resolve_dev_console(&resolved) < 0)
821 return tty_is_vc(tty);
824 const char *default_term_for_tty(const char *tty) {
825 return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220";
829 int fd_columns(int fd) {
830 struct winsize ws = {};
832 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
841 unsigned columns(void) {
845 if (cached_columns > 0)
846 return cached_columns;
849 e = getenv("COLUMNS");
851 (void) safe_atoi(e, &c);
854 c = fd_columns(STDOUT_FILENO);
860 return cached_columns;
863 int fd_lines(int fd) {
864 struct winsize ws = {};
866 if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
875 unsigned lines(void) {
879 if (cached_lines > 0)
885 (void) safe_atoi(e, &l);
888 l = fd_lines(STDOUT_FILENO);
897 /* intended to be used as a SIGWINCH sighandler */
898 #if 0 /// UNNEEDED by elogind
899 void columns_lines_cache_reset(int signum) {
905 void reset_terminal_feature_caches(void) {
909 cached_colors_enabled = -1;
910 cached_underline_enabled = -1;
915 if (cached_on_tty < 0)
916 cached_on_tty = isatty(STDOUT_FILENO) > 0;
918 return cached_on_tty;
921 int getttyname_malloc(int fd, char **ret) {
931 r = ttyname_r(fd, path, sizeof(path));
935 c = strdup(skip_dev_prefix(path));
952 int getttyname_harder(int fd, char **r) {
956 k = getttyname_malloc(fd, &s);
960 if (streq(s, "tty")) {
962 return get_ctty(0, NULL, r);
969 int get_ctty_devnr(pid_t pid, dev_t *d) {
971 _cleanup_free_ char *line = NULL;
977 p = procfs_file_alloca(pid, "stat");
978 r = read_one_line_file(p, &line);
982 p = strrchr(line, ')');
997 if (major(ttynr) == 0 && minor(ttynr) == 0)
1006 int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
1007 char fn[STRLEN("/dev/char/") + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL;
1008 _cleanup_free_ char *s = NULL;
1015 k = get_ctty_devnr(pid, &devnr);
1019 sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr));
1021 k = readlink_malloc(fn, &s);
1027 /* This is an ugly hack */
1028 if (major(devnr) == 136) {
1029 if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
1032 /* Probably something like the ptys which have no
1033 * symlink in /dev/char. Let's return something
1034 * vaguely useful. */
1041 if (startswith(s, "/dev/"))
1043 else if (startswith(s, "../"))
1060 #if 0 /// UNNEEDED by elogind
1061 int ptsname_malloc(int fd, char **ret) {
1074 if (ptsname_r(fd, c, l) == 0) {
1078 if (errno != ERANGE) {
1088 int ptsname_namespace(int pty, char **ret) {
1091 /* Like ptsname(), but doesn't assume that the path is
1092 * accessible in the local namespace. */
1094 r = ioctl(pty, TIOCGPTN, &no);
1101 if (asprintf(ret, "/dev/pts/%i", no) < 0)
1107 int openpt_in_namespace(pid_t pid, int flags) {
1108 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1109 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1115 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1119 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1122 r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
1128 pair[0] = safe_close(pair[0]);
1130 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1132 _exit(EXIT_FAILURE);
1134 master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
1136 _exit(EXIT_FAILURE);
1138 if (unlockpt(master) < 0)
1139 _exit(EXIT_FAILURE);
1141 if (send_one_fd(pair[1], master, 0) < 0)
1142 _exit(EXIT_FAILURE);
1144 _exit(EXIT_SUCCESS);
1147 pair[1] = safe_close(pair[1]);
1149 r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
1152 if (r != EXIT_SUCCESS)
1155 return receive_one_fd(pair[0], 0);
1158 int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
1159 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
1160 _cleanup_close_pair_ int pair[2] = { -1, -1 };
1164 r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
1168 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
1171 r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
1177 pair[0] = safe_close(pair[0]);
1179 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
1181 _exit(EXIT_FAILURE);
1183 master = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC);
1185 _exit(EXIT_FAILURE);
1187 if (send_one_fd(pair[1], master, 0) < 0)
1188 _exit(EXIT_FAILURE);
1190 _exit(EXIT_SUCCESS);
1193 pair[1] = safe_close(pair[1]);
1195 r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
1198 if (r != EXIT_SUCCESS)
1201 return receive_one_fd(pair[0], 0);
1205 static bool getenv_terminal_is_dumb(void) {
1212 return streq(e, "dumb");
1215 bool terminal_is_dumb(void) {
1219 return getenv_terminal_is_dumb();
1222 bool colors_enabled(void) {
1224 /* Returns true if colors are considered supported on our stdout. For that we check $SYSTEMD_COLORS first
1225 * (which is the explicit way to turn colors on/off). If that didn't work we turn colors off unless we are on a
1226 * 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
1227 * we are PID 1 then we do not check whether we are connected to a TTY, because we don't keep /dev/console open
1228 * continously due to fear of SAK, and hence things are a bit weird. */
1230 if (cached_colors_enabled < 0) {
1231 #if 0 /// elogind does not allow such forcing, and we are never init!
1234 val = getenv_bool("SYSTEMD_COLORS");
1236 cached_colors_enabled = val;
1237 else if (getpid_cached() == 1)
1238 /* PID1 outputs to the console without holding it open all the time */
1239 cached_colors_enabled = !getenv_terminal_is_dumb();
1242 cached_colors_enabled = !terminal_is_dumb();
1245 return cached_colors_enabled;
1248 bool dev_console_colors_enabled(void) {
1249 _cleanup_free_ char *s = NULL;
1252 /* Returns true if we assume that color is supported on /dev/console.
1254 * For that we first check if we explicitly got told to use colors or not, by checking $SYSTEMD_COLORS. If that
1255 * isn't set we check whether PID 1 has $TERM set, and if not, whether TERM is set on the kernel command
1256 * line. If we find $TERM set we assume color if it's not set to "dumb", similarly to how regular
1257 * colors_enabled() operates. */
1259 b = getenv_bool("SYSTEMD_COLORS");
1263 if (getenv_for_pid(1, "TERM", &s) <= 0)
1264 (void) proc_cmdline_get_key("TERM", 0, &s);
1266 return !streq_ptr(s, "dumb");
1269 bool underline_enabled(void) {
1271 if (cached_underline_enabled < 0) {
1273 /* The Linux console doesn't support underlining, turn it off, but only there. */
1275 if (colors_enabled())
1276 cached_underline_enabled = !streq_ptr(getenv("TERM"), "linux");
1278 cached_underline_enabled = false;
1281 return cached_underline_enabled;
1284 int vt_default_utf8(void) {
1285 _cleanup_free_ char *b = NULL;
1288 /* Read the default VT UTF8 setting from the kernel */
1290 r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b);
1294 return parse_boolean(b);
1297 int vt_reset_keyboard(int fd) {
1300 /* If we can't read the default, then default to unicode. It's 2017 after all. */
1301 kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
1303 if (ioctl(fd, KDSKBMODE, kb) < 0)