unos-repository

APK repository for unos
git clone git://git.finwo.net/misc/unos-repository
Log | Files | Refs | README

commit 9427b1657ea91c724eb631761002facaa7a1e6c5
parent 478dbe703a4bbc6bbc152eb5352aacd3fdd71be1
Author: finwo <finwo@pm.me>
Date:   Thu, 17 Sep 2026 17:55:30 +0200

Bootable testing

Diffstat:
Amk/run-qemu.sh | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Apackages/base-files/files/inittab | 4++++
Apackages/base-files/files/rcS | 47+++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/base-files/template | 8+++++---
Mpackages/tinyssh/template | 3++-
5 files changed, 179 insertions(+), 4 deletions(-)

diff --git a/mk/run-qemu.sh b/mk/run-qemu.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# mk/run-qemu.sh - build a bootable UNOS image with injected SSH keys and run it in QEMU. +# No hardcoding of key names: discovers pubkeys at runtime. +set -eu + +HERE=$(cd "$(dirname "$0")" && pwd) +ROOT=$(cd "${HERE}/.." && pwd) +BUILD="${ROOT}/build/work" +STAGE="${BUILD}/stage-qemu-root" +IMG="${BUILD}/unos-qemu.img" +IMG_SIZE_MB="${IMG_SIZE_MB:-512}" +QEMU_SSH_PORT="${QEMU_SSH_PORT:-2222}" +QEMU_MEM="${QEMU_MEM:-512}" +KERNEL="${ROOT}/rootfs/boot/vmlinuz" + +# --- collect authorized_keys (automated, no hardcoding) --- +collect_keys() { + tmp=$(mktemp) + # all .pub files + for f in "${HOME}"/.ssh/*.pub; do + [ -e "$f" ] || continue + cat "$f" >> "$tmp" 2>/dev/null || true + done + # authorized_keys if present (may contain certs, deduplicated via sort -u) + if [ -f "${HOME}/.ssh/authorized_keys" ]; then + cat "${HOME}/.ssh/authorized_keys" >> "$tmp" 2>/dev/null || true + fi + # keep only non-empty, non-comment lines? Keep as-is but deduplicate + # filter blank lines, let sort handle dedupe + if [ -s "$tmp" ]; then + sort -u "$tmp" -o "$tmp" + # drop empty lines + grep -v '^[[:space:]]*$' "$tmp" > "$tmp.filtered" 2>/dev/null || true + mv "$tmp.filtered" "$tmp" + fi + # require at least one key + if [ ! -s "$tmp" ]; then + echo "run-qemu.sh: no pubkeys found in ~/.ssh/*.pub or ~/.ssh/authorized_keys" >&2 + rm -f "$tmp" + return 1 + fi + echo "$tmp" +} + +# --- stage rootfs with injected keys --- +stage_rootfs() { + keys_file="$1" + echo "==> staging rootfs" + rm -rf "${STAGE}" + mkdir -p "${STAGE}" + # copy via tar to preserve perms; rootfs is unprivileged dir tree + (cd "${ROOT}/rootfs" && tar -cf - .) | (cd "${STAGE}" && tar -xf -) + + # inject root's authorized_keys (automated) + mkdir -p "${STAGE}/root/.ssh" + cat "$keys_file" > "${STAGE}/root/.ssh/authorized_keys" + chmod 700 "${STAGE}/root/.ssh" + chmod 600 "${STAGE}/root/.ssh/authorized_keys" + chown -R 0:0 "${STAGE}/root/.ssh" 2>/dev/null || true + echo "==> injected $(wc -l < "$keys_file") key(s) into /root/.ssh/authorized_keys" +} + +build_image() { + echo "==> building ${IMG} (${IMG_SIZE_MB}M)" + rm -f "${IMG}" + # Injected authorized_keys is created as $USER, fix ownership via fake-root ns + # before mkfs (which copies ownership verbatim). + if command -v unshare >/dev/null 2>&1; then + unshare --user --map-root-user --mount -- sh -c ' + chown -R 0:0 "$1/root/.ssh" 2>/dev/null || true + mkfs.ext4 -L UNOS -m 0 -d "$1" "$2" "$3" >/dev/null + ' sh "${STAGE}" "${IMG}" "${IMG_SIZE_MB}M" 2>/dev/null || mkfs.ext4 -L UNOS -m 0 -d "${STAGE}" "${IMG}" "${IMG_SIZE_MB}M" >/dev/null + else + mkfs.ext4 -L UNOS -m 0 -d "${STAGE}" "${IMG}" "${IMG_SIZE_MB}M" >/dev/null + fi + echo "==> image ready: ${IMG} ($(du -h "${IMG}" | cut -f1))" + # sanity: label + tune2fs -l "${IMG}" 2>/dev/null | grep -E "Filesystem volume name|Block count" | head -2 +} + +run_qemu() { + [ -f "${KERNEL}" ] || { echo "run-qemu.sh: kernel not found: ${KERNEL} (run mk/rootfs.sh first)" >&2; exit 1; } + [ -f "${IMG}" ] || { echo "run-qemu.sh: image not found: ${IMG}" >&2; exit 1; } + echo "==> launching QEMU (ssh -p ${QEMU_SSH_PORT} root@localhost)" + echo " kernel: ${KERNEL}" + echo " drive: ${IMG}" + echo " mem: ${QEMU_MEM}M" + echo " press Ctrl-a c then 'quit' to exit QEMU" + echo "" + # shellcheck disable=SC2086 + exec qemu-system-x86_64 -enable-kvm -m "${QEMU_MEM}" \ + -kernel "${KERNEL}" \ + -drive file="${IMG}",format=raw,if=virtio \ + -append "console=ttyS0 root=/dev/vda rw" \ + -nographic \ + -netdev user,id=net0,hostfwd=tcp::${QEMU_SSH_PORT}-:22 \ + -device e1000,netdev=net0 +} + +# --- main --- +mode="${1:-run}" +case "$mode" in + build) + kf=$(collect_keys) + stage_rootfs "$kf" + build_image + rm -f "$kf" + echo "==> done (build only). Run '$0 run' to start QEMU." + ;; + run|"") + kf=$(collect_keys) + stage_rootfs "$kf" + build_image + rm -f "$kf" + run_qemu + ;; + *) + echo "usage: $0 [build|run]" >&2 + exit 1 + ;; +esac diff --git a/packages/base-files/files/inittab b/packages/base-files/files/inittab @@ -0,0 +1,4 @@ +::sysinit:/etc/init.d/rcS +::respawn:/sbin/getty -L ttyS0 115200 vt100 +::ctrlaltdel:/sbin/reboot +::shutdown:/bin/umount -a -r diff --git a/packages/base-files/files/rcS b/packages/base-files/files/rcS @@ -0,0 +1,47 @@ +#!/bin/sh +exec >/dev/console 2>&1 +echo "rcS: mounting pseudo-filesystems" +mount -t proc proc /proc 2>/dev/null || true +mount -t sysfs sys /sys 2>/dev/null || true +mount -t devtmpfs devtmpfs /dev 2>/dev/null || true +mkdir -p /dev/pts 2>/dev/null || true +mount -t devpts devpts /dev/pts -o gid=5,mode=620 2>/dev/null || mount -t devpts devpts /dev/pts 2>/dev/null || true +[ -c /dev/ptmx ] || mknod /dev/ptmx c 5 2 2>/dev/null || true +chmod 666 /dev/ptmx 2>/dev/null || true +mkdir -p /run 2>/dev/null || true +if [ -f /etc/hostname ]; then + hostname -F /etc/hostname 2>/dev/null || hostname unos 2>/dev/null || true + echo "rcS: hostname $(hostname 2>/dev/null || cat /etc/hostname)" +fi +echo "rcS: bringing up loopback" +ip link set lo up 2>/dev/null || ifconfig lo up 2>/dev/null || true +echo "rcS: bringing up eth0" +ip link set eth0 up 2>&1 | head -1 +sleep 1 +# qemu user-mode net: dhcp via udhcpc if available, else static fallback +if command -v udhcpc >/dev/null 2>&1; then + udhcpc -i eth0 -q 2>&1 || true + # fallback if dhcp left no address + ip addr show eth0 2>/dev/null | grep -q "inet " || { + echo "rcS: dhcp failed, using static 10.0.2.15/24" + ip addr add 10.0.2.15/24 dev eth0 2>/dev/null || ifconfig eth0 10.0.2.15 netmask 255.255.255.0 2>/dev/null || true + ip route add default via 10.0.2.2 2>/dev/null || route add default gw 10.0.2.2 2>/dev/null || true + echo "nameserver 10.0.2.3" > /etc/resolv.conf 2>/dev/null || true + } +else + ip addr add 10.0.2.15/24 dev eth0 2>/dev/null || ifconfig eth0 10.0.2.15 netmask 255.255.255.0 2>/dev/null || true + ip link set eth0 up 2>/dev/null || true + ip route add default via 10.0.2.2 2>/dev/null || true +fi +echo "rcS: network ready" +ip addr show 2>&1 | head -20 +# start supervision (runsvdir manages /etc/sv/*) +if [ -d /etc/sv ]; then + echo "rcS: starting runsvdir" + runsvdir /etc/sv & +fi +if [ -x /etc/sv/tinyssh/run ]; then + echo "rcS: tinyssh will be supervised" +else + echo "rcS: no tinyssh service found" +fi diff --git a/packages/base-files/template b/packages/base-files/template @@ -1,7 +1,7 @@ # Template file for 'base-files' pkgname=base-files version=0.1.0 -revision=3 +revision=4 short_desc="UNOS filesystem skeleton - passwd, profile, nsswitch, fstab" maintainer="finwo <finwo@pm.me>" license="GPL-2.0-only" @@ -11,8 +11,8 @@ depends= # Own skeleton, nothing from any upstream distro. Traditional split-/usr # layout (matches what busybox `make install` produces: /bin /sbin # /usr/bin /usr/sbin as real dirs - no usrmerge). Deliberately minimal: -# no inittab yet (PID-1 design pending), no resolv.conf (runtime-provided -# via DHCP), no /run or /tmp (tmpfs mounts created at boot). +# no resolv.conf (runtime-provided via DHCP), no /run or /tmp (tmpfs mounts +# created at boot). Init is busybox init + runit via inittab/rcS. do_install() { vinstall ${FILESDIR}/passwd 644 etc vinstall ${FILESDIR}/group 644 etc @@ -25,6 +25,8 @@ do_install() { vinstall ${FILESDIR}/ld.so.conf 644 etc vinstall ${FILESDIR}/fstab 644 etc vinstall ${FILESDIR}/os-release 644 etc + vinstall ${FILESDIR}/inittab 644 etc + vinstall ${FILESDIR}/rcS 755 etc/init.d rcS install -d -m700 "${DESTDIR}/root" install -d "${DESTDIR}/etc/ld.so.conf.d" # standard mount points (fstab references /run and /tmp; init mounts the diff --git a/packages/tinyssh/template b/packages/tinyssh/template @@ -1,7 +1,7 @@ # Template file for 'tinyssh' pkgname=tinyssh version=20260906 -revision=3 +revision=4 short_desc="Small SSH server" maintainer="finwo <finwo@pm.me>" license="CC0-1.0" @@ -42,4 +42,5 @@ do_install() { # owned (empty) so the keydir parent always exists; the run script also # mkdir -p's defensively for wiped-state recovery install -d "${DESTDIR}/etc/tinyssh" + install -d "${DESTDIR}/var/log/tinyssh" }