Add support for tmux grouped sessions

The default tmux behaviour when connecting multiple clients is a little
unexpected. All of the clients are connected to the same view. When the
view on one client is changed, all the other client views get changed
also. With this behaviour it isn't possible to have a different view in
each client connection as one would have with multiple xterm windows.
This differs from the behaviour of screen where the viewport of each
client is independent.

Fix this by making use of the session group feature in tmux. With
session groups, each client creates a new session, but groups it with
an existing session so it can share windows.

However, when a client detaches from a session in a session group, the
session instance doesn't get removed. If one attaches and detaches from
tmux many times, a lot of unused sessions will accumulate (as seen with
the 'tmux list-sessions' command). Solve this by adding a "cull_zombies"
step when connecting to a session. When attaching to a session,
cull_zombies() checks if there are any detached group sessions that
should be killed.
This commit is contained in:
Grant Likely 2016-06-16 17:33:43 +01:00
commit 411807efad

View file

@ -85,6 +85,32 @@ def get_sessions():
i += 1 i += 1
return sessions return sessions
def cull_zombies(session_name):
# When using tmux session groups, closing a client will leave
# unattached "zombie" sessions that will never be reattached.
# Search for and kill any unattached hidden sessions in the same group
if BYOBU_BACKEND == "tmux":
output = subprocess.Popen(["tmux", "list-sessions"], stdout=subprocess.PIPE).communicate()[0]
if sys.stdout.encoding is None:
output = output.decode("UTF-8")
else:
output = output.decode(sys.stdout.encoding)
if not output:
return
# Find the master session to extract the group number. We use
# the group number to be extra sure the right session is getting
# killed. We don't want to accidentally kill the wrong one
pattern = "^%s:.+\\((group \\d+)\\).*$" % session_name
master = re.search(pattern, output, re.MULTILINE)
if not master:
return
# Kill all the matching hidden & unattached sessions
pattern = "^_%s-\\d+:.+\\(%s\\)$" % (session_name, master.group(1))
for s in re.findall(pattern, output, re.MULTILINE):
print("Killing", s.split(":")[0]);
subprocess.Popen(["tmux", "kill-session", "-t", s.split(":")[0]])
def update_environment(session): def update_environment(session):
backend, session_name = session.split("____", 2) backend, session_name = session.split("____", 2)
@ -101,9 +127,10 @@ def update_environment(session):
def attach_session(session): def attach_session(session):
update_environment(session) update_environment(session)
backend, session_name = session.split("____", 2) backend, session_name = session.split("____", 2)
cull_zombies(session_name)
# must use the binary, not the wrapper! # must use the binary, not the wrapper!
if backend == "tmux": if backend == "tmux":
os.execvp("tmux", ["", "-2", "attach", "-t", session_name]) os.execvp("tmux", ["", "-2", "new-session", "-t", session_name, "-s", "_%s-%i" % (session_name, os.getpid())])
else: else:
os.execvp("screen", ["", "-AOxRR", session_name]) os.execvp("screen", ["", "-AOxRR", session_name])