site/boot.sh
back to source
#!/bin/sh
# SPDX-License-Identifier: MIT
# NumericalOS installer - the existing-Linux-host vector.
#
# curl -fsSL https://numericalos.com/boot | sh
#
# Installs the boot chain into a prefix and stops. It does NOT replace PID 1:
# silently taking over init on a running machine is destructive, and a piped
# shell script is the worst possible place to do it from. Taking PID 1 is a
# separate, explicit act - see --take-pid1 below, which prints instructions
# rather than performing surgery.
#
# Self-contained by necessity: this arrives over a pipe with no repository
# checkout to source anything from.
set -eu
NUMOS_BASE="${NUMOS_BASE:-https://numericalos.com}"
NUMOS_PREFIX="${NUMOS_PREFIX:-/opt/numericalos}"
NUMOS_DRY_RUN="${NUMOS_DRY_RUN:-0}"
TAKE_PID1=0
for arg in "$@"; do
case "$arg" in
--prefix=*) NUMOS_PREFIX="${arg#--prefix=}" ;;
--dry-run) NUMOS_DRY_RUN=1 ;;
--take-pid1) TAKE_PID1=1 ;;
--help|-h)
echo "usage: boot [--prefix=DIR] [--dry-run] [--take-pid1]"
exit 0 ;;
*) echo "numos: HALT: unknown option: $arg" >&2; exit 1 ;;
esac
done
die() { echo "numos: HALT: $*" >&2; exit 1; }
say() { echo "numos: $*"; }
[ "$(uname -s)" = "Linux" ] ||
die "this installer targets Linux; found $(uname -s)"
have() { command -v "$1" >/dev/null 2>&1; }
have sha256sum || have shasum || die "need sha256sum or shasum"
have curl || have wget || die "need curl or wget"
fetch() {
if have curl; then
curl -fsSL --max-time 60 -o "$2" "$1" || die "fetch failed: $1"
else
wget -q -O "$2" "$1" || die "fetch failed: $1"
fi
}
sum() {
if have sha256sum; then sha256sum "$1" | cut -d' ' -f1
else shasum -a 256 "$1" | cut -d' ' -f1
fi
}
say "arch $(uname -m)"
say "prefix $NUMOS_PREFIX"
say "source $NUMOS_BASE"
if [ "$NUMOS_DRY_RUN" = "1" ]; then
say "dry run: would install bootstrap.sh, numinit.sh, arch_table.sh, numctl, numos.state"
say "dry run: PID 1 would NOT be replaced"
exit 0
fi
mkdir -p "$NUMOS_PREFIX/lib" || die "cannot create $NUMOS_PREFIX"
fetch "$NUMOS_BASE/artifacts/manifest.txt" "$NUMOS_PREFIX/manifest.txt"
# Every file is verified against the manifest before it is kept. A file that
# does not match is deleted rather than left where the next run would find it
# and treat it as a good local copy.
install_verified() {
name="$1"
dest="$2"
want="$(grep "^F $name " "$NUMOS_PREFIX/manifest.txt" | head -n 1 | cut -d' ' -f3)"
[ -n "$want" ] || die "manifest has no entry for $name"
fetch "$NUMOS_BASE/artifacts/$name" "$dest"
got="$(sum "$dest")"
if [ "$got" != "$want" ]; then
rm -f "$dest"
die "hash mismatch for $name: expected $want got $got"
fi
say "verified $name"
}
install_verified bootstrap.sh "$NUMOS_PREFIX/bootstrap.sh"
install_verified numinit.sh "$NUMOS_PREFIX/numinit.sh"
install_verified arch_table.sh "$NUMOS_PREFIX/lib/arch_table.sh"
install_verified numctl "$NUMOS_PREFIX/numctl"
install_verified numos.state "$NUMOS_PREFIX/numos.state"
chmod +x "$NUMOS_PREFIX/bootstrap.sh" "$NUMOS_PREFIX/numinit.sh" "$NUMOS_PREFIX/numctl"
say "installed to $NUMOS_PREFIX"
say ""
say "run the phase walk without touching this machine's init:"
say " PATH=$NUMOS_PREFIX:\$PATH \\"
say " NUMOS_STATE=$NUMOS_PREFIX/numos.state \\"
say " NUMOS_LIB=$NUMOS_PREFIX/lib \\"
say " NUMOS_PREFIX=$NUMOS_PREFIX \\"
say " sh $NUMOS_PREFIX/bootstrap.sh"
if [ "$TAKE_PID1" = "1" ]; then
say ""
say "--take-pid1 requested. This installer will NOT do it."
say ""
say "Replacing PID 1 on a running machine is not something to do from a"
say "piped shell script. It is done by pointing a bootloader at an"
say "initramfs whose /init is bootstrap.sh - see:"
say " $NUMOS_BASE/skills/numericalos-build-initramfs/SKILL.md"
say " $NUMOS_BASE/skills/numericalos-build-iso-cf/SKILL.md # no-WSL Cloudflare path"
say ""
say "Nothing about this machine's init has been changed."
fi