2012-11-07
Sharing computer and phone resources using Inferno OS
I posted previously about running the Inferno operating system on an Android phone. Inferno on a phone device interests me because of the approaches it takes on distributing resources across machines. By having such a system on multiple machines and devices it should be possible to use features of the phone on a desktop system (like SMS messaging, copying photos back and forth, etc) or use features of servers on the phone (cloud file storage for example).
In this post I'll go over some simple examples of how to share resources using Inferno on a desktop machine and a remote server. I'll then show how an Inferno based phone can shares its resources. I use the hosted version of Inferno for these examples where Inferno runs as a user process under an existing operating system.
Building Inferno
To build Inferno under Linux you need to obtain the inferno-20100120.tgz file from the Inferno download page. This file contains a snapshot of the mercurial based source code repository and the binary font files used by the system. Once unpacked it needs to be updated to the latest source code version using mercurial commands:
$ wget http://www.vitanuova.com/dist/4e/inferno-20100120.tgz
$ tar zxvf inferno-20100120.tgz
$ cd inferno
$ hg pull -u
Once the update is complete you will need to edit the mkconfig
file so that the following settings are changed:
ROOT=/root/of/the/inferno/directory
SYSHOST=Linux
OBJTYPE=386
Ensure that ROOT
matches the directory location where the Inferno source code was unpacked. To build, set your PATH to the Linux/386/bin
subdirectory of the unpacked Inferno source and run the following commands:
$ export PATH=$PATH:~/src/inferno/Linux/386/bin
$ ./makemk.sh
$ mk nuke
$ mk install
The result is an emu
executable living in Linux/386/bin
.
Running Inferno
You can run Inferno and interact directly with a shell using emu
:
$ emu
; ls
...file listing...
This is how I run Inferno on a headless server. On a machine with a display you can run a GUI:
$ emu -g1024x768
; wm/wm
...window system appears...
The -g
command line option sets the size of the Inferno OS host window.
Namespaces
Inferno has a concept called a namespace
which is a hierarchical collection of files or resources. Every process running in Inferno has its own local namespace. By modifying this namespace for each process you can grant or deny access to particular resources.
Some commands that manipulate the namespace are:
bind
mount
unmount
export
These commands will be used in the examples following to show how to share and access resources from local and remote machines.
Sharing directories and files
Bind attaches, moves or hides local resources. The simplest case is binding an existing directory to a new location. For example:
; mkdir /tmp/myappl
; bind /appl /tmp/myappl
This makes the directory /appl
, which contains the source for some of the Inferno OS commands, to the location /tmp/myappl
. the current shell and all its child processes can see the /tmp/myappl directory. Processes that aren't children of the current shell cannot.
Binding over an existing directory hides that directory until it is unbound (using unmount
). It's possible to bind over an existing directory such that the source directory is overlaid with the existing directory. You can choose whether files in the source directory or the destination directory have precedence if there are duplicate names. This is known as a union filesystem.
An example of union filesystem usage is replacing the PATH environment variables in other operating systems. The /dis
directory on Inferno holds the executable Inferno OS commands. Instead of maintaining PATH environment variables for users to manage order of lookup for locally built commands and global OS commands you instead manage it via union directories using bind
.
If there is a directory in the user's home directory called dis
, you can allow commands located there to be looked up first by doing:
; bind -b /usr/myname/dis /dis
if you want files in the global dis
directory to have precedence:
; bind -a /usr/myname/dis /dis
You can stack as many directories as desired. The -a
command line argument means add 'after' the union directory. the -b
command line argument means add 'before' the union directory.
Binding the host filesystem
When running Inferno as a user process on an existing host operating system you can bind to directories that exist on the host. The special kernel device path, '#U', is for the host file system:
; bind '#U*' /tmp/z
Now the directory /tmp/z
is mapped to the root of the host. You can map any host path, not just the root:
; bind '#U*/home' /tmp/z
This can be used to mount a users host filesystem home directory to be used as their Inferno home directory:
; bind '#U*/home' /usr
I start Inferno with the home directory shared and logged into the Inferno system as my host username using a shell script which runs the equivalent of:
$ export PATH=$PATH:/src/inferno/Linux/386/bin
$ export EMU="-r/home/$USER/src/inferno -c1 -g1920x1008"
$ exec emu $* /dis/sh.dis -a -c "bind '#U*/home' /usr; wm/wm wm/logon -u $USER"
For this to work you'll need to ensure you have the directories and files Inferno expects to find in the home directory. The files from the Inferno /usr/inferno
directory can be copied for this purpose.
Accessing remote files
Note: One important point to note in the following examples is that the -A
switch used in both the listen
and mount
commands disables authentication. All the data is being sent unencrypted and no login information was used to connect. Don't do this for real systems, I'm only doing this to show the basic commands. It's possible and recommended to set up authentication and encryption for real world usage. Once done this can replace the use of sftp on Inferno systems. Just export and bind the filesystems needed and all access is authenticated and all data encrypted.
To access a resource on another machine (or another instance of hosted Inferno OS on the same machine) you can run the listen
command on the remote machine, exporting the namespace you want the client machine to be able to bind:
$ emu
; ndb/cs
; ndb/dns
; listen -A 'tcp!*!8000' { export '#U*/home/myuser' & }
;
The ndb/cs
and ndb/dns
commands start the network services used for DNS lookup and other features. the listen
command starts a listener on port 8000 exporting the myuser
home directory from the host filesystem. I picked 8000
as an arbitary port number - any one is fine. A client machine can now connect to this using:
$ emu
; ndb/cs
; ndb/dns
; mkdir /tmp/myuser
; mount -A 'tcp!remote.example.com!8000' /tmp/myuser
; cd /tmp/myuser
; ls
...remote file listing...
; unmount /tmp/myuser
Replace 'remote.example.com' with the IP address or domain name of the remote machine. The mounted directory works like a local directory from the point of view of the client. You can get directory listings, copy files, edit files, etc.
Accessing remote resources
This accessing of remote resources isn't just limited to files. You can export other kernel devices. You can remotely list and debug running processes on a remote machine by exporting and mounting /prog
. On the remote machine:
; listen -A 'tcp!*!8000' { export /prog & }
On the client machine:
; mkdir /tmp/debug
; mount -A 'tcp!remote.example.com!8000' /tmp/debug
; ls /tmp/debug
...list of processes on remote machine...
; unmount /tmp/debug
Accessing remote networks
Another interesting example is mounting a remote machines /net
directory over the top of the existing clients /net
directory. The result of this is all network access made via the shell and its children will use the remote machine's network. This effectively creates a network tunnel that is encrypted and authenticated (if you don't use the -A
switch and setup the authentication system).
To demonstrate this lets assume the client machine can't accept incoming connections due to NAT. The remote machine however is unrestricted. On the remote machine run:
; listen -A 'tcp!*!8000' { export /net & }
On the client machine we connect outwards as before - outbound connections aren't restricted:
; webgrab http://automation.whatismyip.com/n09230945.asp
; cat n09230945.asp
...public facing ip address of client machine...
; mount -A 'tcp!remote.example.com!8000' /net
This mount
usage binds the remote /net
onto our own /net
. Any future network requests from this shell on the client machine will come from the remote:
; webgrab http://automation.whatismyip.com/n09230945.asp
; cat n09230945.asp
...ip address of remote machine...
We can even expose resources on the client machine. The following is run on the client:
; listen -A 'tcp!*!8001' { export / & }
On a third machine, that has no access to the client at all, but can access the remote machine:
; mkdir /tmp/client
; mount -A 'tcp!remote.example.com!8001' /tmp/client
; ls /tmp/client
...client file listing...
You'll note that we're connecting to port 8001 on the remote machine. This is where the listen is attached to due to us mapping the remotes /net
directory on the client. But it's the client's root directory that was exported so that's what the third machine gets access to. No incoming connections are made to the client itself.
Sharing phone resources
For this example you'll need to have Inferno running on an Android phone. This can be done by installing Hellaphone or following my instructions on building and installing Inferno on a Nexus S using Mozilla's Boot to Gecko as a base.
SMS messages can be sent on the phone by writing to the '/phone/sms' file. The following would send a simple text message to number +6412345678 (an invalid New Zealand number):
; echo send 6412345678 'hello' >/phone/sms
By exporting /phone
and mounting it on a desktop system we can control the sending of SMS messages from there. On the phone:
; ndb/cs
; ndb/dns
; listen -A 'tcp!*!8000' { export /phone & }
On the desktop machine:
; ndb/cs
; ndb/dns
; mkdir /tmp/phone
; mount -A 'tcp!ip.address.of.phone!8000' /tmp/phone
; echo send 6412345678 'hello' >/tmp/phone/sms
It's also possible to detect ringing, answer calls, read received SMS messages, etc.
Unfortunately things are a bit more complex in the real world. My phone carrier doesn't allow inbound connections to the phone. They also seem to prevent outbound connections on some ports. Highly annoying. To fix this I installed OpenSSH on the phone using opkg. Using OpenSSH I created a tunnel to my remote server which I could then map /net
in a similar manner to what I've described previously.
On my remote server:
$ emu
; ndb/cs
; ndb/dns
; listen -A 'tcp!*!8000' { export /net & }
On the phone:
# ssh -N myuser@remote.example.com -L 8000:127.0.0.1:8000 &
# emu-g
; ndb/cs
; ndb/dns
; mount -A 'tcp!127.0.0.1!8000' /net
; listen -A 'tcp!*!8001' { export /phone & }
On my desktop:
; ndb/cs
; ndb/dns
; mount -A 'tcp!remote.example.com!8001' /tmp/phone
; echo send 6412345678 'hello' >/tmp/phone/sms
The routing through the remote server is the same as my previous example. There's probably a way of avoiding the OpenSSH tunnelling by using another port but I didn't spend time investigating exactly what the carrier was doing.
Conclusion
Inferno makes it quite easy to share resources amongst devices. The idea of having a phone which can attach to other machines to access files and export phone functionality to other machines to more conveniently write SMS messages and emails appeals to me. It'll be interesting to see how far this can be pushed and what ideas people come up with in this area. Hopefully more development will go into Inferno on the phone and some of these ideas can be explored.
Remember that the examples above that used the -A
switch to mount
and listen
used unencrypted and unauthenticated sessions. This is useful for quick testing but for actual usage -A
should be avoided. Setting up authentication is described at Pete Elmore's post on clusters. The 4th edition release notes also has information on this.
Other useful Inferno resources:
- Peter Elmore's Inferno Posts
- Getting Started with Inferno
- Useful Limbo programs, including an IRC file system and client.
- Hellaphone DEFCON 20 slides
- A Tour of ACME. A screencast of using the ACME editor used in Inferno and Plan 9 (and other systems).