Detect concurrent tmux servers via sockets, not ps argv

The previous heuristic in `all_tmux_processes` grepped `ps` output for
lines starting with the literal string "tmux". This had two failure
modes that both broke auto-save in practice:

  - False negative: tmux invoked via absolute path
    (e.g. `/opt/homebrew/bin/tmux ...` on macOS Homebrew installs)
    never matched `^tmux` and was invisible to the count.

  - False positive: every client process whose argv[0] started with
    "tmux" was counted as a server. Any single tmux server with two
    or more clients attached would trip the "another server running"
    gate, causing continuum to silently skip injecting its save
    interpolation into status-right and disable auto-save until the
    extra clients went away.

The post-startup branch of `another_tmux_server_running` tried to
compensate by subtracting `tmux list-clients` from the ps count, but
that only listed clients of the current server and inherited the same
ps-matching weaknesses.

Replace with socket-based counting. Each tmux server owns exactly one
Unix domain socket in its socket directory; a live server responds to
`tmux -S <socket> list-sessions`. Stale socket files left over from
crashed servers do not respond and are correctly excluded.

This collapses the dual-mode wrapper in continuum.tmux: the same check
is correct on startup and afterward, so the post-startup branch goes
away.

Removes: all_tmux_processes, number_tmux_processes_except_current_server,
number_current_server_client_processes.
Adds: current_tmux_socket_path, number_other_live_tmux_servers.
Public function names (another_tmux_server_running,
another_tmux_server_running_on_startup, current_tmux_server_pid) are
preserved so external callers like continuum_restore.sh keep working.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Cosmin Radu 2026-05-09 17:23:16 -07:00
parent 0698e8f4b1
commit 210520abc1
2 changed files with 41 additions and 24 deletions

View file

@ -19,12 +19,7 @@ handle_tmux_automatic_start() {
}
another_tmux_server_running() {
if just_started_tmux_server; then
another_tmux_server_running_on_startup
else
# script loaded after tmux server start can have multiple clients attached
[ "$(number_tmux_processes_except_current_server)" -gt "$(number_current_server_client_processes)" ]
fi
another_tmux_server_running_on_startup
}
delay_saving_environment_on_first_plugin_load() {

View file

@ -22,28 +22,50 @@ current_tmux_server_pid() {
cut -f2 -d","
}
all_tmux_processes() {
# ignores `tmux source-file .tmux.conf` command used to reload tmux.conf
local user_id=$(id -u)
ps -u $user_id -o "command pid" |
\grep "^tmux" |
\grep -v "^tmux source"
current_tmux_socket_path() {
echo "$TMUX" |
cut -f1 -d","
}
number_tmux_processes_except_current_server() {
all_tmux_processes |
\grep -v " $(current_tmux_server_pid)$" |
wc -l |
sed "s/ //g"
}
# Counts other live tmux servers running for this user.
#
# A tmux server holds its socket FD with the socket's filesystem path
# in lsof's NAME column; a client only holds a peer-pointer connection
# (no path). Unique path-shaped NAMEs across `tmux` processes equal
# the set of servers. Each candidate is probed with `list-sessions` to
# exclude stale socket files from crashed servers.
#
# Falls back to scanning the current socket's directory if lsof is
# unavailable (covers default-dir cases, misses `-S /elsewhere/path`).
number_other_live_tmux_servers() {
local current_socket socket_dir count sock candidates
current_socket="$(current_tmux_socket_path)"
count=0
number_current_server_client_processes() {
tmux list-clients |
wc -l |
sed "s/ //g"
if command -v lsof >/dev/null 2>&1; then
# Pull every absolute-path NAME column from `lsof -U` rows where
# the command is `tmux` and the user is the current user. Dedupe.
candidates="$(lsof -U -a -c tmux -u "$(id -u)" 2>/dev/null \
| awk 'NR>1 && $NF ~ /^\// { print $NF }' \
| sort -u)"
elif [ -n "$current_socket" ]; then
# Fallback: scan the current socket's directory only.
socket_dir="$(dirname "$current_socket")"
candidates="$(find "$socket_dir" -maxdepth 1 -type s 2>/dev/null)"
else
echo 0
return
fi
for sock in $candidates; do
[ "$sock" = "$current_socket" ] && continue
if tmux -S "$sock" list-sessions >/dev/null 2>&1; then
count=$((count + 1))
fi
done
echo "$count"
}
another_tmux_server_running_on_startup() {
# there are 2 tmux processes (current tmux server + 1) on tmux startup
[ "$(number_tmux_processes_except_current_server)" -gt 1 ]
[ "$(number_other_live_tmux_servers)" -gt 0 ]
}