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 2ae762c2842bcf39471c7c108e4731e2f3bbe0b5
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 15 21:33:42 2026 -0600

    feat: implement tymuxd daemon for session-local socket IPC
    
    The tymuxd daemon runs one-per-session and provides a Unix socket
    endpoint for tymux clients to connect to. It:
    
    - Takes a session name argument and validates its length
    - Initializes Eina/Ecore and configures SIGPIPE to be ignored
    - Detects stale or duplicate sessions via probe connection
    - Creates a non-blocking Unix socket at
      $XDG_RUNTIME_DIR/terminology/tymux/<session>.sock
    - Binds the socket and enters Ecore's main loop to accept clients
    - Receives ATTACH messages and adds clients to the session PTY
    - Cleans up socket and session state on exit
    
    This enables the IPC recovery pattern where tymux clients reconnect
    to an already-running shell session in the same process rather than
    spawning new shells each time.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/bin/tymuxd.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/bin/tymuxd.h |   3 +-
 2 files changed, 247 insertions(+), 4 deletions(-)

diff --git a/src/bin/tymuxd.c b/src/bin/tymuxd.c
index 6e718a85..2d777152 100644
--- a/src/bin/tymuxd.c
+++ b/src/bin/tymuxd.c
@@ -1,11 +1,253 @@
 #include "private.h"
 #include "tymuxd.h"
+#include "tymux_session.h"
+#include "termsrv.h"
+
+#include <Ecore.h>
+#include <Eina.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/* ── log domain ──────────────────────────────────────────────────── */
 
 int _log_domain = -1;
 
-int main(int argc, char **argv)
+/* ── global state ────────────────────────────────────────────────── */
+
+static TermDSession      *_session        = NULL;
+static int                _listen_fd      = -1;
+static char               _sock_path[512];
+static Ecore_Fd_Handler  *_listen_handler = NULL;
+
+/* ── accept loop ─────────────────────────────────────────────────── */
+
+static Eina_Bool
+_cb_client_connect(void *data EINA_UNUSED,
+                   Ecore_Fd_Handler *fd_handler EINA_UNUSED)
 {
-   (void)argc;
-   (void)argv;
+   int      cfd;
+   void    *payload = NULL;
+   uint32_t size    = 0;
+   int      type;
+
+   cfd = accept(_listen_fd, NULL, NULL);
+   if (cfd < 0)
+     {
+        if (errno != EAGAIN && errno != EWOULDBLOCK)
+          ERR("accept: %s", strerror(errno));
+        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)
+     {
+        ERR("expected ATTACH from new client (got %d), closing fd %d", type, cfd);
+        free(payload);
+        close(cfd);
+        return ECORE_CALLBACK_RENEW;
+     }
+
+   free(payload);
+
+   termd_session_client_add(_session, cfd);
+   return ECORE_CALLBACK_RENEW;
+}
+
+/* ── entry point ─────────────────────────────────────────────────── */
+
+int
+main(int argc, char **argv)
+{
+   const char        *name;
+   int                probe_fd;
+   struct sockaddr_un sa;
+
+   /* ── argument validation ────────────────────────────────────── */
+
+   if (argc != 2 || argv[1][0] == '-')
+     {
+        fprintf(stderr, "Usage: tymuxd <session-name>\n");
+        return 1;
+     }
+
+   name = argv[1];
+
+   if (strlen(name) > TSRV_MAX_SESSION_NAME)
+     {
+        fprintf(stderr,
+                "tymuxd: session name too long (max %d characters)\n",
+                TSRV_MAX_SESSION_NAME);
+        return 1;
+     }
+
+   /* ── EFL init ───────────────────────────────────────────────── */
+
+   eina_init();
+   ecore_init();
+
+   _log_domain = eina_log_domain_register("tymuxd", NULL);
+   if (_log_domain < 0)
+     EINA_LOG_ERR("Could not create log domain 'tymuxd'");
+
+   /* Prevent SIGPIPE from killing the daemon on broken socket writes */
+   signal(SIGPIPE, SIG_IGN);
+
+   /* ── socket directory ───────────────────────────────────────── */
+
+   if (termsrv_ensure_sessions_dir() < 0)
+     {
+        ERR("could not create sessions directory: %s", strerror(errno));
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   if (termsrv_socket_path(name, _sock_path, sizeof(_sock_path)) < 0)
+     {
+        ERR("socket path too long for session '%s'", name);
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   /* ── duplicate detection ────────────────────────────────────── */
+
+   probe_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+   if (probe_fd < 0)
+     {
+        ERR("socket (probe): %s", strerror(errno));
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   memset(&sa, 0, sizeof(sa));
+   sa.sun_family = AF_UNIX;
+   strncpy(sa.sun_path, _sock_path, sizeof(sa.sun_path) - 1);
+
+   if (connect(probe_fd, (struct sockaddr *)&sa, sizeof(sa)) == 0)
+     {
+        /* A live daemon is already accepting on this socket */
+        close(probe_fd);
+        fprintf(stderr, "tymuxd: session '%s' already running\n", name);
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   close(probe_fd);
+
+   /* Stale socket file from a previous crash — remove it */
+   unlink(_sock_path);
+
+   /* ── create listen socket ───────────────────────────────────── */
+
+   _listen_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+   if (_listen_fd < 0)
+     {
+        ERR("socket (listen): %s", strerror(errno));
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   memset(&sa, 0, sizeof(sa));
+   sa.sun_family = AF_UNIX;
+   strncpy(sa.sun_path, _sock_path, sizeof(sa.sun_path) - 1);
+
+   if (bind(_listen_fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
+     {
+        ERR("bind '%s': %s", _sock_path, strerror(errno));
+        close(_listen_fd);
+        _listen_fd = -1;
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   if (listen(_listen_fd, 8) < 0)
+     {
+        ERR("listen: %s", strerror(errno));
+        unlink(_sock_path);
+        close(_listen_fd);
+        _listen_fd = -1;
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   /* ── create session ─────────────────────────────────────────── */
+
+   _session = termd_session_new(name, NULL);
+   if (!_session)
+     {
+        ERR("termd_session_new failed");
+        unlink(_sock_path);
+        close(_listen_fd);
+        _listen_fd = -1;
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   /* ── register fd handler ────────────────────────────────────── */
+
+   _listen_handler = ecore_main_fd_handler_add(_listen_fd, ECORE_FD_READ,
+                                                _cb_client_connect, NULL,
+                                                NULL, NULL);
+   if (!_listen_handler)
+     {
+        ERR("ecore_main_fd_handler_add failed");
+        termd_session_free(_session);
+        _session = NULL;
+        unlink(_sock_path);
+        close(_listen_fd);
+        _listen_fd = -1;
+        ecore_shutdown();
+        eina_shutdown();
+        return 1;
+     }
+
+   INF("tymuxd: session '%s' ready at %s", name, _sock_path);
+
+   /* ── main loop ──────────────────────────────────────────────── */
+
+   ecore_main_loop_begin();
+
+   /* ── cleanup ────────────────────────────────────────────────── */
+
+   if (_listen_handler)
+     {
+        ecore_main_fd_handler_del(_listen_handler);
+        _listen_handler = NULL;
+     }
+
+   if (_listen_fd >= 0)
+     {
+        close(_listen_fd);
+        _listen_fd = -1;
+     }
+
+   unlink(_sock_path);
+
+   termd_session_free(_session);
+   _session = NULL;
+
+   ecore_shutdown();
+   eina_shutdown();
+
    return 0;
 }
diff --git a/src/bin/tymuxd.h b/src/bin/tymuxd.h
index 5f449927..cd8be4c2 100644
--- a/src/bin/tymuxd.h
+++ b/src/bin/tymuxd.h
@@ -1,3 +1,4 @@
 #ifndef TERMINOLOGY_TYMUXD_H_
 #define TERMINOLOGY_TYMUXD_H_
-#endif
+/* Usage: tymuxd <session-name> */
+#endif /* TERMINOLOGY_TYMUXD_H_ */

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

Reply via email to