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.