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

git pushed a commit to branch tytab
in repository terminology.

View the commit online.

commit d0df9e5b0ff83169b9fdc568751ae2bec047bdcf
Author: [email protected] <[email protected]>
AuthorDate: Sun Mar 29 21:58:10 2026 -0600

    feat: rewrite tytab new to use IPC nonce protocol
    
    tytab new now connects to the terminology IPC socket, registers the
    full payload (name, direction, cmd), receives a short-lived nonce, and
    sends only the nonce via escape sequence. No command payload travels
    through the PTY, eliminating escape sequence injection attacks.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 src/bin/meson.build |   2 +-
 src/bin/tytab.c     | 210 +++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 191 insertions(+), 21 deletions(-)

diff --git a/src/bin/meson.build b/src/bin/meson.build
index b91324f5..7785be71 100644
--- a/src/bin/meson.build
+++ b/src/bin/meson.build
@@ -48,7 +48,7 @@ tyq_sources = ['tycommon.c', 'tycommon.h', 'tyq.c']
 tycat_sources = ['tycommon.c', 'tycommon.h', 'tycat.c', 'extns.c', 'extns.h']
 tyls_sources = ['extns.c', 'extns.h', 'tyls.c', 'tycommon.c', 'tycommon.h']
 tysend_sources = ['tycommon.c', 'tycommon.h', 'tysend.c']
-tytab_sources = ['tycommon.c', 'tycommon.h', 'tytab.c']
+tytab_sources = ['tycommon.c', 'tycommon.h', 'tytab.c', 'ipc.h']
 tyfuzz_sources = ['termptyesc.c', 'termptyesc.h',
                   'backlog.c', 'backlog.h',
                   'termptyops.c', 'termptyops.h',
diff --git a/src/bin/tytab.c b/src/bin/tytab.c
index 0155e91f..4089ef85 100644
--- a/src/bin/tytab.c
+++ b/src/bin/tytab.c
@@ -1,8 +1,50 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <Ecore.h>
+#include <Ecore_Ipc.h>
+#include <Eet.h>
 #include "private.h"
 #include "tycommon.h"
+#include "ipc.h"
+
+static Eet_Data_Descriptor *_tab_req_edd = NULL;
+static Eet_Data_Descriptor *_tab_rep_edd = NULL;
+
+static void
+_eet_init(void)
+{
+   Eet_Data_Descriptor_Class eddc;
+
+   eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(eddc),
+                                             "tab_req",
+                                             sizeof(Ipc_Tab_Request));
+   _tab_req_edd = eet_data_descriptor_stream_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_tab_req_edd, Ipc_Tab_Request,
+                                 "term_id", term_id, EET_T_UINT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_tab_req_edd, Ipc_Tab_Request,
+                                 "name", name, EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_tab_req_edd, Ipc_Tab_Request,
+                                 "cmd", cmd, EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_tab_req_edd, Ipc_Tab_Request,
+                                 "direction", direction, EET_T_STRING);
+
+   eet_eina_stream_data_descriptor_class_set(&eddc, sizeof(eddc),
+                                             "tab_rep",
+                                             sizeof(Ipc_Tab_Reply));
+   _tab_rep_edd = eet_data_descriptor_stream_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_tab_rep_edd, Ipc_Tab_Reply,
+                                 "nonce_str", nonce_str, EET_T_STRING);
+}
+
+static void
+_eet_shutdown(void)
+{
+   if (_tab_req_edd) eet_data_descriptor_free(_tab_req_edd);
+   if (_tab_rep_edd) eet_data_descriptor_free(_tab_rep_edd);
+   _tab_req_edd = NULL;
+   _tab_rep_edd = NULL;
+}
 
 static void
 print_usage(const char *argv0)
@@ -37,7 +79,6 @@ _parse_direction(const char *s)
    return 0;
 }
 
-/* Find index of "--" in argv, or return -1 */
 static int
 _find_separator(int argc, char **argv, int start)
 {
@@ -50,7 +91,6 @@ _find_separator(int argc, char **argv, int start)
    return -1;
 }
 
-/* Join argv[start..argc-1] with spaces into buf */
 static void
 _join_args(char *buf, size_t bufsz, int argc, char **argv, int start)
 {
@@ -68,6 +108,118 @@ _join_args(char *buf, size_t bufsz, int argc, char **argv, int start)
    buf[pos] = '\0';
 }
 
+/* IPC reply state */
+static char *_reply_nonce = NULL;
+static Eina_Bool _reply_received = EINA_FALSE;
+
+static Eina_Bool
+_cb_server_data(void *_data EINA_UNUSED,
+                int _type EINA_UNUSED,
+                void *event)
+{
+   Ecore_Ipc_Event_Server_Data *e = event;
+
+   if ((e->major == TY_IPC_MAJOR_TAB) &&
+       (e->minor == TY_IPC_MINOR_TAB) &&
+       (e->data) && (e->size > 0))
+     {
+        Ipc_Tab_Reply *rep;
+
+        rep = eet_data_descriptor_decode(_tab_rep_edd, e->data, e->size);
+        if (rep)
+          {
+             if (rep->nonce_str && strcmp(rep->nonce_str, "0") != 0)
+               _reply_nonce = strdup(rep->nonce_str);
+             free(rep);
+          }
+     }
+   _reply_received = EINA_TRUE;
+   ecore_main_loop_quit();
+   return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+_cb_server_del(void *_data EINA_UNUSED,
+               int _type EINA_UNUSED,
+               void *_event EINA_UNUSED)
+{
+   _reply_received = EINA_TRUE;
+   ecore_main_loop_quit();
+   return ECORE_CALLBACK_DONE;
+}
+
+static Eina_Bool
+_cb_timeout(void *_data EINA_UNUSED)
+{
+   _reply_received = EINA_TRUE;
+   ecore_main_loop_quit();
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static char *
+_request_nonce(unsigned int term_id, const char *name,
+               char dir, const char *cmd_str)
+{
+   char *hash;
+   Ecore_Ipc_Server *srv;
+   Ecore_Event_Handler *hnd_data, *hnd_del;
+   Ecore_Timer *timer;
+   Ipc_Tab_Request req = { 0 };
+   char dirbuf[2] = { 0, 0 };
+   void *data;
+   int size;
+
+   hash = ipc_hash_get();
+   if (!hash) return NULL;
+
+   srv = ecore_ipc_server_connect(ECORE_IPC_LOCAL_USER, hash, 0, NULL);
+   free(hash);
+   if (!srv)
+     {
+        fprintf(stderr, "failed to connect to terminology IPC\n");
+        return NULL;
+     }
+
+   req.term_id = term_id;
+   req.name = (char *)name;
+   if (dir)
+     {
+        dirbuf[0] = dir;
+        req.direction = dirbuf;
+     }
+   else
+     req.direction = "";
+   req.cmd = (char *)cmd_str;
+
+   data = "" &req, &size);
+   if (!data)
+     {
+        ecore_ipc_server_del(srv);
+        return NULL;
+     }
+
+   ecore_ipc_server_send(srv, TY_IPC_MAJOR_TAB, TY_IPC_MINOR_TAB,
+                          0, 0, 0, data, size);
+   ecore_ipc_server_flush(srv);
+   free(data);
+
+   hnd_data = ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DATA,
+                                      _cb_server_data, NULL);
+   hnd_del = ecore_event_handler_add(ECORE_IPC_EVENT_SERVER_DEL,
+                                     _cb_server_del, NULL);
+   timer = ecore_timer_add(0.5, _cb_timeout, NULL);
+
+   if (!_reply_received)
+     ecore_main_loop_begin();
+
+   ecore_event_handler_del(hnd_data);
+   ecore_event_handler_del(hnd_del);
+   if (timer) ecore_timer_del(timer);
+   ecore_ipc_server_del(srv);
+
+   return _reply_nonce;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -84,7 +236,7 @@ main(int argc, char **argv)
 
    if (!strcmp(argv[1], "name"))
      {
-        /* tytab name [NAME] */
+        /* tytab name [NAME] — direct escape, no IPC needed */
         if (argc >= 3)
           {
              if (strchr(argv[2], ';'))
@@ -102,26 +254,28 @@ main(int argc, char **argv)
      }
    else if (!strcmp(argv[1], "freeze"))
      {
-        /* tytab freeze */
+        /* tytab freeze — direct escape, no IPC needed */
         snprintf(tbuf, sizeof(tbuf), "%c}tf", 0x1b);
         if (ty_write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1))
           perror("write");
      }
    else if (!strcmp(argv[1], "new"))
      {
-        /* tytab new [NAME DIR] [-- CMD...] */
+        /* tytab new [NAME DIR] [-- CMD...] — two-phase IPC + escape */
         int sep = _find_separator(argc, argv, 2);
         int positional_end = (sep >= 0) ? sep : argc;
         int positional_count = positional_end - 2;
         const char *name = NULL;
+        const char *term_id_str;
+        unsigned int term_id;
         char dir = 0;
         char cmdbuf[4096];
+        char *nonce;
 
         cmdbuf[0] = '\0';
 
         if (positional_count >= 2)
           {
-             /* NAME and DIR provided */
              name = argv[2];
              if (strchr(name, ';'))
                {
@@ -137,35 +291,51 @@ main(int argc, char **argv)
           }
         else if (positional_count == 1)
           {
-             /* NAME without DIR is ambiguous — require both */
              fprintf(stderr, "NAME requires a DIR argument\n");
              print_usage(argv[0]);
              return EXIT_FAILURE;
           }
-        /* else: positional_count == 0, no name, no dir */
 
-        /* Build CMD from args after -- */
         if (sep >= 0 && sep + 1 < argc)
           _join_args(cmdbuf, sizeof(cmdbuf), argc, argv, sep + 1);
 
-        /* Format escape: tt[;NAME;DIR[;CMD]] */
-        if (name && dir)
+        term_id_str = getenv("TERMINOLOGY_ID");
+        if (!term_id_str)
           {
-             if (cmdbuf[0])
-               snprintf(tbuf, sizeof(tbuf), "%c}tt;%s;%c;%s", 0x1b, name, dir, cmdbuf);
-             else
-               snprintf(tbuf, sizeof(tbuf), "%c}tt;%s;%c", 0x1b, name, dir);
+             fprintf(stderr, "TERMINOLOGY_ID not set "
+                     "(need recent terminology)\n");
+             return EXIT_FAILURE;
           }
-        else
+        term_id = (unsigned int)strtoul(term_id_str, NULL, 10);
+
+        /* Phase 1: IPC nonce request */
+        ecore_init();
+        ecore_ipc_init();
+        eet_init();
+        _eet_init();
+
+        nonce = _request_nonce(term_id, name ? name : "",
+                               dir, cmdbuf[0] ? cmdbuf : "");
+        if (!nonce)
           {
-             if (cmdbuf[0])
-               snprintf(tbuf, sizeof(tbuf), "%c}tt;;;%s", 0x1b, cmdbuf);
-             else
-               snprintf(tbuf, sizeof(tbuf), "%c}tt", 0x1b);
+             fprintf(stderr, "tytab: request rejected by terminology\n");
+             _eet_shutdown();
+             eet_shutdown();
+             ecore_ipc_shutdown();
+             ecore_shutdown();
+             return EXIT_FAILURE;
           }
 
+        /* Phase 2: send nonce via escape sequence */
+        snprintf(tbuf, sizeof(tbuf), "%c}tt;%s", 0x1b, nonce);
         if (ty_write(1, tbuf, strlen(tbuf) + 1) != (signed)(strlen(tbuf) + 1))
           perror("write");
+
+        free(nonce);
+        _eet_shutdown();
+        eet_shutdown();
+        ecore_ipc_shutdown();
+        ecore_shutdown();
      }
    else
      {

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

Reply via email to