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 146bc6cc60166a467eb5c3522cefd13f7e37c4e2
Author: [email protected] <[email protected]>
AuthorDate: Mon Mar 16 14:08:40 2026 -0600

    fix: resolve critical issues in tymux code review
    
    Remove O_NONBLOCK from tymuxd client fd, which was causing msg_recv to
    fail with EAGAIN errors under blocking conditions. Add strict validation
    for session names (reject empty names and paths with '/') before resource
    allocation to prevent malformed socket creation.
    
    Replace split write() calls with atomic sendmsg+MSG_NOSIGNAL for message
    framing integrity. Add MSG_WAITALL to recvmsg in recv_attached and validate
    message type range [1..9] to reject corrupt frames.
    
    Implement double-fork in both termsession and tymux cmd_new() to prevent
    zombie daemon processes when tymuxd exits. Store TSRV_MSG_TITLE payload
    in shadow_pty->prop.title so window titles update correctly in session mode.
    
    Register Ipc_Instance.session in Eet EDD so --session works via IPC
    single-instance mode.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/bin/ipc.c         |  4 ++++
 src/bin/termsession.c | 17 +++++++++++++++--
 src/bin/termsrv.c     | 18 +++++++++++++-----
 src/bin/tymux.c       | 19 ++++++++++++++++---
 src/bin/tymuxd.c      | 13 ++++---------
 5 files changed, 52 insertions(+), 19 deletions(-)

diff --git a/src/bin/ipc.c b/src/bin/ipc.c
index 6cad2b9f..9384b944 100644
--- a/src/bin/ipc.c
+++ b/src/bin/ipc.c
@@ -116,6 +116,10 @@ ipc_init(void)
                                  "font", font, EET_T_STRING);
    EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
                                  "startup_id", startup_id, EET_T_STRING);
+#ifdef HAVE_TYMUX
+   EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
+                                 "session", session, EET_T_STRING);
+#endif
    EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
                                  "x", x, EET_T_INT);
    EET_DATA_DESCRIPTOR_ADD_BASIC(new_inst_edd, Ipc_Instance,
diff --git a/src/bin/termsession.c b/src/bin/termsession.c
index 918a8e19..9235c645 100644
--- a/src/bin/termsession.c
+++ b/src/bin/termsession.c
@@ -14,6 +14,7 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <sys/mman.h>
+#include <sys/wait.h>
 #include <fcntl.h>
 #include <stdatomic.h>
 #include <time.h>
@@ -172,6 +173,8 @@ _cb_session_data(void *data, Ecore_Fd_Handler *handler EINA_UNUSED)
         }
 
       case TSRV_MSG_TITLE:
+        if (payload && size > 0)
+          eina_stringshare_replace(&ts->shadow_pty->prop.title, payload);
         if (ts->cb_title.func)
           ts->cb_title.func(ts->cb_title.data);
         break;
@@ -224,11 +227,18 @@ _ensure_daemon(const char *session_name, const char *sock_path)
 
    if (child == 0)
      {
-        /* Child: become session leader, redirect stdio, exec daemon */
+        /* First child: become session leader then double-fork so the
+         * daemon is reparented to init and leaves no zombie. */
+        pid_t gc;
         int fd;
 
         setsid();
 
+        gc = fork();
+        if (gc < 0) _exit(1);
+        if (gc > 0) _exit(0); /* first child exits; grandchild continues */
+
+        /* Grandchild: redirect stdio and exec daemon */
         fd = open("/dev/null", O_RDWR);
         if (fd >= 0)
           {
@@ -244,7 +254,10 @@ _ensure_daemon(const char *session_name, const char *sock_path)
         _exit(127);
      }
 
-   /* Parent: poll for socket with 10 ms intervals, 3 s total */
+   /* Parent: reap the short-lived first child immediately */
+   waitpid(child, NULL, 0);
+
+   /* Poll for socket with 10 ms intervals, 3 s total */
    ts_sleep.tv_sec  = 0;
    ts_sleep.tv_nsec = 10 * 1000 * 1000; /* 10 ms */
 
diff --git a/src/bin/termsrv.c b/src/bin/termsrv.c
index 0f65e1de..deda63f8 100644
--- a/src/bin/termsrv.c
+++ b/src/bin/termsrv.c
@@ -73,10 +73,14 @@ termsrv_msg_send(int fd, TermSrvMsgType type,
                  const void *payload, uint32_t size)
 {
     TermSrvMsgHdr hdr = { .type = (uint32_t)type, .size = size };
-    if (write(fd, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) return -1;
-    if (size && payload)
-        if (write(fd, payload, size) != (ssize_t)size) return -1;
-    return 0;
+    struct iovec iov[2] = {
+        { .iov_base = &hdr,            .iov_len = sizeof(hdr) },
+        { .iov_base = (void *)payload, .iov_len = size        },
+    };
+    int iovcnt = (size && payload) ? 2 : 1;
+    struct msghdr msg = { .msg_iov = iov, .msg_iovlen = (size_t)iovcnt };
+    ssize_t n = sendmsg(fd, &msg, MSG_NOSIGNAL);
+    return (n < 0) ? -1 : 0;
 }
 
 int
@@ -100,6 +104,9 @@ termsrv_msg_recv(int fd, void **payload_out, uint32_t *size_out)
            { free(buf); return -1; }
          *payload_out = buf;
       }
+    /* Reject unknown message types; valid range is ATTACH(1)..EXIT(9). */
+    if (hdr.type == 0 || hdr.type > 9) return -1;
+
     *size_out = hdr.size;
     return (int)hdr.type;
 }
@@ -153,7 +160,8 @@ termsrv_recv_attached(int sock_fd, int *shm_fd_out,
     };
 
     *shm_fd_out = -1;
-    if (recvmsg(sock_fd, &msg, 0) < 0) return -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_ATTACHED) return -1;
 
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index ce485c64..440d14dc 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -21,6 +21,7 @@
 #include <sys/un.h>
 #include <fcntl.h>
 #include <time.h>
+#include <sys/wait.h>
 
 /* ── path helpers (mirrors termsrv.c, no EFL) ───────────────────────── */
 
@@ -167,7 +168,9 @@ cmd_new(int argc, char **argv)
         return 1;
      }
 
-   /* Fork a detached tymuxd. */
+   /* Double-fork so tymuxd is reparented to init immediately and leaves
+    * no zombie in this process (or in terminology) when it eventually
+    * exits. */
    pid_t pid = fork();
    if (pid < 0)
      {
@@ -177,9 +180,16 @@ cmd_new(int argc, char **argv)
 
    if (pid == 0)
      {
-        /* Child: become session leader and redirect stdio. */
+        /* First child: become session leader then double-fork. */
+        pid_t gc;
+
         setsid();
 
+        gc = fork();
+        if (gc < 0) _exit(1);
+        if (gc > 0) _exit(0); /* first child exits immediately */
+
+        /* Grandchild: redirect stdio and exec daemon. */
         int devnull = open("/dev/null", O_RDWR);
         if (devnull >= 0)
           {
@@ -196,7 +206,10 @@ cmd_new(int argc, char **argv)
         _exit(127);
      }
 
-   /* Parent: poll for the socket to appear (up to 3 s, 10 ms intervals). */
+   /* Parent: reap the short-lived first child immediately. */
+   waitpid(pid, NULL, 0);
+
+   /* Poll for the socket to appear (up to 3 s, 10 ms intervals). */
    struct timespec ts = { 0, 10 * 1000 * 1000 }; /* 10 ms */
    int attempts = 300; /* 3 s total */
    int ready = 0;
diff --git a/src/bin/tymuxd.c b/src/bin/tymuxd.c
index 2d777152..97c25928 100644
--- a/src/bin/tymuxd.c
+++ b/src/bin/tymuxd.c
@@ -7,7 +7,6 @@
 #include <Eina.h>
 
 #include <errno.h>
-#include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,11 +45,6 @@ _cb_client_connect(void *data EINA_UNUSED,
         return ECORE_CALLBACK_RENEW;
      }
 
-   {
-      int flags = fcntl(cfd, F_GETFL, 0);
-      if (flags >= 0) fcntl(cfd, F_SETFL, flags | O_NONBLOCK);
-   }
-
    type = termsrv_msg_recv(cfd, &payload, &size);
    if (type != TSRV_MSG_ATTACH)
      {
@@ -85,11 +79,12 @@ main(int argc, char **argv)
 
    name = argv[1];
 
-   if (strlen(name) > TSRV_MAX_SESSION_NAME)
+   if (name[0] == '\0' || strlen(name) > TSRV_MAX_SESSION_NAME ||
+       strchr(name, '/'))
      {
         fprintf(stderr,
-                "tymuxd: session name too long (max %d characters)\n",
-                TSRV_MAX_SESSION_NAME);
+                "tymuxd: invalid session name '%s' "
+                "(empty, too long, or contains '/')\n", name);
         return 1;
      }
 

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

Reply via email to