diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 7a2fc76b65..c79cf67445 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -3322,7 +3322,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
TTYReset=
Reset the terminal device specified with TTYPath= before and after
- execution. Defaults to no.
+ execution. This does not erase the screen (see TTYVTDisallocate= below for
+ that). Defaults to no.
@@ -3333,11 +3334,12 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
- TTYRows=
TTYColumns=
+ TTYRows=
Configure the size of the TTY specified with TTYPath=. If unset or
- set to the empty string, the kernel default is used.
+ set to the empty string, it is attempted to retrieve the dimensions of the terminal screen via ANSI
+ sequences, and if that fails the kernel defaults (typically 80x24) are used.
@@ -3345,9 +3347,10 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
TTYVTDisallocate=
- If the terminal device specified with TTYPath= is a virtual console
- terminal, try to deallocate the TTY before and after execution. This ensures that the screen and scrollback
- buffer is cleared. Defaults to no.
+ If the terminal device specified with TTYPath= is a virtual
+ console terminal, try to deallocate the TTY before and after execution. This ensures that the screen
+ and scrollback buffer is cleared. If the terminal device is of any other type of TTY an attempt is
+ made to clear the screen via ANSI sequences. Defaults to no.
diff --git a/src/basic/ansi-color.h b/src/basic/ansi-color.h
new file mode 100644
index 0000000000..efc1dea131
--- /dev/null
+++ b/src/basic/ansi-color.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "terminal-util.h"
+
+/* Regular colors */
+#define ANSI_BLACK "\x1B[0;30m" /* Some type of grey usually. */
+#define ANSI_RED "\x1B[0;31m"
+#define ANSI_GREEN "\x1B[0;32m"
+#define ANSI_YELLOW "\x1B[0;33m"
+#define ANSI_BLUE "\x1B[0;34m"
+#define ANSI_MAGENTA "\x1B[0;35m"
+#define ANSI_CYAN "\x1B[0;36m"
+#define ANSI_WHITE "\x1B[0;37m" /* This is actually rendered as light grey, legible even on a white
+ * background. See ANSI_HIGHLIGHT_WHITE for real white. */
+
+#define ANSI_BRIGHT_BLACK "\x1B[0;90m"
+#define ANSI_BRIGHT_RED "\x1B[0;91m"
+#define ANSI_BRIGHT_GREEN "\x1B[0;92m"
+#define ANSI_BRIGHT_YELLOW "\x1B[0;93m"
+#define ANSI_BRIGHT_BLUE "\x1B[0;94m"
+#define ANSI_BRIGHT_MAGENTA "\x1B[0;95m"
+#define ANSI_BRIGHT_CYAN "\x1B[0;96m"
+#define ANSI_BRIGHT_WHITE "\x1B[0;97m"
+
+#define ANSI_GREY "\x1B[0;38;5;245m"
+
+/* Bold/highlighted */
+#define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m"
+#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m"
+#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m"
+#define _ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m" /* This yellow is currently not displayed well by some terminals */
+#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
+#define ANSI_HIGHLIGHT_MAGENTA "\x1B[0;1;35m"
+#define ANSI_HIGHLIGHT_CYAN "\x1B[0;1;36m"
+#define ANSI_HIGHLIGHT_WHITE "\x1B[0;1;37m"
+#define ANSI_HIGHLIGHT_YELLOW4 "\x1B[0;1;38:5:100m"
+#define ANSI_HIGHLIGHT_KHAKI3 "\x1B[0;1;38:5:185m"
+#define ANSI_HIGHLIGHT_GREY "\x1B[0;1;38:5:245m"
+
+#define ANSI_HIGHLIGHT_YELLOW ANSI_HIGHLIGHT_KHAKI3 /* Replacement yellow that is more legible */
+
+/* Underlined */
+#define ANSI_GREY_UNDERLINE "\x1B[0;4;38:5:245m"
+#define ANSI_BRIGHT_BLACK_UNDERLINE "\x1B[0;4;90m"
+#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
+#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m"
+#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;38:5:185m"
+#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m"
+#define ANSI_HIGHLIGHT_MAGENTA_UNDERLINE "\x1B[0;1;4;35m"
+#define ANSI_HIGHLIGHT_GREY_UNDERLINE "\x1B[0;1;4;38:5:245m"
+
+/* Other ANSI codes */
+#define ANSI_UNDERLINE "\x1B[0;4m"
+#define ANSI_ADD_UNDERLINE "\x1B[4m"
+#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58:5:245m"
+#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
+#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
+
+/* Fallback colors: 256 → 16 */
+#define ANSI_HIGHLIGHT_GREY_FALLBACK "\x1B[0;1;90m"
+#define ANSI_HIGHLIGHT_GREY_FALLBACK_UNDERLINE "\x1B[0;1;4;90m"
+#define ANSI_HIGHLIGHT_YELLOW_FALLBACK "\x1B[0;1;33m"
+#define ANSI_HIGHLIGHT_YELLOW_FALLBACK_UNDERLINE "\x1B[0;1;4;33m"
+
+/* Background colors */
+#define ANSI_BACKGROUND_BLUE "\x1B[44m"
+
+/* Reset/clear ANSI styles */
+#define ANSI_NORMAL "\x1B[0m"
+
+#define DEFINE_ANSI_FUNC(name, NAME) \
+ static inline const char* ansi_##name(void) { \
+ return colors_enabled() ? ANSI_##NAME : ""; \
+ }
+
+#define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
+ static inline const char* ansi_##name(void) { \
+ switch (get_color_mode()) { \
+ case COLOR_OFF: return ""; \
+ case COLOR_16: return ANSI_##FALLBACK; \
+ default : return ANSI_##NAME; \
+ } \
+ }
+
+static inline const char* ansi_underline(void) {
+ return underline_enabled() ? ANSI_UNDERLINE : "";
+}
+
+static inline const char* ansi_add_underline(void) {
+ return underline_enabled() ? ANSI_ADD_UNDERLINE : "";
+}
+
+static inline const char* ansi_add_underline_grey(void) {
+ return underline_enabled() ?
+ (colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : "";
+}
+
+#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
+ static inline const char* ansi_##name(void) { \
+ return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \
+ colors_enabled() ? ANSI_##NAME : ""; \
+ }
+
+
+#define DEFINE_ANSI_FUNC_UNDERLINE_256(name, NAME, FALLBACK) \
+ static inline const char* ansi_##name(void) { \
+ switch (get_color_mode()) { \
+ case COLOR_OFF: return ""; \
+ case COLOR_16: return underline_enabled() ? ANSI_##FALLBACK##_UNDERLINE : ANSI_##FALLBACK; \
+ default : return underline_enabled() ? ANSI_##NAME##_UNDERLINE: ANSI_##NAME; \
+ } \
+ }
+
+DEFINE_ANSI_FUNC(normal, NORMAL);
+DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
+DEFINE_ANSI_FUNC(black, BLACK);
+DEFINE_ANSI_FUNC(red, RED);
+DEFINE_ANSI_FUNC(green, GREEN);
+DEFINE_ANSI_FUNC(yellow, YELLOW);
+DEFINE_ANSI_FUNC(blue, BLUE);
+DEFINE_ANSI_FUNC(magenta, MAGENTA);
+DEFINE_ANSI_FUNC(cyan, CYAN);
+DEFINE_ANSI_FUNC(white, WHITE);
+DEFINE_ANSI_FUNC_256(grey, GREY, BRIGHT_BLACK);
+
+DEFINE_ANSI_FUNC(bright_black, BRIGHT_BLACK);
+DEFINE_ANSI_FUNC(bright_red, BRIGHT_RED);
+DEFINE_ANSI_FUNC(bright_green, BRIGHT_GREEN);
+DEFINE_ANSI_FUNC(bright_yellow, BRIGHT_YELLOW);
+DEFINE_ANSI_FUNC(bright_blue, BRIGHT_BLUE);
+DEFINE_ANSI_FUNC(bright_magenta, BRIGHT_MAGENTA);
+DEFINE_ANSI_FUNC(bright_cyan, BRIGHT_CYAN);
+DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE);
+
+DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
+DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
+DEFINE_ANSI_FUNC_256(highlight_yellow4, HIGHLIGHT_YELLOW4, HIGHLIGHT_YELLOW_FALLBACK);
+DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
+DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
+DEFINE_ANSI_FUNC_256(highlight_grey, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
+DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
+
+static inline const char* _ansi_highlight_yellow(void) {
+ return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
+}
+
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT);
+DEFINE_ANSI_FUNC_UNDERLINE_256(grey_underline, GREY, BRIGHT_BLACK);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow_underline, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA);
+DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_grey_underline, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
+
+static inline const char* ansi_highlight_green_red(bool b) {
+ return b ? ansi_highlight_green() : ansi_highlight_red();
+}
diff --git a/src/basic/build.c b/src/basic/build.c
index 488ed20713..50e975e80d 100644
--- a/src/basic/build.c
+++ b/src/basic/build.c
@@ -3,6 +3,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "build.h"
#include "extract-word.h"
#include "macro.h"
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 3e385e5ee4..cf2c47d214 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -1146,5 +1146,10 @@ int setenvf(const char *name, bool overwrite, const char *valuef, ...) {
if (r < 0)
return -ENOMEM;
+ /* Try to suppress writes if the value is already set correctly (simply because memory management of
+ * environment variables sucks a bit. */
+ if (streq_ptr(getenv(name), value))
+ return 0;
+
return RET_NERRNO(setenv(name, value, overwrite));
}
diff --git a/src/basic/log.c b/src/basic/log.c
index 50b405b843..a8056d2635 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -16,6 +16,7 @@
#include "sd-messages.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "argv-util.h"
#include "env-util.h"
#include "errno-util.h"
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index e50b54006a..c2ff3ca85a 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -21,6 +21,8 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
+#include "chase.h"
#include "constants.h"
#include "devnum-util.h"
#include "env-util.h"
@@ -32,6 +34,7 @@
#include "io-util.h"
#include "log.h"
#include "macro.h"
+#include "missing_magic.h"
#include "namespace-util.h"
#include "parse-util.h"
#include "path-util.h"
@@ -76,7 +79,7 @@ int chvt(int vt) {
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
- return -errno;
+ return fd;
if (vt <= 0) {
int tiocl[2] = {
@@ -246,92 +249,6 @@ int ask_string(char **ret, const char *text, ...) {
return 0;
}
-int reset_terminal_fd(int fd, bool switch_to_text) {
- struct termios termios;
- int r;
-
- /* Set terminal to some sane defaults */
-
- assert(fd >= 0);
-
- if (!isatty_safe(fd))
- return log_debug_errno(errno, "Asked to reset a terminal that actually isn't a terminal: %m");
-
- /* We leave locked terminal attributes untouched, so that Plymouth may set whatever it wants to set,
- * and we don't interfere with that. */
-
- /* Disable exclusive mode, just in case */
- if (ioctl(fd, TIOCNXCL) < 0)
- log_debug_errno(errno, "TIOCNXCL ioctl failed on TTY, ignoring: %m");
-
- /* Switch to text mode */
- if (switch_to_text)
- if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
- log_debug_errno(errno, "KDSETMODE ioctl for switching to text mode failed on TTY, ignoring: %m");
-
-
- /* Set default keyboard mode */
- r = vt_reset_keyboard(fd);
- if (r < 0)
- log_debug_errno(r, "Failed to reset VT keyboard, ignoring: %m");
-
- if (tcgetattr(fd, &termios) < 0) {
- r = log_debug_errno(errno, "Failed to get terminal parameters: %m");
- goto finish;
- }
-
- /* We only reset the stuff that matters to the software. How
- * hardware is set up we don't touch assuming that somebody
- * else will do that for us */
-
- termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
- termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
- termios.c_oflag |= ONLCR | OPOST;
- termios.c_cflag |= CREAD;
- termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE;
-
- termios.c_cc[VINTR] = 03; /* ^C */
- termios.c_cc[VQUIT] = 034; /* ^\ */
- termios.c_cc[VERASE] = 0177;
- termios.c_cc[VKILL] = 025; /* ^X */
- termios.c_cc[VEOF] = 04; /* ^D */
- termios.c_cc[VSTART] = 021; /* ^Q */
- termios.c_cc[VSTOP] = 023; /* ^S */
- termios.c_cc[VSUSP] = 032; /* ^Z */
- termios.c_cc[VLNEXT] = 026; /* ^V */
- termios.c_cc[VWERASE] = 027; /* ^W */
- termios.c_cc[VREPRINT] = 022; /* ^R */
- termios.c_cc[VEOL] = 0;
- termios.c_cc[VEOL2] = 0;
-
- termios.c_cc[VTIME] = 0;
- termios.c_cc[VMIN] = 1;
-
- r = RET_NERRNO(tcsetattr(fd, TCSANOW, &termios));
- if (r < 0)
- log_debug_errno(r, "Failed to set terminal parameters: %m");
-
-finish:
- /* Just in case, flush all crap out */
- (void) tcflush(fd, TCIOFLUSH);
-
- return r;
-}
-
-int reset_terminal(const char *name) {
- _cleanup_close_ int fd = -EBADF;
-
- /* We open the terminal with O_NONBLOCK here, to ensure we
- * don't block on carrier if this is a terminal with carrier
- * configured. */
-
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
- if (fd < 0)
- return fd;
-
- return reset_terminal_fd(fd, true);
-}
-
int open_terminal(const char *name, int mode) {
_cleanup_close_ int fd = -EBADF;
unsigned c = 0;
@@ -344,7 +261,7 @@ int open_terminal(const char *name, int mode) {
* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
*/
- if (mode & O_CREAT)
+ if ((mode & (O_CREAT|O_PATH|O_DIRECTORY|O_TMPFILE)) != 0)
return -EINVAL;
for (;;) {
@@ -517,69 +434,210 @@ int terminal_vhangup_fd(int fd) {
return RET_NERRNO(ioctl(fd, TIOCVHANGUP));
}
-int terminal_vhangup(const char *name) {
- _cleanup_close_ int fd = -EBADF;
+int vt_disallocate(const char *tty_path) {
+ assert(tty_path);
- fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
- if (fd < 0)
- return fd;
+ /* Deallocate the VT if possible. If not possible (i.e. because it is the active one), at least clear
+ * it entirely (including the scrollback buffer). */
- return terminal_vhangup_fd(fd);
-}
-
-int vt_disallocate(const char *name) {
- const char *e;
- int r;
-
- /* Deallocate the VT if possible. If not possible
- * (i.e. because it is the active one), at least clear it
- * entirely (including the scrollback buffer). */
-
- e = path_startswith(name, "/dev/");
- if (!e)
- return -EINVAL;
-
- if (tty_is_vc(name)) {
- _cleanup_close_ int fd = -EBADF;
- unsigned u;
- const char *n;
-
- n = startswith(e, "tty");
- if (!n)
- return -EINVAL;
-
- r = safe_atou(n, &u);
- if (r < 0)
- return r;
-
- if (u <= 0)
- return -EINVAL;
-
- /* Try to deallocate */
- fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
+ int ttynr = vtnr_from_tty(tty_path);
+ if (ttynr > 0) {
+ _cleanup_close_ int fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd < 0)
return fd;
- r = ioctl(fd, VT_DISALLOCATE, u);
- if (r >= 0)
+ /* Try to deallocate */
+ if (ioctl(fd, VT_DISALLOCATE, ttynr) >= 0)
return 0;
if (errno != EBUSY)
return -errno;
}
- /* So this is not a VT (in which case we cannot deallocate it),
- * or we failed to deallocate. Let's at least clear the screen. */
+ /* So this is not a VT (in which case we cannot deallocate it), or we failed to deallocate. Let's at
+ * least clear the screen. */
- _cleanup_close_ int fd2 = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ _cleanup_close_ int fd2 = open_terminal(tty_path, O_WRONLY|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
if (fd2 < 0)
return fd2;
- (void) loop_write(fd2,
- "\033[r" /* clear scrolling region */
- "\033[H" /* move home */
- "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
- 10);
- return 0;
+ return loop_write_full(fd2,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J" /* clear screen including scrollback, requires Linux 2.6.40 */
+ "\033c", /* reset to initial state */
+ SIZE_MAX,
+ 100 * USEC_PER_MSEC);
+}
+
+static int vt_default_utf8(void) {
+ _cleanup_free_ char *b = NULL;
+ int r;
+
+ /* Read the default VT UTF8 setting from the kernel */
+
+ r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b);
+ if (r < 0)
+ return r;
+
+ return parse_boolean(b);
+}
+
+static int vt_reset_keyboard(int fd) {
+ int r, kb;
+
+ assert(fd >= 0);
+
+ /* If we can't read the default, then default to Unicode. It's 2024 after all. */
+ r = vt_default_utf8();
+ if (r < 0)
+ log_debug_errno(r, "Failed to determine kernel VT UTF-8 mode, assuming enabled: %m");
+
+ kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
+ return RET_NERRNO(ioctl(fd, KDSKBMODE, kb));
+}
+
+static int terminal_reset_ioctl(int fd, bool switch_to_text) {
+ struct termios termios;
+ int r;
+
+ /* Set terminal to some sane defaults */
+
+ assert(fd >= 0);
+
+ /* We leave locked terminal attributes untouched, so that Plymouth may set whatever it wants to set,
+ * and we don't interfere with that. */
+
+ /* Disable exclusive mode, just in case */
+ if (ioctl(fd, TIOCNXCL) < 0)
+ log_debug_errno(errno, "TIOCNXCL ioctl failed on TTY, ignoring: %m");
+
+ /* Switch to text mode */
+ if (switch_to_text)
+ if (ioctl(fd, KDSETMODE, KD_TEXT) < 0)
+ log_debug_errno(errno, "KDSETMODE ioctl for switching to text mode failed on TTY, ignoring: %m");
+
+ /* Set default keyboard mode */
+ r = vt_reset_keyboard(fd);
+ if (r < 0)
+ log_debug_errno(r, "Failed to reset VT keyboard, ignoring: %m");
+
+ if (tcgetattr(fd, &termios) < 0) {
+ r = log_debug_errno(errno, "Failed to get terminal parameters: %m");
+ goto finish;
+ }
+
+ /* We only reset the stuff that matters to the software. How
+ * hardware is set up we don't touch assuming that somebody
+ * else will do that for us */
+
+ termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC);
+ termios.c_iflag |= ICRNL | IMAXBEL | IUTF8;
+ termios.c_oflag |= ONLCR | OPOST;
+ termios.c_cflag |= CREAD;
+ termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE;
+
+ termios.c_cc[VINTR] = 03; /* ^C */
+ termios.c_cc[VQUIT] = 034; /* ^\ */
+ termios.c_cc[VERASE] = 0177;
+ termios.c_cc[VKILL] = 025; /* ^X */
+ termios.c_cc[VEOF] = 04; /* ^D */
+ termios.c_cc[VSTART] = 021; /* ^Q */
+ termios.c_cc[VSTOP] = 023; /* ^S */
+ termios.c_cc[VSUSP] = 032; /* ^Z */
+ termios.c_cc[VLNEXT] = 026; /* ^V */
+ termios.c_cc[VWERASE] = 027; /* ^W */
+ termios.c_cc[VREPRINT] = 022; /* ^R */
+ termios.c_cc[VEOL] = 0;
+ termios.c_cc[VEOL2] = 0;
+
+ termios.c_cc[VTIME] = 0;
+ termios.c_cc[VMIN] = 1;
+
+ r = RET_NERRNO(tcsetattr(fd, TCSANOW, &termios));
+ if (r < 0)
+ log_debug_errno(r, "Failed to set terminal parameters: %m");
+
+finish:
+ /* Just in case, flush all crap out */
+ (void) tcflush(fd, TCIOFLUSH);
+
+ return r;
+}
+
+static int terminal_reset_ansi_seq(int fd) {
+ int r, k;
+
+ assert(fd >= 0);
+
+ if (getenv_terminal_is_dumb())
+ return 0;
+
+ r = fd_nonblock(fd, true);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to set terminal to non-blocking mode: %m");
+
+ k = loop_write_full(fd,
+ "\033[!p" /* soft terminal reset */
+ "\033]104\007" /* reset colors */
+ "\033[?7h", /* enable line-wrapping */
+ SIZE_MAX,
+ 100 * USEC_PER_MSEC);
+ if (k < 0)
+ log_debug_errno(k, "Failed to reset terminal through ANSI sequences: %m");
+
+ if (r > 0) {
+ r = fd_nonblock(fd, false);
+ if (r < 0)
+ log_debug_errno(r, "Failed to set terminal back to blocking mode: %m");
+ }
+
+ return k < 0 ? k : r;
+}
+
+void reset_dev_console_fd(int fd, bool switch_to_text) {
+ int r;
+
+ assert(fd >= 0);
+
+ _cleanup_close_ int lock_fd = lock_dev_console();
+ if (lock_fd < 0)
+ log_debug_errno(lock_fd, "Failed to lock /dev/console, ignoring: %m");
+
+ r = terminal_reset_ioctl(fd, switch_to_text);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset /dev/console, ignoring: %m");
+
+ unsigned rows, cols;
+ r = proc_cmdline_tty_size("/dev/console", &rows, &cols);
+ if (r < 0)
+ log_warning_errno(r, "Failed to get /dev/console size, ignoring: %m");
+ else if (r > 0) {
+ r = terminal_set_size_fd(fd, NULL, rows, cols);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set configured terminal size on /dev/console, ignoring: %m");
+ } else
+ (void) terminal_fix_size(fd, fd);
+
+ r = terminal_reset_ansi_seq(fd);
+ if (r < 0)
+ log_warning_errno(r, "Failed to reset /dev/console using ANSI sequences, ignoring: %m");
+}
+
+int lock_dev_console(void) {
+ _cleanup_close_ int fd = -EBADF;
+ int r;
+
+ /* NB: We do not use O_NOFOLLOW here, because some container managers might place a symlink to some
+ * pty in /dev/console, in which case it should be fine to lock the target TTY. */
+ fd = open_terminal("/dev/console", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return fd;
+
+ r = lock_generic(fd, LOCK_BSD, LOCK_EX);
+ if (r < 0)
+ return r;
+
+ return TAKE_FD(fd);
}
int make_console_stdio(void) {
@@ -598,20 +656,7 @@ int make_console_stdio(void) {
return log_error_errno(r, "Failed to make /dev/null stdin/stdout/stderr: %m");
} else {
- unsigned rows, cols;
-
- r = reset_terminal_fd(fd, /* switch_to_text= */ true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
-
- r = proc_cmdline_tty_size("/dev/console", &rows, &cols);
- if (r < 0)
- log_warning_errno(r, "Failed to get terminal size, ignoring: %m");
- else {
- r = terminal_set_size_fd(fd, NULL, rows, cols);
- if (r < 0)
- log_warning_errno(r, "Failed to set terminal size, ignoring: %m");
- }
+ reset_dev_console_fd(fd, /* switch_to_text= */ true);
r = rearrange_stdio(fd, fd, fd); /* This invalidates 'fd' both on success and on failure. */
if (r < 0)
@@ -657,25 +702,38 @@ int vtnr_from_tty(const char *tty) {
return i;
}
- int resolve_dev_console(char **ret) {
- _cleanup_free_ char *active = NULL;
- char *tty;
+int resolve_dev_console(char **ret) {
int r;
assert(ret);
- /* Resolve where /dev/console is pointing to, if /sys is actually ours (i.e. not read-only-mounted which is a
- * sign for container setups) */
+ /* Resolve where /dev/console is pointing to. If /dev/console is a symlink (like in container
+ * managers), we'll just resolve the symlink. If it's a real device node, we'll use if
+ * /sys/class/tty/tty0/active, but only if /sys/ is actually ours (i.e. not read-only-mounted which
+ * is a sign for container setups). */
- if (path_is_read_only_fs("/sys") > 0)
+ _cleanup_free_ char *chased = NULL;
+ r = chase("/dev/console", /* root= */ NULL, /* chase_flags= */ 0, &chased, /* ret_fd= */ NULL);
+ if (r < 0)
+ return r;
+ if (!path_equal(chased, "/dev/console")) {
+ *ret = TAKE_PTR(chased);
+ return 0;
+ }
+
+ r = path_is_read_only_fs("/sys");
+ if (r < 0)
+ return r;
+ if (r > 0)
return -ENOMEDIUM;
+ _cleanup_free_ char *active = NULL;
r = read_one_line_file("/sys/class/tty/console/active", &active);
if (r < 0)
return r;
/* If multiple log outputs are configured the last one is what /dev/console points to */
- tty = strrchr(active, ' ');
+ const char *tty = strrchr(active, ' ');
if (tty)
tty++;
else
@@ -797,7 +855,7 @@ int fd_columns(int fd) {
return -errno;
if (ws.ws_col <= 0)
- return -EIO;
+ return -ENODATA; /* some tty types come up with invalid row/column initially, return a recognizable error for that */
return ws.ws_col;
}
@@ -834,7 +892,7 @@ int fd_lines(int fd) {
return -errno;
if (ws.ws_row <= 0)
- return -EIO;
+ return -ENODATA; /* some tty types come up with invalid row/column initially, return a recognizable error for that */
return ws.ws_row;
}
@@ -864,13 +922,18 @@ unsigned lines(void) {
int terminal_set_size_fd(int fd, const char *ident, unsigned rows, unsigned cols) {
struct winsize ws;
+ assert(fd >= 0);
+
+ if (!ident)
+ ident = "TTY";
+
if (rows == UINT_MAX && cols == UINT_MAX)
return 0;
if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
return log_debug_errno(errno,
"TIOCGWINSZ ioctl for getting %s size failed, not setting terminal size: %m",
- ident ?: "TTY");
+ ident);
if (rows == UINT_MAX)
rows = ws.ws_row;
@@ -889,7 +952,7 @@ int terminal_set_size_fd(int fd, const char *ident, unsigned rows, unsigned cols
ws.ws_col = cols;
if (ioctl(fd, TIOCSWINSZ, &ws) < 0)
- return log_debug_errno(errno, "TIOCSWINSZ ioctl for setting %s size failed: %m", ident ?: "TTY");
+ return log_debug_errno(errno, "TIOCSWINSZ ioctl for setting %s size failed: %m", ident);
return 0;
}
@@ -939,7 +1002,7 @@ int proc_cmdline_tty_size(const char *tty, unsigned *ret_rows, unsigned *ret_col
if (ret_cols)
*ret_cols = cols;
- return 0;
+ return rows != UINT_MAX || cols != UINT_MAX;
}
/* intended to be used as a SIGWINCH sighandler */
@@ -1006,11 +1069,11 @@ int getttyname_harder(int fd, char **ret) {
return 0;
}
-int get_ctty_devnr(pid_t pid, dev_t *d) {
- int r;
+int get_ctty_devnr(pid_t pid, dev_t *ret) {
_cleanup_free_ char *line = NULL;
- const char *p;
unsigned long ttynr;
+ const char *p;
+ int r;
assert(pid >= 0);
@@ -1037,8 +1100,8 @@ int get_ctty_devnr(pid_t pid, dev_t *d) {
if (devnum_is_zero(ttynr))
return -ENXIO;
- if (d)
- *d = (dev_t) ttynr;
+ if (ret)
+ *ret = (dev_t) ttynr;
return 0;
}
@@ -1413,28 +1476,6 @@ bool underline_enabled(void) {
return cached_underline_enabled;
}
-int vt_default_utf8(void) {
- _cleanup_free_ char *b = NULL;
- int r;
-
- /* Read the default VT UTF8 setting from the kernel */
-
- r = read_one_line_file("/sys/module/vt/parameters/default_utf8", &b);
- if (r < 0)
- return r;
-
- return parse_boolean(b);
-}
-
-int vt_reset_keyboard(int fd) {
- int kb;
-
- /* If we can't read the default, then default to unicode. It's 2017 after all. */
- kb = vt_default_utf8() != 0 ? K_UNICODE : K_XLATE;
-
- return RET_NERRNO(ioctl(fd, KDSKBMODE, kb));
-}
-
int vt_restore(int fd) {
static const struct vt_mode mode = {
@@ -1522,50 +1563,47 @@ void get_log_colors(int priority, const char **on, const char **off, const char
}
}
-int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column) {
- int r;
- char cursor_position[STRLEN("\x1B[") + DECIMAL_STR_MAX(int) * 2 + STRLEN(";H") + 1];
-
+int terminal_set_cursor_position(int fd, unsigned row, unsigned column) {
assert(fd >= 0);
+ char cursor_position[STRLEN("\x1B[" ";" "H") + DECIMAL_STR_MAX(unsigned) * 2 + 1];
xsprintf(cursor_position, "\x1B[%u;%uH", row, column);
- r = loop_write(fd, cursor_position, SIZE_MAX);
- if (r < 0)
- return log_warning_errno(r, "Failed to set cursor position, ignoring: %m");
-
- return 0;
+ return loop_write(fd, cursor_position, SIZE_MAX);
}
-int terminal_reset_ansi_seq(int fd) {
- int r, k;
+int terminal_reset_defensive(int fd, bool switch_to_text) {
+ int r = 0;
assert(fd >= 0);
- if (getenv_terminal_is_dumb())
- return 0;
+ /* Resets the terminal comprehensively, but defensively. i.e. both resets the tty via ioctl()s and
+ * via ANSI sequences, but avoids the latter in case we are talking to a pty. That's a safety measure
+ * because ptys might be connected to shell pipelines where we cannot expect such ansi sequences to
+ * work. Given that ptys are generally short-lived (and not recycled) this restriction shouldn't hurt
+ * much.
+ *
+ * The specified fd should be open for *writing*! */
- r = fd_nonblock(fd, true);
- if (r < 0)
- return log_debug_errno(r, "Failed to set terminal to non-blocking mode: %m");
+ if (!isatty_safe(fd))
+ return -ENOTTY;
- k = loop_write_full(fd,
- "\033c" /* reset to initial state */
- "\033[!p" /* soft terminal reset */
- "\033]104\007" /* reset colors */
- "\033[?7h", /* enable line-wrapping */
- SIZE_MAX,
- 50 * USEC_PER_MSEC);
- if (k < 0)
- log_debug_errno(k, "Failed to write to terminal: %m");
+ RET_GATHER(r, terminal_reset_ioctl(fd, switch_to_text));
- if (r > 0) {
- r = fd_nonblock(fd, false);
- if (r < 0)
- log_debug_errno(r, "Failed to set terminal back to blocking mode: %m");
- }
+ if (terminal_is_pty_fd(fd) == 0)
+ RET_GATHER(r, terminal_reset_ansi_seq(fd));
- return k < 0 ? k : r;
+ return r;
+}
+
+int terminal_reset_defensive_locked(int fd, bool switch_to_text) {
+ assert(fd >= 0);
+
+ _cleanup_close_ int lock_fd = lock_dev_console();
+ if (lock_fd < 0)
+ log_debug_errno(lock_fd, "Failed to acquire lock for /dev/console, ignoring: %m");
+
+ return terminal_reset_defensive(fd, switch_to_text);
}
void termios_disable_echo(struct termios *termios) {
@@ -1576,6 +1614,37 @@ void termios_disable_echo(struct termios *termios) {
termios->c_cc[VTIME] = 0;
}
+static int terminal_verify_same(int input_fd, int output_fd) {
+ assert(input_fd >= 0);
+ assert(output_fd >= 0);
+
+ /* Validates that the specified fds reference the same TTY */
+
+ if (input_fd != output_fd) {
+ struct stat sti;
+ if (fstat(input_fd, &sti) < 0)
+ return -errno;
+
+ if (!S_ISCHR(sti.st_mode)) /* TTYs are character devices */
+ return -ENOTTY;
+
+ struct stat sto;
+ if (fstat(output_fd, &sto) < 0)
+ return -errno;
+
+ if (!S_ISCHR(sto.st_mode))
+ return -ENOTTY;
+
+ if (sti.st_rdev != sto.st_rdev)
+ return -ENOLINK;
+ }
+
+ if (!isatty_safe(input_fd)) /* The check above was just for char device, but now let's ensure it's actually a tty */
+ return -ENOTTY;
+
+ return 0;
+}
+
typedef enum BackgroundColorState {
BACKGROUND_TEXT,
BACKGROUND_ESCAPE,
@@ -1601,7 +1670,8 @@ typedef struct BackgroundColorContext {
static int scan_background_color_response(
BackgroundColorContext *context,
const char *buf,
- size_t size) {
+ size_t size,
+ size_t *ret_processed) {
assert(context);
assert(buf || size == 0);
@@ -1677,8 +1747,12 @@ static int scan_background_color_response(
case BACKGROUND_BLUE:
if (c == '\x07') {
- if (context->blue_bits > 0)
+ if (context->blue_bits > 0) {
+ if (ret_processed)
+ *ret_processed = i + 1;
+
return 1; /* success! */
+ }
context->state = BACKGROUND_TEXT;
} else if (c == '\x1b')
@@ -1695,8 +1769,12 @@ static int scan_background_color_response(
break;
case BACKGROUND_STRING_TERMINATOR:
- if (c == '\\')
+ if (c == '\\') {
+ if (ret_processed)
+ *ret_processed = i + 1;
+
return 1; /* success! */
+ }
context->state = c == ']' ? BACKGROUND_ESCAPE : BACKGROUND_TEXT;
break;
@@ -1711,6 +1789,9 @@ static int scan_background_color_response(
}
}
+ if (ret_processed)
+ *ret_processed = size;
+
return 0; /* all good, but not enough data yet */
}
@@ -1724,8 +1805,9 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret
if (!colors_enabled())
return -EOPNOTSUPP;
- if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
- return -EOPNOTSUPP;
+ r = terminal_verify_same(STDIN_FILENO, STDOUT_FILENO);
+ if (r < 0)
+ return r;
if (streq_ptr(getenv("TERM"), "linux")) {
/* Linux console is black */
@@ -1740,47 +1822,57 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret
struct termios new_termios = old_termios;
termios_disable_echo(&new_termios);
- if (tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_termios) < 0)
+ if (tcsetattr(STDIN_FILENO, TCSADRAIN, &new_termios) < 0)
return -errno;
r = loop_write(STDOUT_FILENO, "\x1B]11;?\x07", SIZE_MAX);
if (r < 0)
goto finish;
- usec_t end = usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC);
- char buf[256];
+ usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC);
+ char buf[STRLEN("\x1B]11;rgb:0/0/0\x07")]; /* shortest possible reply */
size_t buf_full = 0;
BackgroundColorContext context = {};
- for (;;) {
- usec_t n = now(CLOCK_MONOTONIC);
+ for (bool first = true;; first = false) {
+ if (buf_full == 0) {
+ usec_t n = now(CLOCK_MONOTONIC);
- if (n >= end) {
- r = -EOPNOTSUPP;
- goto finish;
+ if (n >= end) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n));
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ /* On the first try, read multiple characters, i.e. the shortest valid
+ * reply. Afterwards read byte-wise, since we don't want to read too much, and
+ * unnecessarily drop too many characters from the input queue. */
+ ssize_t l = read(STDIN_FILENO, buf, first ? sizeof(buf) : 1);
+ if (l < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ assert((size_t) l <= sizeof(buf));
+ buf_full = l;
}
- r = fd_wait_for_event(STDIN_FILENO, POLLIN, usec_sub_unsigned(end, n));
+ size_t processed;
+ r = scan_background_color_response(&context, buf, buf_full, &processed);
if (r < 0)
goto finish;
- if (r == 0) {
- r = -EOPNOTSUPP;
- goto finish;
- }
- ssize_t l;
- l = read(STDIN_FILENO, buf, sizeof(buf) - buf_full);
- if (l < 0) {
- r = -errno;
- goto finish;
- }
+ assert(processed <= buf_full);
+ buf_full -= processed;
+ memmove(buf, buf + processed, buf_full);
- buf_full += l;
- assert(buf_full <= sizeof(buf));
-
- r = scan_background_color_response(&context, buf, buf_full);
- if (r < 0)
- goto finish;
if (r > 0) {
assert(context.red_bits > 0);
*ret_red = (double) context.red / ((UINT64_C(1) << context.red_bits) - 1);
@@ -1794,6 +1886,297 @@ int get_default_background_color(double *ret_red, double *ret_green, double *ret
}
finish:
- (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_termios);
+ RET_GATHER(r, RET_NERRNO(tcsetattr(STDIN_FILENO, TCSADRAIN, &old_termios)));
return r;
}
+
+typedef enum CursorPositionState {
+ CURSOR_TEXT,
+ CURSOR_ESCAPE,
+ CURSOR_ROW,
+ CURSOR_COLUMN,
+} CursorPositionState;
+
+typedef struct CursorPositionContext {
+ CursorPositionState state;
+ unsigned row, column;
+} CursorPositionContext;
+
+static int scan_cursor_position_response(
+ CursorPositionContext *context,
+ const char *buf,
+ size_t size,
+ size_t *ret_processed) {
+
+ assert(context);
+ assert(buf || size == 0);
+
+ for (size_t i = 0; i < size; i++) {
+ char c = buf[i];
+
+ switch (context->state) {
+
+ case CURSOR_TEXT:
+ context->state = c == '\x1B' ? CURSOR_ESCAPE : CURSOR_TEXT;
+ break;
+
+ case CURSOR_ESCAPE:
+ context->state = c == '[' ? CURSOR_ROW : CURSOR_TEXT;
+ break;
+
+ case CURSOR_ROW:
+ if (c == ';')
+ context->state = context->row > 0 ? CURSOR_COLUMN : CURSOR_TEXT;
+ else {
+ int d = undecchar(c);
+
+ /* We read a decimal character, let's suffix it to the number we so far read,
+ * but let's do an overflow check first. */
+ if (d < 0 || context->row > (UINT_MAX-d)/10)
+ context->state = CURSOR_TEXT;
+ else
+ context->row = context->row * 10 + d;
+ }
+ break;
+
+ case CURSOR_COLUMN:
+ if (c == 'R') {
+ if (context->column > 0) {
+ if (ret_processed)
+ *ret_processed = i + 1;
+
+ return 1; /* success! */
+ }
+
+ context->state = CURSOR_TEXT;
+ } else {
+ int d = undecchar(c);
+
+ /* As above, add the decimal charatcer to our column number */
+ if (d < 0 || context->column > (UINT_MAX-d)/10)
+ context->state = CURSOR_TEXT;
+ else
+ context->column = context->column * 10 + d;
+ }
+
+ break;
+ }
+
+ /* Reset any positions we might have picked up */
+ if (IN_SET(context->state, CURSOR_TEXT, CURSOR_ESCAPE))
+ context->row = context->column = 0;
+ }
+
+ if (ret_processed)
+ *ret_processed = size;
+
+ return 0; /* all good, but not enough data yet */
+}
+
+int terminal_get_size_by_dsr(
+ int input_fd,
+ int output_fd,
+ unsigned *ret_rows,
+ unsigned *ret_columns) {
+
+ assert(input_fd >= 0);
+ assert(output_fd >= 0);
+
+ int r;
+
+ /* Tries to determine the terminal dimension by means of ANSI sequences rather than TIOCGWINSZ
+ * ioctl(). Why bother with this? The ioctl() information is often incorrect on serial terminals
+ * (since there's no handshake or protocol to determine the right dimensions in RS232), but since the
+ * ANSI sequences are interpreted by the final terminal instead of an intermediary tty driver they
+ * should be more accurate.
+ *
+ * Unfortunately there's no direct ANSI sequence to query terminal dimensions. But we can hack around
+ * it: we position the cursor briefly at an absolute location very far down and very far to the
+ * right, and then read back where we actually ended up. Because cursor locations are capped at the
+ * terminal width/height we should then see the right values. In order to not risk integer overflows
+ * in terminal applications we'll use INT16_MAX-1 as location to jump to — hopefully a value that is
+ * large enough for any real-life terminals, but small enough to not overflow anything or be
+ * recognized as a "niche" value. (Note that the dimension fields in "struct winsize" are 16bit only,
+ * too). */
+
+ if (terminal_is_dumb())
+ return -EOPNOTSUPP;
+
+ r = terminal_verify_same(input_fd, output_fd);
+ if (r < 0)
+ return log_debug_errno(r, "Called with distinct input/output fds: %m");
+
+ struct termios old_termios;
+ if (tcgetattr(input_fd, &old_termios) < 0)
+ return log_debug_errno(errno, "Failed to to get terminal settings: %m");
+
+ struct termios new_termios = old_termios;
+ termios_disable_echo(&new_termios);
+
+ if (tcsetattr(input_fd, TCSADRAIN, &new_termios) < 0)
+ return log_debug_errno(errno, "Failed to to set new terminal settings: %m");
+
+ unsigned saved_row = 0, saved_column = 0;
+
+ r = loop_write(output_fd,
+ "\x1B[6n" /* Request cursor position (DSR/CPR) */
+ "\x1B[32766;32766H" /* Position cursor really far to the right and to the bottom, but let's stay within the 16bit signed range */
+ "\x1B[6n", /* Request cursor position again */
+ SIZE_MAX);
+ if (r < 0)
+ goto finish;
+
+ usec_t end = usec_add(now(CLOCK_MONOTONIC), 333 * USEC_PER_MSEC);
+ char buf[STRLEN("\x1B[1;1R")]; /* The shortest valid reply possible */
+ size_t buf_full = 0;
+ CursorPositionContext context = {};
+
+ for (bool first = true;; first = false) {
+ if (buf_full == 0) {
+ usec_t n = now(CLOCK_MONOTONIC);
+
+ if (n >= end) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ r = fd_wait_for_event(input_fd, POLLIN, usec_sub_unsigned(end, n));
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ r = -EOPNOTSUPP;
+ goto finish;
+ }
+
+ /* On the first try, read multiple characters, i.e. the shortest valid
+ * reply. Afterwards read byte-wise, since we don't want to read too much, and
+ * unnecessarily drop too many characters from the input queue. */
+ ssize_t l = read(input_fd, buf, first ? sizeof(buf) : 1);
+ if (l < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ assert((size_t) l <= sizeof(buf));
+ buf_full = l;
+ }
+
+ size_t processed;
+ r = scan_cursor_position_response(&context, buf, buf_full, &processed);
+ if (r < 0)
+ goto finish;
+
+ assert(processed <= buf_full);
+ buf_full -= processed;
+ memmove(buf, buf + processed, buf_full);
+
+ if (r > 0) {
+ if (saved_row == 0) {
+ assert(saved_column == 0);
+
+ /* First sequence, this is the cursor position before we set it somewhere
+ * into the void at the bottom right. Let's save where we are so that we can
+ * return later. */
+
+ /* Superficial validity checks */
+ if (context.row <= 0 || context.column <= 0 || context.row >= 32766 || context.column >= 32766) {
+ r = -ENODATA;
+ goto finish;
+ }
+
+ saved_row = context.row;
+ saved_column = context.column;
+
+ /* Reset state */
+ context = (CursorPositionContext) {};
+ } else {
+ /* Second sequence, this is the cursor position after we set it somewhere
+ * into the void at the bottom right. */
+
+ /* Superficial validity checks (no particular reason to check for < 4, it's
+ * just a way to look for unreasonably small values) */
+ if (context.row < 4 || context.column < 4 || context.row >= 32766 || context.column >= 32766) {
+ r = -ENODATA;
+ goto finish;
+ }
+
+ if (ret_rows)
+ *ret_rows = context.row;
+ if (ret_columns)
+ *ret_columns = context.column;
+
+ r = 0;
+ goto finish;
+ }
+ }
+ }
+
+finish:
+ /* Restore cursor position */
+ if (saved_row > 0 && saved_column > 0)
+ RET_GATHER(r, terminal_set_cursor_position(output_fd, saved_row, saved_column));
+
+ RET_GATHER(r, RET_NERRNO(tcsetattr(input_fd, TCSADRAIN, &old_termios)));
+ return r;
+}
+
+int terminal_fix_size(int input_fd, int output_fd) {
+ unsigned rows, columns;
+ int r;
+
+ /* Tries to update the current terminal dimensions to the ones reported via ANSI sequences */
+
+ r = terminal_verify_same(input_fd, output_fd);
+ if (r < 0)
+ return r;
+
+ struct winsize ws = {};
+ if (ioctl(output_fd, TIOCGWINSZ, &ws) < 0)
+ return log_debug_errno(errno, "Failed to query terminal dimensions, ignoring: %m");
+
+ r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &columns);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to acquire terminal dimensions via ANSI sequences, not adjusting terminal dimensions: %m");
+
+ if (ws.ws_row == rows && ws.ws_col == columns) {
+ log_debug("Terminal dimensions reported via ANSI sequences match currently set terminal dimensions, not changing.");
+ return 0;
+ }
+
+ ws.ws_col = columns;
+ ws.ws_row = rows;
+
+ if (ioctl(output_fd, TIOCSWINSZ, &ws) < 0)
+ return log_debug_errno(errno, "Failed to update terminal dimensions, ignoring: %m");
+
+ log_debug("Fixed terminal dimensions to %ux%u based on ANSI sequence information.", columns, rows);
+ return 1;
+}
+
+int terminal_is_pty_fd(int fd) {
+ int r;
+
+ assert(fd >= 0);
+
+ /* Returns true if we are looking at a pty, i.e. if it's backed by the /dev/pts/ file system */
+
+ if (!isatty_safe(fd))
+ return false;
+
+ r = is_fs_type_at(fd, NULL, DEVPTS_SUPER_MAGIC);
+ if (r != 0)
+ return r;
+
+ /* The ptmx device is weird, it exists twice, once inside and once outside devpts. To detect the
+ * latter case, let's fire off an ioctl() that only works on ptmx devices. */
+
+ int v;
+ if (ioctl(fd, TIOCGPKT, &v) < 0) {
+ if (ERRNO_IS_NOT_SUPPORTED(errno))
+ return false;
+
+ return -errno;
+ }
+
+ return true;
+}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index 691b37aa51..84d4731ea8 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -11,72 +11,6 @@
#include "macro.h"
#include "time-util.h"
-/* Regular colors */
-#define ANSI_BLACK "\x1B[0;30m" /* Some type of grey usually. */
-#define ANSI_RED "\x1B[0;31m"
-#define ANSI_GREEN "\x1B[0;32m"
-#define ANSI_YELLOW "\x1B[0;33m"
-#define ANSI_BLUE "\x1B[0;34m"
-#define ANSI_MAGENTA "\x1B[0;35m"
-#define ANSI_CYAN "\x1B[0;36m"
-#define ANSI_WHITE "\x1B[0;37m" /* This is actually rendered as light grey, legible even on a white
- * background. See ANSI_HIGHLIGHT_WHITE for real white. */
-
-#define ANSI_BRIGHT_BLACK "\x1B[0;90m"
-#define ANSI_BRIGHT_RED "\x1B[0;91m"
-#define ANSI_BRIGHT_GREEN "\x1B[0;92m"
-#define ANSI_BRIGHT_YELLOW "\x1B[0;93m"
-#define ANSI_BRIGHT_BLUE "\x1B[0;94m"
-#define ANSI_BRIGHT_MAGENTA "\x1B[0;95m"
-#define ANSI_BRIGHT_CYAN "\x1B[0;96m"
-#define ANSI_BRIGHT_WHITE "\x1B[0;97m"
-
-#define ANSI_GREY "\x1B[0;38;5;245m"
-
-/* Bold/highlighted */
-#define ANSI_HIGHLIGHT_BLACK "\x1B[0;1;30m"
-#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m"
-#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m"
-#define _ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m" /* This yellow is currently not displayed well by some terminals */
-#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
-#define ANSI_HIGHLIGHT_MAGENTA "\x1B[0;1;35m"
-#define ANSI_HIGHLIGHT_CYAN "\x1B[0;1;36m"
-#define ANSI_HIGHLIGHT_WHITE "\x1B[0;1;37m"
-#define ANSI_HIGHLIGHT_YELLOW4 "\x1B[0;1;38:5:100m"
-#define ANSI_HIGHLIGHT_KHAKI3 "\x1B[0;1;38:5:185m"
-#define ANSI_HIGHLIGHT_GREY "\x1B[0;1;38:5:245m"
-
-#define ANSI_HIGHLIGHT_YELLOW ANSI_HIGHLIGHT_KHAKI3 /* Replacement yellow that is more legible */
-
-/* Underlined */
-#define ANSI_GREY_UNDERLINE "\x1B[0;4;38:5:245m"
-#define ANSI_BRIGHT_BLACK_UNDERLINE "\x1B[0;4;90m"
-#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
-#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m"
-#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;38:5:185m"
-#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m"
-#define ANSI_HIGHLIGHT_MAGENTA_UNDERLINE "\x1B[0;1;4;35m"
-#define ANSI_HIGHLIGHT_GREY_UNDERLINE "\x1B[0;1;4;38:5:245m"
-
-/* Other ANSI codes */
-#define ANSI_UNDERLINE "\x1B[0;4m"
-#define ANSI_ADD_UNDERLINE "\x1B[4m"
-#define ANSI_ADD_UNDERLINE_GREY ANSI_ADD_UNDERLINE "\x1B[58:5:245m"
-#define ANSI_HIGHLIGHT "\x1B[0;1;39m"
-#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
-
-/* Fallback colors: 256 -> 16 */
-#define ANSI_HIGHLIGHT_GREY_FALLBACK "\x1B[0;1;90m"
-#define ANSI_HIGHLIGHT_GREY_FALLBACK_UNDERLINE "\x1B[0;1;4;90m"
-#define ANSI_HIGHLIGHT_YELLOW_FALLBACK "\x1B[0;1;33m"
-#define ANSI_HIGHLIGHT_YELLOW_FALLBACK_UNDERLINE "\x1B[0;1;4;33m"
-
-/* Background colors */
-#define ANSI_BACKGROUND_BLUE "\x1B[44m"
-
-/* Reset/clear ANSI styles */
-#define ANSI_NORMAL "\x1B[0m"
-
/* Erase characters until the end of the line */
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
@@ -95,10 +29,10 @@
bool isatty_safe(int fd);
-int reset_terminal_fd(int fd, bool switch_to_text);
-int reset_terminal(const char *name);
-int set_terminal_cursor_position(int fd, unsigned int row, unsigned int column);
-int terminal_reset_ansi_seq(int fd);
+int terminal_reset_defensive(int fd, bool switch_to_text);
+int terminal_reset_defensive_locked(int fd, bool switch_to_text);
+
+int terminal_set_cursor_position(int fd, unsigned row, unsigned column);
int open_terminal(const char *name, int mode);
@@ -117,6 +51,9 @@ typedef enum AcquireTerminalFlags {
ACQUIRE_TERMINAL_PERMISSIVE = 1 << 2,
} AcquireTerminalFlags;
+int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
+int release_terminal(void);
+
/* Limits the use of ANSI colors to a subset. */
typedef enum ColorMode {
COLOR_OFF, /* No colors, monochrome output. */
@@ -130,11 +67,7 @@ typedef enum ColorMode {
const char* color_mode_to_string(ColorMode m) _const_;
ColorMode color_mode_from_string(const char *s) _pure_;
-int acquire_terminal(const char *name, AcquireTerminalFlags flags, usec_t timeout);
-int release_terminal(void);
-
int terminal_vhangup_fd(int fd);
-int terminal_vhangup(const char *name);
int terminal_set_size_fd(int fd, const char *ident, unsigned rows, unsigned cols);
int proc_cmdline_tty_size(const char *tty, unsigned *ret_rows, unsigned *ret_cols);
@@ -155,6 +88,8 @@ 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);
int make_console_stdio(void);
int fd_columns(int fd);
@@ -177,99 +112,11 @@ static inline bool colors_enabled(void) {
return get_color_mode() != COLOR_OFF;
}
-#define DEFINE_ANSI_FUNC(name, NAME) \
- static inline const char *ansi_##name(void) { \
- return colors_enabled() ? ANSI_##NAME : ""; \
- }
+int get_ctty_devnr(pid_t pid, dev_t *ret);
+int get_ctty(pid_t, dev_t *ret_devnr, char **ret);
-#define DEFINE_ANSI_FUNC_256(name, NAME, FALLBACK) \
- static inline const char *ansi_##name(void) { \
- switch (get_color_mode()) { \
- case COLOR_OFF: return ""; \
- case COLOR_16: return ANSI_##FALLBACK; \
- default : return ANSI_##NAME; \
- } \
- }
-
-static inline const char* ansi_underline(void) {
- return underline_enabled() ? ANSI_UNDERLINE : "";
-}
-
-static inline const char* ansi_add_underline(void) {
- return underline_enabled() ? ANSI_ADD_UNDERLINE : "";
-}
-
-static inline const char* ansi_add_underline_grey(void) {
- return underline_enabled() ?
- (colors_enabled() ? ANSI_ADD_UNDERLINE_GREY : ANSI_ADD_UNDERLINE) : "";
-}
-
-#define DEFINE_ANSI_FUNC_UNDERLINE(name, NAME) \
- static inline const char *ansi_##name(void) { \
- return underline_enabled() ? ANSI_##NAME##_UNDERLINE : \
- colors_enabled() ? ANSI_##NAME : ""; \
- }
-
-
-#define DEFINE_ANSI_FUNC_UNDERLINE_256(name, NAME, FALLBACK) \
- static inline const char *ansi_##name(void) { \
- switch (get_color_mode()) { \
- case COLOR_OFF: return ""; \
- case COLOR_16: return underline_enabled() ? ANSI_##FALLBACK##_UNDERLINE : ANSI_##FALLBACK; \
- default : return underline_enabled() ? ANSI_##NAME##_UNDERLINE: ANSI_##NAME; \
- } \
- }
-
-DEFINE_ANSI_FUNC(normal, NORMAL);
-DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
-DEFINE_ANSI_FUNC(black, BLACK);
-DEFINE_ANSI_FUNC(red, RED);
-DEFINE_ANSI_FUNC(green, GREEN);
-DEFINE_ANSI_FUNC(yellow, YELLOW);
-DEFINE_ANSI_FUNC(blue, BLUE);
-DEFINE_ANSI_FUNC(magenta, MAGENTA);
-DEFINE_ANSI_FUNC(cyan, CYAN);
-DEFINE_ANSI_FUNC(white, WHITE);
-DEFINE_ANSI_FUNC_256(grey, GREY, BRIGHT_BLACK);
-
-DEFINE_ANSI_FUNC(bright_black, BRIGHT_BLACK);
-DEFINE_ANSI_FUNC(bright_red, BRIGHT_RED);
-DEFINE_ANSI_FUNC(bright_green, BRIGHT_GREEN);
-DEFINE_ANSI_FUNC(bright_yellow, BRIGHT_YELLOW);
-DEFINE_ANSI_FUNC(bright_blue, BRIGHT_BLUE);
-DEFINE_ANSI_FUNC(bright_magenta, BRIGHT_MAGENTA);
-DEFINE_ANSI_FUNC(bright_cyan, BRIGHT_CYAN);
-DEFINE_ANSI_FUNC(bright_white, BRIGHT_WHITE);
-
-DEFINE_ANSI_FUNC(highlight_black, HIGHLIGHT_BLACK);
-DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
-DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
-DEFINE_ANSI_FUNC_256(highlight_yellow, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
-DEFINE_ANSI_FUNC_256(highlight_yellow4, HIGHLIGHT_YELLOW4, HIGHLIGHT_YELLOW_FALLBACK);
-DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
-DEFINE_ANSI_FUNC(highlight_magenta, HIGHLIGHT_MAGENTA);
-DEFINE_ANSI_FUNC(highlight_cyan, HIGHLIGHT_CYAN);
-DEFINE_ANSI_FUNC_256(highlight_grey, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
-DEFINE_ANSI_FUNC(highlight_white, HIGHLIGHT_WHITE);
-
-static inline const char* _ansi_highlight_yellow(void) {
- return colors_enabled() ? _ANSI_HIGHLIGHT_YELLOW : "";
-}
-
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_underline, HIGHLIGHT);
-DEFINE_ANSI_FUNC_UNDERLINE_256(grey_underline, GREY, BRIGHT_BLACK);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_red_underline, HIGHLIGHT_RED);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_green_underline, HIGHLIGHT_GREEN);
-DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_yellow_underline, HIGHLIGHT_YELLOW, HIGHLIGHT_YELLOW_FALLBACK);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_blue_underline, HIGHLIGHT_BLUE);
-DEFINE_ANSI_FUNC_UNDERLINE(highlight_magenta_underline, HIGHLIGHT_MAGENTA);
-DEFINE_ANSI_FUNC_UNDERLINE_256(highlight_grey_underline, HIGHLIGHT_GREY, HIGHLIGHT_GREY_FALLBACK);
-
-int get_ctty_devnr(pid_t pid, dev_t *d);
-int get_ctty(pid_t, dev_t *_devnr, char **r);
-
-int getttyname_malloc(int fd, char **r);
-int getttyname_harder(int fd, char **r);
+int getttyname_malloc(int fd, char **ret);
+int getttyname_harder(int fd, char **ret);
int ptsname_malloc(int fd, char **ret);
@@ -277,20 +124,19 @@ int openpt_allocate(int flags, char **ret_slave);
int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave);
int open_terminal_in_namespace(pid_t pid, const char *name, int mode);
-int vt_default_utf8(void);
-int vt_reset_keyboard(int fd);
int vt_restore(int fd);
int vt_release(int fd, bool restore_vt);
void get_log_colors(int priority, const char **on, const char **off, const char **highlight);
-static inline const char* ansi_highlight_green_red(bool b) {
- return b ? ansi_highlight_green() : ansi_highlight_red();
-}
-
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
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_is_pty_fd(int fd);
diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c
index dbda6e5457..75c26df52f 100644
--- a/src/core/emergency-action.c
+++ b/src/core/emergency-action.c
@@ -2,6 +2,7 @@
#include
+#include "ansi-color.h"
#include "bus-error.h"
#include "bus-util.h"
#include "emergency-action.h"
diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c
index 94c908afd7..ea85c36581 100644
--- a/src/core/exec-invoke.c
+++ b/src/core/exec-invoke.c
@@ -353,16 +353,10 @@ static int setup_input(
if (dup2(params->stdin_fd, STDIN_FILENO) < 0)
return -errno;
- /* Try to make this the controlling tty, if it is a tty, and reset it */
- if (isatty(STDIN_FILENO)) {
+ /* Try to make this the controlling tty, if it is a tty */
+ if (isatty(STDIN_FILENO))
(void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
- if (context->tty_reset)
- (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ true);
-
- (void) exec_context_apply_tty_size(context, STDIN_FILENO, /* tty_path= */ NULL);
- }
-
return STDIN_FILENO;
}
@@ -389,10 +383,6 @@ static int setup_input(
if (tty_fd < 0)
return tty_fd;
- r = exec_context_apply_tty_size(context, tty_fd, tty_path);
- if (r < 0)
- return r;
-
r = move_fd(tty_fd, STDIN_FILENO, /* cloexec= */ false);
if (r < 0)
return r;
@@ -554,7 +544,6 @@ static int setup_output(
if (is_terminal_input(i))
return RET_NERRNO(dup2(STDIN_FILENO, fileno));
- /* We don't reset the terminal if this is just about output */
return open_terminal_as(exec_context_tty_path(context), O_WRONLY, fileno);
case EXEC_OUTPUT_KMSG:
@@ -659,11 +648,11 @@ static int setup_confirm_stdio(
assert(ret_saved_stdin);
assert(ret_saved_stdout);
- saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
+ saved_stdin = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 3);
if (saved_stdin < 0)
return -errno;
- saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
+ saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 3);
if (saved_stdout < 0)
return -errno;
@@ -671,15 +660,19 @@ static int setup_confirm_stdio(
if (fd < 0)
return fd;
+ _cleanup_close_ int lock_fd = lock_dev_console();
+ if (lock_fd < 0)
+ log_debug_errno(lock_fd, "Failed to lock /dev/console, ignoring: %m");
+
r = chown_terminal(fd, getuid());
if (r < 0)
return r;
- r = reset_terminal_fd(fd, /* switch_to_text= */ true);
+ r = terminal_reset_defensive(fd, /* switch_to_text= */ true);
if (r < 0)
return r;
- r = exec_context_apply_tty_size(context, fd, vc);
+ r = exec_context_apply_tty_size(context, fd, fd, vc);
if (r < 0)
return r;
@@ -694,15 +687,16 @@ static int setup_confirm_stdio(
}
static void write_confirm_error_fd(int err, int fd, const char *unit_id) {
- assert(err < 0);
+ assert(err != 0);
+ assert(fd >= 0);
assert(unit_id);
- if (err == -ETIMEDOUT)
+ errno = abs(err);
+
+ if (errno == ETIMEDOUT)
dprintf(fd, "Confirmation question timed out for %s, assuming positive response.\n", unit_id);
- else {
- errno = -err;
- dprintf(fd, "Couldn't ask confirmation for %s: %m, assuming positive response.\n", unit_id);
- }
+ else
+ dprintf(fd, "Couldn't ask confirmation for %s, assuming positive response: %m\n", unit_id);
}
static void write_confirm_error(int err, const char *vc, const char *unit_id) {
@@ -750,7 +744,7 @@ static bool confirm_spawn_disabled(void) {
}
static int ask_for_confirmation(const ExecContext *context, const ExecParameters *params, const char *cmdline) {
- int saved_stdout = -1, saved_stdin = -1, r;
+ int saved_stdout = -EBADF, saved_stdin = -EBADF, r;
_cleanup_free_ char *e = NULL;
char c;
@@ -4031,6 +4025,39 @@ static int send_handoff_timestamp(
return 1;
}
+static void prepare_terminal(
+ const ExecContext *context,
+ ExecParameters *p) {
+
+ _cleanup_close_ int lock_fd = -EBADF;
+
+ /* This is the "constructive" reset, i.e. is about preparing things for our invocation rather than
+ * cleaning up things from older invocations. */
+
+ assert(context);
+ assert(p);
+
+ /* We only try to reset things if we there's the chance our stdout points to a TTY */
+ if (!(is_terminal_output(context->std_output) ||
+ (context->std_output == EXEC_OUTPUT_INHERIT && is_terminal_input(context->std_input)) ||
+ context->std_output == EXEC_OUTPUT_NAMED_FD ||
+ p->stdout_fd >= 0))
+ return;
+
+ if (context->tty_reset) {
+ /* When we are resetting the TTY, then let's create a lock first, to synchronize access. This
+ * in particular matters as concurrent resets and the TTY size ANSI DSR logic done by the
+ * exec_context_apply_tty_size() below might interfere */
+ lock_fd = lock_dev_console();
+ if (lock_fd < 0)
+ log_exec_debug_errno(context, p, lock_fd, "Failed to lock /dev/console, ignoring: %m");
+
+ (void) terminal_reset_defensive(STDOUT_FILENO, /* switch_to_text= */ false);
+ }
+
+ (void) exec_context_apply_tty_size(context, STDIN_FILENO, STDOUT_FILENO, /* tty_path= */ NULL);
+}
+
int exec_invoke(
const ExecCommand *command,
const ExecContext *context,
@@ -4198,6 +4225,11 @@ int exec_invoke(
return log_exec_error_errno(context, params, errno, "Failed to create new process session: %m");
}
+ /* Now, reset the TTY associated to this service "destructively" (i.e. possibly even hang up or
+ * disallocate the VT), to get rid of any prior uses of the device. Note that we do not keep any fd
+ * open here, hence some of the settings made here might vanish again, depending on the TTY driver
+ * used. A 2nd ("constructive") initialization after we opened the input/output fds we actually want
+ * will fix this. */
exec_context_tty_reset(context, params);
if (params->shall_confirm_spawn && exec_context_shall_confirm_spawn(context)) {
@@ -4381,6 +4413,12 @@ int exec_invoke(
return log_exec_error_errno(context, params, r, "Failed to set up standard error output: %m");
}
+ /* Now that stdin/stdout are definiely opened, properly initialize it with our desired
+ * settings. Note: this is a "constructive" reset, it prepares things for us to use. This is
+ * different from the "destructive" TTY reset further up. Also note: we apply this on stdin/stdout in
+ * case this is a tty, regardless if we opened it ourselves or got it passed in pre-opened. */
+ prepare_terminal(context, params);
+
if (context->oom_score_adjust_set) {
/* When we can't make this change due to EPERM, then let's silently skip over it. User
* namespaces prohibit write access to this file, and we shouldn't trip up over that. */
diff --git a/src/core/execute.c b/src/core/execute.c
index 18396db113..cdc1277956 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -25,7 +25,6 @@
#include "cgroup-setup.h"
#include "constants.h"
#include "cpu-set-util.h"
-#include "dev-setup.h"
#include "env-file.h"
#include "env-util.h"
#include "errno-list.h"
@@ -99,56 +98,65 @@ const char* exec_context_tty_path(const ExecContext *context) {
return "/dev/console";
}
-static void exec_context_determine_tty_size(
+int exec_context_apply_tty_size(
const ExecContext *context,
- const char *tty_path,
- unsigned *ret_rows,
- unsigned *ret_cols) {
+ int input_fd,
+ int output_fd,
+ const char *tty_path) {
unsigned rows, cols;
+ int r;
assert(context);
- assert(ret_rows);
- assert(ret_cols);
+ assert(input_fd >= 0);
+ assert(output_fd >= 0);
+
+ if (!isatty_safe(output_fd))
+ return 0;
if (!tty_path)
tty_path = exec_context_tty_path(context);
+ /* Preferably use explicitly configured data */
rows = context->tty_rows;
cols = context->tty_cols;
+ /* Fill in data from kernel command line if anything is unspecified */
if (tty_path && (rows == UINT_MAX || cols == UINT_MAX))
(void) proc_cmdline_tty_size(
tty_path,
rows == UINT_MAX ? &rows : NULL,
cols == UINT_MAX ? &cols : NULL);
- *ret_rows = rows;
- *ret_cols = cols;
+ /* If we got nothing so far and we are talking to a physical device, and the TTY reset logic is on,
+ * then let's query dimensions from the ANSI driver. */
+ if (rows == UINT_MAX && cols == UINT_MAX &&
+ context->tty_reset &&
+ terminal_is_pty_fd(output_fd) == 0 &&
+ isatty_safe(input_fd)) {
+ r = terminal_get_size_by_dsr(input_fd, output_fd, &rows, &cols);
+ if (r < 0)
+ log_debug_errno(r, "Failed to get terminal size by DSR, ignoring: %m");
+ }
+
+ return terminal_set_size_fd(output_fd, tty_path, rows, cols);
}
-int exec_context_apply_tty_size(
- const ExecContext *context,
- int tty_fd,
- const char *tty_path) {
-
- unsigned rows, cols;
-
- exec_context_determine_tty_size(context, tty_path, &rows, &cols);
-
- return terminal_set_size_fd(tty_fd, tty_path, rows, cols);
- }
-
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p) {
_cleanup_close_ int _fd = -EBADF, lock_fd = -EBADF;
- int fd;
+ int fd, r;
assert(context);
+ /* Note that this is potentially a "destructive" reset of a TTY device. It's about getting rid of the
+ * remains of previous uses of the TTY. It's *not* about getting things set up for coming uses. We'll
+ * potentially invalidate the TTY here through hangups or VT disallocations, and hence do not keep a
+ * continous fd open. */
+
const char *path = exec_context_tty_path(context);
- if (p && p->stdin_fd >= 0 && isatty_safe(p->stdin_fd))
- fd = p->stdin_fd;
+ if (p && p->stdout_fd >= 0 && isatty_safe(p->stdout_fd))
+ fd = p->stdout_fd;
else if (path && (context->tty_path || is_terminal_input(context->std_input) ||
is_terminal_output(context->std_output) || is_terminal_output(context->std_error))) {
fd = _fd = open_terminal(path, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
@@ -168,13 +176,19 @@ void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p)
else if (lock_fd < 0)
log_warning_errno(lock_fd, "Failed to lock /dev/console, proceeding without lock: %m");
+ if (context->tty_reset)
+ (void) terminal_reset_defensive(fd, /* switch_to_text= */ true);
+
+ r = exec_context_apply_tty_size(context, fd, fd, path);
+ if (r < 0)
+ log_debug_errno(r, "Failed to configure TTY dimensions, ignoring: %m");
+
if (context->tty_vhangup)
(void) terminal_vhangup_fd(fd);
- if (context->tty_reset)
- (void) reset_terminal_fd(fd, /* switch_to_text= */ true);
-
- (void) exec_context_apply_tty_size(context, fd, path);
+ /* We don't need the fd anymore now, and it potentially points to a hungup TTY anyway, let's close it
+ * hence. */
+ _fd = safe_close(_fd);
if (context->tty_vt_disallocate && path)
(void) vt_disallocate(path);
diff --git a/src/core/execute.h b/src/core/execute.h
index 0414fbad22..b801bfe853 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -526,7 +526,7 @@ int exec_context_get_clean_directories(ExecContext *c, char **prefix, ExecCleanM
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret);
const char* exec_context_tty_path(const ExecContext *context);
-int exec_context_apply_tty_size(const ExecContext *context, int tty_fd, const char *tty_path);
+int exec_context_apply_tty_size(const ExecContext *context, int input_fd, int output_fd, const char *tty_path);
void exec_context_tty_reset(const ExecContext *context, const ExecParameters *p);
uint64_t exec_context_get_rlimit(const ExecContext *c, const char *name);
diff --git a/src/core/job.c b/src/core/job.c
index a877f9f06f..21083497c0 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -6,6 +6,7 @@
#include "sd-messages.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "async.h"
#include "cgroup.h"
#include "dbus-job.h"
diff --git a/src/core/main.c b/src/core/main.c
index 1b78cd652f..e5c2dc914e 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -204,33 +204,58 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) {
return 0;
}
-static int console_setup(void) {
- _cleanup_close_ int tty_fd = -EBADF;
- unsigned rows, cols;
+static int save_console_winsize_in_environment(int tty_fd) {
int r;
- tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ assert(tty_fd >= 0);
+
+ struct winsize ws = {};
+ if (ioctl(tty_fd, TIOCGWINSZ, &ws) < 0) {
+ log_debug_errno(errno, "Failed to acquire console window size, ignoring.");
+ goto unset;
+ }
+
+ if (ws.ws_col <= 0 && ws.ws_row <= 0) {
+ log_debug("No console window size set, ignoring.");
+ goto unset;
+ }
+
+ r = setenvf("COLUMNS", /* overwrite= */ true, "%u", ws.ws_col);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to set $COLUMNS, ignoring: %m");
+ goto unset;
+ }
+
+ r = setenvf("LINES", /* overwrite= */ true, "%u", ws.ws_row);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to set $LINES, ignoring: %m");
+ goto unset;
+ }
+
+ log_debug("Recorded console dimensions in environment: $COLUMNS=%u $LINES=%u.", ws.ws_col, ws.ws_row);
+ return 1;
+
+unset:
+ (void) unsetenv("COLUMNS");
+ (void) unsetenv("LINES");
+ return 0;
+}
+
+static int console_setup(void) {
+
+ if (getpid_cached() != 1)
+ return 0;
+
+ _cleanup_close_ int tty_fd = -EBADF;
+
+ tty_fd = open_terminal("/dev/console", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (tty_fd < 0)
return log_error_errno(tty_fd, "Failed to open /dev/console: %m");
- /* We don't want to force text mode. plymouth may be showing
- * pictures already from initrd. */
- r = reset_terminal_fd(tty_fd, false);
- if (r < 0)
- return log_error_errno(r, "Failed to reset /dev/console: %m");
+ /* We don't want to force text mode. Plymouth may be showing pictures already from initrd. */
+ reset_dev_console_fd(tty_fd, /* switch_to_text= */ false);
- r = proc_cmdline_tty_size("/dev/console", &rows, &cols);
- if (r < 0)
- log_warning_errno(r, "Failed to get /dev/console size, ignoring: %m");
- else {
- r = terminal_set_size_fd(tty_fd, NULL, rows, cols);
- if (r < 0)
- log_warning_errno(r, "Failed to set /dev/console size, ignoring: %m");
- }
-
- r = terminal_reset_ansi_seq(tty_fd);
- if (r < 0)
- log_warning_errno(r, "Failed to reset /dev/console using ANSI sequences, ignoring: %m");
+ save_console_winsize_in_environment(tty_fd);
return 0;
}
@@ -2910,7 +2935,7 @@ static void setup_console_terminal(bool skip_setup) {
(void) release_terminal();
/* Reset the console, but only if this is really init and we are freshly booted */
- if (getpid_cached() == 1 && !skip_setup)
+ if (!skip_setup)
(void) console_setup();
}
diff --git a/src/core/show-status.c b/src/core/show-status.c
index 57ad4db30b..7c7c10f963 100644
--- a/src/core/show-status.c
+++ b/src/core/show-status.c
@@ -72,8 +72,14 @@ int status_vprintf(const char *status, ShowStatusFlags flags, const char *format
int c;
c = fd_columns(fd);
- if (c <= 0)
- c = 80;
+ if (c <= 0) {
+ const char *env = getenv("COLUMNS");
+ if (env)
+ (void) safe_atoi(env, &c);
+
+ if (c <= 0)
+ c = 80;
+ }
sl = status ? strlen(status_indent) : 0;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index ab6e699d33..9d48768d87 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -4,6 +4,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "dbus-unit.h"
diff --git a/src/core/unit.c b/src/core/unit.c
index c281a29391..2d750b7ef4 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -10,6 +10,7 @@
#include "all-units.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "bpf-firewall.h"
#include "bpf-foreign.h"
#include "bpf-socket-bind.h"
diff --git a/src/cryptenroll/cryptenroll-recovery.c b/src/cryptenroll/cryptenroll-recovery.c
index c4bf1fab95..0b1e380e06 100644
--- a/src/cryptenroll/cryptenroll-recovery.c
+++ b/src/cryptenroll/cryptenroll-recovery.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "cryptenroll-recovery.h"
#include "glyph-util.h"
#include "json-util.h"
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
index 92b0289859..c70bfa468f 100644
--- a/src/firstboot/firstboot.c
+++ b/src/firstboot/firstboot.c
@@ -133,7 +133,7 @@ static void print_welcome(int rfd) {
pn = os_release_pretty_name(pretty_name, os_name);
ac = isempty(ansi_color) ? "0" : ansi_color;
- (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false);
+ (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* switch_to_text= */ false);
if (colors_enabled())
printf("\nWelcome to your new installation of \x1B[%sm%s\x1B[0m!\n", ac, pn);
diff --git a/src/home/homectl-recovery-key.c b/src/home/homectl-recovery-key.c
index 015d265435..b5c57e14a8 100644
--- a/src/home/homectl-recovery-key.c
+++ b/src/home/homectl-recovery-key.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "errno-util.h"
#include "glyph-util.h"
#include "homectl-recovery-key.h"
diff --git a/src/home/homectl.c b/src/home/homectl.c
index 65431db8ee..a71548439e 100644
--- a/src/home/homectl.c
+++ b/src/home/homectl.c
@@ -2438,7 +2438,7 @@ static int create_interactively(void) {
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
- (void) reset_terminal_fd(STDIN_FILENO, /* switch_to_text= */ false);
+ (void) terminal_reset_defensive_locked(STDOUT_FILENO, /* switch_to_text= */ false);
for (;;) {
username = mfree(username);
diff --git a/src/import/export.c b/src/import/export.c
index cdb1d6246a..1e82e84d7d 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -7,6 +7,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "build.h"
#include "discover-image.h"
#include "export-raw.h"
diff --git a/src/import/import-fs.c b/src/import/import-fs.c
index 44fc5be8a5..e74d36288a 100644
--- a/src/import/import-fs.c
+++ b/src/import/import-fs.c
@@ -4,6 +4,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "build.h"
#include "btrfs-util.h"
#include "discover-image.h"
diff --git a/src/import/import.c b/src/import/import.c
index 889cd63ff0..97db0b836a 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -7,6 +7,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "build.h"
#include "discover-image.h"
#include "env-util.h"
diff --git a/src/import/pull.c b/src/import/pull.c
index 7c838a5307..46055ce5e7 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -7,6 +7,7 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "build.h"
#include "discover-image.h"
#include "env-util.h"
diff --git a/src/journal/bsod.c b/src/journal/bsod.c
index b2889f02ed..2116c498a4 100644
--- a/src/journal/bsod.c
+++ b/src/journal/bsod.c
@@ -185,7 +185,7 @@ static int display_emergency_message_fullscreen(const char *message) {
if (r < 0)
log_warning_errno(r, "Failed to clear terminal, ignoring: %m");
- r = set_terminal_cursor_position(fd, 2, 4);
+ r = terminal_set_cursor_position(fd, 2, 4);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
@@ -197,7 +197,7 @@ static int display_emergency_message_fullscreen(const char *message) {
qr_code_start_row = w.ws_row * 3U / 5U;
qr_code_start_column = w.ws_col * 3U / 4U;
- r = set_terminal_cursor_position(fd, 4, 4);
+ r = terminal_set_cursor_position(fd, 4, 4);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
@@ -217,7 +217,7 @@ static int display_emergency_message_fullscreen(const char *message) {
if (r < 0)
log_warning_errno(r, "QR code could not be printed, ignoring: %m");
- r = set_terminal_cursor_position(fd, w.ws_row - 1, w.ws_col * 2U / 5U);
+ r = terminal_set_cursor_position(fd, w.ws_row - 1, w.ws_col * 2U / 5U);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
diff --git a/src/journal/journalctl-authenticate.c b/src/journal/journalctl-authenticate.c
index 10630f5e6c..8167aef7f5 100644
--- a/src/journal/journalctl-authenticate.c
+++ b/src/journal/journalctl-authenticate.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "chattr-util.h"
#include "errno-util.h"
#include "fd-util.h"
diff --git a/src/journal/journalctl-show.c b/src/journal/journalctl-show.c
index e8ffc72bb8..9f07facad4 100644
--- a/src/journal/journalctl-show.c
+++ b/src/journal/journalctl-show.c
@@ -4,6 +4,7 @@
#include "sd-event.h"
+#include "ansi-color.h"
#include "fileio.h"
#include "journalctl.h"
#include "journalctl-filter.h"
diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c
index aa46fec91b..5fc3614afb 100644
--- a/src/libsystemd/sd-bus/bus-dump.c
+++ b/src/libsystemd/sd-bus/bus-dump.c
@@ -3,6 +3,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "bus-dump.h"
#include "bus-internal.h"
#include "bus-message.h"
diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c
index e852591a89..0fb93340a6 100644
--- a/src/libsystemd/sd-journal/journal-verify.c
+++ b/src/libsystemd/sd-journal/journal-verify.c
@@ -6,6 +6,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "compress.h"
#include "fd-util.h"
#include "fileio.h"
diff --git a/src/libsystemd/sd-journal/test-journal-verify.c b/src/libsystemd/sd-journal/test-journal-verify.c
index 396ebe192c..dde40bf377 100644
--- a/src/libsystemd/sd-journal/test-journal-verify.c
+++ b/src/libsystemd/sd-journal/test-journal-verify.c
@@ -4,6 +4,7 @@
#include
#include
+#include "ansi-color.h"
#include "chattr-util.h"
#include "fd-util.h"
#include "iovec-util.h"
diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c
index 2219bf26cc..98ffe4db54 100644
--- a/src/libsystemd/sd-json/sd-json.c
+++ b/src/libsystemd/sd-json/sd-json.c
@@ -10,6 +10,7 @@
#include "sd-messages.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "errno-util.h"
#include "escape.h"
#include "ether-addr-util.h"
diff --git a/src/libsystemd/sd-varlink/sd-varlink-idl.c b/src/libsystemd/sd-varlink/sd-varlink-idl.c
index 181e6c193d..76a2499561 100644
--- a/src/libsystemd/sd-varlink/sd-varlink-idl.c
+++ b/src/libsystemd/sd-varlink/sd-varlink-idl.c
@@ -2,6 +2,7 @@
#include "sd-varlink-idl.h"
+#include "ansi-color.h"
#include "json-util.h"
#include "memstream-util.h"
#include "set.h"
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index bf79dc2633..778b31274d 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -19,6 +19,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "ask-password-api.h"
#include "creds-util.h"
#include "fd-util.h"
diff --git a/src/shared/blockdev-list.c b/src/shared/blockdev-list.c
index 2e6ed362f2..120f7201df 100644
--- a/src/shared/blockdev-list.c
+++ b/src/shared/blockdev-list.c
@@ -2,6 +2,7 @@
#include "sd-device.h"
+#include "ansi-color.h"
#include "blockdev-list.h"
#include "blockdev-util.h"
#include "device-util.h"
diff --git a/src/shared/bus-unit-procs.c b/src/shared/bus-unit-procs.c
index 8b462b5627..4d6f2be3c4 100644
--- a/src/shared/bus-unit-procs.c
+++ b/src/shared/bus-unit-procs.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "bus-locator.h"
#include "bus-unit-procs.h"
#include "glyph-util.h"
diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c
index 87177316da..33fe222730 100644
--- a/src/shared/cgroup-show.c
+++ b/src/shared/cgroup-show.c
@@ -7,6 +7,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "bus-error.h"
#include "bus-util.h"
#include "cgroup-show.h"
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index 4b4b62565c..eb72793c04 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -18,23 +18,6 @@
#include "umask-util.h"
#include "user-util.h"
-int lock_dev_console(void) {
- _cleanup_close_ int fd = -EBADF;
- int r;
-
- /* NB: We do not use O_NOFOLLOW here, because some container managers might place a symlink to some
- * pty in /dev/console, in which case it should be fine to lock the target TTY. */
- fd = open_terminal("/dev/console", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (fd < 0)
- return fd;
-
- r = lock_generic(fd, LOCK_BSD, LOCK_EX);
- if (r < 0)
- return r;
-
- return TAKE_FD(fd);
-}
-
int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
static const char symlinks[] =
"-/proc/kcore\0" "/dev/core\0"
diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h
index 5339bc4e5e..92ba6cf764 100644
--- a/src/shared/dev-setup.h
+++ b/src/shared/dev-setup.h
@@ -3,8 +3,6 @@
#include
-int lock_dev_console(void);
-
int dev_setup(const char *prefix, uid_t uid, gid_t gid);
int make_inaccessible_nodes(const char *parent_dir, uid_t uid, gid_t gid);
diff --git a/src/shared/pretty-print.h b/src/shared/pretty-print.h
index 9eb1611905..2c97c354ef 100644
--- a/src/shared/pretty-print.h
+++ b/src/shared/pretty-print.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include "ansi-color.h"
#include "glyph-util.h"
#include "terminal-util.h"
diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c
index 83eef7f7d8..3e11ba456f 100644
--- a/src/shared/ptyfwd.c
+++ b/src/shared/ptyfwd.c
@@ -17,6 +17,7 @@
#include "sd-event.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "env-util.h"
#include "errno-util.h"
#include "extract-word.h"
diff --git a/src/shared/qrcode-util.c b/src/shared/qrcode-util.c
index a63e6cac73..36cd5dcd7c 100644
--- a/src/shared/qrcode-util.c
+++ b/src/shared/qrcode-util.c
@@ -5,6 +5,7 @@
#if HAVE_QRENCODE
#include
+#include "ansi-color.h"
#include "dlfcn-util.h"
#include "locale-util.h"
#include "log.h"
@@ -52,7 +53,7 @@ static void print_border(FILE *output, unsigned width, unsigned row, unsigned co
if (fd < 0)
return (void)log_debug_errno(errno, "Failed to get file descriptor from the file stream: %m");
- r = set_terminal_cursor_position(fd, row, column);
+ r = terminal_set_cursor_position(fd, row, column);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
@@ -64,7 +65,7 @@ static void print_border(FILE *output, unsigned width, unsigned row, unsigned co
fputs(UNICODE_FULL_BLOCK, output);
fputs(ANSI_NORMAL "\n", output);
- r = set_terminal_cursor_position(fd, row + 1, column);
+ r = terminal_set_cursor_position(fd, row + 1, column);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
}
@@ -96,7 +97,7 @@ static void write_qrcode(FILE *output, QRcode *qr, unsigned int row, unsigned in
if (fd < 0)
return (void)log_debug_errno(errno, "Failed to get file descriptor from the file stream: %m");
- r = set_terminal_cursor_position(fd, row + move_down, column);
+ r = terminal_set_cursor_position(fd, row + move_down, column);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
@@ -127,7 +128,7 @@ static void write_qrcode(FILE *output, QRcode *qr, unsigned int row, unsigned in
for (unsigned x = 0; x < 4; x++)
fputs(UNICODE_FULL_BLOCK, output);
- r = set_terminal_cursor_position(fd, row + move_down, column);
+ r = terminal_set_cursor_position(fd, row + move_down, column);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
move_down += 1;
@@ -206,7 +207,7 @@ int print_qrcode_full(FILE *out, const char *header, const char *string, unsigne
row = tty_height - (qr_code_height / 2 ) - 1;
if (header) {
- r = set_terminal_cursor_position(fd, row - 2, tty_width - qr_code_width - 2);
+ r = terminal_set_cursor_position(fd, row - 2, tty_width - qr_code_width - 2);
if (r < 0)
log_warning_errno(r, "Failed to move terminal cursor position, ignoring: %m");
diff --git a/src/systemctl/systemctl-list-dependencies.c b/src/systemctl/systemctl-list-dependencies.c
index a9121f1035..3df9b7abdf 100644
--- a/src/systemctl/systemctl-list-dependencies.c
+++ b/src/systemctl/systemctl-list-dependencies.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "locale-util.h"
#include "sort-util.h"
#include "special.h"
diff --git a/src/systemctl/systemctl-list-jobs.c b/src/systemctl/systemctl-list-jobs.c
index fcfe2ac561..2fecf61317 100644
--- a/src/systemctl/systemctl-list-jobs.c
+++ b/src/systemctl/systemctl-list-jobs.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "bus-error.h"
#include "bus-locator.h"
#include "locale-util.h"
diff --git a/src/systemctl/systemctl-list-machines.c b/src/systemctl/systemctl-list-machines.c
index 4407d2598a..1fffceb38e 100644
--- a/src/systemctl/systemctl-list-machines.c
+++ b/src/systemctl/systemctl-list-machines.c
@@ -4,6 +4,7 @@
#include "sd-login.h"
+#include "ansi-color.h"
#include "bus-map-properties.h"
#include "hostname-util.h"
#include "locale-util.h"
diff --git a/src/systemctl/systemctl-list-unit-files.c b/src/systemctl/systemctl-list-unit-files.c
index 943d7ffec5..975b3ebc54 100644
--- a/src/systemctl/systemctl-list-unit-files.c
+++ b/src/systemctl/systemctl-list-unit-files.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "bus-error.h"
#include "bus-locator.h"
#include "sort-util.h"
diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c
index 184468ed61..b4ccc8ebd7 100644
--- a/src/systemctl/systemctl-list-units.c
+++ b/src/systemctl/systemctl-list-units.c
@@ -2,6 +2,7 @@
#include "sd-login.h"
+#include "ansi-color.h"
#include "bus-error.h"
#include "bus-locator.h"
#include "format-table.h"
diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c
index 8068d77d1c..909f051cde 100644
--- a/src/systemctl/systemctl-start-unit.c
+++ b/src/systemctl/systemctl-start-unit.c
@@ -2,6 +2,7 @@
#include "sd-bus.h"
+#include "ansi-color.h"
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-locator.h"
diff --git a/src/sysupdate/sysupdate-update-set-flags.c b/src/sysupdate/sysupdate-update-set-flags.c
index 66b3d340bb..d9232e4a17 100644
--- a/src/sysupdate/sysupdate-update-set-flags.c
+++ b/src/sysupdate/sysupdate-update-set-flags.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "glyph-util.h"
#include "sysupdate-update-set-flags.h"
#include "terminal-util.h"
diff --git a/src/sysupdate/sysupdate-update-set.c b/src/sysupdate/sysupdate-update-set.c
index 53bcc5d965..0756dff04b 100644
--- a/src/sysupdate/sysupdate-update-set.c
+++ b/src/sysupdate/sysupdate-update-set.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "ansi-color.h"
#include "alloc-util.h"
#include "string-util.h"
#include "sysupdate-update-set.h"
diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c
index f1814768fd..d70b727cee 100644
--- a/src/test/test-ellipsize.c
+++ b/src/test/test-ellipsize.c
@@ -3,6 +3,7 @@
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "constants.h"
#include "escape.h"
#include "string-util.h"
diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c
index 9f8ca15a65..68b9cbe606 100644
--- a/src/test/test-terminal-util.c
+++ b/src/test/test-terminal-util.c
@@ -3,10 +3,13 @@
#include
#include
#include
+#include
#include
+#include
#include
#include "alloc-util.h"
+#include "ansi-color.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
@@ -176,6 +179,77 @@ TEST(get_default_background_color) {
log_notice("R=%g G=%g B=%g", red, green, blue);
}
+TEST(terminal_get_size_by_dsr) {
+ unsigned rows, columns;
+ int r;
+
+ r = terminal_get_size_by_dsr(STDIN_FILENO, STDOUT_FILENO, &rows, &columns);
+ 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);
+
+ 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;
+
+ r = terminal_fix_size(STDIN_FILENO, STDOUT_FILENO);
+ if (r < 0)
+ log_warning_errno(r, "Failed to fix terminal size: %m");
+ else if (r == 0)
+ log_notice("Not fixing terminal size, nothing to do.");
+ else
+ log_notice("Fixed terminal size.");
+}
+
+TEST(terminal_is_pty_fd) {
+ _cleanup_close_ int fd1 = -EBADF, fd2 = -EBADF;
+ _cleanup_free_ char *peer = NULL;
+ int r;
+
+ fd1 = openpt_allocate(O_RDWR, &peer);
+ assert_se(fd1 >= 0);
+ assert_se(terminal_is_pty_fd(fd1) > 0);
+
+ fd2 = open_terminal(peer, O_RDWR|O_CLOEXEC|O_NOCTTY);
+ assert_se(fd2 >= 0);
+ assert_se(terminal_is_pty_fd(fd2) > 0);
+
+ fd1 = safe_close(fd1);
+ fd2 = safe_close(fd2);
+
+ fd1 = open("/dev/null", O_RDONLY|O_CLOEXEC);
+ assert_se(fd1 >= 0);
+ assert_se(terminal_is_pty_fd(fd1) == 0);
+
+ /* In container managers real tty devices might be weird, avoid them. */
+ r = path_is_read_only_fs("/sys");
+ if (r != 0)
+ return;
+
+ FOREACH_STRING(p, "/dev/ttyS0", "/dev/tty1") {
+ _cleanup_close_ int tfd = -EBADF;
+
+ tfd = open_terminal(p, O_CLOEXEC|O_NOCTTY|O_RDONLY|O_NONBLOCK);
+ if (tfd == -ENOENT)
+ continue;
+ if (tfd < 0) {
+ log_notice_errno(tfd, "Failed to open '%s', skipping: %m", p);
+ continue;
+ }
+
+ assert_se(terminal_is_pty_fd(tfd) <= 0);
+ }
+}
+
static void test_get_color_mode_with_env(const char *key, const char *val, ColorMode expected) {
ASSERT_OK(setenv(key, val, true));
reset_terminal_feature_caches();
@@ -209,4 +283,12 @@ TEST(get_color_mode) {
reset_terminal_feature_caches();
}
+TEST(terminal_reset_defensive) {
+ int r;
+
+ r = terminal_reset_defensive(STDOUT_FILENO, /* switch_to_text= */ false);
+ if (r < 0)
+ log_notice_errno(r, "Failed to reset terminal: %m");
+}
+
DEFINE_TEST_MAIN(LOG_INFO);
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index 84ce5e1940..4b1e848749 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -150,9 +150,7 @@ static int agent_ask_password_tty(
if (tty_fd < 0)
return log_error_errno(tty_fd, "Failed to acquire %s: %m", con);
- r = reset_terminal_fd(tty_fd, true);
- if (r < 0)
- log_warning_errno(r, "Failed to reset terminal, ignoring: %m");
+ (void) terminal_reset_defensive_locked(tty_fd, /* switch_to_text= */ true);
log_info("Starting password query on %s.", con);
}
diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
index 1d105047c9..2cdc3bd305 100644
--- a/src/udev/udevadm-info.c
+++ b/src/udev/udevadm-info.c
@@ -13,6 +13,7 @@
#include "sd-json.h"
#include "alloc-util.h"
+#include "ansi-color.h"
#include "device-enumerator-private.h"
#include "device-private.h"
#include "device-util.h"
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index c8c23e811a..ff4625c3be 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -14,6 +14,7 @@
#include "sd-device.h"
+#include "ansi-color.h"
#include "device-private.h"
#include "device-util.h"
#include "format-util.h"
diff --git a/units/console-getty.service.in b/units/console-getty.service.in
index d64112be5e..2de31dd01a 100644
--- a/units/console-getty.service.in
+++ b/units/console-getty.service.in
@@ -20,9 +20,10 @@ Before=getty.target
ConditionPathExists=/dev/console
[Service]
-# The '-o' option value tells agetty to replace 'login' arguments with an option to preserve environment (-p),
-# followed by '--' for safety, and then the entered username.
-ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear --keep-baud - 115200,38400,9600 $TERM
+# The '-o' option value tells agetty to replace 'login' arguments with an
+# option to preserve environment (-p), followed by '--' for safety, and then
+# the entered username.
+ExecStart=-/sbin/agetty -o '-p -- \\u' --noreset --noclear --keep-baud 115200,57600,38400,9600 - ${TERM}
Type=idle
Restart=always
UtmpIdentifier=cons
diff --git a/units/container-getty@.service.in b/units/container-getty@.service.in
index 8847d735fb..7e277a49bc 100644
--- a/units/container-getty@.service.in
+++ b/units/container-getty@.service.in
@@ -25,9 +25,10 @@ Conflicts=rescue.service
Before=rescue.service
[Service]
-# The '-o' option value tells agetty to replace 'login' arguments with an option to preserve environment (-p),
-# followed by '--' for safety, and then the entered username.
-ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear - $TERM
+# The '-o' option value tells agetty to replace 'login' arguments with an
+# option to preserve environment (-p), followed by '--' for safety, and then
+# the entered username.
+ExecStart=-/sbin/agetty -o '-p -- \\u' --noreset --noclear - ${TERM}
Type=idle
Restart=always
RestartSec=0
diff --git a/units/getty@.service.in b/units/getty@.service.in
index 80b8f3e922..ce8db9764e 100644
--- a/units/getty@.service.in
+++ b/units/getty@.service.in
@@ -34,11 +34,10 @@ Before=rescue.service
ConditionPathExists=/dev/tty0
[Service]
-# the VT is cleared by TTYVTDisallocate
# The '-o' option value tells agetty to replace 'login' arguments with an
# option to preserve environment (-p), followed by '--' for safety, and then
# the entered username.
-ExecStart=-/sbin/agetty -o '-p -- \\u' --noclear - $TERM
+ExecStart=-/sbin/agetty -o '-p -- \\u' --noreset --noclear - ${TERM}
Type=idle
Restart=always
RestartSec=0
diff --git a/units/serial-getty@.service.in b/units/serial-getty@.service.in
index 6bf101eac9..5669b19aff 100644
--- a/units/serial-getty@.service.in
+++ b/units/serial-getty@.service.in
@@ -33,7 +33,7 @@ Before=rescue.service
# The '-o' option value tells agetty to replace 'login' arguments with an
# option to preserve environment (-p), followed by '--' for safety, and then
# the entered username.
-ExecStart=-/sbin/agetty -o '-p -- \\u' --keep-baud 115200,57600,38400,9600 - $TERM
+ExecStart=-/sbin/agetty -o '-p -- \\u' --noreset --noclear --keep-baud 115200,57600,38400,9600 - ${TERM}
Type=idle
Restart=always
UtmpIdentifier=%I