#!/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