This is an automated email from the git hooks/post-receive script.
git pushed a commit to branch tymux
in repository terminology.
View the commit online.
commit fa91812d3cdb63510f45d5ec775196a54231b234
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 22 12:51:26 2026 -0600
feat: implement tymux backlog buffer request/response
Add BACKLOG_BUF_REQ/BACKLOG_BUF message handling so clients can lazily
fetch older backlog buffer memfds from the daemon. The daemon validates
the requested buffer index and responds with the memfd via SCM_RIGHTS.
The client temporarily clears O_NONBLOCK for the blocking recv, then
restores the original flags.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
src/bin/termd_tymux.c | 18 +++++++++++++
src/bin/termsrv.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++
src/bin/termsrv.h | 13 +++++++++
src/bin/termtymux.c | 49 ++++++++++++++++++++++++++++++++++
src/bin/termtymux.h | 14 ++++++++++
5 files changed, 167 insertions(+)
diff --git a/src/bin/termd_tymux.c b/src/bin/termd_tymux.c
index 13919289..857d6ebb 100644
--- a/src/bin/termd_tymux.c
+++ b/src/bin/termd_tymux.c
@@ -444,6 +444,24 @@ _cb_client_data(void *data, Ecore_Fd_Handler *fd_handler EINA_UNUSED)
}
break;
+ case TSRV_MSG_BACKLOG_BUF_REQ:
+ if (payload && size >= sizeof(TermSrvMsgBacklogBufReq))
+ {
+ TermSrvMsgBacklogBufReq *req = payload;
+ int idx = (int)req->buf_id - sess->oldest_buf_id;
+ if (idx >= 0 && idx < sess->backlog_buf_count)
+ {
+ TermBacklogBufHeader *h = sess->backlog_bufs[idx].hdr;
+ termsrv_send_backlog_buf(client->fd,
+ sess->backlog_bufs[idx].fd,
+ req->buf_id,
+ h->cols,
+ h->num_lines,
+ h->max_lines);
+ }
+ }
+ break;
+
case TSRV_MSG_DETACH:
free(payload);
_client_remove(sess, client);
diff --git a/src/bin/termsrv.c b/src/bin/termsrv.c
index cd4a824e..60a24cc5 100644
--- a/src/bin/termsrv.c
+++ b/src/bin/termsrv.c
@@ -261,4 +261,77 @@ termsrv_recv_attached(int sock_fd, int *shm_fd_out, int *backlog_fd_out,
return 0;
}
+int
+termsrv_send_backlog_buf(int sock_fd, int buf_fd,
+ uint32_t buf_id, uint32_t cols,
+ uint32_t num_lines, uint32_t max_lines)
+{
+ TermSrvMsgHdr hdr = { .type = TSRV_MSG_BACKLOG_BUF,
+ .size = sizeof(TermSrvMsgBacklogBuf) };
+ TermSrvMsgBacklogBuf pl = {
+ .buf_id = buf_id,
+ .cols = cols,
+ .num_lines = num_lines,
+ .max_lines = max_lines,
+ };
+ struct iovec iov[2] = {
+ { &hdr, sizeof(hdr) },
+ { &pl, sizeof(pl) }
+ };
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ struct msghdr msg = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ .msg_control = cmsgbuf,
+ .msg_controllen = (socklen_t)CMSG_SPACE(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));
+ memcpy(CMSG_DATA(cm), &buf_fd, sizeof(int));
+
+ return (sendmsg(sock_fd, &msg, MSG_NOSIGNAL) < 0) ? -1 : 0;
+}
+
+int
+termsrv_recv_backlog_buf(int sock_fd, int *buf_fd_out,
+ uint32_t *buf_id_out, uint32_t *cols_out,
+ uint32_t *num_lines_out, uint32_t *max_lines_out)
+{
+ TermSrvMsgHdr hdr;
+ TermSrvMsgBacklogBuf pl;
+ char cmsgbuf[CMSG_SPACE(sizeof(int))];
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ struct iovec iov[2] = {
+ { &hdr, sizeof(hdr) },
+ { &pl, sizeof(pl) }
+ };
+ struct msghdr msg = {
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ .msg_control = cmsgbuf,
+ .msg_controllen = sizeof(cmsgbuf)
+ };
+
+ *buf_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;
+ if (hdr.type != TSRV_MSG_BACKLOG_BUF) return -1;
+ if (hdr.size != sizeof(TermSrvMsgBacklogBuf)) return -1;
+
+ struct cmsghdr *cm = CMSG_FIRSTHDR(&msg);
+ if (!cm || cm->cmsg_type != SCM_RIGHTS ||
+ cm->cmsg_len < CMSG_LEN(sizeof(int))) return -1;
+ memcpy(buf_fd_out, CMSG_DATA(cm), sizeof(int));
+
+ *buf_id_out = pl.buf_id;
+ *cols_out = pl.cols;
+ *num_lines_out = pl.num_lines;
+ *max_lines_out = pl.max_lines;
+ return 0;
+}
+
#endif /* HAVE_TYMUX */
diff --git a/src/bin/termsrv.h b/src/bin/termsrv.h
index 305009a9..97921815 100644
--- a/src/bin/termsrv.h
+++ b/src/bin/termsrv.h
@@ -206,5 +206,18 @@ int termsrv_recv_attached(int sock_fd, int *shm_fd_out, int *backlog_fd_out,
uint32_t *backlog_max_lines_out,
uint32_t *oldest_buf_id_out);
+/* Send BACKLOG_BUF with the buffer memfd via SCM_RIGHTS.
+ * Returns 0/-1. */
+int termsrv_send_backlog_buf(int sock_fd, int buf_fd,
+ uint32_t buf_id, uint32_t cols,
+ uint32_t num_lines, uint32_t max_lines);
+
+/* Receive BACKLOG_BUF and extract the buffer memfd.
+ * Blocking receive — caller must ensure no concurrent reads on sock_fd.
+ * Returns 0/-1. */
+int termsrv_recv_backlog_buf(int sock_fd, int *buf_fd_out,
+ uint32_t *buf_id_out, uint32_t *cols_out,
+ uint32_t *num_lines_out, uint32_t *max_lines_out);
+
#endif /* HAVE_TYMUX */
#endif /* TERMINOLOGY_TERMSRV_H_ */
diff --git a/src/bin/termtymux.c b/src/bin/termtymux.c
index 5a8bbc25..bb2ebc49 100644
--- a/src/bin/termtymux.c
+++ b/src/bin/termtymux.c
@@ -635,4 +635,53 @@ termtymux_resize(TermTymux *ts, int cols, int rows)
ERR("termtymux: send RESIZE: %s", strerror(errno));
}
+int
+termsession_request_backlog_buf(TermTymux *ts, uint32_t buf_id,
+ int *fd_out, uint32_t *cols_out,
+ uint32_t *num_lines_out,
+ uint32_t *max_lines_out)
+{
+ TermSrvMsgBacklogBufReq req;
+ int flags;
+ int ret;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ts, -1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(fd_out, -1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cols_out, -1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(num_lines_out, -1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(max_lines_out, -1);
+
+ if (ts->sock_fd < 0)
+ return -1;
+
+ req.buf_id = buf_id;
+ if (termsrv_msg_send(ts->sock_fd, TSRV_MSG_BACKLOG_BUF_REQ,
+ &req, sizeof(req)) < 0)
+ {
+ ERR("termsession: send BACKLOG_BUF_REQ: %s", strerror(errno));
+ return -1;
+ }
+
+ /* Temporarily clear O_NONBLOCK so termsrv_recv_backlog_buf can use
+ * MSG_WAITALL to receive header + payload + cmsg atomically.
+ * The socket was set non-blocking after the attach handshake; restore
+ * it unconditionally after the recv so the Ecore fd handler is not
+ * affected regardless of the recv outcome. */
+ flags = fcntl(ts->sock_fd, F_GETFL, 0);
+ if (flags >= 0)
+ fcntl(ts->sock_fd, F_SETFL, flags & ~O_NONBLOCK);
+
+ ret = termsrv_recv_backlog_buf(ts->sock_fd, fd_out,
+ &buf_id, cols_out,
+ num_lines_out, max_lines_out);
+ if (ret < 0)
+ ERR("termsession: recv BACKLOG_BUF failed");
+
+ /* Restore non-blocking mode regardless of recv outcome */
+ if (flags >= 0)
+ fcntl(ts->sock_fd, F_SETFL, flags);
+
+ return ret;
+}
+
#endif /* HAVE_TYMUX */
diff --git a/src/bin/termtymux.h b/src/bin/termtymux.h
index 51a04313..2862c1e4 100644
--- a/src/bin/termtymux.h
+++ b/src/bin/termtymux.h
@@ -49,5 +49,19 @@ void termtymux_free(TermTymux *ts);
/* Send a resize event to the daemon. */
void termtymux_resize(TermTymux *ts, int cols, int rows);
+/*
+ * Request a specific backlog buffer from the daemon and receive it
+ * synchronously. The socket is briefly set to blocking mode for the
+ * recv, then restored to O_NONBLOCK.
+ *
+ * On success *fd_out is a new memfd (caller must close it when done);
+ * *cols_out, *num_lines_out and *max_lines_out describe the buffer.
+ * Returns 0 on success, -1 on failure.
+ */
+int termsession_request_backlog_buf(TermTymux *ts, uint32_t buf_id,
+ int *fd_out, uint32_t *cols_out,
+ uint32_t *num_lines_out,
+ uint32_t *max_lines_out);
+
#endif /* HAVE_TYMUX */
#endif /* TERMINOLOGY_TERMTYMUX_H_ */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.