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.