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

git pushed a commit to branch feat/e-wl-proxy-planb
in repository enlightenment.

View the commit online.

commit ccf98175288a7c02d2abb13d6bc921778053e643
Author: [email protected] <[email protected]>
AuthorDate: Sat Jun 13 11:32:11 2026 -0600

    feat(e_wl_proxy): listening socket, fd-passing pipe, and fd retention queue
    
    The transport layer beneath the proxy: a lock-guarded listening UNIX socket
    and a backend connect helper (e_wl_proxy_socket); a read-and-forward pipe that
    carries SCM_RIGHTS file descriptors across the proxy and closes them exactly
    once on every path, with EMFILE throttling and MSG_CTRUNC detection
    (e_wl_proxy_pipe); and an owned-fd FIFO that retains arrived descriptors in
    order so they can be re-attached to the right message during replay
    (e_wl_proxy_fdq).
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 src/bin/e_wl_proxy/e_wl_proxy_fdq.c    |  49 ++++++++++
 src/bin/e_wl_proxy/e_wl_proxy_fdq.h    |  29 ++++++
 src/bin/e_wl_proxy/e_wl_proxy_pipe.c   | 158 +++++++++++++++++++++++++++++++++
 src/bin/e_wl_proxy/e_wl_proxy_socket.c |  99 +++++++++++++++++++++
 4 files changed, 335 insertions(+)

diff --git a/src/bin/e_wl_proxy/e_wl_proxy_fdq.c b/src/bin/e_wl_proxy/e_wl_proxy_fdq.c
new file mode 100644
index 000000000..f92fc0c42
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_fdq.c
@@ -0,0 +1,49 @@
+#include "config.h"
+#include "e_wl_proxy_fdq.h"
+#include <unistd.h>
+
+int
+e_wl_proxy_fdq_count(const E_Wl_Proxy_Fdq *q)
+{
+   return q ? (q->tail - q->head) : 0;
+}
+
+int
+e_wl_proxy_fdq_push(E_Wl_Proxy_Fdq *q, int fd)
+{
+   if (!q) { if (fd >= 0) close(fd); return -1; }
+   if (q->tail - q->head >= E_WL_PROXY_FDQ_CAP)
+     {
+        /* queue full: drop (close) rather than overflow */
+        if (fd >= 0) close(fd);
+        return -1;
+     }
+   /* compact when head has advanced and tail hit the cap */
+   if (q->tail == E_WL_PROXY_FDQ_CAP && q->head > 0)
+     {
+        int i, n = q->tail - q->head;
+        for (i = 0; i < n; i++) q->fd[i] = q->fd[q->head + i];
+        q->head = 0; q->tail = n;
+     }
+   q->fd[q->tail++] = fd;
+   return 0;
+}
+
+int
+e_wl_proxy_fdq_pop(E_Wl_Proxy_Fdq *q)
+{
+   int fd;
+   if (!q || q->head >= q->tail) return -1;
+   fd = q->fd[q->head++];
+   if (q->head == q->tail) { q->head = 0; q->tail = 0; }   /* reset when drained */
+   return fd;
+}
+
+void
+e_wl_proxy_fdq_clear(E_Wl_Proxy_Fdq *q)
+{
+   if (!q) return;
+   while (q->head < q->tail)
+     { int fd = q->fd[q->head++]; if (fd >= 0) close(fd); }
+   q->head = 0; q->tail = 0;
+}
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_fdq.h b/src/bin/e_wl_proxy/e_wl_proxy_fdq.h
new file mode 100644
index 000000000..26aed8982
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_fdq.h
@@ -0,0 +1,29 @@
+#ifndef E_WL_PROXY_FDQ_H
+#define E_WL_PROXY_FDQ_H
+
+/* A small FIFO of owned file descriptors. Wayland delivers fds in order; we
+ * hold dup'd copies here until the message that consumes them is fully parsed.
+ * push() takes ownership of the fd; pop() transfers it to the caller; clear()
+ * close()s everything still queued. */
+
+#define E_WL_PROXY_FDQ_CAP 256
+
+typedef struct _E_Wl_Proxy_Fdq
+{
+   int fd[E_WL_PROXY_FDQ_CAP];
+   int head;   /* index of next pop */
+   int tail;   /* index of next push; count = tail - head */
+} E_Wl_Proxy_Fdq;
+
+/* Number of fds currently queued. */
+int  e_wl_proxy_fdq_count(const E_Wl_Proxy_Fdq *q);
+/* Append fd (ownership transferred to the queue). If the queue is full, the fd
+ * is close()d and dropped. Returns 0 if queued, -1 if dropped. */
+int  e_wl_proxy_fdq_push(E_Wl_Proxy_Fdq *q, int fd);
+/* Remove and return the oldest fd (ownership transferred to caller), or -1 if
+ * the queue is empty. */
+int  e_wl_proxy_fdq_pop(E_Wl_Proxy_Fdq *q);
+/* close() every fd still queued and reset to empty. */
+void e_wl_proxy_fdq_clear(E_Wl_Proxy_Fdq *q);
+
+#endif
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_pipe.c b/src/bin/e_wl_proxy/e_wl_proxy_pipe.c
new file mode 100644
index 000000000..37fb5c622
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_pipe.c
@@ -0,0 +1,158 @@
+#include "config.h"
+#include "e_wl_proxy.h"
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+/* Write pipe->buf[off..len) plus any pending fds to `to`. fds ride on the
+ * first sendmsg that places any byte; once off > 0 they are gone. */
+int
+e_wl_proxy_pipe_flush(int to, E_Wl_Proxy_Pipe *pipe)
+{
+   while (pipe->len - pipe->off > 0)
+     {
+        struct iovec iov;
+        struct msghdr msg;
+        union {
+           struct cmsghdr align;
+           char buf[CMSG_SPACE(E_WL_PROXY_MAX_FDS * sizeof(int))];
+        } cmsgbuf;
+        ssize_t sent;
+
+        iov.iov_base = pipe->buf + pipe->off;
+        iov.iov_len = pipe->len - pipe->off;
+
+        memset(&msg, 0, sizeof(msg));
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+
+        if (pipe->off == 0 && pipe->nfds > 0)
+          {
+             struct cmsghdr *c;
+             size_t fdbytes = pipe->nfds * sizeof(int);
+
+             memset(&cmsgbuf, 0, sizeof(cmsgbuf));
+             msg.msg_control = cmsgbuf.buf;
+             msg.msg_controllen = CMSG_SPACE(fdbytes);
+             c = CMSG_FIRSTHDR(&msg);
+             c->cmsg_level = SOL_SOCKET;
+             c->cmsg_type = SCM_RIGHTS;
+             c->cmsg_len = CMSG_LEN(fdbytes);
+             memcpy(CMSG_DATA(c), pipe->fds, fdbytes);
+          }
+
+        do {
+             sent = sendmsg(to, &msg, MSG_NOSIGNAL);
+        } while (sent < 0 && errno == EINTR);
+
+        if (sent < 0)
+          {
+             if (errno == EAGAIN || errno == EWOULDBLOCK) return 1;
+             return -1;
+          }
+
+        /* fds (if any) were delivered with this sendmsg; close our copies */
+        if (pipe->nfds > 0)
+          {
+             int i;
+             for (i = 0; i < pipe->nfds; i++)
+               if (pipe->fds[i] >= 0) close(pipe->fds[i]);
+             pipe->nfds = 0;
+          }
+        pipe->off += sent;
+     }
+
+   pipe->len = 0;
+   pipe->off = 0;
+   return 0;
+}
+
+ssize_t
+e_wl_proxy_forward(int from, int to, E_Wl_Proxy_Pipe *pipe,
+                   int *out_fds, int *out_nfds)
+{
+   struct iovec iov;
+   struct msghdr msg;
+   union {
+      struct cmsghdr align;
+      char buf[CMSG_SPACE(E_WL_PROXY_MAX_FDS * sizeof(int))];
+   } cmsgbuf;
+   struct cmsghdr *c;
+   ssize_t n;
+
+   /* caller guarantees the pipe is drained before reading more */
+   if (pipe->len != 0) return 0;
+
+   iov.iov_base = pipe->buf;
+   iov.iov_len = sizeof(pipe->buf);
+
+   memset(&msg, 0, sizeof(msg));
+   msg.msg_iov = &iov;
+   msg.msg_iovlen = 1;
+   msg.msg_control = cmsgbuf.buf;
+   msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+   do {
+        n = recvmsg(from, &msg, MSG_CMSG_CLOEXEC);
+   } while (n < 0 && errno == EINTR);
+
+   if (n == 0) return -1;                 /* EOF */
+   if (n < 0)
+     {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) return 0;
+        return -1;
+     }
+
+   /* collect any fds that arrived with this chunk */
+   pipe->nfds = 0;
+   for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c))
+     {
+        if (c->cmsg_level == SOL_SOCKET && c->cmsg_type == SCM_RIGHTS)
+          {
+             int cnt, i;
+             if (c->cmsg_len < CMSG_LEN(0)) continue;   /* malformed cmsg: skip */
+             cnt = (c->cmsg_len - CMSG_LEN(0)) / sizeof(int);
+             for (i = 0; i < cnt && pipe->nfds < E_WL_PROXY_MAX_FDS; i++)
+               memcpy(&pipe->fds[pipe->nfds++], CMSG_DATA(c) + i * sizeof(int), sizeof(int));
+          }
+     }
+
+   /* If ancillary fds did not fit they were dropped by the kernel; forwarding
+    * the bytes without them would corrupt the protocol stream, so tear down. */
+   if (msg.msg_flags & MSG_CTRUNC)
+     {
+        int i;
+        for (i = 0; i < pipe->nfds; i++) close(pipe->fds[i]);
+        pipe->nfds = 0;
+        return -1;
+     }
+
+   /* hand the caller a dup of each arrived fd for retention (the originals are
+    * forwarded and closed by the transport once delivered) */
+   if (out_nfds) *out_nfds = 0;
+   if (out_fds && out_nfds)
+     {
+        int i;
+        for (i = 0; i < pipe->nfds; i++)
+          {
+             int d = -1;
+             if (pipe->fds[i] >= 0)
+               { d = dup(pipe->fds[i]); if (d >= 0) fcntl(d, F_SETFD, FD_CLOEXEC); }
+             out_fds[(*out_nfds)++] = d;
+          }
+     }
+
+   pipe->len = (int)n;
+   pipe->off = 0;
+
+   /* push it out now; any unsent remainder stays buffered for the writable cb.
+    * A flush error means the *write* side (`to`) died — return -2 so the caller
+    * can tell it apart from a read-side EOF (-1) and react per direction (e.g.
+    * freeze the client when the backend dies, rather than tear the client down). */
+   if (e_wl_proxy_pipe_flush(to, pipe) < 0) return -2;
+
+   return n;
+}
diff --git a/src/bin/e_wl_proxy/e_wl_proxy_socket.c b/src/bin/e_wl_proxy/e_wl_proxy_socket.c
new file mode 100644
index 000000000..a52b7f66e
--- /dev/null
+++ b/src/bin/e_wl_proxy/e_wl_proxy_socket.c
@@ -0,0 +1,99 @@
+#include "config.h"
+#include "e_wl_proxy.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/file.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int
+e_wl_proxy_socket_path(const char *name, char *buf, size_t buflen)
+{
+   const char *runtime;
+   int n;
+
+   if (!name || name[0] == '\0' || strchr(name, '/')) return -1;
+
+   runtime = getenv("XDG_RUNTIME_DIR");
+   if (!runtime || runtime[0] == '\0') return -1;
+
+   n = snprintf(buf, buflen, "%s/%s", runtime, name);
+   if (n < 0 || (size_t)n >= buflen) return -1;
+
+   return 0;
+}
+
+static int
+_fill_addr(const char *name, struct sockaddr_un *addr)
+{
+   char path[256];
+
+   if (e_wl_proxy_socket_path(name, path, sizeof(path)) < 0) return -1;
+   if (strlen(path) >= sizeof(addr->sun_path)) return -1;
+
+   memset(addr, 0, sizeof(*addr));
+   addr->sun_family = AF_UNIX;
+   strcpy(addr->sun_path, path);
+   return 0;
+}
+
+int
+e_wl_proxy_listen(const char *name)
+{
+   struct sockaddr_un addr;
+   char lockpath[256 + 8];
+   char path[256];
+   int fd, lock;
+
+   if (e_wl_proxy_socket_path(name, path, sizeof(path)) < 0) return -1;
+   if (_fill_addr(name, &addr) < 0) return -1;
+
+   /* take "<path>.lock" so two servers can't share the name */
+   snprintf(lockpath, sizeof(lockpath), "%s.lock", path);
+   lock = open(lockpath, O_CREAT | O_CLOEXEC, 0660);
+   if (lock < 0) return -1;
+   if (flock(lock, LOCK_EX | LOCK_NB) < 0) { close(lock); return -1; }
+
+   /* a leftover socket from a crash is safe to remove now we hold the lock */
+   unlink(path);
+
+   fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+   if (fd < 0) { close(lock); return -1; }
+
+   if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) goto err;
+   if (listen(fd, 128) < 0) goto err;
+
+   /* intentionally leak `lock`: it must stay open for the socket's lifetime.
+    * The process exit releases it. */
+   return fd;
+
+err:
+   close(fd);
+   close(lock);
+   return -1;
+}
+
+int
+e_wl_proxy_connect(const char *name)
+{
+   struct sockaddr_un addr;
+   int fd;
+
+   if (_fill_addr(name, &addr) < 0) return -1;
+
+   fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+   if (fd < 0) return -1;
+
+   if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+     {
+        if (errno != EINPROGRESS) { close(fd); return -1; }
+        /* nonblocking connect in progress: fine for a unix socket, it
+         * completes effectively immediately; caller treats it as connected. */
+     }
+
+   return fd;
+}

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

Reply via email to