This is an automated email from the git hooks/post-receive script.

git pushed a commit to reference refs/pull/36/head
in repository terminology.

View the commit online.

commit 32a12cbd592d8208d4e0994a845d173a9f2d1a42
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 22 12:47:38 2026 -0600

    feat: extend tymux ATTACHED handshake with backlog buffer
    
    The ATTACHED response now carries the head backlog buffer's memfd
    alongside the screen memfd via SCM_RIGHTS, plus metadata about
    available backlog buffers. The proxy gracefully ignores the backlog
    fd. The client receives it but defers mmap until buffer management
    is wired.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/bin/termd_tymux.c    | 25 ++++++++++++++++--------
 src/bin/termsrv.c        | 49 ++++++++++++++++++++++++++++++++++++------------
 src/bin/termsrv.h        | 22 ++++++++++++++++------
 src/bin/termtymux.c      | 27 +++++++++++++++++++++-----
 src/bin/tymux.c          | 16 +++++++++++++++-
 src/tests/test_termsrv.c | 12 +++++++++---
 6 files changed, 116 insertions(+), 35 deletions(-)

diff --git a/src/bin/termd_tymux.c b/src/bin/termd_tymux.c
index 37cc1b00..13919289 100644
--- a/src/bin/termd_tymux.c
+++ b/src/bin/termd_tymux.c
@@ -633,14 +633,23 @@ termd_tymux_client_add(TermDTymux *sess, int cfd)
    if (cfd < 0)
      return;
 
-   if (termsrv_send_attached(cfd, sess->shm_fd,
-                             (uint32_t)sess->pty->w,
-                             (uint32_t)sess->pty->h) < 0)
-     {
-        ERR("termsrv_send_attached failed for fd %d", cfd);
-        close(cfd);
-        return;
-     }
+   {
+      int head_fd = (sess->backlog_buf_count > 0)
+                    ? sess->backlog_bufs[sess->backlog_buf_count - 1].fd
+                    : -1;
+
+      if (termsrv_send_attached(cfd, sess->shm_fd, head_fd,
+                                (uint32_t)sess->pty->w,
+                                (uint32_t)sess->pty->h,
+                                (uint32_t)sess->backlog_buf_count,
+                                TERMSRV_BACKLOG_MAX_LINES,
+                                (uint32_t)sess->oldest_buf_id) < 0)
+        {
+           ERR("termsrv_send_attached failed for fd %d", cfd);
+           close(cfd);
+           return;
+        }
+   }
 
    client = calloc(1, sizeof(*client));
    if (!client)
diff --git a/src/bin/termsrv.c b/src/bin/termsrv.c
index d0ea6b6c..cd4a824e 100644
--- a/src/bin/termsrv.c
+++ b/src/bin/termsrv.c
@@ -172,41 +172,58 @@ termsrv_msg_recv(int fd, void **payload_out, uint32_t *size_out)
 }
 
 int
-termsrv_send_attached(int sock_fd, int shm_fd,
-                      uint32_t cols, uint32_t rows)
+termsrv_send_attached(int sock_fd, int shm_fd, int backlog_fd,
+                      uint32_t cols, uint32_t rows,
+                      uint32_t backlog_buf_count,
+                      uint32_t backlog_max_lines,
+                      uint32_t oldest_buf_id)
 {
     TermSrvMsgHdr      hdr = { .type = TSRV_MSG_ATTACHED,
                                .size = sizeof(TermSrvMsgAttached) };
-    TermSrvMsgAttached pl  = { .cols = cols, .rows = rows };
+    TermSrvMsgAttached pl  = {
+        .cols              = cols,
+        .rows              = rows,
+        .backlog_buf_count = backlog_buf_count,
+        .backlog_max_lines = backlog_max_lines,
+        .oldest_buf_id     = oldest_buf_id,
+    };
+    int nfds = (backlog_fd >= 0) ? 2 : 1;
 
     struct iovec iov[2] = {
         { &hdr, sizeof(hdr) },
         { &pl,  sizeof(pl)  }
     };
-    char cmsgbuf[CMSG_SPACE(sizeof(int))];
+    /* Control buffer sized for up to 2 fds */
+    char cmsgbuf[CMSG_SPACE(2 * sizeof(int))];
     memset(cmsgbuf, 0, sizeof(cmsgbuf));
     struct msghdr msg = {
         .msg_iov        = iov,
         .msg_iovlen     = 2,
         .msg_control    = cmsgbuf,
-        .msg_controllen = sizeof(cmsgbuf)
+        .msg_controllen = (socklen_t)CMSG_SPACE(nfds * sizeof(int))
     };
     struct cmsghdr *cm = CMSG_FIRSTHDR(&msg);
     cm->cmsg_level = SOL_SOCKET;
     cm->cmsg_type  = SCM_RIGHTS;
-    cm->cmsg_len   = CMSG_LEN(sizeof(int));
+    cm->cmsg_len   = CMSG_LEN((size_t)nfds * sizeof(int));
     memcpy(CMSG_DATA(cm), &shm_fd, sizeof(int));
+    if (nfds == 2)
+      memcpy((char *)CMSG_DATA(cm) + sizeof(int), &backlog_fd, sizeof(int));
 
     return (sendmsg(sock_fd, &msg, MSG_NOSIGNAL) < 0) ? -1 : 0;
 }
 
 int
-termsrv_recv_attached(int sock_fd, int *shm_fd_out,
-                      uint32_t *cols_out, uint32_t *rows_out)
+termsrv_recv_attached(int sock_fd, int *shm_fd_out, int *backlog_fd_out,
+                      uint32_t *cols_out, uint32_t *rows_out,
+                      uint32_t *backlog_buf_count_out,
+                      uint32_t *backlog_max_lines_out,
+                      uint32_t *oldest_buf_id_out)
 {
     TermSrvMsgHdr      hdr;
     TermSrvMsgAttached pl;
-    char cmsgbuf[CMSG_SPACE(sizeof(int))];
+    /* Control buffer sized for up to 2 fds */
+    char cmsgbuf[CMSG_SPACE(2 * sizeof(int))];
     memset(cmsgbuf, 0, sizeof(cmsgbuf));
     struct iovec iov[2] = {
         { &hdr, sizeof(hdr) },
@@ -219,7 +236,8 @@ termsrv_recv_attached(int sock_fd, int *shm_fd_out,
         .msg_controllen = sizeof(cmsgbuf)
     };
 
-    *shm_fd_out = -1;
+    *shm_fd_out     = -1;
+    *backlog_fd_out = -1;
     ssize_t n = recvmsg(sock_fd, &msg, MSG_WAITALL);
     if (n < (ssize_t)(sizeof(hdr) + sizeof(pl))) return -1;
     if (msg.msg_flags & MSG_CTRUNC) return -1;
@@ -231,8 +249,15 @@ termsrv_recv_attached(int sock_fd, int *shm_fd_out,
         cm->cmsg_len < CMSG_LEN(sizeof(int))) return -1;
     memcpy(shm_fd_out, CMSG_DATA(cm), sizeof(int));
 
-    *cols_out = pl.cols;
-    *rows_out = pl.rows;
+    /* Check whether a second fd (backlog) was also sent */
+    if (cm->cmsg_len >= CMSG_LEN(2 * sizeof(int)))
+      memcpy(backlog_fd_out, (char *)CMSG_DATA(cm) + sizeof(int), sizeof(int));
+
+    *cols_out              = pl.cols;
+    *rows_out              = pl.rows;
+    *backlog_buf_count_out = pl.backlog_buf_count;
+    *backlog_max_lines_out = pl.backlog_max_lines;
+    *oldest_buf_id_out     = pl.oldest_buf_id;
     return 0;
 }
 
diff --git a/src/bin/termsrv.h b/src/bin/termsrv.h
index 761ebc3e..305009a9 100644
--- a/src/bin/termsrv.h
+++ b/src/bin/termsrv.h
@@ -188,13 +188,23 @@ int termsrv_msg_send(int fd, TermSrvMsgType type,
  * *payload_out is NULL and *size_out is 0 when there is no payload. */
 int termsrv_msg_recv(int fd, void **payload_out, uint32_t *size_out);
 
-/* Send ATTACHED with memfd via SCM_RIGHTS. Returns 0/-1. */
-int termsrv_send_attached(int sock_fd, int shm_fd,
-                          uint32_t cols, uint32_t rows);
+/* Send ATTACHED with shm memfd (and optional backlog memfd) via SCM_RIGHTS.
+ * Pass backlog_fd >= 0 to include the head backlog buffer fd.
+ * Returns 0/-1. */
+int termsrv_send_attached(int sock_fd, int shm_fd, int backlog_fd,
+                          uint32_t cols, uint32_t rows,
+                          uint32_t backlog_buf_count,
+                          uint32_t backlog_max_lines,
+                          uint32_t oldest_buf_id);
 
-/* Receive ATTACHED and extract the shm fd. Returns 0/-1. */
-int termsrv_recv_attached(int sock_fd, int *shm_fd_out,
-                          uint32_t *cols_out, uint32_t *rows_out);
+/* Receive ATTACHED and extract the shm fd (and optional backlog fd).
+ * *backlog_fd_out is set to -1 if no backlog fd was sent.
+ * Returns 0/-1. */
+int termsrv_recv_attached(int sock_fd, int *shm_fd_out, int *backlog_fd_out,
+                          uint32_t *cols_out, uint32_t *rows_out,
+                          uint32_t *backlog_buf_count_out,
+                          uint32_t *backlog_max_lines_out,
+                          uint32_t *oldest_buf_id_out);
 
 #endif /* HAVE_TYMUX */
 #endif /* TERMINOLOGY_TERMSRV_H_ */
diff --git a/src/bin/termtymux.c b/src/bin/termtymux.c
index 037e84ab..5a8bbc25 100644
--- a/src/bin/termtymux.c
+++ b/src/bin/termtymux.c
@@ -395,9 +395,13 @@ termtymux_attach(const char *tymux_name,
 {
    char              sock_path[512];
    struct sockaddr_un addr;
-   int               sock_fd = -1;
-   int               shm_fd  = -1;
+   int               sock_fd             = -1;
+   int               shm_fd              = -1;
+   int               backlog_fd          = -1;
    uint32_t          srv_cols, srv_rows;
+   uint32_t          backlog_buf_count   = 0;
+   uint32_t          backlog_max_lines   = 0;
+   uint32_t          oldest_buf_id       = 0;
    TermShmHeader    *shm  = MAP_FAILED;
    TermTymux      *ts   = NULL;
    Termpty          *ty   = NULL;
@@ -455,14 +459,25 @@ termtymux_attach(const char *tymux_name,
         goto fail;
      }
 
-   /* Receive ATTACHED + memfd */
-   if (termsrv_recv_attached(sock_fd, &shm_fd,
-                             &srv_cols, &srv_rows) < 0)
+   /* Receive ATTACHED + shm memfd (and optional backlog memfd) */
+   if (termsrv_recv_attached(sock_fd, &shm_fd, &backlog_fd,
+                             &srv_cols, &srv_rows,
+                             &backlog_buf_count,
+                             &backlog_max_lines,
+                             &oldest_buf_id) < 0)
      {
         ERR("termtymux: recv ATTACHED failed");
         goto fail;
      }
 
+   /* Backlog fd: close it for now — client buffer management (Task 8)
+    * will mmap it once that infrastructure is in place. */
+   if (backlog_fd >= 0)
+     {
+        close(backlog_fd);
+        backlog_fd = -1;
+     }
+
    /* Map shared memory read-only */
    shm = mmap(NULL, TERMSRV_SHM_TOTAL_SIZE,
               PROT_READ, MAP_SHARED, shm_fd, 0);
@@ -550,6 +565,8 @@ fail:
      munmap(shm, TERMSRV_SHM_TOTAL_SIZE);
    if (shm_fd >= 0)
      close(shm_fd);
+   if (backlog_fd >= 0)
+     close(backlog_fd);
    if (sock_fd >= 0)
      close(sock_fd);
    return NULL;
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index a5d3a020..5b27a539 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -110,6 +110,9 @@ typedef struct {
 typedef struct {
    uint32_t cols;
    uint32_t rows;
+   uint32_t backlog_buf_count;
+   uint32_t backlog_max_lines;
+   uint32_t oldest_buf_id;
 } _TsrvMsgAttached;
 
 typedef struct {
@@ -297,7 +300,8 @@ _proto_recv_attached(int sock_fd, int *shm_fd_out,
 {
    _TsrvMsgHdr      hdr;
    _TsrvMsgAttached pl;
-   char cmsgbuf[CMSG_SPACE(sizeof(int))];
+   /* Control buffer sized for up to 2 fds */
+   char cmsgbuf[CMSG_SPACE(2 * sizeof(int))];
    memset(cmsgbuf, 0, sizeof(cmsgbuf));
    struct iovec iov[2] = {
       { &hdr, sizeof(hdr) },
@@ -322,6 +326,16 @@ _proto_recv_attached(int sock_fd, int *shm_fd_out,
        cm->cmsg_len < CMSG_LEN(sizeof(int))) return -1;
    memcpy(shm_fd_out, CMSG_DATA(cm), sizeof(int));
 
+   /* If a second fd (backlog) was sent, close it — the proxy does not
+    * use backlog buffers. */
+   if (cm->cmsg_len >= CMSG_LEN(2 * sizeof(int)))
+     {
+        int backlog_fd = -1;
+        memcpy(&backlog_fd, (char *)CMSG_DATA(cm) + sizeof(int), sizeof(int));
+        if (backlog_fd >= 0)
+          close(backlog_fd);
+     }
+
    *cols_out = pl.cols;
    *rows_out = pl.rows;
    return 0;
diff --git a/src/tests/test_termsrv.c b/src/tests/test_termsrv.c
index 18978980..9d8639e6 100644
--- a/src/tests/test_termsrv.c
+++ b/src/tests/test_termsrv.c
@@ -80,12 +80,18 @@ test_fd_passing(void)
     int pipefd[2];
     assert(pipe(pipefd) == 0);
 
-    assert(termsrv_send_attached(sv[0], pipefd[0], 132, 43) == 0);
+    assert(termsrv_send_attached(sv[0], pipefd[0], -1, 132, 43, 0, 0, 0) == 0);
 
-    int shm_fd = -1;
+    int shm_fd = -1, backlog_fd = -1;
     uint32_t cols = 0, rows = 0;
-    assert(termsrv_recv_attached(sv[1], &shm_fd, &cols, &rows) == 0);
+    uint32_t backlog_buf_count = 0, backlog_max_lines = 0, oldest_buf_id = 0;
+    assert(termsrv_recv_attached(sv[1], &shm_fd, &backlog_fd,
+                                 &cols, &rows,
+                                 &backlog_buf_count,
+                                 &backlog_max_lines,
+                                 &oldest_buf_id) == 0);
     assert(shm_fd >= 0 && shm_fd != pipefd[0]);  /* received a dup */
+    assert(backlog_fd == -1);  /* no backlog fd sent */
     assert(cols == 132 && rows == 43);
 
     /* verify the transferred fd is functional: write to pipefd[1],

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to