From e81b411cd58c1e129a3f7554beff5cd0bc16fbd8 Mon Sep 17 00:00:00 2001
From: Thomas Schneider <thomas@fsmpi.rwth-aachen.de>
Date: Fri, 10 May 2024 15:03:55 +0200
Subject: [PATCH] Remodel for kiosk raspi setup

---
 .gitignore    |   7 ++-
 Makefile      |  13 ++---
 README.md     |  85 +++++++++++++++++++++++++++++
 launch.sh     |  31 -----------
 make-image.sh | 144 +++++++++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 225 insertions(+), 55 deletions(-)
 create mode 100644 README.md
 delete mode 100755 launch.sh

diff --git a/.gitignore b/.gitignore
index 49765b9..c836431 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-/image.sqfs
-/initramfs-virt
-/vmlinuz-virt
-/apk-cache
+apk-cache
+root.img
+boot.tar
diff --git a/Makefile b/Makefile
index 9bf2a8c..5258dea 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,10 @@
 IMAGE=docker.io/library/alpine
-TAG=3.18
+TAG=3.19
+ARCH=arm64
 
-image.sqfs:
-	podman run --rm --volume $$PWD:/build --workdir /build \
+image:
+	podman run --arch $(ARCH) \
+		--rm --volume $$PWD:/build --workdir /build \
 		$(IMAGE):$(TAG) \
-		./make-image.sh
-
-.PHONY: image.sqfs
+		./make-image.sh $(ARCH)
+.PHONY: image
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4f073bb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,85 @@
+# Kiosk terminal image builder for Raspberry Pi
+
+This project builds an image for a Raspberry Pi that boots directly into a
+browser with a predefined web page.  It is a soft fork of
+https://git.fsmpi.rwth-aachen.de/thomas/ci-vm-image (which was designed for CI
+VM images on amd64).
+
+The system runs from RAM, almost all changes at runtime are ephemeral.  The
+device can safely be unplugged without shutting down with no real risk of data
+loss.  As the boot partition is mounted read-write in order to regularly update
+the software clock, there is still a minor chance, but in practice it is very
+unlikely to corrupt the file system.
+
+On boot, the `kiosk` user is automatically logged in and launches Firefox in
+kiosk mode with `/boot/CONFIG/kiosk.html` (see below).  Terminal switching is
+possible, both `root` and `kiosk` passwords are empty.  When Firefox quits, it
+will automatically be relaunched, unless `/tmp/noautologin` exists.
+
+:::warning
+**Bug**: Firefox will not show (only blank screen) until the first user input,
+such as mouse movement or a key press.
+:::
+
+## Building
+
+Requirements: Podman and a native arm64 host or a correctly configured
+and container-compatible qemu-binfmt setup.
+
+```
+% make           # for arm64, RPi 3 and later
+% make ARCH=arm  # for armv7, RPi 2
+```
+
+RPi 1 (armv6/armhf) is currently untested and unsupported, but could work.  One
+would need to pass `--variant` to the `podman` invocation or use a single-arch
+image such as `docker.io/arm32v6/alpine`.
+
+The results (`boot.tar`, `root.img`) are placed in the `arm64` or `arm`
+directory.
+
+### SELinux
+
+On SELinux-enabled systems (such as Fedora), the container may not have access
+to the directories.  Either move the whole building directory to an
+appropriately labelled place, or customise the scripts to place the in-container
+directories elsewhere, or set the type context label of the build directory to
+`container_file_t` (e.&#8239;g., `chcon -R -t container_file_t .`).  You may
+need to install `container-selinux` manually on Fedora.
+
+## Flashing
+
+  1. Partition an SD card with the typical RPi layout:
+	 1. a small-ish (~200MiB should be enough, cf.&nbsp;size of `boot.tar`)
+        partition at the beginning of MBR type `0x0c` for the boot file system,
+     2. and a partition of at least 2GiB and type `0x83` for the root file
+        system.
+  2. Create a FAT32 file system (e.&#8239;g., `mkfs.vfat /dev/mmcblk0p1`) on the
+     first partition.
+  3. Mount the first partition and extract `boot.tar` to its root.  You can
+     ignore the error that the `boot` symlink cannot be created.
+  4. Copy `root.img` to the second partition (e.&#8239;g., `pv -pterbaY root.img
+     > /dev/mmcblk0p2`)
+
+## Configuration
+
+All configurables are stored in the `CONFIG` directory of the boot partition,
+i.&#8239;e., `/boot/CONFIG/` in the running system.
+
+`swclock`
+: Software clock.  On boot, system time is set to the time stamp of this file.
+  Every hour, the time stamp of this file is updated.  During normal operation,
+  time should be updated via NTP.
+
+`interfaces`
+: Configuration for [ifupdown-ng](https://github.com/ifupdown-ng/ifupdown-ng).
+
+`hostname`
+: System host name.
+
+`kiosk.html`
+: Opened on boot, would typically contain a redirect to a web site.
+
+`wpa_supplicant.conf`
+: Additional WLAN configuration, required for WPA-EAP (PSK is possible via
+  `interfaces` alone).
diff --git a/launch.sh b/launch.sh
deleted file mode 100755
index 7c42af1..0000000
--- a/launch.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/sh
-set -e
-set -x
-exec qemu-system-x86_64 \
-	-M microvm,x-option-roms=off,pit=off,pic=off,rtc=off,isa-serial=off,pcie=on \
-	-bios /usr/share/qemu/qboot.rom \
-	-enable-kvm \
-	-cpu host \
-	-m 1536M \
-	-nodefaults \
-	-no-user-config \
-	-nographic \
-	-device virtio-balloon-device \
-	-device virtio-rng-device \
-	-drive id=root,file=image.sqfs,format=raw,if=none \
-	-device virtio-blk-device,drive=root \
-	-kernel vmlinuz-virt \
-	-initrd initramfs-virt \
-	-append "root=/dev/vda overlaytmpfs console=hvc0 earlyprintk=hvc0" \
-	-chardev stdio,mux=on,id=stdio \
-	-device virtio-serial-device \
-	-device virtconsole,chardev=stdio,name=console.0 \
-	-mon chardev=stdio,mode=readline \
-	-netdev user,id=net \
-	-device virtio-net-device,netdev=net \
-	-chardev socket,path=qga,server=on,wait=off,id=qga \
-	-device virtserialport,chardev=qga,name=org.qemu.guest_agent.0 \
-	-chardev socket,id=virtfs,path=/tmp/virtfs.sock \
-	-device vhost-user-fs-pci,chardev=virtfs,tag=virtfs,queue-size=1024 \
-	-object memory-backend-memfd,id=mem,size=1536M,share=on \
-	-numa node,memdev=mem
diff --git a/make-image.sh b/make-image.sh
index bf4c68b..d66d270 100755
--- a/make-image.sh
+++ b/make-image.sh
@@ -1,5 +1,7 @@
 #!/bin/sh
 
+# Environment variable from outside
+# shellcheck disable=SC2154
 if test x"$container" != xpodman; then
 	echo This script is supposed to be run inside the container. >&2
 	exit 1
@@ -7,42 +9,156 @@ fi
 set -e
 set -x
 
+mkdir -p "$1"
+cd "$1"
+
 mkdir -p /image/etc/apk
 mkdir -p apk-cache
 ln -s "$PWD/apk-cache" /etc/apk/cache
 ln -s "$PWD/apk-cache" /image/etc/apk/cache
 cp -r /etc/apk/repositories /etc/apk/keys /image/etc/apk
-apk add --root /image --update-cache --initdb alpine-base mkinitfs \
-	qemu-guest-agent git git-lfs gitlab-runner bash buildah
-echo 'features="base squashfs virtio"' > /image/etc/mkinitfs/mkinitfs.conf
-apk add --root /image linux-virt
+
+apk add --root /image --update-cache --initdb \
+	alpine-base mkinitfs raspberrypi raspberrypi-utils rng-tools chrony \
+	kbd-bkeymaps font-terminus \
+	dosfstools e2fsprogs \
+	weston weston-backend-drm weston-shell-desktop weston-terminal \
+	mesa-dri-gallium \
+	pam-rundir seatd shadow-login agetty \
+	wpa_supplicant wireless-tools iw iwd \
+	eudev udev-init-scripts udev-init-scripts-openrc \
+	firefox-esr font-dejavu font-noto
+echo 'features="base ext4 kms mmc usb"' > /image/etc/mkinitfs/mkinitfs.conf
+apk add --root /image linux-rpi raspberrypi-bootloader
 
 oldpwd="$PWD"
 cd /image/etc/runlevels
-for i in devfs dmesg mdev hwdrivers cgroups; do
+for i in devfs dmesg cgroups localmount udev udev-trigger udev-settle udev-postmount; do
 	ln -s /etc/init.d/$i sysinit/$i
 done
-for i in modules hostname sysctl bootmisc syslog osclock networking; do
+for i in bootmisc consolefont hostname klogd loadkmap modules networking rngd seedrng swclock sysctl syslog; do
 	ln -s /etc/init.d/$i boot/$i
 done
-ln -s /etc/init.d/killprocs shutdown/killprocs
-for i in qemu-guest-agent; do
+for i in killprocs mount-ro savecache; do
+	ln -s /etc/init.d/$i shutdown/$i
+done
+for i in crond chronyd seatd; do
 	ln -s /etc/init.d/$i default/$i
 done
 cd "$oldpwd"
 
-echo hvc0::respawn:/sbin/getty 115200 hvc0 >> /image/etc/inittab
-cat <<EOF >/image/etc/network/interfaces
+mkdir /image/boot/CONFIG
+
+echo 'swclock_file=/boot/CONFIG/swclock' > /image/etc/conf.d/swclock
+touch /image/boot/CONFIG/swclock
+
+cat <<EOF > /image/etc/periodic/hourly/swclock.sh
+#!/bin/sh
+touch /boot/CONFIG/swclock
+EOF
+chmod +x /image/etc/periodic/hourly/swclock.sh
+
+ln -s /boot/CONFIG/interfaces /image/etc/network/interfaces
+cat <<EOF >/image/boot/CONFIG/interfaces
 auto lo
 iface lo inet loopback
 
 auto eth0
 iface eth0 inet dhcp
+
+#auto wlan0
+#iface wlan0
+#	use dhcp
+#	use wifi
+#	wifi-config-path /boot/CONFIG/wpa_supplicant.conf
+
+#auto wlan0
+#iface wlan0
+#	use dhcp
+#	use wifi
+#	wifi-ssid Freifunk
+
+#auto wlan0
+#iface wlan0
+#	use dhcp
+#	use wifi
+#	wifi-ssid FranzBox1337
+#	wifi-psk hunter2
+EOF
+
+ln -fs /boot/CONFIG/hostname /image/etc/hostname
+echo localhost > /image/boot/CONFIG/hostname
+
+cat <<EOF >> /image/etc/fstab
+/dev/mmcblk0p1 /boot vfat fmask=0133,dmask=0022 0 2
+tmpfs /tmp tmpfs nosuid,nodev 0 0
+EOF
+
+cat <<EOF > /image/boot/CONFIG/kiosk.html
+<!doctype html>
+<meta http-equiv="refresh" content="0; url=https://kiosk.kif.rocks" />
+EOF
+
+cat <<EOF > /image/boot/CONFIG/wpa_supplicant.conf
+network={
+	ssid="eduroam"
+	key_mgmt=WPA_EAP
+	eap=PWD
+	identity="u1138@jku-lummerland.edu"
+	password="hunter2"
+}
 EOF
 
+chroot /image setup-user -u -g seat kiosk
+
+cat <<EOF > /image/home/kiosk/.profile
+if [ \$(tty) = /dev/tty1 -a ! -e /tmp/noautologin ] ; then
+	exec weston
+fi
+EOF
+
+cat <<EOF > /image/home/kiosk/kiosk.sh
+#!/bin/sh
+exec firefox-esr --kiosk /boot/CONFIG/kiosk.html
+EOF
+chmod +x /image/home/kiosk/kiosk.sh
+chown 1000:1000 /image/home/kiosk/kiosk.sh
+
+mkdir /image/home/kiosk/.config
+cat <<EOF > /image/home/kiosk/.config/weston.ini
+[core]
+shell=kiosk
+xwayland=false
+
+[keyboard]
+keymap_layout=de
+
+[autolaunch]
+path=/home/kiosk/kiosk.sh
+watch=true
+EOF
+
+chown -R 1000:1000 /image/home/kiosk
+
+chroot /image /sbin/setup-keymap de de
+
+echo 'consolefont="ter-128n.psf.gz"' > /image/etc/conf.d/consolefont
+
+sed \
+	-e '/tty1/c\tty1::respawn:/sbin/agetty --autologin kiosk --noclear tty1' \
+	-i /image/etc/inittab
+
+echo 'root=/dev/mmcblk0p2 rootfstype=ext4 fsck.repair=yes rootwait overlaytmpfs' > /image/boot/cmdline.txt
+echo 'dtoverlay=vc4-kms-v3d' > /image/boot/usercfg.txt
+
 find /image/var/cache/apk -name APKINDEX.\* -delete
 rm -f /image/etc/apk/cache
-mv /image/boot/vmlinuz-virt /image/boot/initramfs-virt .
-apk add squashfs-tools
-rm -f image.sqfs
-mksquashfs /image/ image.sqfs
+
+rm -f boot.tar
+tar -C /image/boot -cf boot.tar .
+find /image/boot -mindepth 1 -delete
+
+apk add e2fsprogs
+rm -f root.img
+truncate -s 2G root.img
+mkfs.ext4 -d /image/ root.img
-- 
GitLab