This document intends to help getting started with Inferno. The topics listed below are covered. The sections do not depend on each other, so can be read in a different order.
Keep in mind this is only an introduction and thus limited in scope. If something is not described clearly or just plain wrong (even the smallest typo), please let me know at mechiel@ueber.net.
Inferno is an operating system for distributed systems. It was created at Bell Labs and Vita Nuova now owns the rights to it, and maintains the code. The source code is available under a combination of liberal open source licenses. Inferno shares many ideas (and code!) with Plan 9 from Bell Labs. Some applications have been converted from Plan 9 to run on Inferno. This is why the Plan 9 and Inferno communities overlap, and why documentation for Plan 9 is often useful to Inferno users as well.
Many existing resources explain what sets Inferno apart from other operation systems. Vita Nuova's page on Inferno describes it best. (others: wikipedia, cat-v) Here is a summary:
In this section we'll compile hosted Inferno, i.e. Inferno that runs on top of your operating system.
Vita Nuova publishes the latest sources of Inferno in a bitbucket Mercurial repository, inferno-os. It contains all of Inferno except some of the fonts (due to license restrictions). Snapshots of the repository are available from the inferno-os google code download page, and from Vita Nuova's download page. The snapshot from the Vita Nuova page is highly recommended since it does include all the fonts. Download the "snapshot of the complete source tree [...] including the non-free fonts". This is the easiest and recommended way to install Inferno.
The snapshot contains the directory inferno-os. Once exctracted, you can fetch updates using Mercurial by running (from the inferno directory): hg pull and then hg update.
The snapshot contains emu binaries (the name of the program for hosted Inferno) for some platforms. This is especially useful on Windows machines that don't have a C compiler.
Before compiling Inferno, we'll take a quick look at the layout of the files & directories in inferno-os. This should give you a better understanding of the system and desired result of the compilation. That in turn will help solve problems you might encounter. If you are in a hurry, skip to the compiling section right away (and come back here while compiling).
Note that the inferno directory serves a dual purpose:
$SYSHOST/$OBJTYPE/bin/emu Emu is the Inferno-as-application binary you will be executing. The goal of compiling Inferno is to create this file. SYSHOST is e.g.: Linux, Nt, Plan 9, OpenBSD, FreeBSD (or one of the other values). OBJTYPE is the architecture your SYSHOST is running under, e.g. 386 for Intel 386, or arm for ARM, etc. $SYSHOST/$OBJTYPE/ bin/* lib/* include/* include/* holds the SYSHOST-specific includes for compiling emu. bin/* and lib/* contain compiled binaries and libraries.
mkfiles/* These mkfiles specify the tools (compilers, linkers) and flags used for compiling. Each SYSHOST and OBJTYPE have their own files. You don't have to do anything with these. utils/* C programs used when building emu. utils/mk/ is needed first, to get the compilation going. Another tool that will be build is util/iyacc/ ("i" in iyacc is for Inferno, to prevent name clash with the yacc on your system). lib*/ The lib*/ directories contain C library code that is used in (linked into) emu. include/*.h Common header files for C code. Such as the lib*/ code, and emu/* and os/* code too (described below). emu/$SYSHOST/* C code for emu specific to the SYSHOST. This typically contains only little C code, and a tiny amount of assembler. emu/port/* C code for emu common to all OS'es. os/* os/* contains all the C code for native Inferno (running on bare hardware). For brevity, it isn't described here in detail. The layout should be obvious though. limbo/ The C version of the limbo compiler. The Limbo version is in appl/cmd/limbo/ (described below). This is compiled first and then used during compilation.
mkconfig This configuration file is included by all mkfiles used when working with Inferno. It contains variables for the SYSHOST, OBJTYPE, and the full path of the inferno directory. This has to be configured correctly!
appl/cmd/* The limbo source for most limbo programs in the system. Some programs (such as the limbo compiler in limbo/) have their own subdirectory. The resulting programs are installed in /dis/ (described below). appl/lib/* Limbo source too, but used as libraries. The split between appl/cmd/ and appl/lib/ is just organisatory. There is no difference in the type/style of limbo code, or compilation process. appl/wm/* Limbo source for the graphical (wm) programs. Again, just split for cleaner directory trees. appl/* Some programs or "subsystems" have their own directories. Once more, they just contain the same type of limbo code as the other directories. module/* Contains limbo .m files, the equivalent of C's .h files. Only limbo libraries need a separate module interface description. Thus, there is usually a direct mapping between files in module/ and files in appl/lib/.
dis/*.dis dis/lib/*.dis dis/wm/*.dis The source compiled in the appl/* directories are installed to /dis. The shell will look here when it is asked to execute a program. It's just like /bin (and the various other bin directories) on unix.
mnt/ The default mount point for local programs, like factotum on /mnt/factotum. n/ The default mount point for (remote) services. A special program is often run here that synthesizes directories on demand, when you chdir to them. That is why this directory is empty. dev/ net/ env/ Place holder for binding kernel devices. Some kernel devices are bound by emu's initialization code. /net will hold the kernel network stack, /env the environment variables, and /dev most other kernel devices. lib/* Database files, such as dictionaries. lib/ndb/* Network configuration files. fonts/* Fonts, used by the graphical programs. icons/* Icons, used by graphical programs. services/* Mostly log files, generated by services (such as httpd). locale/* Mostly time zone files. The file locale/timezone is used by Inferno. Copy the appropriate time zone file to locale/timezone to configure the time zone.
doc/ Documentation, original source and pdf & postscript versions. These are the same as published on Vita Nuova's web site. man/$SECTION/* Manual pages. They are very good, definitely worth reading.
usr/inferno/ The home directory of user (usr) "inferno". tools/ Some external/additional C code that might be used with Inferno. Not really interesting for now.
Unless you are using pre-compiled binaries, you are now ready to compile hosted Inferno for your operating system.
To start hosted Inferno, execute emu. You'll have to provide it with the location of the root file system. Either on the command-line or through an environment variable. Additionally, the flags -c0 and -c1 are often used (to disable/enable the just-in-time compiler for dis programs). Emu also accepts arguments: a program to start instead of the default shell. Wm/wm executes the file $home/lib/wmsetup when starting. For example:
# from unix shell, start emu (Inferno) $SYSHOST/$OBJTYPE/bin/emu -r$HOME/inferno -c0 -g1024x768 # (wait for inferno shell, with semicolon as prompt) # from inferno shell, start the window manager wm/wm
The mechanisms described above can be used to make it easier to start Inferno:
$EMU is used by emu to find additional command-line arguments. Add this to your .profile:
EMU="-r$HOME/inferno -c0 -g1024x768"
Create the shell script infwm (or a file name of your choice) somewhere in your PATH, with the contents below. Emu will use the EMU environment variable set in the .profile. It binds the SYSHOST directory /home on Inferno's /usr, then launches the window manager wm/wm with automatic login as the user you started emu as. This makes all user directories from the host operating system available on /usr, and your home directory on /usr/$USER. Wm/logon sets $home and a few other variables.
#!/bin/sh exec emu $* /dis/sh.dis -c "bind '#U*/home' /usr; wm/wm wm/logon -u $USER"
When wm/wm starts, it executes commands in $HOME/lib/wmsetup. Put the following lines in that file to start network services and factotum (the authentication agent):
ndb/cs auth/factotum auth/feedkey
You are now ready to try out some programs that come with Inferno. Some will be familiar, others will require more effort to understand. The recommended way to run programs is from the window manager, wm/wm. At startup it loads a start menu from which (graphical Tk) programs can be started. Wm/sh ("Shell" in the menu) is a window in which a shell is run (a bit like an xterm but simpler), wm/man is the manual page viewer. Try editing files with acme, start the browser charon (and no, unfortunately it doesn't support css, nor many other modern browser features), and start some other programs from the start menu or shell.
If you know the standard unix tools, you'll quickly find your way. These programs work mostly the same, only flags & features are sometimes different, or absent: ar, basename, cal, cat, cd, chgrp, chmod, cmp, cp, date, dd, diff, du, echo, env, fortune, fmt, freq, grep, gzip, kill, ls, m4, man, mkdir, mv, ps, pwd, rm, sed, sleep, sort, strings, tail, tee, telnet, time, touch, tr, uniq, wc.
Before describing new commands specific to namespaces & styx server usage, we continue with a section on where to find answers to your questions and where to go for further reading.
Since Inferno rid itself of most of its (ancient) unix ancestry, many of its concepts and programs will be unfamiliar and require learning. This includes the programming language Limbo. Various sources & methods are available to help. Different people have different ways of learning, so its best to mix and match them to your liking:
Since namespaces & styx file servers are both new and so powerful, they deserve to be explained in their own section. First namespaces and the idea of styx servers are described, then examples of styx servers are given.
This next section introduces the concepts of namespaces. In case you already know about namespaces (perhaps you're a Plan 9 user), you can safely skip to some examples of Inferno styx servers.
A namespace is a collection of mount points and binds, much like mount points on unix systems. In Inferno, you mount a server speaking the styx protocol over a file descriptor (such as a network connection, or a pipe to a program). Thus, a mount introduces a new file tree in the namespace. A bind on the other hand just makes a part of the namespace appear in another place in the namespace, as an alias.
The namespace actually consists of two parts: regular paths starting with a /, and a "special" set of paths that start with an "#" followed by a single character. These special paths are kernel devices, file trees served by the kernel, often giving access to hardware such as a disk, or to kernel data structures such as processes. Only the regular paths of the namespace can be changed by binds and mounts. This is done by the commands bind, mount & unmount (which use system calls of the same name). Ns prints the current namespace. When executed from the shell, it's the shells namespace that ns prints (ns prints its own namespace, which it inherited from the shell). To illustrate, this is the namespace from a freshly started emu, without window manager:
; ns bind / / bind -ac '#U' / bind /dev /dev bind -b '#^' /dev bind /chan /chan bind -b '#^' /chan bind -b '#m' /dev bind -b '#c' /dev bind '#p' /prog bind '#d' /fd bind /net /net bind -a '#I' /net bind -a /dev /dev bind -a /net /net bind /net.alt /net.alt bind -a /net.alt /net.alt bind -c '#e' /env cd / ;
As you can see most lines bind a kernel device on a location in the regular file system. Such as #U (the contents of the Inferno root directory) on /, and #m (the mouse) on /dev, and #I (the network stack) on /net. Options -a and -b make the contents of the first path appear after or before the original contents of the second path (the second path will then contain the union of the first path and itself). If option -c is given, file creation at the target path will be allowed. The namespace shown above was set up by the Inferno initialization code, as a way to bootstrap the system and give a reasonable default namespace (e.g. the network stack isn't strictly necessary for a freshly started Inferno, but it is convenient to have in place).
Mount introduces an external file tree into the namespace, just as it does on unix systems. The file tree is accessed by talking the styx protocol over a file descriptor. The kernel takes care of the styx-part by translating open/read/write/stat/etc system calls into styx messages (the messages are described in section 5 of the manual pages), and the response messages into return values. The mount system call expects a file descriptor to talk styx over. The mount program has convenient syntax for mounting three types of styx servers:
Note that mount performs Inferno authentication when mounting. Plan 9 has a different type of authentication, that occurs at a different point in connection set up. While you are trying out the commands, you can turn off authentication with option -A, this will make it easier to get started. Do remember to turn on authentication for your services later!
Finally we can test a few styx servers. Beside the obvious on-disk storage of "physical" files and directories (see kfs), many other resources can be represented as file servers. The examples below should give an idea of what you can do with styx servers.
An existing namespace can be exported with styxlisten. Use it as in the following example:
styxlisten 'tcp!*!styx' export /
This will listen for tcp connections on the port "styx" (which is defined in /lib/ndb/common to be 6666). All connections will be served by a single invocation of export / (styxlisten expects the program to serve the styx protocol on its file descriptor 0). The program "export" simply exports the namespace starting at the parameter it is passed, the root of the file system in this case. These simple programs (they are just over 300 lines combined!) give a lot of power. Just as with mount, option -A disables authentication on the connection.
Mntgen is a simple program that makes (fakes actually) a directory when you cd to it. This is used on /n, where all kinds of network services are mounted. Since the services and network machines are numerous, it is impractical to create directories for all those services. With mntgen, you can have an empty /n and still mount services on any subdirectory. The following examples should illustrate the use of mntgen. First, a reconnaissance of /n without mntgen:
% cd /n % ls -l % % ls -ld test ls: stat test: 'test' file does not exist % % cd test cd: test: 'test' file does not exist % % bind '#m' /n/mouse bind: /n/mouse: '/n/mouse' does not exist %
As you see, /n is empty. Now the same, but with the mntgen:
% mount {mntgen} /n % % cd /n/ % % ls -ld test d-r-xr-xr-x M 2 me me 0 Jan 01 1970 test % % ls -ld another d-r-xr-xr-x M 2 me me 0 Jan 01 1970 another % % cd more % pwd /n/more % % bind '#m' /n/mouse % ls -l /n/mouse ---w--w--w- m 0 mjl mjl 0 Apr 15 20:37 /n/mouse/cursor --rw-rw-rw- m 0 mjl mjl 0 Apr 15 20:37 /n/mouse/pointer %
Ftpfs is an ftp client. It simply presents the files of an ftp server as a file system. The example should be self-explanatory:
% ftpfs -a none@example.org -m /n/ftp ftp.openbsd.org 220 openbsd.srv.ualberta.ca FTP server ready. 331 Guest login ok, send your email address as password. % % cd /n/ftp % cd pub/OpenBSD % ls -l d-rwxr-xr-x M 6 0 0 1024 Sep 28 2008 4.2 d-rwxr-xr-x M 6 0 0 1024 Sep 28 2008 4.3 d-rwxr-xr-x M 6 0 0 1024 Sep 04 2008 4.4 d-rwx------ M 6 0 0 512 Mar 09 17:04 4.5 d-rwxr-xr-x M 6 318 0 512 Feb 19 23:24 OpenBGPD d-rwxr-xr-x M 6 318 0 512 Jan 16 06:05 OpenNTPD d-rwxrwxr-x M 6 0 122 4608 Feb 22 17:34 OpenSSH --rw-r--r-- M 6 0 121 238 Feb 07 1997 README d-rwxr-xr-x M 6 20001 20001 454144 Apr 17 01:03 distfiles d-rwxr-xr-x M 6 308 0 2048 Jan 12 04:20 doc --rw-r--r-- M 6 0 0 8741 Sep 03 2008 ftplist d-rwxr-xr-x M 6 0 100 1024 Apr 07 03:59 patches d-rwxr-xr-x M 6 0 122 512 Aug 31 2008 snapshots d-rwxr-xr-x M 6 0 100 1024 Apr 07 21:07 songs d-rwxr-xr-x M 6 0 121 512 Jun 28 2008 tmp d-rwxr-xr-x M 6 0 100 512 Jan 07 2005 tools %
Tarfs mounts a tar file (read-only). Tar is a simple file format, but a traditional "file system" with on-disk storage is really just the same type of program, just with a different file format (plus they usually implement writes).
% tarfs gps.tar /n/tar % cd /n/tar % ls -l d-rwxrwxr-x M 16 0 0 0 Jul 07 2006 gps % cd gps % ls -l --rw-rw-r-- M 16 0 0 287 Mar 04 2006 dat.h --rw-rw-r-- M 16 0 0 4501 Mar 04 2006 gpsevermore.c --rw-rw-r-- M 16 0 0 18924 Apr 09 2006 gpsfs.c --rw-rw-r-- M 16 0 0 219 Mar 04 2006 mkfile --rw-rw-r-- M 16 0 0 2795 Mar 04 2006 util.c % wc gpsfs.c 1010 2713 18920 gpsfs.c % cd % unmount /n/tar
Memfs is a file system that stores files in memory. Ideal for mounting on /tmp:
% ls -l /tmp/test ls: stat /tmp/test: '/tmp/test' does not exist % % mount -bc {memfs -s} /tmp % % echo test >/tmp/test % ls -l /tmp/test --rw-r--r-- M 20 mjl memfs 5 Apr 17 22:43 /tmp/test % % unmount /tmp % ls -l /tmp/test ls: stat /tmp/test: '/tmp/test' does not exist %
The network stack is provided by #I. It has a directory per protocol, e.g. tcp. Since #I is mounted on /net by default, the tcp stack is available on /net/tcp. Library functions that deal with network connections (such as dial and announce, for connecting and serving) are implemented with normal open/read/write calls on files in /net/tcp. To show the ease with which /net/tcp can be used, consider the following shell script that performs a HTTP request:
<>[3]/net/tcp/clone { dir=/net/tcp/^`{cat <[0=3]} echo connect 74.125.77.99!80 >$dir/ctl && { echo 'GET /search?q=inferno-os&btnI=I''m+Feeling+Lucky HTTP/1.1' && echo 'connection: close' && echo 'host: www.google.com' && echo '' }>$dir/data cat $dir/data }
And the output:
HTTP/1.1 302 Found Location: http://www.vitanuova.com/inferno/ Cache-Control: private Content-Type: text/html; charset=UTF-8 Date: Sat, 18 Apr 2009 19:57:52 GMT Server: gws Content-Length: 230 Connection: close <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>302 Moved</TITLE></HEAD><BODY> <H1>302 Moved</H1> The document has moved <A HREF="http://www.vitanuova.com/inferno/">here</A>. </BODY></HTML>
That's the power of a well-designed abstraction, and powerful tools like the Inferno shell. In a similar fashion, dns requests can be performed using /net/dns, and more general network address translation can be done using /net/cs.
To give you a taste of Limbo programming, here is an implementation of "hello world", and instructions on how to compile and run it.
# this file implements the module interface "Helloworld" implement Helloworld; # include the files sys.m & draw.m (from /module) include "sys.m"; include "draw.m"; # "Helloworld" is a module, it has the function ("fn") "init" with two parameters. # both parameters are unnamed ("nil") because they are not used. Helloworld: module { init: fn(nil: ref Draw->Context, nil: list of string); }; # implementation of the function "init", as described in the # module interface "Helloworld" above. "init" in limbo is like "main" in C. init(nil: ref Draw->Context, nil: list of string) { # declare "sys" by assigning it the result of loading the Sys module. sys := load Sys Sys->PATH; # call function "print" from the loaded module "sys" sys->print("hello world!\n"); }
; limbo -gw helloworld.b ; ./helloworld hello world! ;
A list of updates to this document:
This document is in the public domain.