/ SmartOS

Configuring Readline on SmartOS

One of the more annoying differences I noticed in the switch from Linux to SmartOS was while connecting to a terminal via PuTTY: The former supports using the Home and End keys to navigate to the beginning and end of a command-line buffer while the latter does not, instead spitting tilde characters into the command-line wherever you happen to have the cursor.

My Google-fu was weak and unfocused (see: distracted) at the time, so I'd usually just spend a few minutes fruitlessly searching for an answer until deciding to give up and focus on the original problem I was working on, trying my best to ignore the fact that I couldn't zip back and forth across a multi-row command like I was used to being able to on Linux.

It finally got bad enough that I committed myself to finding a solution before I moved back to my original task. This blog post is the culmination of my findings. Thanks to geedoubleya and rofrol from Stack Exchange for their work on this answer which set me on the right track for researching this article.

Bonus: Most of this guide should apply well to any modern readline installation!

Bash and Readline

What I was used to were features of GNU Bash and more specifically GNU Readline, the library that provides the line-editing and history functionality for Bash. Fortunately, with SmartOS, my problem was one of misconfiguration, not of missing software, as both Bash and Readline are already installed in most (if not all?) SmartOS images.

You can actually confirm this by testing against the default Readline key bindings. The relevant ones are as follows:

  • beginning-of-line (ctrl-a) moves the cursor to the beginning of the line.
  • end-of-line (ctrl-e) moves the cursor to the end of the line.

And while we could just stick with those, where's the fun in limiting ourselves to that? Readline supports reconfiguration via a simple configuration file (~/.inputrc or /etc/inputrc).

Readline configuration

Readline configuration file syntax supports two common definition structures: Variables can be set with the form set <variable> <value> and key bindings can be defined with the form keyname: function-name or "key sequence": function-name forms.

Setting variables will alter the run-time behavior of Readline. Variables that I found interesting include:

  • bell-style set to none if you don't want to hear a terminal ring again.
  • colored-stats set to on if you want to see completions in different colors to indicate their file type.
  • editing mode set to either emacs (default) or vi depending on your religious affiliation.
  • enable-keypad set to on if you want to make your life easier with arrow keys.
  • expand-tilde set to on if you want to expand the tilde to a full path.
  • history-size control the number of lines stored in the history buffer (and saved to .bash_history).
  • horizontal-scroll-mode set to on will cause a command-line to scroll across the bottom of the screen instead of wrap to a new line.
  • keymap sets the overall keymap for key binding commands, options include emacs (default), emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move, vi-command and vi-insert.
  • keyseq-timeout sets the timeout duration in milliseconds for Readline to wait when reading an ambiguous key sequence.
  • mark-symlinked-directories set to on if you want symlinks to directories to have a / appended to their names.

Key bindings will allow you to map specific functions or macros to keys. Functions that I found interesting and relevant include:

  • beginning-of-line moves the cursor to the beginning of the current line.
  • end-of-line moves the cursor to the end of the current line.
  • forward-char moves the cursor forward one character.
  • backward-char moves the cursor backward one character.
  • forward-word moves the cursor forward to the end of the next word.
  • backward-word moves the cursor backward to the start of the current or previous word.
  • clear-screen clears the screen and redraws the current line at the top of the screen.
  • redraw-current-line redraws the current line in its current location.
  • previous-history moves back through the history list, displaying the previous command.
  • next-history moves forward through the history list, displaying the next command.
  • beginning-of-history displays the first entry of the history list.
  • end-of-history displays the last entry of the history list, the current line being entered.
  • history-search-backward moves backward through the history list, displaying lines that match your partially typed command.
  • history-search-forward moves forwards through the history list, displaying lines that match your partially typed command.

There's a bunch of other stuff in the documentation (like conditional blocks and a BUNCH of other bindable functions) that while I found interesting, didn't have a really practical place to apply it while writing this article. You may want to give it a proper read if you'd like to know more.

When testing your configuration, you can prompt a reload by using the key sequence Ctrl-X Ctrl-R, instead of logging out and back in again.

Guest Zone configuration

As mentioned before, the system-wide configuration file is located at /etc/inputrc which can be overridden by individual users with a file at ~/.inputrc. If you tend to switch roles into different users often, I recommend using the system-wide configuration file, as those configuration directives will be applied to all users on the system.

Global Zone Configuration

Since SmartOS Global Zones are refreshed on reboot, we'll need to set up a transient SMF manifest to load our settings into the global zone each time it boots up.

Download the above manifest to /opt/custom/smf/readline.xml and copy your inputrc file to /usbkey/config.inc/inputrc. This will ensure that your SmartOS global zone will copy /usbkey/config.inc/inputrc from persistent memory into the ramdisk (/etc/inputrc) upon each boot.

My Inputrc

While a vim user, I prefer not having to deal with that editor's intricacies while I'm in bash. Also, I tend to ssh from Windows, PuTTY handles my copy buffer, so all I really want is for readline to handle is command-line navigation. I originally wanted to do a pretty straightforward interface where basic arrow keys provided navigation and modifier keys (Alt, and Ctrl) increased magnitude, but it turns out that arrow keys in a terminal are just "Metafied" ASCII, and pressing the Alt key while pressing one of those keys is just the equivalent of a normal ASCII sequence. If I were to attempt these bindings, I would end up with normal ASCII in my terminal: Not exactly what I was looking for.

So, for now, I'm stuck using Home, End, PageUp and PageDown just like the rest of us mortals.

/etc/inputrc:

"\e[1~": beginning-of-line
"\e[4~": end-of-line
"\e[5~": history-search-backward
"\e[6~": history-search-forward
"\e[3~": delete-char