joeylib2/installer/lib/haiku.sh
2026-10-19 15:46:47 -05:00

198 lines
7.4 KiB
Bash

# shellcheck shell=bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# Shared by the Haiku components (haikuGcc, haikuGcc2, haikuVm, haikuVm32),
# the BeOS VM (installer/lib/beos.sh, which uses its BFS helpers and job
# runner protocol) and the Be API ports' gates (scripts/lib/beGate.sh): the
# Haiku R1/beta6 release they are all made from, getting files into and out
# of its disk images and packages, and the job runner its VM images boot
# into. Sourced, not executed. ARCH is a Haiku architecture: x86_64, or
# x86_gcc2 (the 32-bit hybrid, gcc2h, whose primary compiler is gcc 2.95
# for BeOS compatibility).
#
# The job runner: a VM image's UserBootscript (written by haikuVmPrepare)
# looks for a disk labeled HAIKU_JOB_VOLUME, mounts it, waits for the
# desktop and the media services (the boot script runs before they are up,
# and a program opening its sound before the media server has started gets
# none), runs its job.sh there with the output going to job.log beside it,
# then shuts the machine down, so QEMU exits. A boot without that disk is an
# ordinary desktop. Lines on the first serial port mark the job's progress
# (HAIKU_JOB_STARTED, HAIKU_JOB_FINISHED).
# The anyboot images: a mirror listed on haiku-os.org/get-haiku/r1beta6, and
# the SHA256 sums that page gives.
HAIKU_ISO_BASE="https://mirrors.rit.edu/haiku/r1beta6"
HAIKU_ISO_NAME_x86_64="haiku-r1beta6-x86_64-anyboot.iso"
HAIKU_ISO_SHA256_x86_64="bb7743474fd175aea511be1799764ba054465285bbdf54c4722ca61d1fb4f5c9"
HAIKU_ISO_NAME_x86_gcc2="haiku-r1beta6-x86_gcc2h-anyboot.iso"
HAIKU_ISO_SHA256_x86_gcc2="5a55ad9429f1de9fb5ca5cbcd54de3733ff247cc7919ce5d690fb1271cb1dc76"
# The release's packages (hrev59866_79, what the images were built from) in
# Haiku's repository, and HaikuPorts' (the compiler's runtime), by the
# checksums the repositories' indexes give.
HAIKU_REVISION="r1~beta6_hrev59866_79"
HAIKU_REPO_BASE="https://eu.hpkg.haiku-os.org/haiku/r1beta6"
HAIKU_PORTS_BASE="https://eu.hpkg.haiku-os.org/haikuports/master"
# The job runner's disk label and serial port lines, and the applications
# it waits for: Deskbar, the media server and its add-on host.
HAIKU_JOB_VOLUME="JoeyJob"
HAIKU_JOB_WAIT_APPS="application/x-vnd.Be-TSKB application/x-vnd.Be.media-server application/x-vnd.Be.addon-host"
HAIKU_JOB_STARTED="JOEY: job started"
HAIKU_JOB_FINISHED="JOEY: job finished"
# Seconds the runner waits for the job disk to appear while booting.
HAIKU_JOB_WAIT_SECONDS=10
# Haiku's build-host tools (component haikuTools).
HAIKU_TOOLS="${HAIKU_TOOLS:-${JOEY_OPT:-}/haikuTools/bin}"
# haikuBfs IMAGE [OFFSET] -- run the bfs_shell commands on stdin against the
# BFS volume in IMAGE (at byte OFFSET, for a partitioned disk), the volume
# at /myfs and host files as :PATH. Its output is printed; returns 1 when a
# command failed. sync is run before the volume is let go: bfs_shell's quit
# reports the volume busy after a copy and would not write it otherwise.
haikuBfs() {
local image="$1"
local offset="${2:-}"
local out
local errors
out="$({ cat; printf 'sync\nquit\n'; } | "$HAIKU_TOOLS/bfs_shell" ${offset:+--start-offset "$offset"} "$image" 2>&1 || true)"
printf '%s\n' "$out"
errors="$(printf '%s\n' "$out" | grep -i -e 'error' | grep -v 'Unmounting FS failed: Device or resource busy' || true)"
[ -z "$errors" ]
}
# haikuBfsNew IMAGE SIZE LABEL -- a new BFS volume of SIZE (truncate's
# suffixes: 64M) in the file IMAGE.
haikuBfsNew() {
rm -f "$1"
truncate -s "$2" "$1"
"$HAIKU_TOOLS/bfs_shell" --initialize "$1" "$3" > /dev/null
}
# haikuBootOffset IMAGE -- the byte offset of an anyboot image's first
# partition, its BFS system volume (from the MBR: entry 1, at byte 446,
# holds its start sector, little-endian, at byte 8).
haikuBootOffset() {
local mbrEntry1=446
local entryStartLba=8
local sectorBytes=512
local b
read -ra b <<< "$(od -An -tu1 -v -j $((mbrEntry1 + entryStartLba)) -N 4 "$1")"
[ "${#b[@]}" -eq 4 ] || return 1
echo $(((b[0] | b[1] << 8 | b[2] << 16 | b[3] << 24) * sectorBytes))
}
# haikuIso ARCH -- download (once) and print the release's anyboot image.
haikuIso() {
local name="HAIKU_ISO_NAME_$1"
local sum="HAIKU_ISO_SHA256_$1"
fetchFile "$HAIKU_ISO_BASE/${!name}" "${!sum}"
}
# haikuIsoPackage ARCH FILE DEST -- copy the package FILE out of the
# release image's /system/packages to DEST.
haikuIsoPackage() {
local iso
iso="$(haikuIso "$1")"
haikuBfs "$iso" "$(haikuBootOffset "$iso")" <<< "cp /myfs/system/packages/$2 :$3" > /dev/null || die "cannot copy $2 out of $iso"
[ -s "$3" ] || die "$2 is not in $iso"
}
# haikuRepoPackage BASE ARCH PATH FILE SHA256 -- download (once) and print
# a package from a Haiku repository: BASE/ARCH/PATH/packages/FILE.
haikuRepoPackage() {
fetchFile "$1/$2/$3/packages/$4" "$5" "$4"
}
# haikuSysroot DEST PACKAGE... -- the parts of the packages a cross compiler
# needs (lib and develop) unpacked into DEST/boot/system, where a Haiku
# compiler looks for them.
haikuSysroot() {
local dest="$1"
local package
shift
mkdir -p "$dest/boot/system"
for package in "$@"; do
# A package with only one of the two warns about the other.
"$HAIKU_TOOLS/package" extract -C "$dest/boot/system" "$package" lib develop 2> /dev/null || die "cannot unpack $package"
done
}
# haikuVmPrepare ARCH IMAGE -- the release's anyboot image copied to IMAGE,
# booting straight to the desktop (no first-boot prompt) and running the job
# runner.
haikuVmPrepare() {
local iso
local tmp
iso="$(haikuIso "$1")"
tmp="$(mktemp -d)"
haikuJobRunner > "$tmp/UserBootscript"
# The first-boot prompt (language and keymap, or the installer) comes
# up while there are no locale settings.
: > "$tmp/Locale settings"
cp "$iso" "$2.part"
haikuBfs "$2.part" "$(haikuBootOffset "$2.part")" > /dev/null <<EOF || die "cannot write the job runner into $2"
cp :$tmp/UserBootscript /myfs/home/config/settings/boot/UserBootscript
cp ":$tmp/Locale settings" "/myfs/home/config/settings/Locale settings"
EOF
rm -rf "$tmp"
mv "$2.part" "$2"
}
# haikuVmPin ARCH -- what a VM image component is rebuilt for: the release
# image and the job runner.
haikuVmPin() {
local sum="HAIKU_ISO_SHA256_$1"
printf '%s:%s\n' "${!sum}" "$(haikuJobRunner | sha256sum | cut -d' ' -f1)"
}
# haikuVmVerify IMAGE -- the job runner is on the image's system volume.
haikuVmVerify() {
local runner
runner="$(haikuBfs "$1" "$(haikuBootOffset "$1")" <<< "cat /myfs/home/config/settings/boot/UserBootscript" || true)"
[[ "$runner" == *"$HAIKU_JOB_VOLUME"* ]]
}
# haikuJobRunner -- the UserBootscript the VM images run at every boot.
haikuJobRunner() {
cat <<EOF
#!/bin/sh
# JoeyLib's job runner (installer/lib/haiku.sh): a disk labeled
# $HAIKU_JOB_VOLUME holding job.sh is mounted, job.sh is run there (its output
# in job.log) and the machine shuts down. Without one, an ordinary boot.
serial=/dev/ports/pc_serial0
tries=0
while [ ! -f /$HAIKU_JOB_VOLUME/job.sh ]; do
mountvolume $HAIKU_JOB_VOLUME > /dev/null 2>&1
tries=\$((tries + 1))
if [ \$tries -gt $HAIKU_JOB_WAIT_SECONDS ]; then
exit 0
fi
sleep 1
done
for app in $HAIKU_JOB_WAIT_APPS; do
waitfor -m \$app
done
echo "$HAIKU_JOB_STARTED" > \$serial
cd /$HAIKU_JOB_VOLUME
/bin/sh ./job.sh > job.log 2>&1
echo "$HAIKU_JOB_FINISHED \$?" > \$serial
cd /
sync
shutdown -q
EOF
}