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"
59 #include "path-util.h"
61 static volatile unsigned cached_columns
= 0;
62 static volatile unsigned cached_lines
= 0;
64 static volatile int cached_on_tty
= -1;
65 static volatile int cached_colors_enabled
= -1;
66 static volatile int cached_underline_enabled
= -1;
69 _cleanup_close_
int fd
;
71 fd
= open_terminal("/dev/tty0", O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NONBLOCK
);
77 TIOCL_GETKMSGREDIRECT
,
81 if (ioctl(fd
, TIOCLINUX
, tiocl
) < 0)
84 vt
= tiocl
[0] <= 0 ? 1 : tiocl
[0];
87 if (ioctl(fd
, VT_ACTIVATE
, vt
) < 0)
93 int read_one_char(FILE *f
, char *ret
, usec_t t
, bool *need_nl
) {
94 struct termios old_termios
, new_termios
;
95 char c
, line
[LINE_MAX
];
100 if (tcgetattr(fileno(f
), &old_termios
) >= 0) {
101 new_termios
= old_termios
;
103 new_termios
.c_lflag
&= ~ICANON
;
104 new_termios
.c_cc
[VMIN
] = 1;
105 new_termios
.c_cc
[VTIME
] = 0;
107 if (tcsetattr(fileno(f
), TCSADRAIN
, &new_termios
) >= 0) {
110 if (t
!= USEC_INFINITY
) {
111 if (fd_wait_for_event(fileno(f
), POLLIN
, t
) <= 0) {
112 tcsetattr(fileno(f
), TCSADRAIN
, &old_termios
);
117 k
= fread(&c
, 1, 1, f
);
119 tcsetattr(fileno(f
), TCSADRAIN
, &old_termios
);
125 *need_nl
= c
!= '\n';
132 if (t
!= USEC_INFINITY
) {
133 if (fd_wait_for_event(fileno(f
), POLLIN
, t
) <= 0)
138 if (!fgets(line
, sizeof(line
), f
))
139 return errno
> 0 ? -errno
: -EIO
;
143 if (strlen(line
) != 1)
153 #define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC)
155 int ask_char(char *ret
, const char *replies
, const char *fmt
, ...) {
167 if (colors_enabled())
168 fputs(ANSI_HIGHLIGHT
, stdout
);
176 if (colors_enabled())
177 fputs(ANSI_NORMAL
, stdout
);
181 r
= read_one_char(stdin
, &c
, DEFAULT_ASK_REFRESH_USEC
, &need_nl
);
188 puts("Bad input, please try again.");
199 if (strchr(replies
, c
)) {
204 puts("Read unexpected character, please try again.");
208 int ask_string(char **ret
, const char *text
, ...) {
216 if (colors_enabled())
217 fputs(ANSI_HIGHLIGHT
, stdout
);
223 if (colors_enabled())
224 fputs(ANSI_NORMAL
, stdout
);
229 if (!fgets(line
, sizeof(line
), stdin
))
230 return errno
> 0 ? -errno
: -EIO
;
232 if (!endswith(line
, "\n"))
251 int reset_terminal_fd(int fd
, bool switch_to_text
) {
252 struct termios termios
;
255 /* Set terminal to some sane defaults */
259 /* We leave locked terminal attributes untouched, so that
260 * Plymouth may set whatever it wants to set, and we don't
261 * interfere with that. */
263 /* Disable exclusive mode, just in case */
264 (void) ioctl(fd
, TIOCNXCL
);
266 /* Switch to text mode */
268 (void) ioctl(fd
, KDSETMODE
, KD_TEXT
);
270 /* Set default keyboard mode */
271 (void) vt_reset_keyboard(fd
);
273 if (tcgetattr(fd
, &termios
) < 0) {
278 /* We only reset the stuff that matters to the software. How
279 * hardware is set up we don't touch assuming that somebody
280 * else will do that for us */
282 termios
.c_iflag
&= ~(IGNBRK
| BRKINT
| ISTRIP
| INLCR
| IGNCR
| IUCLC
);
283 termios
.c_iflag
|= ICRNL
| IMAXBEL
| IUTF8
;
284 termios
.c_oflag
|= ONLCR
;
285 termios
.c_cflag
|= CREAD
;
286 termios
.c_lflag
= ISIG
| ICANON
| IEXTEN
| ECHO
| ECHOE
| ECHOK
| ECHOCTL
| ECHOPRT
| ECHOKE
;
288 termios
.c_cc
[VINTR
] = 03; /* ^C */
289 termios
.c_cc
[VQUIT
] = 034; /* ^\ */
290 termios
.c_cc
[VERASE
] = 0177;
291 termios
.c_cc
[VKILL
] = 025; /* ^X */
292 termios
.c_cc
[VEOF
] = 04; /* ^D */
293 termios
.c_cc
[VSTART
] = 021; /* ^Q */
294 termios
.c_cc
[VSTOP
] = 023; /* ^S */
295 termios
.c_cc
[VSUSP
] = 032; /* ^Z */
296 termios
.c_cc
[VLNEXT
] = 026; /* ^V */
297 termios
.c_cc
[VWERASE
] = 027; /* ^W */
298 termios
.c_cc
[VREPRINT
] = 022; /* ^R */
299 termios
.c_cc
[VEOL
] = 0;
300 termios
.c_cc
[VEOL2
] = 0;
302 termios
.c_cc
[VTIME
] = 0;
303 termios
.c_cc
[VMIN
] = 1;
305 if (tcsetattr(fd
, TCSANOW
, &termios
) < 0)
309 /* Just in case, flush all crap out */
310 (void) tcflush(fd
, TCIOFLUSH
);
315 int reset_terminal(const char *name
) {
316 _cleanup_close_
int fd
= -1;
318 /* We open the terminal with O_NONBLOCK here, to ensure we
319 * don't block on carrier if this is a terminal with carrier
322 fd
= open_terminal(name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NONBLOCK
);
326 return reset_terminal_fd(fd
, true);
329 int open_terminal(const char *name
, int mode
) {
334 * If a TTY is in the process of being closed opening it might
335 * cause EIO. This is horribly awful, but unlikely to be
336 * changed in the kernel. Hence we work around this problem by
337 * retrying a couple of times.
339 * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
346 fd
= open(name
, mode
, 0);
353 /* Max 1s in total */
357 usleep(50 * USEC_PER_MSEC
);
361 if (isatty(fd
) <= 0) {
369 int acquire_terminal(
371 AcquireTerminalFlags flags
,
374 _cleanup_close_
int notify
= -1, fd
= -1;
375 usec_t ts
= USEC_INFINITY
;
379 assert(IN_SET(flags
& ~ACQUIRE_TERMINAL_PERMISSIVE
, ACQUIRE_TERMINAL_TRY
, ACQUIRE_TERMINAL_FORCE
, ACQUIRE_TERMINAL_WAIT
));
381 /* We use inotify to be notified when the tty is closed. We create the watch before checking if we can actually
382 * acquire it, so that we don't lose any event.
384 * Note: strictly speaking this actually watches for the device being closed, it does *not* really watch
385 * whether a tty loses its controlling process. However, unless some rogue process uses TIOCNOTTY on /dev/tty
386 * *after* closing its tty otherwise this will not become a problem. As long as the administrator makes sure
387 * not configure any service on the same tty as an untrusted user this should not be a problem. (Which he
388 * probably should not do anyway.) */
390 if ((flags
& ~ACQUIRE_TERMINAL_PERMISSIVE
) == ACQUIRE_TERMINAL_WAIT
) {
391 notify
= inotify_init1(IN_CLOEXEC
| (timeout
!= USEC_INFINITY
? IN_NONBLOCK
: 0));
395 wd
= inotify_add_watch(notify
, name
, IN_CLOSE
);
399 if (timeout
!= USEC_INFINITY
)
400 ts
= now(CLOCK_MONOTONIC
);
404 struct sigaction sa_old
, sa_new
= {
405 .sa_handler
= SIG_IGN
,
406 .sa_flags
= SA_RESTART
,
410 r
= flush_fd(notify
);
415 /* We pass here O_NOCTTY only so that we can check the return value TIOCSCTTY and have a reliable way
416 * to figure out if we successfully became the controlling process of the tty */
417 fd
= open_terminal(name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
421 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed if we already own the tty. */
422 assert_se(sigaction(SIGHUP
, &sa_new
, &sa_old
) == 0);
424 /* First, try to get the tty */
425 r
= ioctl(fd
, TIOCSCTTY
,
426 (flags
& ~ACQUIRE_TERMINAL_PERMISSIVE
) == ACQUIRE_TERMINAL_FORCE
) < 0 ? -errno
: 0;
428 /* Reset signal handler to old value */
429 assert_se(sigaction(SIGHUP
, &sa_old
, NULL
) == 0);
431 /* Success? Exit the loop now! */
435 /* Any failure besides -EPERM? Fail, regardless of the mode. */
439 if (flags
& ACQUIRE_TERMINAL_PERMISSIVE
) /* If we are in permissive mode, then EPERM is fine, turn this
440 * into a success. Note that EPERM is also returned if we
441 * already are the owner of the TTY. */
444 if (flags
!= ACQUIRE_TERMINAL_WAIT
) /* If we are in TRY or FORCE mode, then propagate EPERM as EPERM */
451 union inotify_event_buffer buffer
;
452 struct inotify_event
*e
;
455 if (timeout
!= USEC_INFINITY
) {
458 assert(ts
!= USEC_INFINITY
);
460 n
= now(CLOCK_MONOTONIC
);
461 if (ts
+ timeout
< n
)
464 r
= fd_wait_for_event(notify
, POLLIN
, ts
+ timeout
- n
);
471 l
= read(notify
, &buffer
, sizeof(buffer
));
473 if (IN_SET(errno
, EINTR
, EAGAIN
))
479 FOREACH_INOTIFY_EVENT(e
, buffer
, l
) {
480 if (e
->mask
& IN_Q_OVERFLOW
) /* If we hit an inotify queue overflow, simply check if the terminal is up for grabs now. */
483 if (e
->wd
!= wd
|| !(e
->mask
& IN_CLOSE
)) /* Safety checks */
490 /* We close the tty fd here since if the old session ended our handle will be dead. It's important that
491 * we do this after sleeping, so that we don't enter an endless loop. */
501 int release_terminal(void) {
502 static const struct sigaction sa_new
= {
503 .sa_handler
= SIG_IGN
,
504 .sa_flags
= SA_RESTART
,
507 _cleanup_close_
int fd
= -1;
508 struct sigaction sa_old
;
511 fd
= open("/dev/tty", O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NONBLOCK
);
515 /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
516 * by our own TIOCNOTTY */
517 assert_se(sigaction(SIGHUP
, &sa_new
, &sa_old
) == 0);
519 r
= ioctl(fd
, TIOCNOTTY
) < 0 ? -errno
: 0;
521 assert_se(sigaction(SIGHUP
, &sa_old
, NULL
) == 0);
526 int terminal_vhangup_fd(int fd
) {
529 if (ioctl(fd
, TIOCVHANGUP
) < 0)
535 int terminal_vhangup(const char *name
) {
536 _cleanup_close_
int fd
;
538 fd
= open_terminal(name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NONBLOCK
);
542 return terminal_vhangup_fd(fd
);
545 int vt_disallocate(const char *name
) {
546 _cleanup_close_
int fd
= -1;
551 /* Deallocate the VT if possible. If not possible
552 * (i.e. because it is the active one), at least clear it
553 * entirely (including the scrollback buffer) */
555 e
= path_startswith(name
, "/dev/");
559 if (!tty_is_vc(name
)) {
560 /* So this is not a VT. I guess we cannot deallocate
561 * it then. But let's at least clear the screen */
563 fd
= open_terminal(name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
568 "\033[r" /* clear scrolling region */
569 "\033[H" /* move home */
570 "\033[2J", /* clear screen */
575 n
= startswith(e
, "tty");
579 r
= safe_atou(n
, &u
);
586 /* Try to deallocate */
587 fd
= open_terminal("/dev/tty0", O_RDWR
|O_NOCTTY
|O_CLOEXEC
|O_NONBLOCK
);
591 r
= ioctl(fd
, VT_DISALLOCATE
, u
);
600 /* Couldn't deallocate, so let's clear it fully with
602 fd
= open_terminal(name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
607 "\033[r" /* clear scrolling region */
608 "\033[H" /* move home */
609 "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
614 int make_console_stdio(void) {
617 /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
619 fd
= acquire_terminal("/dev/console", ACQUIRE_TERMINAL_FORCE
|ACQUIRE_TERMINAL_PERMISSIVE
, USEC_INFINITY
);
621 return log_error_errno(fd
, "Failed to acquire terminal: %m");
623 r
= reset_terminal_fd(fd
, true);
625 log_warning_errno(r
, "Failed to reset terminal, ignoring: %m");
629 return log_error_errno(r
, "Failed to duplicate terminal fd: %m");
631 reset_terminal_feature_caches();
636 bool tty_is_vc(const char *tty
) {
639 return vtnr_from_tty(tty
) >= 0;
642 bool tty_is_console(const char *tty
) {
645 return streq(skip_dev_prefix(tty
), "console");
648 int vtnr_from_tty(const char *tty
) {
653 tty
= skip_dev_prefix(tty
);
655 if (!startswith(tty
, "tty") )
658 if (tty
[3] < '0' || tty
[3] > '9')
661 r
= safe_atoi(tty
+3, &i
);
671 char *resolve_dev_console(char **active
) {
674 /* Resolve where /dev/console is pointing to, if /sys is actually ours
675 * (i.e. not read-only-mounted which is a sign for container setups) */
677 if (path_is_read_only_fs("/sys") > 0)
680 if (read_one_line_file("/sys/class/tty/console/active", active
) < 0)
683 /* If multiple log outputs are configured the last one is what
684 * /dev/console points to */
685 tty
= strrchr(*active
, ' ');
691 if (streq(tty
, "tty0")) {
694 /* Get the active VC (e.g. tty1) */
695 if (read_one_line_file("/sys/class/tty/tty0/active", &tmp
) >= 0) {
704 int get_kernel_consoles(char ***consoles
) {
705 _cleanup_strv_free_
char **con
= NULL
;
706 _cleanup_free_
char *line
= NULL
;
712 r
= read_one_line_file("/sys/class/tty/console/active", &line
);
718 _cleanup_free_
char *tty
= NULL
;
721 r
= extract_first_word(&active
, &tty
, NULL
, 0);
727 if (streq(tty
, "tty0")) {
729 r
= read_one_line_file("/sys/class/tty/tty0/active", &tty
);
734 path
= strappend("/dev/", tty
);
738 if (access(path
, F_OK
) < 0) {
739 log_debug_errno(errno
, "Console device %s is not accessible, skipping: %m", path
);
744 r
= strv_consume(&con
, path
);
749 if (strv_isempty(con
)) {
750 log_debug("No devices found for system console");
752 r
= strv_extend(&con
, "/dev/console");
762 bool tty_is_vc_resolve(const char *tty
) {
763 _cleanup_free_
char *active
= NULL
;
767 tty
= skip_dev_prefix(tty
);
769 if (streq(tty
, "console")) {
770 tty
= resolve_dev_console(&active
);
775 return tty_is_vc(tty
);
778 const char *default_term_for_tty(const char *tty
) {
779 return tty
&& tty_is_vc_resolve(tty
) ? "linux" : "vt220";
782 int fd_columns(int fd
) {
783 struct winsize ws
= {};
785 if (ioctl(fd
, TIOCGWINSZ
, &ws
) < 0)
794 unsigned columns(void) {
798 if (cached_columns
> 0)
799 return cached_columns
;
802 e
= getenv("COLUMNS");
804 (void) safe_atoi(e
, &c
);
807 c
= fd_columns(STDOUT_FILENO
);
813 return cached_columns
;
816 int fd_lines(int fd
) {
817 struct winsize ws
= {};
819 if (ioctl(fd
, TIOCGWINSZ
, &ws
) < 0)
828 unsigned lines(void) {
832 if (cached_lines
> 0)
838 (void) safe_atoi(e
, &l
);
841 l
= fd_lines(STDOUT_FILENO
);
850 /* intended to be used as a SIGWINCH sighandler */
851 void columns_lines_cache_reset(int signum
) {
856 void reset_terminal_feature_caches(void) {
860 cached_colors_enabled
= -1;
861 cached_underline_enabled
= -1;
866 if (cached_on_tty
< 0)
867 cached_on_tty
= isatty(STDOUT_FILENO
) > 0;
869 return cached_on_tty
;
872 int make_stdio(int fd
) {
877 if (dup2(fd
, STDIN_FILENO
) < 0)
879 if (dup2(fd
, STDOUT_FILENO
) < 0 && r
>= 0)
881 if (dup2(fd
, STDERR_FILENO
) < 0 && r
>= 0)
887 /* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */
888 stdio_unset_cloexec();
893 int make_null_stdio(void) {
896 null_fd
= open("/dev/null", O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
900 r
= make_stdio(null_fd
);
902 reset_terminal_feature_caches();
907 int getttyname_malloc(int fd
, char **ret
) {
917 r
= ttyname_r(fd
, path
, sizeof(path
));
921 c
= strdup(skip_dev_prefix(path
));
938 int getttyname_harder(int fd
, char **r
) {
942 k
= getttyname_malloc(fd
, &s
);
946 if (streq(s
, "tty")) {
948 return get_ctty(0, NULL
, r
);
955 int get_ctty_devnr(pid_t pid
, dev_t
*d
) {
957 _cleanup_free_
char *line
= NULL
;
963 p
= procfs_file_alloca(pid
, "stat");
964 r
= read_one_line_file(p
, &line
);
968 p
= strrchr(line
, ')');
983 if (major(ttynr
) == 0 && minor(ttynr
) == 0)
992 int get_ctty(pid_t pid
, dev_t
*_devnr
, char **r
) {
993 char fn
[STRLEN("/dev/char/") + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b
= NULL
;
994 _cleanup_free_
char *s
= NULL
;
1001 k
= get_ctty_devnr(pid
, &devnr
);
1005 sprintf(fn
, "/dev/char/%u:%u", major(devnr
), minor(devnr
));
1007 k
= readlink_malloc(fn
, &s
);
1013 /* This is an ugly hack */
1014 if (major(devnr
) == 136) {
1015 if (asprintf(&b
, "pts/%u", minor(devnr
)) < 0)
1018 /* Probably something like the ptys which have no
1019 * symlink in /dev/char. Let's return something
1020 * vaguely useful. */
1027 if (startswith(s
, "/dev/"))
1029 else if (startswith(s
, "../"))
1046 int ptsname_malloc(int fd
, char **ret
) {
1059 if (ptsname_r(fd
, c
, l
) == 0) {
1063 if (errno
!= ERANGE
) {
1073 int ptsname_namespace(int pty
, char **ret
) {
1076 /* Like ptsname(), but doesn't assume that the path is
1077 * accessible in the local namespace. */
1079 r
= ioctl(pty
, TIOCGPTN
, &no
);
1086 if (asprintf(ret
, "/dev/pts/%i", no
) < 0)
1092 int openpt_in_namespace(pid_t pid
, int flags
) {
1093 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, usernsfd
= -1, rootfd
= -1;
1094 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1100 r
= namespace_open(pid
, &pidnsfd
, &mntnsfd
, NULL
, &usernsfd
, &rootfd
);
1104 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1107 r
= safe_fork("(sd-openpt)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &child
);
1113 pair
[0] = safe_close(pair
[0]);
1115 r
= namespace_enter(pidnsfd
, mntnsfd
, -1, usernsfd
, rootfd
);
1117 _exit(EXIT_FAILURE
);
1119 master
= posix_openpt(flags
|O_NOCTTY
|O_CLOEXEC
);
1121 _exit(EXIT_FAILURE
);
1123 if (unlockpt(master
) < 0)
1124 _exit(EXIT_FAILURE
);
1126 if (send_one_fd(pair
[1], master
, 0) < 0)
1127 _exit(EXIT_FAILURE
);
1129 _exit(EXIT_SUCCESS
);
1132 pair
[1] = safe_close(pair
[1]);
1134 r
= wait_for_terminate_and_check("(sd-openpt)", child
, 0);
1137 if (r
!= EXIT_SUCCESS
)
1140 return receive_one_fd(pair
[0], 0);
1143 int open_terminal_in_namespace(pid_t pid
, const char *name
, int mode
) {
1144 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, usernsfd
= -1, rootfd
= -1;
1145 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1149 r
= namespace_open(pid
, &pidnsfd
, &mntnsfd
, NULL
, &usernsfd
, &rootfd
);
1153 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1156 r
= safe_fork("(sd-terminal)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &child
);
1162 pair
[0] = safe_close(pair
[0]);
1164 r
= namespace_enter(pidnsfd
, mntnsfd
, -1, usernsfd
, rootfd
);
1166 _exit(EXIT_FAILURE
);
1168 master
= open_terminal(name
, mode
|O_NOCTTY
|O_CLOEXEC
);
1170 _exit(EXIT_FAILURE
);
1172 if (send_one_fd(pair
[1], master
, 0) < 0)
1173 _exit(EXIT_FAILURE
);
1175 _exit(EXIT_SUCCESS
);
1178 pair
[1] = safe_close(pair
[1]);
1180 r
= wait_for_terminate_and_check("(sd-terminal)", child
, 0);
1183 if (r
!= EXIT_SUCCESS
)
1186 return receive_one_fd(pair
[0], 0);
1189 static bool getenv_terminal_is_dumb(void) {
1196 return streq(e
, "dumb");
1199 bool terminal_is_dumb(void) {
1203 return getenv_terminal_is_dumb();
1206 bool colors_enabled(void) {
1208 if (cached_colors_enabled
< 0) {
1211 val
= getenv_bool("SYSTEMD_COLORS");
1213 cached_colors_enabled
= val
;
1214 else if (getpid_cached() == 1)
1215 /* PID1 outputs to the console without holding it open all the time */
1216 cached_colors_enabled
= !getenv_terminal_is_dumb();
1218 cached_colors_enabled
= !terminal_is_dumb();
1221 return cached_colors_enabled
;
1224 bool underline_enabled(void) {
1226 if (cached_underline_enabled
< 0) {
1228 /* The Linux console doesn't support underlining, turn it off, but only there. */
1230 if (colors_enabled())
1231 cached_underline_enabled
= !streq_ptr(getenv("TERM"), "linux");
1233 cached_underline_enabled
= false;
1236 return cached_underline_enabled
;
1239 int vt_default_utf8(void) {
1240 _cleanup_free_
char *b
= NULL
;
1243 /* Read the default VT UTF8 setting from the kernel */
1245 r
= read_one_line_file("/sys/module/vt/parameters/default_utf8", &b
);
1249 return parse_boolean(b
);
1252 int vt_reset_keyboard(int fd
) {
1255 /* If we can't read the default, then default to unicode. It's 2017 after all. */
1256 kb
= vt_default_utf8() != 0 ? K_UNICODE
: K_XLATE
;
1258 if (ioctl(fd
, KDSKBMODE
, kb
) < 0)