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

Reply via email to