Circa 2003 I had only one computer. I could only aford one, so I only had one. And even if I had more, I wouldn’t know what to do with them. I dabbled in various Linux distributions, and later OpenSolaris and Illumos. Sometime in college, in Chicago, I got a second computer (a laptop to complement my desktop). That presented all kinds of difficulties with syncing. Primarily because SSH conections between computers on the university network were laggy and unreliable. We used to joke that syncing ZFS datasets over a USB thumb-drive had better throughput and latency. Finally, when SmartOS wouldn’t run on neither my desktop nor my laptop, I got a pretty big used server that ran in NYC, and the SSH connection wasn’t a problem.
From the very beginning I was a fanatically enthusiastic (and outspoken) adopter of two things: the terminal and the tiling window manager (i.e. Xmonad, i3, dwm). These two tools complemented each other very well. My typical usage of the window-manager is that terminals get tiled, while graphical things like browsers and document viewers are used in full-screen mode. However, with my adoption of the client-server model, some problems materialized. Firstly, some client-side OSes don’t have a particular window-manager, or an up-to-date version of my window manager of choice. This meant that, sometimes, switches to a new OS, necessitate a switch between window-managers, and the use of older releases. Second, the window manager might have awkward default parameters, which I’d have to configure. Third, some components would just not be installed, or wouldn’t work for some reason. Fourth, some OSes compile the WM to look for the configuration files in a very non-obvious, unlikely place — forcing me to figure out where to put the config, before I can get it working. Finally, and most annoyingly, if I lose internet connectivity, all of the ssh-sessions close, making it necessary for me to login again, and to do a dance where I chdir to the previous project directories, and re-open all of my files for editing, and so forth.
I’ve had to deal with this juggling of the software and configs enough times, when I switch client OSes, that it’s become something of a productivity bottle-neck. X11 is extremely customizable, but this customization gets tedious after a few times. And if you have more than 1 client (I also have a Mac), it becomes very tiring having to move between a flavor of tiling management on one laptop, and the Mac’s floating management on the other. Mac’s Terminal tabs are ok, but they just don’t get the job done efficiently enough.
The good news is that the tiling of terminals can be moved from the client to the server. The two most popular pieces of software for handling this are: tmux and screen. I use tmux because it came highly recommended from my colleague.
By using tmux, you effectively centralize the tiling of terminals, making this functionality available on any client-device that has the ability to establish SSH connections. Tmux is scriptable from the command-line. This means that you can automate the creation of new sessions and new terminals. You can also send key-strokes to terminals, via script. This makes it possible for tmux to — for example — create a session for each project, and open a project’s files in an editor, and automatically put you in the build-directory and run a preliminary
make all — upon a single login over SSH. I call this “jump-starting”, but that’s just me. This kind of automation is useful in both a development and administration setting.
Tmux’s defaults, however, are not really sensible. So I’ll walk you through installing it, reconfiguring it, and setting up a “jump-start” script. Also, while, tmux can be installed and run from the global zone, it is preferable that it is installed and configured in a non-global zone.
To install tmux on SmartOS, simply do the following:
That’s all there is to it. You can launch it from the command-line by typing
tmux. You’ll see that tmux has launched a new shell, and drawn a status-bar on the bottom of the terminal. If you hit
Ctrl-b d you’ll get dumped back to the original terminal which spawned tmux. If you type
tmux ls, you’ll see that the session is still active. You can re-attach to that session by executing
tmux attach -t 0.
So that’s pretty nifty. You don’t have to worry about losing connectivity. If fact, more than 1 computer can attach to the same session. This means that people can collaboratively edit code, for example. Tmux has terrible default bindings, that I won’t expand on here. We will redefine the bindings in the
.tmux.conf file, and continue from there.
Before we redefine tmux’s bindings, we should redefine the client-device’s
CAPSLOCK key to be interpreted as a
Ctrl key. On X11, you want to execute the following:
On OS X, you want to do the following:
Select the dropdown box next to
Caps Lock, and choose
Tmux expects to find a config file in
~/.tmux.conf. If it doesn’t it will use the defaults. The config file can change the tmux colors and key bindings. Here is the first section of my config file:
We set the prefix to
Ctrl-a. This way we can enter tmux’s command mode while keeping our hands on the home row.
r to the tmux command
source-file ~/.tmux.conf. Pressing
C-a r will automatically reload the config and update tmux. We also disable the mouse mode because no self-respecting programmer should use the mouse.
We tell tmux that the terminal has 256 colors supported.
On the client-side you’ll want to make sure that your terminal emulator is configured to show 256 colors and the UTF8 character set. If the UTF8 support is disabled, you’ll see weird characters every time you split a window into a pane, for example — more on that later.
You can also reset the colors. The default colors aren’t really my cup of tea. I use
Solarized everywhere I can. Everyone I’ve spoken to, either loves solarized or hates solarized. Clearly it’s a polarizing color scheme. I’m not advocating for solarized here, just showing which colors you can change.
Tmux can split a window in half either horizontally or vertically. The default keys require you to hold down
Shift. That’s a drag, so we rebind it to lower-case keys:
A window is just a box that has panes. Every window starts with 1 pane which has a shell running in it. Splitting a window vertically by hitting
C-a - will cut the current pane in half horizontally and create a second underneath the first pane.
Splitting the window horizontally, by hitting
C-a =, will split the current pane in half vertically, and create a second pane next to the current one. Obviously the terms horizontal and vertical don’t refer to the direction of the cut but to the direction in which a new pane is appended to window.
We can resize panes and move between them using the keyboard. Unfortunately, the keys for this are cumbersome to use, especially since I — as a long time vim user — instictively use the vim movement keys. Here is the section of the config that maps movement to the vim movement keys:
In tmux the unit of currency is the session. The session is what you can attach to and detach from. Each session has at least one window, and each window has at least one pane. Windows are akin to what X11 calls “workspaces”, and panes are kind of like what X11 calls “windows”.
The session is an awesome concept. There is no limit to the number of sessions you can have, while there is a limit to the number of windows you can have (10 — more than that becomes cumbersome to manage), and the number of panes you can have (your screen-size isn’t infinite).
The session is a very useful way to organize your work. For development, I open 1 session for each of my active projects. Within the session, I open a bunch of windows, such as “source”, “build”, and “git”. Within the windows, I open up to four panes. So in the “source” window, I have 4 vim instances running, each opened to a distinct source-file.
Now you may be thinking that opening a distinct session per project, and a window per task, and pane per sub-unit of work, is kind of tedious to set up. Well, it is. And having to do this every time is a major pain in the neck. The best thing about tmux is that it can be controlled from the command-line using the
tmux command. You can tell tmux to open a session, name the session after a project, and to open some named windows. You can then tell tmux to send keystrokes to the session’s windows and panes. Effectively, tmux can set up a session with all of the appropriate files opened in their windows and panes.
You can roll this sequence commands into a shell script, and execute it to automatically generate a development session. I personally, put this sequence of commands into my
.kshrc file, so that the sessions get opened the first time I log in. This effectively jump-starts my development, after I reboot the zone or the server.
Here is a sample of what tmux can do. In this example, we will use the the
smartos-live repository. Here is the
smartos-live section of the
.kshrc file. There are probably better ways to jump-start your sessions, but at the moment I am using
.kshrc to facilitate this. I’ll post an update if I figure out something more elegant — for example maybe an SMF service that does this for you.
If you put this in your
.kshrc file, log out, and log in, nothing will seem amiss. But if you run
tmux ls, you’ll see:
If you run
tmux attach -t smartos-live, you’ll be placed into a session with 3 windows: “source”, “build”, and “git”. The source-window has 4 panes, each with vim open to a specific source-file. The build-window has 2 panes, one has an instance of
vim Makefile, while the other has
vim configure.local. The git-window, has 4 panes: one in which
git status has been run, one in which
git log has been run, one in which
vim README.md has been run, and a shell that is already in the root of the repo.
To leave the session simply type:
C-a :detach. You won’t lose the session, since it’s running in the background. And you can re-attach later. You can use the above KSH code, to create other sessions. For example, you can replace
session=... with a new session-name, and the
prjprefix=... with a new project-directory. The only thing you have to change inside of the body of the if-block, is the specific file-names of the source-files (since these differ between projects). But if you know that you will always open 4 files in 4 panes, you can abstract that away too with 4 new variables.
You don’t have to worry about creating the same session twice, since the session-creating code is firmly placed within the if-block, and only runs if a session with the given name doesn’t already exist.
Also, in case it’s unclear, the
send-keys command’s syntax means the following:
We send a
Ctrl-m so that a carriage-return is passed to the pane we are targeting.
It is absolutely crucial that you put a
sleep 10 before you start executing tmux commands. Remember that this is run from
.kshrc, and tmux itself launches instances of KSH. If you don’t force a delay before running the tmux section of the script, you’ll end up with a race-condition, that creates the same windows and panes multiple times in the same session. It is sufficient to do this before creating the first session — no need to this for every session.
This post is far from a comprehensive guide to using Tmux. I strongly suggest that you read the man pages.