Namespace lifting

The usual model for using Plan 9 namespaces is to have a single leader process construct a namespace out of resources known to be needed by it or its kids. The kids assume that their dependencies are available at known locations and will often fail to run entirely in their absence, rather than trying to bring in the missing pieces themselves. An example of this is the root namespace constructed by the boot process that mounts the network stack on /net and gets inherited by descendant namespaces. All networking programs simply expect the network resource there.

The graphical user environment follows a similar model. When a user logs in a new namespace is forked of the root namespace and their profile script is run by a shell within this namespace. The profile script’s job is to mount and bind all the resources that a user might expect to need during a terminal session: a plumber, mail file server, factotum, and so on. Rio is run on top of this namespace as the last thing.

Subsequently, windows created by rio inherit a copy of this template namespace through forking. Forking is the only namespace operation directly provided by rio. In particular, rio doesn’t provide a way to modify its namespace.

This can still be achieved by an unwieldy mechanism provided by plumber’s “Local” rule. The Local rule is a command backdoor into rio’s namespace and can be used to modify it.

I propose an alternative technique based on pin(1), a pinned I/O shell. This idea is stole…cough inspired! by mycroftiv’s hub(1) workflows.

With pin we can “pin” a fully capable interactive shell running in the same namespace as rio, providing a familiar way of interaction. The way to do this follows.

Grab a copy of mq(4) and pin(1), both available in the same git repo:

   git/clone git:// && cd mq && mk install

Pin a conveniently named rc right before starting rio, perhaps in your profile:

   [plumber, upas/fs, etc.]
   pin -c rio0

With rio running we can create a window and attach to the pinned shell. For lack of a better name we’ll call this a control shell:

   pin -a rio0

To show that we can modify rio’s namespace let’s bind something an observe what happens. Run in the control shell:

   bind /sys/src /n/src

Create a new window and check that the system’s source code is indeed bound to a new name /n/src:

   ls /n/src

Careful readers will have guessed that this didn’t change the namespaces of windows that were created prior to changing rio’s namespace. Remember that rio creates (almost) identical copies of its namespace for every new window, but these are in fact different entities independent of each other.

This same trick can be applied in other situations. An example that comes to mind is providing access to a namespace being exported over the network.

Let’s start by exporting an empty mountpoint, which we’ll later populate using namespace operations from our control shell.

Firstly, we’ll need to use srvfs(4) to create a persistent mountable namespace which we’ll mount in the network listener and export with exportfs(4). You might have thought that we could use exportfs(4) directly but that way we’d be actually exporting a different namespace for each connection.


   pin -c exportctl
   srvfs export /n/export

This will create a /srv/export pipe “containing” our namespace. Next we’ll create a listener to export this pipe to network clients. Exportfs with the -S flag can be used for the task:

   aux/listen1 tcp!*!999 /bin/exportfs -S /srv/export

Let’s see if we can connect, run the following:

   srv tcp!!999 export /n/export

This should mount our directory to /n/export on the client side. Run ls /n/export and assert that it’s empty.

Now let’s change the exported namespace using our exportctl pin on the server machine.

   pin -a exportctl
   bind /usr /n/export

Go back to the client machine and run ls /n/export again, this time user directories should show up.

That’s it folks. I’d like to hear if you find some other tricks to do with this.