Preserving (some) session state with tmux and bash

December 15, 2010. This describes some preliminary work I'm doing to make reboots less painful by preserving some session state using tmux and bash. The preserved state is working directory and command history. Better that nothing.

The Problem

I work by physical context. Usually between 10 and 30 xterms running at once, in different virtual spaces across several monitors. Each of those used to run gnu screen, but I'm now (December 2010) starting to switch to tmux. Anyhow, the point is that location matters: each xterm is in its proper place, and that's how I organize my work and remember what I'm working on.

Reboots are infrequent, but they happen. Reboots cost me hours of lost work. This article describes some tricks I'm playing with to preserve the most important part of my context: working directory and command history. This is a work in progress.

Overview

The key players are tmux and bash. And the key concept is a per-window history file ("window" in the sense of tmux windows). What I care about most is my bash history and my current list of directories (pushd/popd) in a given window. This article provides a way to preserve that across reboots, and reestablish enough of a working context to save me a few hours.

Let's say I have two xterms, l and r (for left and right). Each of those is running a tmux session, and each of those sessions has four tmux windows:

                 l                                    r
    +---------------------------+        +---------------------------+
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |                           |        |                           |
    |  0:root 1:w1  2:n  3:x    |        | 0:root 1:cpan 2:foo 3:y   |
    +---------------------------+        +---------------------------+

In my workflow, the window names are pretty constant. So I address the above as l.root, l.w1, and so on. Note that l.root and r.root are distinct.

Here comes a 2-hour power outage, or an upgrade to a new kernel. On reboot, I can get my sessions back via these commands from my .xsession:

  tmux-session new l.root l.w1   l.n   l.x
  tmux-session new r.root r.cpan r.foo r.y

Those are permanent windows in my setup. If I had been working on a temporary window at the time of the crash, and the window was called devel.bz123456, I could restore it from any shell by typing:

   $ tmux-session new devel.bz123456

When each of those windows comes up, it will:

  • be cd'ed to the same place as when the window closed;
  • have the same directory stack (pushd); and
  • have the command history for that exact window.

It's not perfect: terminal output and scrollback is lost, and so is environment. But it's good enough for me, for now.


How It Works

Your .bashrc, asks tmux "what's my window name?" If tmux gives the right kind of answer, bash sets a custom $HISTFILE for this shell. And that's really about it.

Make It Happen

Download .bashrc.tmux (login dotfile) and tmux-session (perl script). Put the dotfile in your home directory and the script in your $PATH.

Add this to your .bash_profile if you have one; otherwise add it to whatever dotfile runs on login shells:

if [ ! -z "$PS1" ]; then
    _tmuxrc="$HOME/.bashrc.tmux"
    if [ -e $_tmuxrc ]; then
        source $_tmuxrc
        _tmux-init-history
    fi
fi

Then quit all your tmux sessions and start some new ones using tmux-session new. (You can do them by hand, or differently, later. For now just follow what I say):

$ mkdir --mode=0700 ~/.bash_history.d

$ tmux-session new aaa.bbb aaa.ccc
$ tmux attach aaa

You're now in tmux with two windows. Swap between them, type some commands. Change directories. Exit. That wraps up your tmux session. Then repeat the last two commands. Do some up-arrows, a pwd. You're back in the same place, sort of.


Further Reading

Read the source. Enjoy.

Also left as an exercise for the reader:

  • Using PROMPT_COMMAND to flush history after each command (e.g. to survive crashes).
  • Finding a way to handle new windows created via ESC-c (which will be called "bash" by default).
  • Handling window renames (probably via .logout)

Created: 2010 December 15
Last updated: 2010 December 16.