This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit a2f716687dbee0bd5de929301b1859d269f086c1 Author: Marco Casaroli <[email protected]> AuthorDate: Tue Nov 3 13:46:01 2026 +0100 drivers/net/telnet: Support TIOCSWINSZ and send SIGWINCH. A telnet client sends a new window size with the NAWS option. The driver kept the size, but it did not tell the foreground process, so a full screen program did not see the change. The driver also did not support TIOCSWINSZ, its TIOCGWINSZ wrote through a NULL argument, and TIOCGWINSZ did not set ws_xpixel and ws_ypixel. The driver now keeps a struct winsize. A new size from the client or from TIOCSWINSZ sends SIGWINCH to the PID that TIOCSCTTY gave, as for SIGINT and SIGTSTP. The same size again sends no signal. A NAWS size has no pixel values, so the driver sets them to 0. The driver sends SIGWINCH for a NAWS size after telnet_receive() and before it waits for more data. In a flat build the signal handler runs at once and can read from the session. In a protected or kernel build the signal ends a wait of the reader with EINTR, as on other systems. TIOCGWINSZ and TIOCSWINSZ return -EINVAL for a NULL argument, as TCGETS does in the serial driver. Assisted-by: Claude Code:claude-opus-5-5 Signed-off-by: Marco Casaroli <[email protected]> --- drivers/net/telnet.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/drivers/net/telnet.c b/drivers/net/telnet.c index b7dd8226aa6..bd62aa3aa4a 100644 --- a/drivers/net/telnet.c +++ b/drivers/net/telnet.c @@ -127,9 +127,11 @@ struct telnet_dev_s uint16_t td_offset; /* Offset to the valid, pending bytes in the rxbuffer */ uint16_t td_pending; /* Number of valid, pending bytes in the rxbuffer */ #ifdef CONFIG_TELNET_SUPPORT_NAWS + bool td_winch; /* Window size changed, send SIGWINCH */ uint16_t td_rows; /* Number of NAWS rows */ uint16_t td_cols; /* Number of NAWS cols */ int td_sb_count; /* Count of TELNET_SB bytes received */ + struct winsize td_winsize; /* Window size (TIOCGWINSZ, TIOCSWINSZ) */ #endif #ifdef HAVE_SIGNALS pid_t td_pid; @@ -167,6 +169,11 @@ static bool telnet_putchar(FAR struct telnet_dev_s *priv, uint8_t ch, int *nwritten); static void telnet_sendopt(FAR struct telnet_dev_s *priv, uint8_t option, uint8_t value); +#if defined(CONFIG_TELNET_SUPPORT_NAWS) && defined(HAVE_SIGNALS) +static void telnet_winch(FAR struct telnet_dev_s *priv); +#else +# define telnet_winch(priv) +#endif /* Telnet character driver methods */ @@ -569,6 +576,16 @@ static ssize_t telnet_receive(FAR struct telnet_dev_s *priv, case 3: priv->td_rows = (priv->td_rows & 0xff00) | ch; ninfo("NAWS: %d,%d", priv->td_cols, priv->td_rows); + + if (priv->td_winsize.ws_row != priv->td_rows || + priv->td_winsize.ws_col != priv->td_cols) + { + priv->td_winsize.ws_row = priv->td_rows; + priv->td_winsize.ws_col = priv->td_cols; + priv->td_winsize.ws_xpixel = 0; + priv->td_winsize.ws_ypixel = 0; + priv->td_winch = true; + } break; } @@ -683,6 +700,24 @@ static void telnet_sendopt(FAR struct telnet_dev_s *priv, uint8_t option, } } +/**************************************************************************** + * Name: telnet_winch + * + * Description: + * Send SIGWINCH to the foreground process after a window size change. + * + ****************************************************************************/ + +#if defined(CONFIG_TELNET_SUPPORT_NAWS) && defined(HAVE_SIGNALS) +static void telnet_winch(FAR struct telnet_dev_s *priv) +{ + if (priv->td_pid > 0) + { + nxsig_kill(priv->td_pid, SIGWINCH); + } +} +#endif + /**************************************************************************** * Name: telnet_open ****************************************************************************/ @@ -862,6 +897,18 @@ static ssize_t telnet_read(FAR struct file *filep, FAR char *buffer, src = &priv->td_rxbuffer[priv->td_offset]; nread = telnet_receive(priv, src, priv->td_pending, buffer, len); + +#ifdef CONFIG_TELNET_SUPPORT_NAWS + /* Send SIGWINCH after telnet_receive(), before the next wait. In a + * flat build the handler runs at once and can use this session. + */ + + if (priv->td_winch) + { + priv->td_winch = false; + telnet_winch(priv); + } +#endif } while (nread == 0); @@ -1059,6 +1106,8 @@ static int telnet_session(FAR struct telnet_session_s *session) priv->td_rows = 25; priv->td_cols = 80; priv->td_sb_count = 0; + priv->td_winsize.ws_row = 25; + priv->td_winsize.ws_col = 80; #endif /* Clone the internal socket structure. We do this so that it will be @@ -1216,12 +1265,34 @@ static int telnet_ioctl(FAR struct file *filep, int cmd, unsigned long arg) #ifdef CONFIG_TELNET_SUPPORT_NAWS case TIOCGWINSZ: { - FAR struct winsize *pw = (FAR struct winsize *)((uintptr_t)arg); + FAR struct winsize *ws = (FAR struct winsize *)((uintptr_t)arg); + + if (ws == NULL) + { + ret = -EINVAL; + break; + } + + *ws = priv->td_winsize; + } + break; + + case TIOCSWINSZ: + { + FAR const struct winsize *ws = + (FAR const struct winsize *)((uintptr_t)arg); - /* Get row/col from the private data */ + if (ws == NULL) + { + ret = -EINVAL; + break; + } - pw->ws_row = priv->td_rows; - pw->ws_col = priv->td_cols; + if (memcmp(&priv->td_winsize, ws, sizeof(*ws)) != 0) + { + priv->td_winsize = *ws; + telnet_winch(priv); + } } break; #endif
