[PATCH v2] libmisc/shell: Work around tmux bug in row and column

Chris Johns chrisj at rtems.org
Wed Dec 13 07:10:03 UTC 2023


On 13/12/2023 5:58 pm, chrisj at rtems.org wrote:
> From: Chris Johns <chrisj at rtems.org>
> 
> - 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..d8853d74a4 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,39 +943,38 @@ 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;
>          }
>        }
>        tcsetattr (fd, TCSADRAIN, &cterm);
>      }
>    }
>    if (ok) {
> +    if (rtems_shell_term_row_column_swapped(fd, timeout)) {

This here is wrong as the call has to be in the isatty() block. I will send v3.

Chris

> +      int tmp = lines;
> +      lines = cols;
> +      cols = tmp;
> +    }
>      snprintf(buf, sizeof(buf) - 1, "%d", lines);
>      setenv("LINES", buf, 1);
>      snprintf(buf, sizeof(buf) - 1, "%d", cols);


More information about the devel mailing list