It's user error.

Interlude: Inferno at Work

And here is this, instead of any of the things that I said I’d post. It might be helpful to people who end up in the same situation as me, and want to use Inferno at work, and in particular to use Inferno on OSX. Hostnames have been changed to protect the innocent. I’m going to call this an “Interlude” or perhaps even non-canon. This entry is part how-to and part introduction to using Inferno for development, and it jumps between a few topics.

New Job

I’ve started a new job, which is why it’s been a bit quiet here. It’s a web-based company, and I’m writing Ruby for its back-end. It’s a bit implausible to sneak Inferno into the codebase for now. (Although we’re relatively technology-agnostic in theory, so if a use case pops up where it looks like Inferno is the tool for the job…)

Using Inferno Anyway

I can use whatever I want on my machine to do my dev work, right? I’m using Inferno. It’s been a bit of an ordeal for various reasons that other users of the OS might also encounter. Hopefully it’s useful to people running into the same problems.

“Why would you do that?”

MacOS X. (Please see the disclaimer at the bottom if you are preparing an angry reply.)

They gave me a shiny, new Macbook Pro. I’m not a huge fan of the UI, and the shiny, new Macbook is more frustrating than previous shiny, new Macbooks I have been issued at work: it won’t boot Linux last I checked.

I’ve been doing a lot of work in Inferno, though. I’ve built up a lot of tools and knowledge about Linux over the years, but Inferno’s a much nicer environment than OSX or Linux, so I’ve been using it somewhat heavily as my development environment at home. Being virtualized, Inferno will (or should) work the same on OSX, so why not try it out as my environment at work?

I was not quite used to it, but since the hard bits of programming are not actually editing and running the code but thinking, I thought that the productivity loss whould be low. As a pleasant surprise, the productivity loss was actually negative.

Building Inferno

You have to install XCode to get a workable GCC, and you’ll need a workable GCC, since CLang (the LLVM C compiler, the default on newer versions of OSX) doesn’t compile Inferno. You will need XCode, however, to get the 10.something headers and libraries. The whole process is shockingly manual: register iTunes, tell it to download XCode, tell it to install XCode, open XCode, tell it to install GCC, dialogs and progress bars flying all over. I get jabs on occasion from Mac guys (not always unprovoked) about using Linux with a ports-based package system, but the OS does come with a compiler and I don’t have to click dialog boxes or be present while software is installed.

Once you’ve got GCC, you’ll need to tweak the mkfiles somewhat, since Apple has recently introduced some incompatibilities. Here’s a patch for the temporary issue, until Inferno’s fixed.

After that, starting up Inferno and hitting command-F (to run Inferno full-screen) makes the Mac look like an Inferno terminal. Perfect! I can lie to myself about what’s actually running on the computer.

Ports Blocked

Once I got Inferno installed and running, I hit another problem. My ISP seems to be filtering ports in the range Inferno uses (some of which happen to coincide with common ports for running IRC servers). I’m lucky enough to have a Linode server, so I set up some tunnels on my desktop machine:


sho="ssh -t -C -o 'ExitOnForwardFailure yes'"
# This is essentially a list of every Inferno-specific port,
# from /lib/ndb/services.
for i in 6666 6667 6668 6669 6670 6671 6672 6673 2202 6674 6660 6675 6676; do
	sho="$sho -R0.0.0.0:$i:localhost:$i"

sho="$sho some-special-username@linode"
echo $sho
exec sh -c "$sho"

This also required that I set up some-special-username (whose shell, incidentally, is top) and add GatewayPorts clientspecified to the Linode server’s sshd_config, of course.

Home away from home

Now that I can get home, I needed a little script that wouldn’t make it a pain.

% cat $home/dis/hh
#!/dis/sh -n

load std

(hnet args) = $*
and {~ 0 $#hnet} {hnet = tcp!linode}

or {ftest -f $home^/keyring/^$hnet} {getauthinfo $hnet}
mount -C ideacbc/md5 -c -k $hnet $hnet^!styx /n/home-machine

That did the trick nicely. (A previous post covers the mounting and the getting of auth info.) The reason for the getauthinfo(8)‌ is that I don’t persist the key to my desktop on my work computer. I usually keep the key in memfs(4)‌ so that it is visible from whatever namespace. getauthinfo also contains a mechanism for keeping the key in memory and serving it as a file, which gives you a little more control over which process can see the key. The -C option to mount(1)‌, if you’ve forgotten or not read the man page, lets you encrypt connections and specify the algorithm.

Port fail^H^H^Horwarding

One more problem is that ssh’s port forwarding mechanism drops connections it decides are idle. So if I happened to go a long enough time without saving a file, I’d find the filesystem had disappeared. I believe there’s some ssh mechanism for fixing this, but I got around that with the following small Inferno shell script:

wm/sh -n -w 400 -h 80 -c 'load std; while {} {ls -lt|sed 1q;date;sleep 29 || exit}'

Not exactly clean, but it keeps me from having to remount. It pops up a little shell window that shows the most recently changed file in the current directory every 29 seconds. 29 isn’t magical; I just try to choose unique prime numbers for things that happen at regular intervals so that they’re marginally less likely to repeatedly collide with other things that happen at regular intervals. (This is obvious in retrospect, but it didn’t occur to my cron-like concept of “regular intervals” until I read an interesting article relating design and prime numbered intervals.)

Stinking VPN: We do not need one of those

Now that /n/home-machine (in reality, the names for the machine and the mountpoint are much shorter) has my home machine’s filesystem, I have access to its /net. /net contains the files used to interact with the network; no special system calls are needed for Inferno (or Plan 9) when talking to the network, you might recall. And since the namespace is simple to manipulate, all network traffic can be tunneled through the remote machine simply by doing this:

bind -bc /n/home-machine/net /net

This means that all network traffic originating in that shell and its children gets tunneled through the encrypted connection to my desktop at home. OpenVPN? HTTP proxies? Those and similar software (even NAT!) get replaced by a single line of shell. Not to mention that this is per-process, so you can turn the tunnel on or off arbitrarily for some processes, tunnel traffic through some other network for other processes, and so on.

In which is described what Pete is accustomed to, followed by digressions

My usual setup is the very typical for “Linux guys”: screen (with the vertical split patch as recommended by K. Mandla, whose recommendations for Linux I recommend), occasionally dvtm and dtach, vim, ctags, and rxvt-unicode, which emulates xterm to some extent, which emulates a DEC VT100 from 1978, which emulates a Teletype ASR-33 from 1963 (more photos are available for the interested), which put the output of commands onto physical paper and is the reason why your shell is talking to some file called /dev/tty. (In fact, to some extent, screen and dvtm must also emulate those terminals.) I do use a smattering of other things; for example, Emacs guys at work make (good-natured) jokes at my expense for using ed for the occasional editing task. Emacs, by the way, also has a VT100 emulator.

A generous viewer of the TTY situation might pause to reflect that nearly 50 years of (roughly) backwards-compatibile interfaces a serious achievement. Certainly it is, but interfaces having made some serious leaps and bounds since 1963, a different take on the situation is that there are better ways to present a textual interface to a user. The shell windows (which are not terminals in the sense people usually mean in Unix) in Plan 9, Inferno, Acme, etc., take their heritage from famous curmudgeon Rob Pike’s Blit terminal. The case is better made in his 1994 paper on Acme.

Acme is a different thing from those things

Acme won’t run in a VT-100 emulator, of course. It even tries by default to throw variable-width typefaces at you. (One concession I’ve not made to it; code seems easier to me to read in fixed-width typefaces. Among other things, it’s easier to count spaces.)

I had, when I started doing this, still not quite gotten used to Acme; I am more at home with sam(1)‌. However, there is no sam for Inferno, although there is an ed and MJL has done a graphical vi called vixen. As I said before, editing the code is not the hard part of programming, so a mostly unfamiliar editor was really only a minor hassle.

I’ve gotten used to Acme, having made it to 90% of my vi competence level in just a couple of weeks’ time, and the learning curve is leveling off. I’m now doing all of my editing in it at work, and most of the editing I do at home. (The main drawback is the three-button mouse requirement, which can sting on a laptop.) It’s very simple to learn, very simple to use, and feels natural in an inordinately small amount of time. Having the whole Inferno environment available is by itself a huge productivity boost (among other things, editing remote files is obviously a better experience), even if Acme were not a very nice editor. A lot of my use of vi comes from typing :%!, which can be done in Acme with Edit ,|.

I really only miss ctags-like functionality, but I suspect that getting slightly better at scripting Acme’s innards will help with producing a suitable replacement. Acme does understand the typical path/to/file:51 that most compilers and interpretors spit out when an error occurs, though. Acme’s source is in /appl/acme, of course, and a fairly simple read, although for all its simplicity, it is the largest single application to ship with Inferno.

I recommend giving the editor a shot if you feel like trying something new out. You are, of course, more likely to be at home if you like the shell and pipes rather than IDEs. There is a fork of Inferno called Acme-SAC that uses Acme as its primary UI, and Plan 9 from User Space contains Acme and sam, among other things.

Filesystems, again!

There are some things that Acme can do that other editors would require a series of hacks for, though. Since the focus in Inferno is on filesystem-based interfaces, Acme does, of course, provide one. Running Acme’s win command, or just executing wm/sh& from inside Acme, will give you a shell that can see /mnt/acme. Like any other piece of the namespace, you can export(4)‌ the /mnt/acme filesystem as well.

What can you do with this filesystem? Since Acme exposes anything one might care to do as part of the filesystem, really you can do anything you can do in Acme: highlight text, undo changes, execute a search, etc. You can completely control the editor from the shell, or even remotely from across the internet. The editor as a whole can be controlled from the top level of the filesystem, and there is a directory for each open Acme window, for controlling that window specifically, getting the contents of buffers, etc. Opening up Acme and then reading /mnt/acme/1/body will read the in-editor text of the first window which can be useful if the text is not saved or even associated with a file. Writing to it will put the text into the window, so arbitrary scripts can be used to manipulate text. There’s a special directory new that opens a new window when it is touched, so the output of a command can be opened up in a new editor window by redirecting its output to /mnt/acme/new/body. For a trivial example, let’s rot13 the current snarf (clipboard) buffer:

tr a-zA-Z n-za-mN-ZA-M < /chan/snarf > /mnt/acme/new/body

The possibilities are, of course, only limited to things you can imagine doing to a text file. If that is of interest to you and you’d like to know more, comprehensive documentation can be found on the acme(4)‌ man page. It is possibly obvious at this point, but it bears stating that you may script Acme in whatever language you wish, since much of the scripting in the editor happens by means of pipes with the Edit command or by interacting with its filesystem.

Basic interoperability with the host OS

There are some things that you can’t do with Inferno, however, and among them are “running Ruby”, and “using Postgres”, which I still need for my work. (Technically, you can do those things by linking them into emu, but that is more work than it looks like.) For just running commands from the shell, Inferno does provide an os(1)‌ command, which is more than happy to run Ruby. A number of commands that one might wish to run require some cajoling to produce a prompt if they decide there’s no TTY and infer that the program is not interactive, but things work well overall.

Another option, one which I’ve had some fun with, is to start up Xvnc, run matchbox for the window manager (no borders, no toolbar) and rxvt-unicode for the terminal, and use vncv on Inferno. This gets you a little window right there next to all the other windows that are running Inferno programs, although this window has a Linux terminal emulator inside it and can, for example, attach to a screen session where IRB (Ruby’s REPL) and the Postgres shell are waiting patiently for user input. Somewhere along the chain, through OSX, Inferno, vncv, and Xvnc, the meta keys occasionally get confused, so although it is fun, I don’t use this solution much.

Music players and slightly less basic interoperability

One area where Inferno has stumbled a bit is sound, so I’m still using CMUS to listen to my music. This presents a minor annoyance, since it requires popping out of the Inferno environment to tell the music player what to do.

…Or does it?

CMUS comes with cmus-remote, which accepts commands to the music player on standard input and sometimes prints to standard output, so let’s say that on one machine you run this:

listen 'tcp!*!9022' { os cmus-remote }

and on another, you run this:

dial -k tcp!the-other-box tcp!the-other-box!9022 { echo player-play }

Those two one-line scripts are a music player being exposed (with authentication and everything, including even encryption if necessary) to the network, and a command being sent to it.

Now, the dial line, as simple and fun as it is, would get a bit tedious to type if you wanted to, say, skip the next five songs.

For a very simple UI, you can add buttons to the shell window, just under the title bar, by echoing some commands to /chan/shctl, so my first pass was to do just that:

fn ncmus {
	args := $*
	dial -k tcp!the-other-box tcp!the-other-box!9022 { echo $args }

fn nc-button {
	(name cmd) := $*
	echo ${quote button $name 'ncmus '^$"cmd^'
'} >/chan/shctl

nc-button `{unicode 21d0} player-prev
nc-button `{unicode 25a1} player-stop
nc-button `{unicode 25b7} player-play
# ...And so on, for all the player's functions that I wanted easy access to.

This created a nice little row of buttons that I could use to remotely control the music player. It’s not quite as convenient as a real interface, though, so I ended up doing a little Limbo application, using Tk. It’s a bit lengthy for a subsection of a post, and of somewhat limited application, but it does cover a few important APIs from a Limbo perspective, so I’d like to cover it (or something like it) in the future.

Note that `{unicode 21d0} appearing in the source to that script instead of ⇐ is not an Inferno thing, but is due to a quirk of the stack I’m using for my blog. Inferno being totally fine with UTF-8, you can actually use ⇐ as the name of a variable or function in the shell or in Limbo. If it wasn’t doing its job so well despite this quirk, I’d do an Nth rewrite of the blog in Limbo. I’d almost certainly do a rewrite if someone (perhaps me, if I end up with more spare time) will do a port of werc to Inferno.

Do that to Everything

Of course, the combination of os, listen, and dial will work for any program that reads from standard input and writes to standard output, including /bin/sh. (Don’t forget to add some encryption and digestion via -C to the dial if you are doing anything more sensitive than controlling a music player. You can force this on the server side with the -a argument to listen.) They need a bit of trivial tweaking if, for example, you want interactivity on the client side or for a single long-running command to be exposed at the same address on the server side, but there you have it: arbitrary commands in Inferno or the host OS, exposed securely across the network to authenticated users.

Grid stuff is actually next this time

I have, without remorse, broken promises I made to the internet, among them that any minute now, I’d get into the more interesting bits of distributed computing in Inferno. That entry’s half-written. For that article, I’m indebted again to Alex Efros for his help with scripts and example code, particularly with the registry. But, yes, barring the unforseen, that actually really is coming.

Postscript/Disclaimer: “Pete, you should love OSX, it is pretty great.”

Tried it, didn’t like it. This post is intended to be mostly informational rather than an opinion piece. For the convenience of those who have trouble with this, I have prepared the following Javascript to protect your delicate sensibilities:

window.document.body.innerHTML = (window.document.body.innerHTML.
	replace(/MacOS X|OSX/g, "Windows XP").
	replace(/Macbook( Pro)?/gi, "Compaq Presario"));

Post-postscript: Hacker News

4ad has submitted this article to Hacker News:

Tags: inferno

<< Previous: "An Aside about Uriel"
Next: "Try Inferno without Installing It" >>