Mike Bland

Getting Go on Plan 9

After a bit of hunting and poking around, I got my Plan 9 virtual machine on the net, installed the Go programming language, and made good on a promise.

- Alexandria
Tags: Go, Plan 9, technical

Turns out I got my Plan 9 installation online and set up with Git (sort of) and the Go programming language sooner than I thought I would. Shortly after that, I fulfilled my promise to run bitly/oauth2_proxy tests on Plan 9. Here are the grotty details for anyone who cares to pursue a similar path.

By the way, I was delighted to find out that VMware Fusion’s clipboard integration with Plan 9 was seamless. Would’ve been painful to recreate these commands by other means.

Network Configuration

The first order of business: getting the network connection up. Here’s what I was confronted with the first time I tried to ping this here site:

term% ip/ping mike-bland.com
ip/ping: couldn't dial icmp!mike-bland.com!1: cs: can't translate address: '/srv/dns' file does not exist

Somehow I figured out that I needed to run the ip/ipconfig command; I think I was looking for ifconfig (per other UNIX-like systems) and came across the man page for ip/ipconfig instead. However, even though the interface was clearly up, and the DNS was configured to use my local router, and I could ping Google’s DNS server at 8.8.8.8 successfully, name resolution still wasn’t working.

term% cat /net/ndb
ip=127.0.0.1 ipmask=/104 ipgw=::
  sys=gnot

term% ip/ipconfig
term% cat /net/ndb
ip=127.0.0.1 ipmask=/104 ipgw=::
  sys=gnot
  sys=gnot
  dns=X.X.X.1

term% ip/ping mike-bland.com
ip/ping: couldn't dial icmp!mike-bland.com!1: cs: can't translate address: '/srv/dns' file does not exist

term% ndb/dns -r
term% ip/ping mike-bland.com
ip/ping: couldn't dial icmp!mike-bland.com!1: cs: temporary problem: dns: dns failure

term% ip/ping 8.8.8.8
sending 32 64 byte messages 1000 ms apart to icmp!8.8.8.8!1
0: rtt 22546 µs, avg rtt 22546 µs, ttl = 43
1: rtt 22226 µs, avg rtt 22386 µs, ttl = 43
2: rtt 22294 µs, avg rtt 22355 µs, ttl = 43

Through some creative web searching I discovered an article holding the key to Plan 9 name server configuration (which also happened to mention the video settings I described in my previous post). Setting the DNSSERVER environment variable before launching ndb/dns -r did the trick. (By the way, all environment variables are visible within the /env directory; Plan 9 takes the “everything is a file” approach to an extreme.)

term% DNSSERVER=8.8.8.8
term% ip/ipconfig
term% cat /net/ndb
ip=127.0.0.1 ipmask=/104 ipgw=::
  sys=gnot
  sys=gnot
  dns=X.X.X.1

term% ndb/dns -r
term% ip/ping mike-bland.com
sending 32 64 byte messages 1000 ms apart to icmp!mike-bland.com!1
0: rtt 12682 µs, avg rtt 12682 µs, ttl = 52
1: rtt 13069 µs, avg rtt 12875 µs, ttl = 52
2: rtt 12618 µs, avg rtt 12789 µs, ttl = 52

Patching the SSL/TLS library

Since there is no official binary release of Go for Plan 9, I needed to build it from source. Typically one would run git clone https://github.com/golang/go or some such, but as Git isn’t available for Plan 9 (a clever hack described below notwithstanding), we can instead fetch a tarball of the sources at a tagged version directly from GitHub using Plan 9’s hget program (analogous to wget or curl on other Unices), in this case Go 1.4.2:

term% hget -o go1.4.2.tar.gz https://github.com/golang/go/archive/go1.4.2.tar.gz
tlsClient: tls: local invalid x509/rsa certificate
[...snip repeated errors...]
hget: too many errors with no progress fd out of range or not open

Whoops; seems hget doesn’t grok GitHub’s SSL certs out-of-the-box. Fortunately, I found an os.plan9.general thread pinpointing the problem and its solution. The problem is apparently related to Plan 9’s libsec only understanding SHA1 signatures by default. Fortunately, it’s relatively easy to patch the Plan 9 source code using an “official” patch, which the thread helpfully described how to do:

term% 9fs sources
term% PATCH=libsec-x509-sha256rsa
term% mkdir -p $home/patch/$PATCH
term% bind -bc $home/patch/$PATCH /n/sources/patch/$PATCH
term% patch/apply $PATCH
merge...backup...copy...
to update sources:
  update /sys/src/libsec/port/x509.c

term% ls -l /sys/src/libsec/port/x509.c
--rw-rw-r-- M 8 sys sys 54397 Jun  8 06:39 /sys/src/libsec/port/x509.c

With the patched file in place, I figured out by reading how to compile a Plan 9 kernel the proper commands for recompiling libsec:

term% cd /sys/src/libsec
term% mk -e -n
[...snip...]
ar vu /386/lib/libsec.a ...  x509.8 ...
[...snip...]

From there, it was easy to recompile hget and copy it over:

term% cd /sys/src/cmd
term% mk -e hget
hget.8(0) < /386/include/u.h(1367352099)
hget.8(0) < /sys/include/libc.h(1379544328)
hget.8(0) < /sys/include/bio.h(1367352282)
hget.8(0) < hget.c(1265743728)
8^c -FTVw hget.c
8.hget(0) < hget.8(1433760689)
8^l  -o 8.hget hget.8
hget(0) < 8.hget(1433760689)
mv 8.hget hget

term% ls -l hget
--rwxrwxr-x M 8 glenda sys 247569 Jun  8 06:51 hget
term% ls -l /bin/hget
--rwxrwxr-x M 8 sys sys 244009 May 15  2014 /bin/hget
term% cp hget /bin/hget

Getting and Installing Go

With a patched libsec and recompiled hget supporting SHA2 signatures, it’s now a straightforward affair to grab the Go sources from GitHub and build the code:

term% hget -o go1.4.2.tar.gz https://github.com/golang/go/archive/go1.4.2.tar.gz
term% ls -l
--rw------- M 8 glenda glenda 10971897 Jun  8 06:52 go1.4.2.tar.gz

term% gunzip -c go1.4.2.tar.gz | tar x
term% ls
go-go1.4.2
go1.4.2.tar.gz
pax_global_header

term% ls go-go1.4.2
go-go1.4.2/.gitattributes
go-go1.4.2/.gitignore
go-go1.4.2/.hgignore
go-go1.4.2/.hgtags
go-go1.4.2/AUTHORS
go-go1.4.2/CONTRIBUTORS
go-go1.4.2/LICENSE
go-go1.4.2/PATENTS
go-go1.4.2/README
go-go1.4.2/VERSION
[...snip...]

term% cd go-go1.4.2/src
term% ./all.rc
# Building C bootstrap tool.
cmd/dist

# Building compilers and Go bootstrap tool for host, plan9/386.
lib9
libbio
liblink
cmd/cc
[...snip...]

So the Go compiler and libraries built successfully, and all the tests passed…except the final multithreaded runtime test, which deadlocked and aborted after four minutes. Oh well, can’t have everything. I went ahead and set up the GOPATH and path environment variables and verified that the go tool was accessible (haven’t figured out what the equivalent to $HOME/.bashrc is for the rc shell yet):

term% cd
term% mkdir go
term% GOPATH=$home/go
term% echo $GOPATH
/usr/glenda/go
term% path=($path $GOPATH/bin $home/src/go-go1.4.2/bin)
term% echo $path
. /bin /usr/glenda/go/bin /usr/glenda/src/go-go1.4.2/bin
term% go version
go version go1.4.2 plan9/386

Getting Git (sort of)

There’s one last tool we need before we have a functioning Go environment: Git. Problem is, there’s no Plan 9 port of Git, and apparently producing one would take a non-trivial amount of effort. Fortunately, the maintainer of the Plan 9 port of Go hacked together an rc shell script to approximate the Git commands necessary to clone the repo and support go get:

term% hget -o $home/bin/rc/git http://9legacy.org/9legacy/tools/git

term% git clone https://github.com/golang/go ./go
term% ls go
go/.git
go/.gitattributes
go/.gitignore
go/AUTHORS
go/CONTRIBUTING.md
go/CONTRIBUTORS
go/LICENSE
go/PATENTS
go/README.md
[...snip...]

Bear in mind, however, this clone contains the master branch, not a stable release, which is what is needed to run the continuous integration servers. Hence, we built Go earlier using the tarball for version 1.4.2. (Still, later I may build this version and see if the deadlock in the runtime test goes away.)

With both the git script and the go binary in place, now we’re ready to run go get to fetch a bunch of helpful tools:

term% go get golang.org/x/tools/cmd/...
# golang.org/x/tools/cmd/stress
go/src/golang.org/x/tools/cmd/stress/stress.go:65: undefined: syscall.SIGABRT

Whoops. Looks like someone didn’t set their build tags properly for Plan

  1. At least we can still grab godoc:

    term% go get golang.org/x/tools/cmd/godoc

Building and Testing bitly/oauth2_proxy

Finally, we’re ready for the moment of truth…

term% go get github.com/bitly/oauth2_proxy
term% oauth2_proxy -h
Usage of oauth2_proxy:
  -authenticated-emails-file="": authenticate against emails via file (one per line)
[...snip...]

Success! We fetched, built, and successfully executed oauth2_proxy on Plan 9! Now, finally, to run the bitly/oauth2_proxy tests on Plan 9 as I’d promised:

term% cd $GOPATH/src/github.com/bitly/oauth2_proxy
term% go test ./...
# github.com/bitly/oauth2_proxy
cookies_test.go:5:2: cannot find package "github.com/bmizerany/assert" in any of:
  /usr/glenda/src/go-go1.4.2/src/github.com/bmizerany/assert (from $GOROOT)
  /usr/glenda/go/src/github.com/bmizerany/assert (from $GOPATH)
FAIL  github.com/bitly/oauth2_proxy [setup failed]
[...snip...]

Whoops, forgot to run go get -t to install the test dependencies:

term% go get -t
term% go test ./...
ok    github.com/bitly/oauth2_proxy 0.968s
ok    github.com/bitly/oauth2_proxy/api 0.722s
ok    github.com/bitly/oauth2_proxy/providers 1.220s

Success again! Of course, just to make certain, let’s run the test.sh script that comes with the oauth2_proxy sources (rc helpfully ignores what it doesn’t understand and runs the rest in this case):

term% cat test.sh
#!/bin/bash
set -e

go test -timeout 60s ./...
GOMAXPROCS=4 go test -timeout 60s -race ./...

term% rc ./test.sh
set: '/usr/glenda/src/go-go1.4.2/bin/set' does not exist
ok    github.com/bitly/oauth2_proxy 0.970s
ok    github.com/bitly/oauth2_proxy/api 1.245s
ok    github.com/bitly/oauth2_proxy/providers 1.274s
go test: -race is only supported on linux/amd64, freebsd/amd64, darwin/amd64 and windows/amd64

What’s next?

So I’ve managed to reach my goal of building and executing the oauth2_proxy tests on Plan 9. What else am I to do with Plan 9 now?

There’s nothing imminently practical for me to do, but I do love noodling about with different operating systems to see what tools are available, what design philosophies shaped the environment, and how easy (or not) they are to use. In the short term, I’m fascinated by the acme terminal/window manager/text editor. It seems a powerful tool that I’ve yet to fully appreciate, and may give me a new way of looking at things.

Funny thing is, I don’t necessarily need to run Plan 9 to try acme, thanks to the Plan 9 from User Space project. In fact, it’s even available via Homebrew for OS X. Fun!