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 beadb0d248d83f0f699d6360c7dd95ca1d3d1e8c
Author: [email protected] <[email protected]>
AuthorDate: Mon May 4 22:12:50 2026 -0600
refactor(tymux): snapshot launch cwd in client; drop daemon CWD broadcast
The previous daemon-side design tried to track the inner shell's cwd by
readlinking /proc/<pid>/cwd and broadcasting a TSRV_MSG_CWD on DETACH and
in _pty_exit_cb. This approach had a critical race: by the time _pty_exit_cb
fires, the inner shell can already be reaped, leaving /proc/<pid>/cwd invalid,
and causing the respawned shell to land at $HOME instead of the intended cwd.
It also violated tmux semantics—when you detach, your host shell should stay
where it was, not follow the inner shell's final location.
The new design snapshots the outer shell's cwd at switch-in time, inside
_termio_tymux_switch_job, just before tearing down the local Termpty. This
path is stashed as launch_cwd on the TermTymux struct and consulted by
_smart_pty_exited → _termio_tymux_demote during detach or respawn. No daemon
protocol overhead, no /proc races, no pid reap edge case.
Concretely: removed TSRV_MSG_CWD enum, termsrv_send_cwd helper, and all daemon
readlink+broadcast code; kept the proxy protocol cap at >13 (rather than >11)
to preserve the earlier PAUSE/RESUME fix; added launch_cwd field to TermTymux
with proper cleanup; snapshot cwd in _termio_tymux_switch_job before PTY
teardown; demote consults launch_cwd instead of last_cwd.
User-visible: `exit` from /var/log now lands at /var/log (was $HOME).
Ctrl+b d still respects the launch cwd, matching tmux mental model.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
src/bin/termd_tymux.c | 26 --------------------------
src/bin/termio.c | 20 +++++++++++++++++++-
src/bin/termsrv.c | 11 ++---------
src/bin/termsrv.h | 5 -----
src/bin/termtymux.c | 25 +------------------------
src/bin/termtymux.h | 2 +-
src/bin/tymux.c | 3 +--
7 files changed, 24 insertions(+), 68 deletions(-)
diff --git a/src/bin/termd_tymux.c b/src/bin/termd_tymux.c
index 5c7163a3..f0c06ea6 100644
--- a/src/bin/termd_tymux.c
+++ b/src/bin/termd_tymux.c
@@ -1,7 +1,6 @@
#include "private.h"
#include <errno.h>
-#include <limits.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdlib.h>
@@ -469,19 +468,6 @@ _pty_exit_cb(void *data)
_tymux_flush_and_notify(sess);
}
- {
- char proc_path[64], cwd_buf[PATH_MAX];
- ssize_t n = -1;
- if (sess->pty && sess->pty->pid > 0)
- {
- snprintf(proc_path, sizeof(proc_path),
- "/proc/%d/cwd", (int)sess->pty->pid);
- n = readlink(proc_path, cwd_buf, sizeof(cwd_buf));
- }
- if (n < 0) n = 0;
- EINA_LIST_FOREACH(sess->clients, l, c)
- termsrv_send_cwd(c->fd, cwd_buf, (uint32_t)n);
- }
pay.exit_code = (int32_t)sess->pty->exit_code;
EINA_LIST_FOREACH(sess->clients, l, c)
termsrv_msg_send(c->fd, TSRV_MSG_EXIT, &pay, sizeof(pay));
@@ -566,18 +552,6 @@ _cb_client_data(void *data, Ecore_Fd_Handler *fd_handler EINA_UNUSED)
break;
case TSRV_MSG_DETACH:
- {
- char proc_path[64], cwd_buf[PATH_MAX];
- ssize_t n = -1;
- if (sess->pty && sess->pty->pid > 0)
- {
- snprintf(proc_path, sizeof(proc_path),
- "/proc/%d/cwd", (int)sess->pty->pid);
- n = readlink(proc_path, cwd_buf, sizeof(cwd_buf));
- }
- if (n < 0) n = 0;
- termsrv_send_cwd(client->fd, cwd_buf, (uint32_t)n);
- }
free(payload);
_client_remove(sess, client);
return ECORE_CALLBACK_CANCEL;
diff --git a/src/bin/termio.c b/src/bin/termio.c
index 337d26f3..bf125e1e 100644
--- a/src/bin/termio.c
+++ b/src/bin/termio.c
@@ -3749,7 +3749,7 @@ _smart_pty_exited(void *data)
if (sd->tymux)
{
- const char *cwd = sd->tymux->last_cwd;
+ const char *cwd = sd->tymux->launch_cwd;
_termio_tymux_demote(data, sd, cwd);
return;
}
@@ -3974,6 +3974,21 @@ _termio_tymux_switch_job(void *data)
if (!tymux_name) return;
sd->tymux_pending = NULL;
+ /* Snapshot the outer shell's cwd before we tear it down — used as the
+ * spawn cwd if the user later detaches or the tymux session ends. */
+ char launch_cwd_buf[PATH_MAX];
+ ssize_t launch_cwd_n = -1;
+ if (!sd->tymux && sd->pty && sd->pty->pid > 0)
+ {
+ char proc_path[64];
+ snprintf(proc_path, sizeof(proc_path),
+ "/proc/%d/cwd", (int)sd->pty->pid);
+ launch_cwd_n = readlink(proc_path, launch_cwd_buf,
+ sizeof(launch_cwd_buf) - 1);
+ if (launch_cwd_n > 0)
+ launch_cwd_buf[launch_cwd_n] = '\0';
+ }
+
/* Clean up any previous tymux or plain PTY state before attaching. */
if (sd->tymux)
{
@@ -4012,6 +4027,9 @@ _termio_tymux_switch_job(void *data)
sd->tymux_name = tymux_name; /* take ownership of the stringshare */
sd->tymux = tymux;
+ if (launch_cwd_n > 0)
+ sd->tymux->launch_cwd = eina_stringshare_add(launch_cwd_buf);
+
/* Wire callbacks — same pattern as the tymux branch in termio_add(). */
sd->tymux->cb_change.func = _smart_pty_change;
sd->tymux->cb_change.data = ""
diff --git a/src/bin/termsrv.c b/src/bin/termsrv.c
index 6684b8e6..42fd6ac8 100644
--- a/src/bin/termsrv.c
+++ b/src/bin/termsrv.c
@@ -121,9 +121,9 @@ termsrv_msg_recv(int fd, void **payload_out, uint32_t *size_out, int *fd_out)
}
/* Reject unknown message types before allocating — valid range is
- * ATTACH(1)..CWD(14). Checking type first prevents a malloc
+ * ATTACH(1)..RESUME(13). Checking type first prevents a malloc
* that would otherwise leak on the error path. */
- if (hdr.type == 0 || hdr.type > 14) return -1;
+ if (hdr.type == 0 || hdr.type > 13) return -1;
if (hdr.size > TERMSRV_MAX_MSG_PAYLOAD) return -1;
/* Now do the real read. Use recvmsg so that we can extract an optional
@@ -315,12 +315,5 @@ termsrv_send_backlog_buf(int sock_fd, int buf_fd,
}
-int
-termsrv_send_cwd(int sock_fd, const char *path, uint32_t len)
-{
- if (len > TERMSRV_MAX_MSG_PAYLOAD) return -1;
- return termsrv_msg_send(sock_fd, TSRV_MSG_CWD, path, len);
-}
-
#endif /* HAVE_TYMUX */
diff --git a/src/bin/termsrv.h b/src/bin/termsrv.h
index 25b70183..85c866a8 100644
--- a/src/bin/termsrv.h
+++ b/src/bin/termsrv.h
@@ -95,7 +95,6 @@ typedef enum {
TSRV_MSG_BACKLOG_BUF = 11, /* server → client: backlog buffer fd response */
TSRV_MSG_PAUSE = 12, /* server → client: you are paused (hijacked) */
TSRV_MSG_RESUME = 13, /* server → client: you are restored */
- TSRV_MSG_CWD = 14, /* server → client: inner-shell cwd path */
} TermSrvMsgType;
/* Attach mode — first byte of ATTACH payload */
@@ -242,10 +241,6 @@ int termsrv_send_backlog_buf(int sock_fd, int buf_fd,
uint32_t buf_id, uint32_t cols,
uint32_t num_lines, uint32_t max_lines);
-/* Send a CWD message. `path` is a non-NUL-terminated path of `len`
- * bytes; `len` may be 0 to indicate "no cwd available". Returns 0/-1. */
-int termsrv_send_cwd(int sock_fd, const char *path, uint32_t len);
-
#endif /* HAVE_TYMUX */
#endif /* TERMINOLOGY_TERMSRV_H_ */
diff --git a/src/bin/termtymux.c b/src/bin/termtymux.c
index dcd6bfb7..d60d4953 100644
--- a/src/bin/termtymux.c
+++ b/src/bin/termtymux.c
@@ -748,29 +748,6 @@ _cb_tymux_data(void *data, Ecore_Fd_Handler *handler EINA_UNUSED)
ts->cb_bell.func(ts->cb_bell.data);
break;
- case TSRV_MSG_CWD:
- {
- char *path = NULL;
- if (payload && size > 0 && size < (1u << 16))
- {
- path = malloc(size + 1);
- if (path)
- {
- memcpy(path, payload, size);
- path[size] = '\0';
- eina_stringshare_replace(&ts->last_cwd, path);
- free(path);
- }
- /* malloc failure: keep stale value rather than clear it */
- }
- else
- {
- /* Empty payload = "no cwd available"; clear any stale value. */
- eina_stringshare_replace(&ts->last_cwd, NULL);
- }
- }
- break;
-
case TSRV_MSG_EXIT:
if (recv_fd >= 0) { close(recv_fd); recv_fd = -1; }
if (ts->cb_exit.func)
@@ -1321,7 +1298,7 @@ termtymux_free(TermTymux *ts)
ts->backlog_bufs = NULL;
}
- eina_stringshare_replace(&ts->last_cwd, NULL);
+ eina_stringshare_replace(&ts->launch_cwd, NULL);
free(ts);
}
diff --git a/src/bin/termtymux.h b/src/bin/termtymux.h
index 3d26a3a8..7bcc160d 100644
--- a/src/bin/termtymux.h
+++ b/src/bin/termtymux.h
@@ -27,7 +27,7 @@ struct _TermTymux {
size_t shm_size;
Termpty *shadow_pty;
uint64_t last_seq;
- Eina_Stringshare *last_cwd; /* most recent cwd reported by daemon, or NULL */
+ Eina_Stringshare *launch_cwd; /* outer shell's cwd at switch-in time, or NULL */
Ecore_Fd_Handler *handler;
diff --git a/src/bin/tymux.c b/src/bin/tymux.c
index 08ea4d6d..ba4a9b0d 100644
--- a/src/bin/tymux.c
+++ b/src/bin/tymux.c
@@ -104,7 +104,6 @@ typedef enum {
_TSRV_MSG_BACKLOG_BUF = 11,
_TSRV_MSG_PAUSE = 12,
_TSRV_MSG_RESUME = 13,
- _TSRV_MSG_CWD = 14,
} _TsrvMsgType;
typedef struct {
@@ -289,7 +288,7 @@ _proto_recv(int fd, void **payload_out, uint32_t *size_out)
return -1;
}
- if (hdr.type == 0 || hdr.type > 14) return -1;
+ if (hdr.type == 0 || hdr.type > 13) return -1;
if (hdr.size > (1u << 20)) return -1;
if (hdr.size > 0)
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.