Compare commits
10 commits
3d2ab9137e
...
f946d9b08d
| Author | SHA1 | Date | |
|---|---|---|---|
| f946d9b08d | |||
| 9174d6b07c | |||
| 727e0629af | |||
| a0c4463b17 | |||
| 0093035862 | |||
| 5a294ba208 | |||
| 15483dae25 | |||
| 9785470527 | |||
| f035ed0c9d | |||
| e493f9088c |
18 changed files with 587 additions and 66 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,6 +1,5 @@
|
|||
art
|
||||
crud
|
||||
blob
|
||||
media
|
||||
/*root
|
||||
|
||||
|
|
@ -9,6 +8,7 @@ blog/vcf*-20*.html
|
|||
blog.html
|
||||
blog.inner.html
|
||||
*.head.html
|
||||
blog/feed.xml
|
||||
|
||||
index.html
|
||||
git.html
|
||||
|
|
|
|||
47
GNUmakefile
47
GNUmakefile
|
|
@ -1,14 +1,12 @@
|
|||
DEPLOY_HTTPROOT ?= httproot
|
||||
DEPLOY_BLOBS = andrea0s-plain-noextfonts.svg \
|
||||
blob/ebrimabd.ttf \
|
||||
blob/window-capture \
|
||||
blob/vcf-2024-pile.jpg
|
||||
AWESOME_BLOGS := -s https://lwn.net/headlines/rss \
|
||||
-s https://lobste.rs/rss \
|
||||
-s https://blog.haskell.org/atom.xml \
|
||||
-s https://www.phoronix.com/rss.php \
|
||||
-s https://static.fsf.org/fsforg/rss/news.xml \
|
||||
-s https://emersion.fr/blog/rss.xml
|
||||
DEPLOY_BLOBS := $(foreach blob,$(wildcard blob/*),$(DEPLOY_HTTPROOT)/$(blob))
|
||||
|
||||
# AWESOME_BLOGS := -s https://lwn.net/headlines/rss \
|
||||
# -s https://lobste.rs/rss \
|
||||
# -s https://blog.haskell.org/atom.xml \
|
||||
# -s https://www.phoronix.com/rss.php \
|
||||
# -s https://static.fsf.org/fsforg/rss/news.xml \
|
||||
# -s https://emersion.fr/blog/rss.xml
|
||||
|
||||
UNAME = $(shell uname)
|
||||
ifeq ($(UNAME),Linux)
|
||||
|
|
@ -29,13 +27,11 @@ blog/%.indicator.html: indicators
|
|||
blog/%.html: %.head.html nav.head.html %.indicator.html nav.tail.html window.head.html %.inner.html window.tail.html
|
||||
cat $^ > $@
|
||||
|
||||
vcf%.html: vcf%.head.html nav.head.html blog/vcf%.indicator.html nav.tail.html window.head.html blog/vcf%.inner.html window.tail.html
|
||||
cat $^ > $@
|
||||
|
||||
blog.inner.html: $(BLOG_SRC)
|
||||
scripts/blogposts.bash \
|
||||
| modules/openring/openring -n 12 -p 3 \
|
||||
$(AWESOME_BLOGS) > $@
|
||||
blog/feed.xml blog.inner.html: $(BLOG_SRC) scripts/blogposts.bash #modules/openring/openring
|
||||
scripts/blogposts.bash > $@
|
||||
# scripts/blogposts.bash \
|
||||
# | modules/openring/openring -n 12 -p 3 \
|
||||
# $(AWESOME_BLOGS) > $@
|
||||
|
||||
modules/openring/openring.go: .gitmodules
|
||||
git submodule update --init --recursive -- modules/openring
|
||||
|
|
@ -66,17 +62,22 @@ index.html: indexhead.html nav.head.html index.indicator.html nav.tail.html inde
|
|||
%.html: %.head.html nav.head.html %.indicator.html nav.tail.html window.head.html %.inner.html window.tail.html
|
||||
cat $^ > $@
|
||||
|
||||
test: index.html
|
||||
$(OPEN) $<
|
||||
test: deploy
|
||||
#$(OPEN) $@.html
|
||||
cd $(DEPLOY_HTTPROOT) \
|
||||
&& (ln -s . '~targetdisk' || :) \
|
||||
&& python -m http.server
|
||||
|
||||
$(DEPLOY_HTTPROOT)/blob/%: blob/% $(DEPLOY_HTTPROOT)/blob
|
||||
cp -rv $< $@
|
||||
|
||||
$(DEPLOY_HTTPROOT)/blob:
|
||||
mkdir -p $(@)
|
||||
cp -rv $(DEPLOY_BLOBS) $(@)
|
||||
mkdir -p $@
|
||||
|
||||
deploy: all $(DEPLOY_HTTPROOT)/blob
|
||||
deploy: all $(DEPLOY_BLOBS)
|
||||
cp -rv *.css *.html blog media \
|
||||
$(DEPLOY_HTTPROOT)
|
||||
|
||||
all: blog-posts index.html blog.html resume.html vcfmw-2024.html
|
||||
all: blog-posts index.html blog.html resume.html
|
||||
|
||||
.PHONY: all blog-posts indicators deploy
|
||||
|
|
|
|||
BIN
blob/AnonymiceProNerdFontMono-Bold.ttf
Normal file
BIN
blob/AnonymiceProNerdFontMono-Bold.ttf
Normal file
Binary file not shown.
BIN
blob/AnonymiceProNerdFontMono-BoldItalic.ttf
Normal file
BIN
blob/AnonymiceProNerdFontMono-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
blob/AnonymiceProNerdFontMono-Italic.ttf
Normal file
BIN
blob/AnonymiceProNerdFontMono-Italic.ttf
Normal file
Binary file not shown.
BIN
blob/AnonymiceProNerdFontMono-Regular.ttf
Normal file
BIN
blob/AnonymiceProNerdFontMono-Regular.ttf
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
BIN
blob/ebrimabd.ttf
Normal file
BIN
blob/ebrimabd.ttf
Normal file
Binary file not shown.
BIN
blob/hey-paul.gif
Normal file
BIN
blob/hey-paul.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
BIN
blob/you-get-a-file.jpg
Normal file
BIN
blob/you-get-a-file.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
|
|
@ -18,7 +18,8 @@ and Xen?
|
|||
With [QEMU](https://www.qemu.org/) your VMs are defined as the arguments passed
|
||||
to QEMU on its invocation at the command line. For example, you might invoke a
|
||||
VM as such (note that **`>`** is a
|
||||
[**$PS2** prompt](http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x157.html)):
|
||||
[$PS2 prompt](http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x157.html)):
|
||||
|
||||
```
|
||||
$ qemu-system-x86_64 -machine type=q35 --enable-kvm -cpu host -smp cpus=8 \
|
||||
> -m 512M -netdev user,id=net0 -device e1000,netdev=net0 -hda dsk/vm-hdd.qcow
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ with `xwininfo`:
|
|||
|
||||
<center>
|
||||
<video height="100%" width="700vmin" autoplay loop>
|
||||
<source src="/media/xwininfo.mp4" type="video/mp4" />
|
||||
<source src="/~targetdisk/media/xwininfo.mp4" type="video/mp4" />
|
||||
</video>
|
||||
</center>
|
||||
|
||||
|
|
|
|||
466
blog/2025-08-18_9p-ubuntu-server.md
Normal file
466
blog/2025-08-18_9p-ubuntu-server.md
Normal file
|
|
@ -0,0 +1,466 @@
|
|||
# 9p rootfs on Ubuntu Server VM Using QEMU
|
||||
|
||||
This post is a guide on how to install Ubuntu Server to a Plan 9 filesystem.
|
||||
|
||||
If you are impatient you can skip to the actual tutorial content by clicking
|
||||
[here](#teal-deer).
|
||||
|
||||
## What is Plan 9?
|
||||
[Plan 9](https://en.wikipedia.org/wiki/Plan_9_from_Bell_Labs) started out as a
|
||||
research operating system developed at
|
||||
[Bell Labs](https://en.wikipedia.org/wiki/Bell_Labs). It extends the
|
||||
"everything is a file" API metaphor of UNIX even farther. Filesystem APIs for
|
||||
networking sockets, graphics, and more. Any resource of any system, be it
|
||||
remote or local, can be accessed if you have a mounted filesystem with these
|
||||
special files in them!
|
||||
|
||||

|
||||
|
||||
To accomplish all of this resource and file sharing, the
|
||||
[Plan 9 Filesystem Protocol](https://en.wikipedia.org/wiki/9P_(protocol)) (also
|
||||
known as **9p**) was devised.
|
||||
|
||||
Within the last few years, a lot of people have begun to realise that 9p was
|
||||
really neat and started to use it in Linux. It is especially popular for
|
||||
sharing resources with virtual machines because of how lightweight it is as a
|
||||
protocol.
|
||||
|
||||
### What happened to Plan 9?
|
||||
Sadly because of the pressures of the capitalistic hellscape we all live under,
|
||||
Bell Labs changed hands and some of this research got pared down. Bell Labs was
|
||||
first spun off into AT&T Technologies which was then bought by
|
||||
Lucent that later merged with the French Alcatel to form Alcatel-Lucent that
|
||||
then got bought again by the undead corpse of Nokia. Standard murder-execution,
|
||||
er, merger-acquisition stuff.
|
||||
|
||||

|
||||
|
||||
The researchers were scattered like ashes to the
|
||||
wind, many of them finding homes at places like Google et. al. They took their
|
||||
work with them to the BSD, Linux, and Macintosh systems that they ended up
|
||||
working with at their new jobs. They ported a lot of the features from Plan 9
|
||||
to user spaces of other operating systems and even made kernel modules and
|
||||
extensions to add back some of the features they sorely missed.
|
||||
|
||||
*(See [#1](#no1) for more details on this.)*
|
||||
|
||||
## Why use a 9p rootfs?
|
||||
The most common way that developers boot their VMs is using disk images. This
|
||||
is "fine," but it introduces a lot of annoyances and inefficiencies.
|
||||
|
||||
Let's look at the latter first. When you are booting a virtual machine with a
|
||||
disk image you are literally telling your virtual machine software to emulate an
|
||||
entire computer motherboard *and* hard drive. This isn't something that can
|
||||
typically be accelerated with your host system's virtualisation features,
|
||||
either. Your virtual machine software (like QEMU, for instance) literally has
|
||||
to do the grunt work of emulating the behavior a motherboard's
|
||||
[controller chips](https://en.wikipedia.org/wiki/Intel_440BX),
|
||||
[buses](https://en.wikipedia.org/wiki/Bus_(computing)), and attached devices
|
||||
(like the drive serving up your disk image).
|
||||
|
||||
Let's also look at why booting a VM this way might be annoying for a developer.
|
||||
Because all of your VM's files are trapped in a disk image file this means that
|
||||
you have run an extra command to `mount` the disk image on some sort of loopback
|
||||
device. If you use a QEMU Qcow image you have to do an extra step of mapping
|
||||
the image to a
|
||||
[network block device](https://www.qemu.org/docs/master/tools/qemu-nbd.html).
|
||||
Some of these things may require administrative/`root` privileges without
|
||||
special configuration magic. Even worse, you often can't directly write to a
|
||||
running VM's filesystem without first turning off the virtual machine! This
|
||||
leads users down iSCSI, NFS, SMB, and SSH rabbit holes if they want to poke at
|
||||
the filesystems of running VMs. What if there was a better way?
|
||||
|
||||
With 9p filesystems, you can inject the filesystem directly into a mapped space of
|
||||
guest's kernel without the baggage of emulating block devices and running
|
||||
traditional filesystems designed for physical disks. You can directly modify
|
||||
the filesystems of a running VM in situ without any catastrophic consequences.
|
||||
You can even map the permissions of the shared filesystem to the user account
|
||||
running the VM.
|
||||
|
||||
Meaning on your VM you can have this on the mapped guest 9p filesystem:
|
||||
|
||||
```
|
||||
root@ubuntu-server:~# ls /
|
||||
total 32K
|
||||
drwxr-xr-x 1 1000 1000 266 Aug 7 05:15 .
|
||||
drwxr-xr-x 1 1000 1000 266 Aug 7 05:15 ..
|
||||
-rwxr-xr-x 1 1000 1000 6.9K Aug 7 05:10 arch-chroot
|
||||
lrwxrwxrwx 1 root root 7 Apr 22 2024 bin -> usr/bin
|
||||
drwxr-xr-x 1 root root 0 Feb 26 2024 bin.usr-is-merged
|
||||
drwxr-xr-x 1 root root 306 Aug 7 17:26 boot
|
||||
drwxr-xr-x 15 root root 3.7K Aug 8 17:59 dev
|
||||
drwxr-xr-x 1 root root 2.8K Aug 7 18:03 etc
|
||||
drwxr-xr-x 1 root root 0 Apr 22 2024 home
|
||||
lrwxrwxrwx 1 root root 7 Apr 22 2024 lib -> usr/lib
|
||||
drwxr-xr-x 1 root root 0 Apr 8 2024 lib.usr-is-merged
|
||||
lrwxrwxrwx 1 root root 9 Apr 22 2024 lib64 -> usr/lib64
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 media
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 mnt
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 opt
|
||||
dr-xr-xr-x 417 root root 0 Aug 8 17:59 proc
|
||||
drwx------ 1 root root 142 Aug 8 2025 root
|
||||
drwxr-xr-x 15 root root 500 Aug 8 17:59 run
|
||||
lrwxrwxrwx 1 root root 8 Apr 22 2024 sbin -> usr/sbin
|
||||
drwxr-xr-x 1 root root 0 Mar 31 2024 sbin.usr-is-merged
|
||||
drwxr-xr-x 1 root root 12 Aug 7 17:44 snap
|
||||
drwxr-xr-x 1 root root 0 Aug 7 05:00 srv
|
||||
dr-xr-xr-x 13 root root 0 Aug 8 17:59 sys
|
||||
drwxrwxrwt 1 root root 640 Aug 8 17:59 tmp
|
||||
drwxr-xr-x 1 root root 94 Aug 7 05:00 usr
|
||||
drwxr-xr-x 1 root root 124 Aug 7 17:44 var
|
||||
```
|
||||
|
||||
...and get this on your host machine:
|
||||
```
|
||||
targetdisk@vm-host:~$ ls 9p/
|
||||
total 32K
|
||||
drwxr-xr-x 1 targetdisk targetdisk 266 Aug 7 00:15 .
|
||||
drwxr-xr-x 1 targetdisk targetdisk 76 Aug 5 14:48 ..
|
||||
-rwxr-xr-x 1 targetdisk targetdisk 6.9K Aug 7 00:10 arch-chroot
|
||||
-rw------- 1 targetdisk targetdisk 7 Apr 22 2024 bin
|
||||
drwx------ 1 targetdisk targetdisk 0 Feb 26 2024 bin.usr-is-merged
|
||||
drwx------ 1 targetdisk targetdisk 306 Aug 7 12:26 boot
|
||||
drwx------ 1 targetdisk targetdisk 128 Aug 7 00:00 dev
|
||||
drwx------ 1 targetdisk targetdisk 2.8K Aug 7 13:03 etc
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 22 2024 home
|
||||
-rw------- 1 targetdisk targetdisk 7 Apr 22 2024 lib
|
||||
-rw------- 1 targetdisk targetdisk 9 Apr 22 2024 lib64
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 8 2024 lib.usr-is-merged
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 media
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 mnt
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 opt
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 22 2024 proc
|
||||
drwx------ 1 targetdisk targetdisk 142 Aug 8 12:59 root
|
||||
drwx------ 1 targetdisk targetdisk 44 Aug 7 00:06 run
|
||||
-rw------- 1 targetdisk targetdisk 8 Apr 22 2024 sbin
|
||||
drwx------ 1 targetdisk targetdisk 0 Mar 31 2024 sbin.usr-is-merged
|
||||
drwx------ 1 targetdisk targetdisk 12 Aug 7 12:44 snap
|
||||
drwx------ 1 targetdisk targetdisk 0 Aug 7 00:00 srv
|
||||
drwx------ 1 targetdisk targetdisk 0 Apr 22 2024 sys
|
||||
drwx------ 1 targetdisk targetdisk 478 Aug 8 12:59 tmp
|
||||
drwx------ 1 targetdisk targetdisk 94 Aug 7 00:00 usr
|
||||
drwx------ 1 targetdisk targetdisk 124 Aug 7 12:44 var
|
||||
```
|
||||
<a name="teal-deer">
|
||||
|
||||
## Installing Ubuntu Server to a 9p filesystem
|
||||
|
||||
### Get Ubuntu Server
|
||||
First, download the
|
||||
[Ubuntu Server installer ISO](https://ubuntu.com/download/server)
|
||||
from [Ubuntu's website](https://ubuntu.com/).
|
||||
|
||||
### Start the VM
|
||||
Make a directory you'd like to export as a `9p` filesystem. For the purposes of
|
||||
this demonstration, we'll call our directory `ubuntu-9p`:
|
||||
```bash
|
||||
mkdir ubuntu-9p
|
||||
```
|
||||
|
||||
Now you can run your installer VM with your Ubuntu ISO attached and your 9p
|
||||
filesystem mapped and exported! You'll need to substitute the path for
|
||||
`UBUNTU_ISO` to the path of your downloaded Ubuntu Server ISO. I threw on some
|
||||
extra flags to enable some of the extra sandboxing features of QEMU.
|
||||
|
||||
Run the following on your host:
|
||||
```bash
|
||||
qemu-system-$(uname -m) \
|
||||
-cpu max \
|
||||
-enable-kvm \
|
||||
-smp $(nproc) \
|
||||
-nodefaults \
|
||||
-no-user-config \
|
||||
-nographic \
|
||||
-chardev stdio,id=virtcons0 \
|
||||
-device virtio-serial-pci \
|
||||
-device virtconsole,chardev=virtcons0 \
|
||||
-m 8G \
|
||||
-net user,hostfwd=tcp::2221-:22 \
|
||||
-net nic \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev-fs0,mount_tag=fs0 \
|
||||
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
|
||||
-drive file=UBUNTU_ISO,media=cdrom,id=virtiso1 \
|
||||
-fsdev local,security_model=mapped,id=fsdev-fs0,multidevs=remap,path=ubuntu-9p
|
||||
```
|
||||
|
||||
This will start a virtual machine with the local directory `ubuntu-9p` mapped to
|
||||
a 9p filesystem on the guest named `fs0`.
|
||||
|
||||
### Get to a shell
|
||||
After a little wait the VM should boot into the text-mode Ubuntu installer
|
||||
interface. At the time of writing the Ubuntu installer doesn't support
|
||||
installing to systems without block devices. This means we will have to install
|
||||
by other means in a shell on our booted install media.
|
||||
|
||||
Select `Enter shell` from the `[ Help ]` menu.
|
||||
|
||||
```
|
||||
================================================================================
|
||||
Serial ┌──────────────[ Help ]┐
|
||||
=======================================================│ Help on this screen │=
|
||||
│ Keyboard shortcuts │
|
||||
As the installer is running on a serial console, it h│ Enter shell │
|
||||
mode, using only the ASCII character set and black an│ View error reports │
|
||||
├──────────────────────┤
|
||||
If you are connecting from a terminal emulator such a│ About this installer │
|
||||
supports unicode and rich colours you can switch to "│ Help on SSH access │
|
||||
unicode, colours and supports many languages. ├──────────────────────┤
|
||||
│ Toggle rich mode │
|
||||
You can also connect to the installer over the networ└──────────────────────┘
|
||||
allow use of rich mode.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[ Continue in rich mode > ]
|
||||
[ Continue in basic mode > ]
|
||||
[ View SSH instructions ]
|
||||
```
|
||||
|
||||
If during this install process you want a bigger terminal you can `exit` the
|
||||
shell and select `Help on SSH access`. You'll get the name of the `installer`
|
||||
account and a password that was randomly-generated when you booted the Ubuntu
|
||||
live installer media.
|
||||
|
||||
```
|
||||
┌────────────────────────── Help on SSH access ──────────────────────────┐
|
||||
│ │
|
||||
│ It is possible to connect to the installer over the network, which │
|
||||
│ might allow the use of a more capable terminal and can offer more │
|
||||
│ languages than can be rendered in the Linux console. │
|
||||
│ │
|
||||
│ To connect, SSH to installer@10.0.2.15. │
|
||||
│ │
|
||||
│ The password you should use is "Qhs_3:@C^#H'nw`4rd6%". │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ [ Close ] │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
In another terminal on your host machine `ssh` like so:
|
||||
```bash
|
||||
ssh -p 2221 installer@127.0.0.1
|
||||
```
|
||||
|
||||
If you run the VM multiple times, you might have to edit your
|
||||
`~/.ssh/known_hosts` file to edit the host fingerprint entry for the `127.0.0.1:2221`
|
||||
host.
|
||||
|
||||
### Mount your 9p filesystem
|
||||
Now that you have a working shell, you'll need to mount the 9p filesystem:
|
||||
```
|
||||
root@ubuntu-server:/# mount -t 9p -o trans=virtio fs0 /mnt
|
||||
```
|
||||
|
||||
### Bootstrap the base Ubuntu system
|
||||
With the destination 9p filesystem mounted, it's now time to bootstrap a base
|
||||
Ubuntu installation to it. The Ubuntu Server installation media at the time of
|
||||
writing does not include the `debootstrap` tool so we'll have to install it.
|
||||
|
||||
First, update the package lists:
|
||||
```
|
||||
root@ubuntu-server:/# apt update
|
||||
```
|
||||
|
||||
Now, install the `debootstrap` tool:
|
||||
```
|
||||
root@ubuntu-server:/# apt install debootstrap
|
||||
```
|
||||
|
||||
It's time to begin bootstrapping our base system. You'll need the shortened
|
||||
codename of Ubuntu release you'd like to install. For instance, if
|
||||
you were installing Ubuntu 20.04 "Noble Numbat," you'd use the short name
|
||||
`noble`.
|
||||
*(See [#2](#no2) for more details.)*
|
||||
|
||||
Bootstrap the new system with the `debootstrap` tool like so:
|
||||
```
|
||||
root@ubuntu-server:/# debootstrap noble /mnt
|
||||
```
|
||||
|
||||
### Change root to the target system
|
||||
For the rest of the installation process, the rest of the steps will need to be
|
||||
performed from a shell on the target system we just bootstrapped. To do this,
|
||||
we'll need to pass some pseudo-filesystems on from our live installation
|
||||
environment and execute the `chroot` command to "change root" into the target
|
||||
that lives on our 9p filesystem.
|
||||
|
||||
First, let's pass over the live environment's pseudo-filesystems as `bind`
|
||||
mounts:
|
||||
```
|
||||
root@ubuntu-server:/# mount -o bind /proc /mnt/proc
|
||||
root@ubuntu-server:/# mount -o bind /dev /mnt/dev
|
||||
root@ubuntu-server:/# mount -o bind /dev/pts /mnt/dev/pts
|
||||
root@ubuntu-server:/# mount -o bind /sys /mnt/sys
|
||||
```
|
||||
|
||||
Now change root to the 9p filesystem:
|
||||
```
|
||||
root@ubuntu-server:/# chroot /mnt /usr/bin/bash
|
||||
```
|
||||
|
||||
### Installing a kernel
|
||||
The earlier `debootstrap` command line installed most of the components of a
|
||||
working system, but not quite all of them. The next thing you'll need to do is
|
||||
install a Linux kernel. Later on you'll be passing this kernel and its
|
||||
associated initial RAM disk image as command-line flags to QEMU as `-kernel` and
|
||||
`-initrd`, respectively.
|
||||
|
||||
We're going to pick the `linux-virtual` package here for a smaller footprint on
|
||||
the host machine:
|
||||
```
|
||||
root@ubuntu-server:/# apt install linux-virtual
|
||||
```
|
||||
|
||||
### Enable 9p kernel modules
|
||||
To allow the installed system to boot properly, you'll need to add 9p kernel
|
||||
modules to the initial RAM disk image. The following lines need to be appended
|
||||
to the end of `/etc/initramfs-tools/modules`:
|
||||
```
|
||||
9p
|
||||
9pnet
|
||||
9pnet_virtio
|
||||
```
|
||||
|
||||
You can add them with an editor like `vi` or with shell I/O redirects like so:
|
||||
```
|
||||
root@ubuntu-server:/# cd /etc/initramfs-tools
|
||||
root@ubuntu-server:/etc/initramfs-tools# echo 9p >> modules
|
||||
root@ubuntu-server:/etc/initramfs-tools# echo 9pnet >> modules
|
||||
root@ubuntu-server:/etc/initramfs-tools# echo 9pnet_virtio >> modules
|
||||
```
|
||||
|
||||
The lines added to `/etc/initramfs-tools/modules` tell Ubuntu's scripts to
|
||||
include the `9p`, `9pnet`, and `9pnet_virtio` kernel modules in the initial RAM
|
||||
disk image. With that done, update the initial RAM filesystem:
|
||||
```
|
||||
root@ubuntu-server:/# update-initramfs -u
|
||||
```
|
||||
|
||||
### Enable DHCP on boot
|
||||
To get internet access when your VM boots, you'll need to configure a network
|
||||
interface. The VM can automatically get an IP address and a gateway at with
|
||||
DHCP enabled on its virtual network interface (`ens2` on my VM). Ubuntu has
|
||||
this ~~terrible~~ thing called [netplan](https://netplan.io/) in its
|
||||
base installation that can be used to configure network interfaces. Since it's
|
||||
already here, we might as well use it.
|
||||
|
||||
Get the name of the virtual Ethernet interface with the `ip` command:
|
||||
```
|
||||
root@ubuntu-server:/# ip a
|
||||
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
|
||||
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
|
||||
inet 127.0.0.1/8 scope host lo
|
||||
valid_lft forever preferred_lft forever
|
||||
inet6 ::1/128 scope host noprefixroute
|
||||
valid_lft forever preferred_lft forever
|
||||
2: ens2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
|
||||
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
|
||||
altname enp0s2
|
||||
inet 10.0.2.15/24 metric 100 brd 10.0.2.255 scope global dynamic ens2
|
||||
valid_lft 85077sec preferred_lft 85077sec
|
||||
inet6 fec0::5054:ff:fe12:3456/64 scope site dynamic mngtmpaddr noprefixroute
|
||||
valid_lft 86078sec preferred_lft 14078sec
|
||||
inet6 fe80::5054:ff:fe12:3456/64 scope link
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
Your network interface should be named something like `ens2`. With that known,
|
||||
tell the `netplan` command to enable DHCPv4 on the interface like so:
|
||||
```
|
||||
root@ubuntu-server:/# netplan set --origin-hint ens2 ethernets.ens2.dhcp4=true
|
||||
```
|
||||
|
||||
This will create a YAML file with the `.yaml` extension in `/etc/netplan` that
|
||||
has `ens2` somewhere in the name. Hints are merely suggestions, after all.
|
||||
|
||||
### Set root password
|
||||
There's one last thing you need to do before you can enjoy your newly-installed
|
||||
VM: set a `root` password! You can do it by invoking the `passwd` command with
|
||||
no arguments:
|
||||
```
|
||||
root@ubuntu-server:/# passwd
|
||||
New password:
|
||||
Retype new password:
|
||||
passwd: password updated successfully
|
||||
```
|
||||
|
||||
With the password set, you may now `exit` the changed-root shell and `poweroff`
|
||||
the installer VM.
|
||||
|
||||
## Enjoying your VM
|
||||
You can now boot your new VM with the following command:
|
||||
```
|
||||
qemu-system-$(uname -m) \
|
||||
-cpu max \
|
||||
-enable-kvm \
|
||||
-smp $(nproc) \
|
||||
-nodefaults \
|
||||
-no-user-config \
|
||||
-nographic \
|
||||
-chardev stdio,id=virtcons0 \
|
||||
-device virtio-serial-pci \
|
||||
-device virtconsole,chardev=virtcons0 \
|
||||
-m 8G \
|
||||
-net user,hostfwd=tcp::2222-:22 \
|
||||
-net nic \
|
||||
-device virtio-9p-pci,id=fs0,fsdev=fsdev-fs0,mount_tag=fs0 \
|
||||
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
|
||||
-kernel ubuntu-9p/boot/$(<vmlinuz) \
|
||||
-append "earlyprintk=ttyS0 root=fs0 rw rootfstype=9p rootflags=trans=virtio,version=9p2000.L,msize=5000000,cache=mmap,posixacl console=ttyS0" \
|
||||
-initrd ubuntu-9p/boot/$(<initrd.img) \
|
||||
-fsdev local,security_model=mapped,id=fsdev-fs0,multidevs=remap,path=ubuntu-9p
|
||||
```
|
||||
|
||||
After a little wait, you should be greeted with a text-mode `getty` login prompt.
|
||||
Log into the `root` account with the password you set earlier.
|
||||
|
||||
### Logging with SSH
|
||||
If you would like to log in with SSH, you'll need to install the OpenSSH server
|
||||
software and configure it. Install it like so:
|
||||
```
|
||||
root@ubuntu-server:~# apt install openssh-server
|
||||
```
|
||||
|
||||
The Ubuntu install scripts for the OpenSSH server package should enable the
|
||||
daemon automatically, but you won't be able to get in yet. To do that, you'll
|
||||
need to copy a public key from your host so you can log in. If you don't
|
||||
already have a key, you can make one with `ssh-keygen`.
|
||||
|
||||
Once you have a key that you'd like to use, make a place to put it on the guest
|
||||
VM:
|
||||
```
|
||||
root@ubuntu-server:~# mkdir .ssh
|
||||
root@ubuntu-server:~# chmod 700 .ssh
|
||||
root@ubuntu-server:~# touch .ssh/authorized_keys
|
||||
root@ubuntu-server:~# chmod 600 .ssh/authorized_keys
|
||||
```
|
||||
|
||||
In another terminal on your host machine, add the public key you'd like to use to
|
||||
the end of the `authorized_keys` file like so:
|
||||
```
|
||||
targetdisk@vm-host:~$ cat ~/.ssh/id_ed25519.pub >> ubuntu-9p/root/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
Now you can log in to the VM with the following command:
|
||||
```bash
|
||||
ssh -p 2222 root@127.0.0.1
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
That's it! You now have a working minimal Ubuntu server installation that you
|
||||
can use and abuse from within and without, thanks to the Plan 9 filesystem!
|
||||
You'll probably want to save the long QEMU command to a shell script so that
|
||||
you can quickly boot up your VM later.
|
||||
|
||||
## SEE ALSO
|
||||
1. <a name="no1">[Why did Plan 9's creators give up on Plan 9?](https://fqa.9front.org/fqa0.html#0.2.3)
|
||||
via [9front](https://9front.org)
|
||||
2. <a name="no2">[Ubuntu Version History](https://en.wikipedia.org/wiki/Ubuntu_version_history) via [Wikipedia](https://wikipedia.org)
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta charset="utf-8" name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="page.css" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div class="fc">
|
||||
<img src="andrea0s-plain-noextfonts.svg" />
|
||||
<img src="/~targetdisk/blob/andrea0s-plain-noextfonts.svg" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
|||
18
page.css
18
page.css
|
|
@ -3,6 +3,10 @@ body {
|
|||
margin: 0px;
|
||||
}
|
||||
|
||||
pre code {
|
||||
font-family: AnonymicePro Nerd Font;
|
||||
}
|
||||
|
||||
.black {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
|
|
@ -49,6 +53,13 @@ body {
|
|||
margin: 0.7pt;
|
||||
padding: 2%;
|
||||
background-color: #ffffff;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.content-border-right {
|
||||
|
|
@ -130,5 +141,10 @@ body {
|
|||
|
||||
@font-face {
|
||||
font-family: Ebrima;
|
||||
src: url(ebrimabd.ttf);
|
||||
src: url(/~targetdisk/blob/ebrimabd.ttf);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: AnonymicePro Nerd Font;
|
||||
src: url(/~targetdisk/blob/AnonymiceProNerdFontMono-Regular.ttf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +1,92 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
HEADING='Posts'
|
||||
TITLE="Andrea's blog"
|
||||
BASENAME=${BASENAME-blog}
|
||||
BASE_URL=${BASE_URL-https://sqt.wtf}
|
||||
DESCRIPTION="Andrea's personal blog."
|
||||
N_DESC_CHARS=500
|
||||
|
||||
articles=( $BASENAME/*.md )
|
||||
|
||||
cat <<HDR
|
||||
<div class="inner">
|
||||
<h1>Posts</h1>
|
||||
<h1>$HEADING</h1>
|
||||
<dl> <!-- no longer me -->
|
||||
HDR
|
||||
|
||||
for blog_post_md in blog/*.md; do
|
||||
date=$(grep -o '[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' <<<"$blog_post_md")
|
||||
title=$(grep -m 1 '^#\s\+' "$blog_post_md" | sed 's/^#\s\+//')
|
||||
html=$(sed 's/\.md$/.html/' <<<"$blog_post_md")
|
||||
cat > blog/feed.xml <<RSSHDR
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>$TITLE</title>
|
||||
<link>$BASE_URL/$BASENAME.html</link>
|
||||
<description>$DESCRIPTION</description>
|
||||
RSSHDR
|
||||
|
||||
for ((i=${#articles[@]}-1; i>=0; i--)); do
|
||||
post_md=${articles[$i]}
|
||||
date=$(grep -o '[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}' <<<"$post_md")
|
||||
title=$(grep -m 1 '^#\s\+' "$post_md" | sed 's/^#\s\+//')
|
||||
html=$(sed 's/\.md$/.html/' <<<"$post_md")
|
||||
rss_desc=$(grep -v '^\s*$' "$post_md" | grep -v '^\s*\*by' | grep -v '^#' | grep -v '^\s*!\[' | grep -v '<.\+>'| head -c $N_DESC_CHARS)'...'
|
||||
|
||||
cat <<INNIE
|
||||
<dt>$date</dt>
|
||||
<dd><a href="$html">$title</a></dd>
|
||||
INNIE
|
||||
|
||||
cat >> $BASENAME/feed.xml <<RSSINNIE
|
||||
<item>
|
||||
<title>$title</title>
|
||||
<link>$BASE_URL/~targetdisk/$html</link>
|
||||
<description>$rss_desc</description>
|
||||
<pubDate>$(date -d "$date" '+%a, %d %b %Y %H:%M:%S %Z')</pubDate>
|
||||
</item>
|
||||
RSSINNIE
|
||||
done
|
||||
|
||||
cat <<FTR # Short for Fetterman
|
||||
</dl> <!-- no longer on the dl -->
|
||||
|
||||
<h2>Other People's Posts</h2>
|
||||
{{range .Articles}}
|
||||
<div>
|
||||
<h4 class="title" dir="auto">
|
||||
<a href="{{.Link}}" target="_blank" rel="noopener">{{.Title}}</a>
|
||||
</h4>
|
||||
<p class="summary" dir="auto">{{.Summary}}</p>
|
||||
<small class="source">
|
||||
via <a href="{{.SourceLink}}">{{.SourceTitle}}</a>
|
||||
</small>
|
||||
<small class="date">{{.Date | datef "January 2, 2006"}}</small>
|
||||
<p><a href="/~targetdisk/$BASENAME/feed.xml">RSS</a></p>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
</br></br>
|
||||
<small>
|
||||
<p>
|
||||
Generated by
|
||||
<a href="https://git.sr.ht/~sircmpwn/openring">openring</a>
|
||||
</p>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.webring .title {
|
||||
margin: 0;
|
||||
}
|
||||
.webring .summary {
|
||||
font-size: 0.8rem;
|
||||
flex: 1 1 0;
|
||||
}
|
||||
</style>
|
||||
FTR
|
||||
|
||||
#
|
||||
# <h2>Other People's Posts</h2>
|
||||
# {{range .Articles}}
|
||||
# <div>
|
||||
# <h4 class="title" dir="auto">
|
||||
# <a href="{{.Link}}" target="_blank" rel="noopener">{{.Title}}</a>
|
||||
# </h4>
|
||||
# <p class="summary" dir="auto">{{.Summary}}</p>
|
||||
# <small class="source">
|
||||
# via <a href="{{.SourceLink}}">{{.SourceTitle}}</a>
|
||||
# </small>
|
||||
# <small class="date">{{.Date | datef "January 2, 2006"}}</small>
|
||||
# </div>
|
||||
# {{end}}
|
||||
#
|
||||
# </br></br>
|
||||
# <small>
|
||||
# <p>
|
||||
# Generated by
|
||||
# <a href="https://git.sr.ht/~sircmpwn/openring">openring</a>
|
||||
# </p>
|
||||
# </small>
|
||||
#</div>
|
||||
#
|
||||
#<style>
|
||||
#.webring .title {
|
||||
# margin: 0;
|
||||
#}
|
||||
#.webring .summary {
|
||||
# font-size: 0.8rem;
|
||||
# flex: 1 1 0;
|
||||
#}
|
||||
#</style>
|
||||
#FTR
|
||||
|
||||
cat >> $BASENAME/feed.xml <<RSSFTR
|
||||
</channel>
|
||||
</rss>
|
||||
RSSFTR
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue