lx
is an attempt at using Linux machines as cpu servers,
providing a user experience analogous to using the Plan 9
command cpu(1)
:
- The
lx
stdin/out/err are connected to the remote process, and notes received bylx
are translated into signals to the Linux process - The specific namespace of the Plan 9 client is available
to the Linux process under
/9
; with configuration it is possible to have some directories available from Linux under the same path as on Plan 9 (e.g.$home
). When possible the working directory is preserved when running the remote process - If
lx
is run from a rio window and an X11 client is started, it will be displayed in this window
The intent is to leverage the larger range of software available on Linux without compromising too much the native Plan 9 user experience.
No authentication or encryption is implemented, as this is not necessary for my current use cases.
I run Linux commands on the host running my Plan 9 kvm, but
it should be possible to use a vmx
VM instead.
rc
shell:lx
- Use Linux commands:
ls -l | lx gawk '{print $4}'
- Browser:
lx firefox
. For integration with plumber, seebin/firefox
- Write Linux programs from the comfort of Plan 9
- Write Linux programs accessing e.g.
/dev/draw
or/dev/net
. Then in a waylx
can be seen as implementing a new objtype: uglier, slower and limited, but at least giving us access to more programming languages than before.
To take a concrete example, here is an approximation of what
happens when I run lx gcc blah.c
from Plan 9 directory
/usr/henri/src/blah
:
- The current namespace is exported by calling
exportfs -r /
lx
connects to serverlxsrv
running on Linux, which creates a detached process to handle the session.- This process creates a new mount namespace using
unshare(2)
and mounts the fs exported from Plan 9 on/9
- It then bind-mounts
/9/usr/henri
(my home directory) onto/usr/henri
- It chdirs to the working directory of
lx
, i.e./usr/henri/src/blah
- It runs
gcc blah.c
, with its stdin/out/err connected to the corresponding file descriptors of thelx
process. - Pressing the delete key once (resp. twice, thrice) sends SIGINT (resp. SIGHUP, SIGKILL) to the Linux process (actually its process group).
X11 clients are supported using a VNC server. Each
time lx
is invoked, a "fake" X11 socket is created.
The Linux command is started with the DISPLAY
environment variable pointing to this socket. If it gets
opened, a VNC server is started on Linux, vncv
is started
on Plan 9, and traffic is proxied from the proxy
socket to the actual VNC server socket.
- On Linux: Plan 9 from User Space
- On Linux: A VNC server if X11 clients will be run
- Connectivity: the client
lx
needs to connect to the port served by the Linux serverlxsrv
; The Linux host needs to connect to a range of ports connected toexportfs
servers.
- Define the
PLAN9
environment variable pointing to the p9p base directory, add$PLAN9/bin
to yourPATH
. - Clone the repo on both Plan 9 and Linux
- On Plan 9:
mk -f mkfile.clt install
- On Linux:
mk -f mkfile.srv install
Once you have a working setup and assuming you cloned the repo
in a bind-mounted directory (via the mounts
config setting
or lx
option -m
), you can build client and server
by running mk both.install
from Plan 9.
This will use lx
for the linux-side build.
Skip the VNC sections below if you will not be running X11 clients.
$ sudo mkdir /9 && sudo chown $USER:$USER /9
# Optionally, if you want a bind mount of /9/usr/USER on /usr/USER
# This assumes your username is the same on both sides
$ sudo mkdir /usr/$USER && sudo chown $USER:$USER /usr/$USER
The namespace exported from Plan 9 is mounted on Linux
using the 9pfuse
command provided by p9p.
Environment variable PLAN9
must be defined when starting
lxsrv
, pointing to the p9p base directory.
P9p is required as well to build the server executable, simply because I decided to use the Plan 9-style p9p libraries for implementing the server.
For commands "
and ""
to work, you can add at the beginning
of $PLAN9/bin/wintext
the following line:
[ -f /9/dev/text ] && exec cat /9/dev/text
lx
has only been tested with tigervnc
, but other implementations
are expected to work with little or no code change.
Once you have confirmed X11 clients work with lx
, you can
eliminate an overlong hardcoded wait for the VNC server:
In the script /usr/bin/tigervnc
I changed line
sleep(3);
into
use Time::HiRes qw(usleep); usleep(200000);
If this sleep is too short, you will get an error like
/tmp/.X11-unix/X212: No such file or directory
Create/edit ~/.vnc/xstartup
with this content:
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
xsetroot -solid grey
xhost +local:
lx-dwm # or any other window manager
The VNC startup file must run a window manager. Any such
program can be used, but a version of dwm
is provided
here, which hides unnecessary tag buttons and
provides another to close the currently selected
client. To allow coexistence with an existing dwm
it is installed as lx-dwm
.
It will be installed along with lxsrv
when running the mk
command mentioned in the Build section above.
lx
reads its configuration from path $home/lib/lx
.
Here is an example allowing connection to a single Linux host
called rabbit
:
default-host=rabbit
minport=3000
maxport=3049
mounts=/usr/roger:/usr/roger
command=rc -il
If more customisation is required, refer to man page lx(6)
.
lx
has only been tested in one environment and with a few
commands. These are the known problems:
- If the
lx
client is slayed, a few processes are leaked - If an X11 client launched directly from
lx
prints to stdout/stderr (e.g.xev
), the messages will corrupt thevncv
display. Redirect the output if this is a problem. - The p9p graphical programs do not work, because they do not work with VNC server (try others?)
- Some X11 clients (only
llpp
so far) insist on connecting to the X server using an abstract socket. But our proxy socket is created using p9p'sannounce
, which does not support abstract. To fix this we'd need to use the linuxsocket
syscall directly. - Passing environment variables to a Linux command is currently
difficult and requires clunky command lines like
lx bash -c 'X=1; echo $X'
. - ~.6s lag on each
lx
invocation, due to runningexportfs
and9pfuse
each time: If that ever became a problem for some use case it should be possible to maintain a persistent session, e.g. from a Plan 9 fs. - The p9p command
mc
does not know the width of the Plan 9 window, and uses the width of the terminal thatlxsrv
was started from. - Insecure: no auth on server request; no encryption.
Some Linux commands do file operations not supported on Plan
9 (symlinks, lockfiles), or not supported from a Linux mount
namespace (Linux rename(2)
) (FIXME wasn't it, test with
simple program)
I encountered such problems with go
, rustc
, git
.
The only solution is to run such commands on the native
Linux fs. To still get decent integration with Plan 9, I mount
my Linux home directory on Plan 9 using
u9fs
onto the same path as on Linux, so /home/henri
.
Then if for example I do rust work, I run acme and shells
from /home/henri
instead of /usr/henri
, and from there
lx cargo
works as expected. Not elegant but it works.
For this I installed u9fs on Linux, and I added the following
to my Plan 9 $lib/profile
:
aux/stub -d /home
srv tcp!mylinuxhost!49151 u9fs # not using 9fs b/c of my custom port
mkdir -p /mnt/u9fs
mount -c /srv/u9fs /mnt/u9fs
bind /mnt/u9fs/home /home
Because you will forget to type lx
before go
or cargo
,
you can create e.g. a go
script which invokes lx go
. See
example scripts under ./bin
.
Note that git9
seems to be working fine on u9fs.
Using lx
option -d
will show debugging information that can
help troubleshoot problems.
The client and server maintain session log files
/tmp/lx.$user/*.log
, resp.
/tmp/lxsrv.$user/*.log
which contain the
same information printed out by the client when run with
option -d
, plus some events occurring when there is no established
connection to the client's stderr. If you are having a problem with
VNC you may want to check its log under ~/.vnc
.
lx
does not provide a pty - if you want a unix shell session or
terminal emulation, use ssh(1)
and vt(1)
.
can't open display
: it may be that
the VNC server is too slow to start. Increase VNCWAITMS in
lxsrv.c. Note that this will increase the delay before get-
ting the X11 client displayed.
unable to find a free vnc proxy port
: you killed the server
with SIGKILL or you encountered a bug. You can run on Linux the
command rm /tmp/.X11-unix/X1??
to delete all VNC proxy sockets.
This may break X11 clients currently running underlx
.
The VNC proxy sockets are /tmp/.X11-unix/Xn
, with 100 <= n <= 199;
the real VNC sockets have 200 <= n <= 299.
This has been tested on a 9front kvm running on Arch Linux.
When working on lx
I concurrently use two versions:
a stable version, built from the master branch; and a dev version,
built from another branch, in which the binaries have different
names. This allows me to use the stable version to build and
run the dev version.
The name lx
is actually read from file ./name
. When working
on the dev branch, this file contains the string lx2
, so that
the deployed binaries are lx2
and lx2srv
.
I keep two windows open:
- One running
mk watch
, to watch the files for change and trigger rebuilds - Another running
mk srvloop
, which continuously runslx2srv
on port 8000, so as not to conflict withlxsrv
which uses the default port of 9000. FIXME 9pfuse breaks if master lxsrv is already running?? Until fixed run from Linux:while(){ echo '####' `{date}; lx2srv -p 8000 || sleep 1 }