shithub: docs.9front.org

ref: 3e3275363ba0db7377ad1de228a6886bbf292606
dir: docs.9front.org/namespace-lifting.md

View raw version
# Namespace lifting

The usual model for using Plan 9 namespaces is to have a single leader
process construct a namespace by mounting resources that are 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 if they
can't find those, rather than trying to bring in the missing pieces
themselves.  A good example of this is the root namespace constructed by
the boot process that mounts a network stack on /net.  This then gets
inherited by descendant namespaces and is expected to be available by
all network programs.

The graphical user environment follows a similar model.  When a user
logs in, a new namespace is forked from the root namespace and user's
profile script is run by a shell running inside this namespace.  A 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.  After constructing the namespace rio
is run in it as the last thing.

Subsequently, new windows created by rio inherit a copy of its
namespace.  Forking this template namespace is the only namespace
operation supported by rio.  In particular, rio itself doesn't provide a
way to modify its namespace.

This can still be achieved by an unwieldy mechanism provided by a
plumber's "Local" rule.  The Local rule provides a command backdoor
into the originating 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) system 
management workflows.

With pin we can "pin" a fully capable interactive shell running in the same
namespace as rio, which can provide a more convenient way to
interact with it. The way to do this follows. You'll have to grab and
install a copy of mq(4) and pin(1), both available in the same git repo:

	git/clone git://src.a-b.xyz/mq && cd mq && mk install

Create a conveniently named pinned rc session right before starting rio,
perhaps in your profile:

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

The -n flag prevents pin from automatically attaching to the session,
otherwise the I/O on the pinned shell would bleed into rio's GUI.

Now that the rio is running we can create a window and attach to the
shell just created. For lack of a better name we'll call this shell a control
shell:

	pin rio0

To show that we can modify rio's namespace from this shell let's bind
something and observe what happens. In the same pin window run:

	bind /sys/src /n/src

Then, 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 us 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 contexts.  An example that comes
to mind is providing access to a namespace that is being exported over
the network. Let's start by exporting an empty mountpoint, which we'll
later populate using namespace ops from our namespace 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.

Run

	pin -n 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!your.ip.or.name!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 exportctl
	bind /usr /n/export

Go back to the client machine and run the `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.