Servers in 9front
In the original Plan 9 grids, computers were divided into 3 types; File servers, terminals, CPU servers. The File server was a specialized computer running a custom kernel, and all it did was provide the file storage. Terminals were computers with monitors, mice, and keyboards. And CPU servers were multi-processor computers that ran headless and diskless, and provided computational resources. So you would write programs at a terminal, store the files on the fileserver, and compile them on a CPU server.
Eventually this settled to 2 types; CPU servers and terminals. Files ervers are now CPU servers that happen to handle storage. The basic distinction comes down to;
-
Is this a computer you sit at?
-
Is this a computer that you interact with over a network?
So a CPU server doesn't have to be a computer with lots of CPU power, or lots of storage.
All further examples will be done in 9front.
CPU Server
Getting a machine running 9front to become a CPU server is very simple. It just needs a line in the plan9.ini file it uses to boot to have;
service=cpu
This will be checked by various bits of code that set things up to run as a CPU server. Early on, it will be checked by the kernel at boot. For the 9pc64 kernel typically used in 64bit x86 computers, /sys/src/9/pc64/main.c will check for 'service' is 'cpu' and set up memory and cpu resources differently from a terminal.
Next, /sys/src/9/boot/bootrc will check the 'service' for 'cpu', and then pass '-c' to the init program. Init is found at /sys/src/cmd/init.c, and it will then tell the system to coninue to boot using /rc/bin/cpurc.
The biggest difference between cpurc and /rc/bin/termrc, which is called when a terminal boots, is that cpurc sets up network listeners and no screen or mouse, and termrc sets up the screen and mouse, but no network listeners.
Headless operation
A CPU server does not set up the sceen and mouse because the assumption is that, as a server, it will be running without them. There are a few things that should be done to make the machine boot up with no need for user interaction.
These are set in plan9.ini to make the system boot without user interaction.
service=cpu
nvram=/dev/sdE0/nvram
nvrlen=512
nvroff=0
fs=192.168.1.9
auth=192.168.1.9
user=glenda
nobootprompt=tls
explination
- service=cpu
This tells the machine to boot as a CPU server.
- nvram=/dev/sdE0/nvram
This specifies a location for the Non Volatile RAM. This is an old term, but today it is applied to some bit of storage that Plan9 would use to hold the hostowner's password. This allows the machine to authenticate with the auth server and get permission to access the file server. If you installed 9front to a hard drive, the default options will place a 1 block (512 bytes) partition on the hard drive named "nvram". So '/dev/sdE0/nvram' mean to look on the first sata drive for a plan9 partition named 'nvram'.
- nvrlen=512
- nvroff=0
These options are not strictly needed, but the system is flexible about where nvram may be found, and these can help the system load it. If the system is running with no hard drive, nvram can be loaded off a USB thumb drive or by other means. The length of the nvram data and it's offset in the storage can be specified.
- fs=192.168.1.9
- auth=192.168.1.9
This is to tell the machine the address of the file and auth servers. Otherwise the user will be prompted to enter them at the console.
- user=glenda
This sets the hostowner of the system. So user 'glenda' will be the owner of all the hardware on the system, and be able to kill processes and the like.
- nobootprompt=tls
The default 9front install will use 'bootargs=', and whatever is after the '=' will be the dafault option if enter is pressed, but it will stop the boot process and wait for input. Using 'nobootprompt' makes the system just run what ever is set without stopping. In this case 'tls' will tell it to set up an encrypted connection to the file server (already specified with 'fs='), which will also automatically run an authorization request to the auth server (at 'auth=').
File Server
In 9front, a file server is just a CPU server that happens to server files. The most notable difference is that it doesn't need to know the location of a file server, because it is one. While it is possible to have multiple file servers, it is best to have a single system be used to hold the basic system files, for ease of administration.
An example of a plan9.ini for a file server;
service=cpu
user=glenda
nvram=/dev/sdN0/nvram
nobootprompt=local!/dev/sdN0/fscache -a tcp!*!564
In the nobootprompt, 'local' tell the system that it is going to be getting the base file system from local storage. This example is using the first NVME drive with cwfs installed, so it looks for the default 'fscache' partition.
The file system code will have some method to "announce", which means it will listen on a network port and server files via 9P. In this case, it is listening for tcp traffic from (asterick) which means from anyone, on port 564, which is considered the default Plan9 file server port. Else where in the system, it may be seen as port '9fs', which is set with other standard ports in /lib/ndb/common.
Auth Server
An auth server is not set by anything in plan9.ini. Instead, it is set in /lib/ndb/local. In /rc/bin/cpurc, it will check if the 'auth=' entry for the local network is the same of the system's name.
...
if(~ $#auth 0)
auth=`{ndb/query -cia sys $sysname auth}
if(@{
for(a in $sysname `{ndb/query -cia sys $sysname ip} `{ndb/query -cia sys $sysname dom}){
if(~ $a $auth)
exit ''
}
exit 'no'
}){
# cpu+auth server
auth/keyfs -wp -m /mnt/keys /adm/keys
aux/listen -q -t /rc/bin/service.auth -d $serviced tcp
}
if not {
# cpu server
aux/listen -q -d $serviced tcp
}
...
So if a system is listed as an auth server in /lib/ndb/local, then it will run auth/keyfs, and also run the additional listeners found in /rc/bin/service.auth.
Tips & Tricks
nvram in kernel
For CPU servers with absolutely no local storage to hold the nvram data, it is possible to compile an nvram file into the kernel. To do this, make a file and fill it with null bytes.
dd -if /dev/zero -of /usr/glenda/fakenvram -bs 512 -count 1
This will make a 512 byte file.
Then set the nvram environmental variable to that file.
nvram=/usr/glenda/fakenvram
Next, run auth/wrkey, which checks for $nvram and writes into it.
auth/wrkey
This will prompt for the hostowner you want and their password.
Then you need to modify the config file for the kernel you are compiling. I find it best to make another directory in home, copy the 'fakenvram' there, and make a copy of the config file to modify. Then I bind it before the kernel directory when its time to compile. Or go a step further and add a modified mkfile with a CONFLIST that adds an additional config file (ex. pc64nvram).
For pc64, the kernel code for 64bit x86, the config file is /sys/src/9/pc64/pc64. At the end of the config file is a listing of files to include with the kernel. You can add the fakenvram file to it.
...
bootdir
boot
/$objtype/bin/paqfs
/$objtype/bin/auth/factotum
$CONF.bootfs.paq bootfs.paq
fakenvram
Then add that to the plan9.ini for that computer. Being completely storage free, it would likely be booting via PXE, so the plan9.ini would be loaded from /cfg/pxe/[mac adddress].
bootfile=/amd64/9pc64nvram
service=cpu
nvram=/boot/fakenvram
nvrlen=512
nvroff=0
fs=192.168.1.9
auth=192.168.1.9
user=glenda
nobootprompt=tls
broad definition cpu server
CPU server really just means a device that shares it's resources over a network rather then be a computer you sit at with a mouse and keyboard. It doesn't have to be something with a powerful CPU.
Raspberry Pi's and other small embedded type computers can also be CPU servers. A typical home wifi router would be a be a CPU server if it was running 9front.
CPU servers can be configured to share any part of its hardware.
For example, the I²C bus of a Raspberry Pi could be imported to a 9front terminal.
rimport -b my-rpi '#J' /n
That command would import the #J kernel device, which is I²C, from a system named 'my-rpi', and mount it in /n before anything already there.
Or suppose you connect a GPS reciever to the uart on the Raspberry Pi and use the included aux/gpsfs to read it. By default, aux/gpsfs will post to /srv/gps and mount to /mnt/gps. You could rimport that /mnt/gps, provided it is always in the Raspberry Pi's default namespace. Or you could set up a listener to share it.
aux/listen1 tcp!*!9090 exportfs -S /srv/gps
On your laptop you can run
srv tcp!my-rpi!9090 gps /mnt/gps
And that would put a /srv/gps on your laptop and you could read the GPS files at /mnt/gps.
So you can put the various peices of hardware from any CPU server in the namespace of the terminal you are sitting at. So your Intel laptop could have seemingly local access to a Raspberry Pi's GPIO pins. Or the soundcard on a nearby desktop with better speakers. Or the network stack of a 9front machine running on a server somewhere else in the world, letting you route your network traffic through it.