From 5321b957b4027be66873eac730d4ce141021e368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 16 May 2025 15:30:02 +0200 Subject: [PATCH 1/5] basic/terminal-util: query terminal name by DCS As requested in https://github.com/systemd/systemd/issues/36994, use DCS + q name ST. This works, but has limited terminal support: xterm, foot, kitty. --- src/basic/terminal-util.c | 128 +++++++++++++++++++++++++++++++++- src/basic/terminal-util.h | 18 +++-- src/test/test-terminal-util.c | 15 ++++ 3 files changed, 152 insertions(+), 9 deletions(-) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 51b3a83d29..4b1b0ecf6c 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -48,6 +48,9 @@ "\033?12l" /* reset cursor blinking */ \ "\033 1q" /* reset cursor style */ +/* How much to wait for a reply to a terminal sequence */ +#define CONSOLE_REPLY_WAIT_USEC (333 * USEC_PER_MSEC) + static volatile unsigned cached_columns = 0; static volatile unsigned cached_lines = 0; @@ -2128,7 +2131,7 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret if (r < 0) goto finish; - usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC); + usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC); char buf[STRLEN(ANSI_OSC "11;rgb:0/0/0" ANSI_ST)]; /* shortest possible reply */ size_t buf_full = 0; BackgroundColorContext context = {}; @@ -2331,7 +2334,7 @@ int terminal_get_size_by_dsr( if (r < 0) goto finish; - usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC); + usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC); char buf[STRLEN("\x1B[1;1R")]; /* The shortest valid reply possible */ size_t buf_full = 0; CursorPositionContext context = {}; @@ -2460,6 +2463,127 @@ int terminal_fix_size(int input_fd, int output_fd) { return 1; } +#define MAX_TERMINFO_LENGTH 64 +/* python -c 'print("".join(hex(ord(i))[2:] for i in "name").upper())' */ +#define DCS_TERMINFO_Q ANSI_DCS "+q" "6E616D65" ANSI_ST +/* The answer is either 0+r… (invalid) or 1+r… (OK). */ +#define DCS_TERMINFO_R0 ANSI_DCS "0+r" ANSI_ST +#define DCS_TERMINFO_R1 ANSI_DCS "1+r" "6E616D65" "=" /* This is followed by Pt ST. */ +assert_cc(STRLEN(DCS_TERMINFO_R0) <= STRLEN(DCS_TERMINFO_R1 ANSI_ST)); + +static int scan_terminfo_response( + const char *buf, + size_t size, + char **ret_name) { + int r; + + assert(buf); + assert(ret_name); + + /* Check if we have enough space for the shortest possible answer. */ + if (size < STRLEN(DCS_TERMINFO_R0)) + return -EAGAIN; + + /* Check if the terminating sequence is present */ + if (memcmp(buf + size - STRLEN(ANSI_ST), ANSI_ST, STRLEN(ANSI_ST)) != 0) + return -EAGAIN; + + if (size <= STRLEN(DCS_TERMINFO_R1 ANSI_ST)) + return -EINVAL; /* The answer is invalid or empty */ + + if (memcmp(buf, DCS_TERMINFO_R1, STRLEN(DCS_TERMINFO_R1)) != 0) + return -EINVAL; /* The answer is not valid */ + + _cleanup_free_ void *dec = NULL; + size_t dec_size; + r = unhexmem_full(buf + STRLEN(DCS_TERMINFO_R1), size - STRLEN(DCS_TERMINFO_R1 ANSI_ST), + /* secure= */ false, + &dec, &dec_size); + if (r < 0) + return r; + + assert(((const char *) dec)[dec_size] == '\0'); /* unhexmem appends NUL for our convenience */ + if (memchr(dec, '\0', dec_size) || string_has_cc(dec, NULL) || !filename_is_valid(dec)) + return -EUCLEAN; + + *ret_name = TAKE_PTR(dec); + return 0; +} + +int terminal_get_terminfo_by_dcs(int fd, char **ret_name) { + int r; + + assert(fd >= 0); + assert(ret_name); + + /* Note: fd must be in non-blocking read-write mode! */ + + struct termios old_termios; + if (tcgetattr(fd, &old_termios) < 0) + return -errno; + + struct termios new_termios = old_termios; + termios_disable_echo(&new_termios); + + if (tcsetattr(fd, TCSADRAIN, &new_termios) < 0) + return -errno; + + r = loop_write(fd, DCS_TERMINFO_Q, SIZE_MAX); + if (r < 0) + goto finish; + + usec_t end = usec_add(now(CLOCK_MONOTONIC), CONSOLE_REPLY_WAIT_USEC); + char buf[STRLEN(DCS_TERMINFO_R1) + MAX_TERMINFO_LENGTH + STRLEN(ANSI_ST)]; + size_t bytes = 0; + + for (;;) { + usec_t n = now(CLOCK_MONOTONIC); + if (n >= end) { + r = -EOPNOTSUPP; + break; + } + + r = fd_wait_for_event(fd, POLLIN, usec_sub_unsigned(end, n)); + if (r < 0) + break; + if (r == 0) { + r = -EOPNOTSUPP; + break; + } + + /* On the first read, read multiple characters, i.e. the shortest valid reply. Afterwards + * read byte by byte, since we don't want to read too much and drop characters from the input + * queue. */ + ssize_t l = read(fd, buf + bytes, bytes == 0 ? STRLEN(DCS_TERMINFO_R0) : 1); + if (l < 0) { + if (errno == EAGAIN) + continue; + r = -errno; + break; + } + + assert((size_t) l <= sizeof(buf) - bytes); + bytes += l; + + r = scan_terminfo_response(buf, bytes, ret_name); + if (r != -EAGAIN) + break; + + if (bytes == sizeof(buf)) { + r = -EOPNOTSUPP; /* The response has the right prefix, but we didn't find a valid + * answer with a terminator in the alloted space. Something is + * wrong, possibly some unrelated bytes got injected into the + * answer. */ + break; + } + } + +finish: + /* We ignore failure here. We already got a reply and if cleanup fails, we can't help that. */ + (void) tcsetattr(fd, TCSADRAIN, &old_termios); + return r; +} + int terminal_is_pty_fd(int fd) { int r; diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 17dbdfdfd5..79683f5a85 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -19,16 +19,19 @@ #define ANSI_WINDOW_TITLE_PUSH "\x1b[22;2t" #define ANSI_WINDOW_TITLE_POP "\x1b[23;2t" -/* ANSI "string terminator" character ("ST"). Terminal emulators typically allow three different ones: 0x07, - * 0x9c, and 0x1B 0x5C. We'll avoid 0x07 (BEL, aka ^G) since it might trigger unexpected TTY signal - * handling. And we'll avoid 0x9c since that's also valid regular codepoint in UTF-8 and elsewhere, and - * creates ambiguities. Because of that some terminal emulators explicitly choose not to support it. Hence we - * use 0x1B 0x5c */ -#define ANSI_ST "\e\\" +/* The "device control string" ("DCS") start sequence */ +#define ANSI_DCS "\eP" /* The "operating system command" ("OSC") start sequence */ #define ANSI_OSC "\e]" +/* ANSI "string terminator" character ("ST"). Terminal emulators typically allow three different ones: 0x07, + * 0x9c, and 0x1B 0x5C. We'll avoid 0x07 (BEL, aka ^G) since it might trigger unexpected TTY signal handling. + * And we'll avoid 0x9c since that's also valid regular codepoint in UTF-8 and elsewhere, and creates + * ambiguities. Because of that some terminal emulators explicitly choose not to support it. Hence we use + * 0x1B 0x5c. */ +#define ANSI_ST "\e\\" + bool isatty_safe(int fd); typedef enum TerminalResetFlags { @@ -142,9 +145,10 @@ void termios_disable_echo(struct termios *termios); int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue); int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, unsigned *ret_columns); - int terminal_fix_size(int input_fd, int output_fd); +int terminal_get_terminfo_by_dcs(int fd, char **ret_name); + int terminal_is_pty_fd(int fd); int pty_open_peer(int fd, int mode); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index f128ddca1f..41bd4fbcec 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -213,6 +213,21 @@ TEST(terminal_fix_size) { log_notice("Fixed terminal size."); } +TEST(terminal_get_terminfo_by_dcs) { + _cleanup_free_ char *name = NULL; + int r; + + /* We need a non-blocking read-write fd. */ + _cleanup_close_ int fd = fd_reopen(STDIN_FILENO, O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (fd < 0) + return (void) log_info_errno(fd, "Cannot reopen stdin in read-write mode: %m"); + + r = terminal_get_terminfo_by_dcs(fd, &name); + if (r < 0) + return (void) log_info_errno(r, "Can't get terminal terminfo via DCS: %m"); + log_info("terminal terminfo via DCS: %s, $TERM: %s", name, strnull(getenv("TERM"))); +} + TEST(terminal_is_pty_fd) { _cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF; int r; From 38b79aa0ea950654bbb5c398aa3bee6a90e7e86a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sat, 17 May 2025 16:07:50 +0200 Subject: [PATCH 2/5] test-terminal-util: print how long the asynchronous queries take --- src/test/test-terminal-util.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 41bd4fbcec..ba045bedce 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -16,6 +16,7 @@ #include "strv.h" #include "terminal-util.h" #include "tests.h" +#include "time-util.h" #include "tmpfile-util.h" #define LOREM_IPSUM "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor " \ @@ -175,7 +176,10 @@ TEST(get_default_background_color) { double red, green, blue; int r; + usec_t n = now(CLOCK_MONOTONIC); r = get_default_background_color(&red, &green, &blue); + log_info("%s took %s", __func__+5, + FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), n), USEC_PER_MSEC)); if (r < 0) log_notice_errno(r, "Can't get terminal default background color: %m"); else @@ -186,25 +190,31 @@ TEST(terminal_get_size_by_dsr) { unsigned rows, columns; int r; + usec_t n = now(CLOCK_MONOTONIC); r = terminal_get_size_by_dsr(STDIN_FILENO, STDOUT_FILENO, &rows, &columns); + log_info("%s took %s", __func__+5, + FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), n), USEC_PER_MSEC)); if (r < 0) - log_notice_errno(r, "Can't get screen dimensions via DSR: %m"); - else { - log_notice("terminal size via DSR: rows=%u columns=%u", rows, columns); + return (void) log_notice_errno(r, "Can't get screen dimensions via DSR: %m"); - struct winsize ws = {}; + log_notice("terminal size via DSR: rows=%u columns=%u", rows, columns); - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) - log_warning_errno(errno, "Can't get terminal size via ioctl, ignoring: %m"); - else - log_notice("terminal size via ioctl: rows=%u columns=%u", ws.ws_row, ws.ws_col); - } + struct winsize ws = {}; + + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) + log_warning_errno(errno, "Can't get terminal size via ioctl, ignoring: %m"); + else + log_notice("terminal size via ioctl: rows=%u columns=%u", ws.ws_row, ws.ws_col); } TEST(terminal_fix_size) { int r; + usec_t n = now(CLOCK_MONOTONIC); + r = terminal_fix_size(STDIN_FILENO, STDOUT_FILENO); + log_info("%s took %s", __func__+5, + FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), n), USEC_PER_MSEC)); if (r < 0) log_warning_errno(r, "Failed to fix terminal size: %m"); else if (r == 0) @@ -222,7 +232,11 @@ TEST(terminal_get_terminfo_by_dcs) { if (fd < 0) return (void) log_info_errno(fd, "Cannot reopen stdin in read-write mode: %m"); + usec_t n = now(CLOCK_MONOTONIC); + r = terminal_get_terminfo_by_dcs(fd, &name); + log_info("%s took %s", __func__+5, + FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), n), USEC_PER_MSEC)); if (r < 0) return (void) log_info_errno(r, "Can't get terminal terminfo via DCS: %m"); log_info("terminal terminfo via DCS: %s, $TERM: %s", name, strnull(getenv("TERM"))); From e3b050a5c2ed2cdb5b0ea85f964c17e6ad29cd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 19 May 2025 15:50:42 +0200 Subject: [PATCH 3/5] basic/terminal-util: add a heuristic check whether terminfo file exists --- src/basic/terminal-util.c | 51 +++++++++++++++++++++++++++++++++++ src/basic/terminal-util.h | 2 ++ src/test/test-terminal-util.c | 29 ++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 4b1b0ecf6c..9f34148d2a 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -2584,6 +2584,57 @@ finish: return r; } +int have_terminfo_file(const char *name) { + /* This is a heuristic check if we have the file, using the directory layout used on + * current Linux systems. Checks for other layouts can be added later if appropriate. */ + int r; + + assert(filename_is_valid(name)); + + _cleanup_free_ char *p = path_join("/usr/share/terminfo", CHAR_TO_STR(name[0]), name); + if (!p) + return log_oom_debug(); + + r = RET_NERRNO(access(p, F_OK)); + if (r == -ENOENT) + return false; + if (r < 0) + return r; + return true; +} + +int query_term_for_tty(const char *tty, char **ret_term) { + _cleanup_free_ char *dcs_term = NULL; + int r; + + assert(tty); + assert(ret_term); + + if (tty_is_vc_resolve(tty)) + return strdup_to(ret_term, "linux"); + + /* Try to query the terminal implementation that we're on. This will not work in all + * cases, which is fine, since this is intended to be used as a fallback. */ + + _cleanup_close_ int tty_fd = open_terminal(tty, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (tty_fd < 0) + return log_debug_errno(tty_fd, "Failed to open %s to query terminfo: %m", tty); + + r = terminal_get_terminfo_by_dcs(tty_fd, &dcs_term); + if (r < 0) + return log_debug_errno(r, "Failed to query %s for terminfo: %m", tty); + + r = have_terminfo_file(dcs_term); + if (r < 0) + return log_debug_errno(r, "Failed to look for terminfo %s: %m", dcs_term); + if (r == 0) + return log_info_errno(SYNTHETIC_ERRNO(ENODATA), + "Terminfo %s not found for %s.", dcs_term, tty); + + *ret_term = TAKE_PTR(dcs_term); + return 0; +} + int terminal_is_pty_fd(int fd) { int r; diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 79683f5a85..688d6808a6 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -148,6 +148,8 @@ int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, un int terminal_fix_size(int input_fd, int output_fd); int terminal_get_terminfo_by_dcs(int fd, char **ret_name); +int have_terminfo_file(const char *name); +int query_term_for_tty(const char *tty, char **ret_term); int terminal_is_pty_fd(int fd); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index ba045bedce..6ab3c7fa16 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -242,6 +242,35 @@ TEST(terminal_get_terminfo_by_dcs) { log_info("terminal terminfo via DCS: %s, $TERM: %s", name, strnull(getenv("TERM"))); } +TEST(have_terminfo_file) { + int r; + + FOREACH_STRING(s, + "linux", + "xterm", + "vt220", + "xterm-256color", + "nosuchfile") { + r = have_terminfo_file(s); + log_info("%s: %s → %s", __func__+5, s, r >= 0 ? yes_no(r) : STRERROR(r)); + ASSERT_OK(r); + } +} + +TEST(query_term_for_tty) { + int r; + + FOREACH_STRING(s, + "/dev/console", + "/dev/stdin", + "/dev/stdout") { + _cleanup_free_ char *term = NULL; + + r = query_term_for_tty(s, &term); + log_info("%s: %s → %s/%s", __func__+5, s, STRERROR(r), strnull(term)); + } +} + TEST(terminal_is_pty_fd) { _cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF; int r; From ad6ca4a6129fa0fb8e8c800d05cf2c7ed5d0bcbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 20 May 2025 19:02:31 +0200 Subject: [PATCH 4/5] core: use terminal DCS sequence to set $TERM query_term_for_tty() is used in two places: in fixup_environment(), which affects PID1 itself, and in build_environment(), which affects spawned services. There is obviously some cost to the extra call, but I think it's worthwhile to do it. When $TERM is set incorrectly, basic output works OK, but then there are various annoying corner cases. In particular, we get the support for color (or lack of it) wrong, and when output is garbled, users are annoyed. Things like text editors are almost certain to behave incorrectly. Testing in test-terminal-util indicates that the time required to make a successful query is on the order of a dozen microseconds, and an unsuccessful query costs as much as our timeout, i.e. currently 1/3 ms. I think this is an acceptable tradeoff. No caching is used, because fixup_environment() is only called once, and the other place in build_environment(), only affects services which are connected to a tty, which is only a handful of services, and often only started in special circumstances. Fixes https://github.com/systemd/systemd/issues/36994. --- src/basic/terminal-util.h | 3 +++ src/core/exec-invoke.c | 14 +++++++++++--- src/core/main.c | 6 ++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 688d6808a6..8438a3fb21 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -143,6 +143,9 @@ assert_cc((TTY_MODE & 0711) == 0600); void termios_disable_echo(struct termios *termios); +/* The $TERM value we use for terminals other than the Linux console */ +#define FALLBACK_TERM "vt220" + int get_default_background_color(double *ret_red, double *ret_green, double *ret_blue); int terminal_get_size_by_dsr(int input_fd, int output_fd, unsigned *ret_rows, unsigned *ret_columns); int terminal_fix_size(int input_fd, int output_fd); diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index b27358d8dc..f4e090297c 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -2038,7 +2038,7 @@ static int build_environment( } if (exec_context_needs_term(c)) { - _cleanup_free_ char *cmdline = NULL; + _cleanup_free_ char *cmdline = NULL, *dcs_term = NULL; const char *tty_path, *term = NULL; tty_path = exec_context_tty_path(c); @@ -2063,8 +2063,16 @@ static int build_environment( term = cmdline; } + if (!term && tty_path) { + /* This handles real virtual terminals (returning "linux") and + * any terminals which support the DCS +q query sequence. */ + r = query_term_for_tty(tty_path, &dcs_term); + if (r >= 0) + term = dcs_term; + } + if (!term) { - /* If no precise $TERM is known and we pick a fallback default, then let's also set + /* If $TERM is not known and we pick a fallback default, then let's also set * $COLORTERM=truecolor. That's because our fallback default is vt220, which is * generally a safe bet (as it supports PageUp/PageDown unlike vt100, and is quite * universally available in terminfo/termcap), except for the fact that real DEC @@ -2083,7 +2091,7 @@ static int build_environment( our_env[n_env++] = x; - term = default_term_for_tty(tty_path); + term = FALLBACK_TERM; } x = strjoin("TERM=", term); diff --git a/src/core/main.c b/src/core/main.c index 2005d733c6..b1f7cc941b 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1594,8 +1594,10 @@ static int fixup_environment(void) { return r; } - const char *t = term ?: default_term_for_tty("/dev/console"); - if (setenv("TERM", t, /* overwrite= */ true) < 0) + if (!term) + (void) query_term_for_tty("/dev/console", &term); + + if (setenv("TERM", term ?: FALLBACK_TERM, /* overwrite= */ true) < 0) return -errno; /* The kernels sets HOME=/ for init. Let's undo this. */ From f256e48d4ec9515d2bfc0d923d9a0b58d7c4abf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 27 May 2025 18:12:00 +0200 Subject: [PATCH 5/5] basic/terminal-util: drop now-unused default_term_for_tty --- src/basic/terminal-util.c | 4 ---- src/basic/terminal-util.h | 1 - src/test/test-terminal-util.c | 15 --------------- 3 files changed, 20 deletions(-) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 9f34148d2a..c1989cf0b0 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1246,10 +1246,6 @@ bool tty_is_vc_resolve(const char *tty) { return tty_is_vc(tty); } -const char* default_term_for_tty(const char *tty) { - return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220"; -} - int fd_columns(int fd) { struct winsize ws = {}; diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 8438a3fb21..2869469353 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -100,7 +100,6 @@ bool tty_is_vc(const char *tty); bool tty_is_vc_resolve(const char *tty); bool tty_is_console(const char *tty) _pure_; int vtnr_from_tty(const char *tty); -const char* default_term_for_tty(const char *tty); void reset_dev_console_fd(int fd, bool switch_to_text); int lock_dev_console(void); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 6ab3c7fa16..358d578ccb 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -29,21 +29,6 @@ TEST(colors_enabled) { log_info("colors_enabled: %s", yes_no(colors_enabled())); } -TEST(default_term_for_tty) { - puts(default_term_for_tty("/dev/tty23")); - puts(default_term_for_tty("/dev/ttyS23")); - puts(default_term_for_tty("/dev/tty0")); - puts(default_term_for_tty("/dev/pty0")); - puts(default_term_for_tty("/dev/pts/0")); - puts(default_term_for_tty("/dev/console")); - puts(default_term_for_tty("tty23")); - puts(default_term_for_tty("ttyS23")); - puts(default_term_for_tty("tty0")); - puts(default_term_for_tty("pty0")); - puts(default_term_for_tty("pts/0")); - puts(default_term_for_tty("console")); -} - TEST(read_one_char) { _cleanup_fclose_ FILE *file = NULL; char r;