basic/terminal-util: use non-blocking writes when sending ANSI sequences in terminal_get_size()

terminal_get_size() writes ANSI escape sequences (CSI 18 and DSR
queries) to the output fd to determine terminal dimensions. This is
called during early boot via reset_dev_console_fd() and from service
execution contexts via exec_context_apply_tty_size().

Previously, these writes used loop_write() on a blocking fd, which
could block indefinitely if the terminal is not consuming data — for
example on a serial console with flow control asserted, or a
disconnected terminal. This is the same problem that was solved for
terminal_reset_ansi_seq() in systemd/systemd#32369 by temporarily
setting the fd to non-blocking mode with a write timeout.

Apply the same pattern here: set the output fd to non-blocking in
terminal_get_size() before issuing the queries, and restore blocking
mode afterward. Change the loop_write() calls in
terminal_query_size_by_dsr() and terminal_query_size_by_csi18() to
loop_write_full() with a 100ms timeout so writes fail gracefully
instead of hanging.

Also introduce the CONSOLE_ANSI_SEQUENCE_TIMEOUT_USEC constant for all
timeouts used across all ANSI sequence writes and reads (vt_disallocate(),
terminal_reset_ansi_seq(), and the two size query functions). 333ms
is now used for all timeouts in terminal-util.c.

Also introduce a cleanup function for resetting a fd back to blocking
mode after it was made non-blocking.
This commit is contained in:
Daan De Meyer
2026-04-02 16:08:56 +02:00
committed by Daan De Meyer
parent e98a26ddc0
commit ba10d789a8
3 changed files with 43 additions and 27 deletions
+2
View File
@@ -112,6 +112,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL);
int fd_nonblock(int fd, bool nonblock);
int stdio_disable_nonblock(void);
void nonblock_resetp(int *fd);
int fd_cloexec(int fd, bool cloexec);
int fd_cloexec_many(const int fds[], size_t n_fds, bool cloexec);