From 411807efad24d5795839c4c707534c1ef9b9ce60 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 16 Jun 2016 17:33:43 +0100 Subject: [PATCH] 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. --- usr/lib/byobu/include/select-session.py | 29 ++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/usr/lib/byobu/include/select-session.py b/usr/lib/byobu/include/select-session.py index 0fb28c9f..930736b4 100755 --- a/usr/lib/byobu/include/select-session.py +++ b/usr/lib/byobu/include/select-session.py @@ -85,6 +85,32 @@ def get_sessions(): i += 1 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): backend, session_name = session.split("____", 2) @@ -101,9 +127,10 @@ def update_environment(session): def attach_session(session): update_environment(session) backend, session_name = session.split("____", 2) + cull_zombies(session_name) # must use the binary, not the wrapper! 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: os.execvp("screen", ["", "-AOxRR", session_name])