A while ago I discovered tmux, a nice terminal multiplexer that I choose to adopt as a modern replacement to GNU screen. Here I don't want to begin an holy war debating which of the two is actually the best, tmux was the simply choice I made; more interesting is, I hope, the way I ended up using it after few months of struggle.

I often connect to remote servers and issue long-running commands, inadvertently killing them due to a mistaken log-out (probably due to a premature killng of the terminal); but even when I'm not so reckless, I would yet find useful to be able to interrupt a remote session and restart it in a second moment, perhaps reconnecting from a different client. Of course, tmux can be an excellent solution to both problems, provided some attention is posed.

First of all, I added the following to the end of .bash_profile:

if [[ $PS1 && $SSH_TTY && -z $TMUX && $1 != $3 && ! -r ~/.notmux ]]; then
        if ! tmux has-session -t remote; then
                exec tmux new-session -s remote
                exec tmux \
                        new-session -d -s remote_$$ -t remote \;\
                        new-window \;\
                        attach \;\
                        set-option destroy-unattached on

the external test ensures that:

  • bash is running interactively,
  • we are in a ssh session, but
  • not in a tmux one (to avoid nesing the multiplexer),
  • not connecting from the same host (again, to avoid nesting; we can't test such case using the TMUX variable since it will not propagate in the environment across the ssh connection ),
  • and no safety file is present (more on this later).

Finally, if a session named "remote" does not exist we create (and attach to) it; on the other hand, we need to create a new "child" session (remember that in tmux all clients connected to a session share the same active window), add a new window to it, attach to it and instruct it to vanish when we'll detach from it.

The purpose of the safety file is to be able to login normally (without multiplexing at all) in case something wents bad with the configuration above; in such a case, it would be enough to run ssh ... 'cat > .notmux' before connecting again interactively.

Once attached, one can "detach" or "exit" leaving the current window (and child session) alive or, respectively, destroying them; a couple of aliases in .bashrc do the job:

alias detach='[[ $TMUX  ]] && tmux has-session -t remote && tmux detach-client'
alias exit='[[ $TMUX  ]] && tmux has-session -t remote && tmux detach-client \; kill-window'

One last thing to fix is the handling of ssh agent forwarding. What can happen (a common issue also with screen) is that SSH_AUTH_SOCK gets defined during the first connection and then (once such connection is closed) becomes "stale" in all subsequent connections. To fix would suffice to grab the value of SSH_AUTH_SOCK from the enclosing environment and use it to update the one in the running session. Fortunately, tmux comes in handy with its show-environment command and update-environment option. The following alias does the trick:

alias usa='[[ $TMUX ]] && tmux has-session -t remote && eval $(tmux show-environment -t remote | grep ^SSH_AUTH_SOCK)'

So, when ssh asks your passphrase even if it is not supposed to (since you are forwarding the agent), just kill it, run usa (acronym for "update ssh agent") and retry.