From 7260887fa989c0141e7265cd851e00b4101410d8 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Wed, 13 Dec 2023 13:42:23 +1100 Subject: libmisc/shell: Work around tmux bug in row and column - Extend the timeout to 150 msec for long remote sessions - Improve the performance of the detection Closes #4975 Closes #4977 --- cpukit/libmisc/shell/shell.c | 150 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 125 insertions(+), 25 deletions(-) diff --git a/cpukit/libmisc/shell/shell.c b/cpukit/libmisc/shell/shell.c index cd83aa56d1..9cefc80255 100644 --- a/cpukit/libmisc/shell/shell.c +++ b/cpukit/libmisc/shell/shell.c @@ -805,6 +805,109 @@ void rtems_shell_print_env( } #endif +/* + * Wait for the string to return or timeout. + */ +static bool rtems_shell_term_wait_for(const int fd, const char* str, const int timeout) +{ + int msec = timeout; + int i = 0; + while (msec-- > 0 && str[i] != '\0') { + char ch[2]; + if (read(fd, &ch[0], 1) == 1) { + fflush(stdout); + if (ch[0] != str[i++]) { + return false; + } + msec = timeout; + } else { + usleep(1000); + } + } + if (msec == 0) { + return false; + } + return true; +} + +/* + * Buffer a string up to the end string + */ +static int rtems_shell_term_buffer_until(const int fd, + char* buf, + const int size, + const char* end, + const int timeout) +{ + int msec = timeout; + int i = 0; + int e = 0; + memset(&buf[0], 0, size); + while (msec-- > 0 && i < size && end[e] != '\0') { + char ch[2]; + if (read(fd, &ch[0], 1) == 1) { + fflush(stdout); + buf[i++] = ch[0]; + if (ch[0] == end[e]) { + e++; + } else { + e = 0; + } + msec = timeout; + } else { + usleep(1000); + } + } + if (msec == 0 || end[e] != '\0') { + return -1; + } + i -= e; + if (i < size) { + buf[i] = '\0'; + } + return i; +} + +/* + * Determine if the terminal has the row and column values + * swapped + * + * https://github.com/tmux/tmux/issues/3457 + * + * Tmux has a bug where the lines and cols are swapped. There is a lag + * in the time it takes to get the fix into code so see if tmux is + * running and which version and work around the bug. + * + * The terminal device needs to have VMIN=0, and VTIME=0 + */ +static bool rtems_shell_term_row_column_swapped(const int fd, const int timeout) { + char buf[64]; + memset(&buf[0], 0, sizeof(buf)); + /* + * CSI > Ps q + * Ps = 0 => DCS > | text ST + */ + fputs("\033[>0q", stdout); + fflush(stdout); + if (rtems_shell_term_wait_for(fd, "\033P>|", timeout)) { + int len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), "\033\\", timeout); + if (len > 0) { + if (memcmp(buf, "tmux ", 5) == 0) { + static const char* bad_versions[] = { + "3.2", "3.2a", "3.3", "3.3a" + }; + size_t i; + for (i = 0; i < RTEMS_ARRAY_SIZE(bad_versions); ++i) { + if (strcmp(bad_versions[i], buf + 5) == 0) { + return true; + } + } + } + } + } + return false; +} + /* * Direct method to get the size of an XTERM window. * @@ -814,6 +917,7 @@ static void rtems_shell_winsize( void ) { const int fd = fileno(stdin); struct winsize ws; + const int timeout = 150; char buf[64]; bool ok = false; int lines = 0; @@ -831,9 +935,6 @@ static void rtems_shell_winsize( void ) term.c_cc[VMIN] = 0; term.c_cc[VTIME] = 0; if (tcsetattr (fd, TCSADRAIN, &term) >= 0) { - int msec = 50; - int len = 0; - int i = 0; memset(&buf[0], 0, sizeof(buf)); /* * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Miscellaneous @@ -842,35 +943,34 @@ static void rtems_shell_winsize( void ) */ fputs("\033[18t", stdout); fflush(stdout); - while (msec-- > 0 && len < sizeof(buf)) { - char ch[2]; - if (read(fd, &ch[0], 1) == 1) { - buf[len++] = ch[0]; - msec = 50; - } else { - usleep(1000); - } - } - while (i < len) { - static const char resp[] = "\033[8;"; - if (memcmp(resp, &buf[i], sizeof(resp) - 1) == 0) { - i += sizeof(resp) - 1; - while (i < len && buf[i] != ';') { + if (rtems_shell_term_wait_for(fd, "\033[8;", timeout)) { + int len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), ";", timeout); + if (len > 0) { + int i; + lines = 0; + i = 0; + while (i < len) { lines *= 10; lines += buf[i++] - '0'; } - cols = 0; - ++i; - while (i < len && buf[i] != 't') { - cols *= 10; - cols += buf[i++] - '0'; + len = rtems_shell_term_buffer_until(fd, buf, sizeof(buf), "t", timeout); + if (len > 0) { + cols = 0; + i = 0; + while (i < len) { + cols *= 10; + cols += buf[i++] - '0'; + } + ok = true; } - } else { - i++; } - ok = true; } } + if (rtems_shell_term_row_column_swapped(fd, timeout)) { + int tmp = lines; + lines = cols; + cols = tmp; + } tcsetattr (fd, TCSADRAIN, &cterm); } } -- cgit v1.2.3