#!/bin/sh # Copyright (C) 2024-2025 T2 SDE Project # SPDX-License-Identifier: GPL-2.0 # Takeover based on Copyright 2017 Hector Martin "marcan" # TODO: check cpio, tar, zstd # TODO: include ca-certificates in the installer stage2 # TODO: check essential tools # TODO: more error handling # TODO: check for X, Wayland and drmcon and do special things # TODO: testing, testing, and the most more testing # TODO: support taking over macOS! !!! x86 and Apple Silicon!! # TODO: support ssh remote login # TODO: option to preserve mounts (and improve t2/installer, too) # TODO: add size check also to t2/installer # TODO: can we support taking over Windows (Subsystem for Linux), too? EFI part? set -e # Resistance is Futile # ourhardworkbythesewordsguardedpleasedontsteal(c)T2ComputerInc # Here is to the troublemakers, the crazy on # *, base/*, base/* but not */x11, base/{firmware,kernel,boot} extract() { echo "$pkg ($ver)" $curl $curlopt $URL/$ID/pkgs/$pkg-$ver.tar.zst | zstd -d | tar x -C "$TO" } bootstrap() { pkgsel=$1 section=0 ID=$(curl $curlopt $URL/ | grep linux | cut -d '"' -f 2) [ ! "$ID" ] && echo "Could not extract ID" && exit $curl $curlopt $URL/$ID/pkgs/packages.db | gunzip | while read line; do if [ "${line}" = $'\004' ]; then section=0 [ $selected = 1 ] && extract elif [ "${line}" = $'\027' ]; then ((++section)) else # each 1st new section line pkg name if [ $section = 0 ]; then pkg=$line selected=0 else if [[ $section = 1 && "$line" = "[V]"* ]]; then ver=${line#\[V\] } elif [[ $section = 1 && "$line" = "[C]"* ]]; then cat=${line#\[C\] } # TODO: iterate multiple Categories! #echo "$pkg> $cat" selected=1 case "$cat" in */kernel*|*/firmware*|*/boot*) [ $pkgsel = minimal-container ] && selected=0 ;; *base/x11*) [ $pkgsel = minimal -o $pkgsel = minimal-container ] && selected=0 ;; *base/*) : ;; *) [ $pkgsel = all ] || selected=0 esac fi fi fi done } isLE() { elf_magic="$(head -c 6 /proc/self/exe | tail -c 1)" [ "$elf_magic" = "$(printf '\001')" ] } arch=$(uname -s -m) arch2=${arch#* } BASE=https://dl.t2sde.org/binary URL2=.$arch2 INITRD=boot/initrd-6.14.1-t2 VER=25.4 case ${arch} in Linux*aarch64) URL=2025/t2-$VER-arm64-base-wayland-glibc-gcc.d ;; Linux*armv7) URL=2025/t2-$VER-armv7-base-wayland-glibc-gcc-armv7-a.d ;; Linux*alpha) INITRD=boot/initrd-6.14.2-t2 URL=2025/t2-$VER-alpha-base-wayland-glibc-gcc-ev56.d ;; Linux*hppa) URL=2025/t2-$VER-hppa-base-desktop-glibc-gcc.d ;; Linux*hppa64) URL=2025/t2-$VER-hppa6432-base-desktop-glibc-gcc.d ;; Linux*ia64) URL=2025/t2-$VER-ia64-base-desktop-glibc-gcc-itanium2.d ;; Linux*mips) isLE && URL2=.mipsel && URL=2025/t2-$VER-mipsel-base-wayland-glibc-gcc-mips32r2.d ;; Linux*ppc) URL=2025/t2-$VER-ppc-base-wayland-glibc-gcc-603.d ;; Linux*ppc64) URL2=.ppc6432 URL=2025/t2-$VER-ppc6432-base-wayland-glibc-gcc-970.d isLE && URL2=.ppc64le && URL=2025/t2-$VER-ppc64le-base-wayland-glibc-gcc-power8.d ;; Linux*riscv32) URL=2025/t2-$VER-riscv-base-desktop-glibc-gcc.d ;; Linux*riscv64) URL=2025/t2-$VER-riscv64-base-wayland-glibc-gcc.d ;; Linus*sparc64*) URL2=.sparc6432 INITRD=boot/initrd-6.14.2-t21 URL=2025/t2-$VER-sparc6432-base-wayland-glibc-gcc.d ;; Linux*x86_64) URL=2025/t2-$VER-x86-64-base-wayland-glibc-gcc-nocona.d ;; Linux*i?86) URL2=.i486 URL=2025/t2-$VER-i586-base-wayland-glibc-gcc-i586.d ;; esac if [ -z "$URL" ]; then echo "T2/install does not yet support '$arch'." curl -Ls -A "curl ($arch)" https://t2linux.com/no-install >/dev/null echo "Please let us know at: https://t2linux.com/support" exit 1 fi xtractonly= # get actual shell tty, as we run from a curl pipe, ... [ -e /proc/$PPID/fd ] && TTY=$(readlink -f /proc/$PPID/fd/0) || TTY=/dev/tty if [ "${TTY##/dev/pts}" != "${TTY}" ]; then # check parent, e.g.sudo TTY2=$(sed -n 's/PPid:\t*//p' /proc/$$/status) TTY2=$(sed -n 's/PPid:\t*//p' /proc/$TTY2/status) TTY2=$(readlink /proc/$TTY2/fd/0) case "$TTY2" in /dev/pts*|/dev/null) echo "T2/install does not yet support takeover on $TTY,$TTY2." xtractonly=1 ;; *) TTY=$TTY2 ;; esac fi if [ $(id -u) != 0 ]; then echo "T2/install takeover requires root permissions, e.g. via su or sudo." xtractonly=1 fi TO=/T2 curl=curl curlopt="-L --progress-bar" if ! type $curl >/dev/null; then curl=wget curlopt="--progress=bar -O -" fi URL=$BASE/$URL URL2=$BASE/$URL2 echo echo "!!! EXPERIMENTAL new code v0.2.1 !!!" echo "!!! It may or may NOT WORK AND DELETE ALL YOUR DATA !!!" echo " ${URL##*/}" [ "$xtractonly" ] && echo -n "Package selection to bootstrap (minimal{,-xorg,-container},all)? " || echo -n "Package selection to bootstrap, or system takeover (minimal{,-xorg,-container},all,takeover)? " read pkgset < /dev/tty case "$pkgset" in xorg|container) pkgset=minimal-$pkgset ;; minimal|minimal-xorg|minimal-container|all) : ;; "") exit ;; *) if [ "$pkgset" = takeover -a ! "$xtractonly" ]; then : else echo "Unmatched '$pkgset'" exit 1 fi ;; esac if [ "$pkgset" != takeover ]; then echo -n "Directory name to bootstrap into: " read TO < /dev/tty [ "$TO" ] || exit mkdir -p "$TO" || exit bootstrap $pkgset echo "Done. You can now chroot into '$TO' and have fun!" exit fi OLD_INIT=$(readlink -f /proc/1/exe) OLD_TELINIT= if type telinit >/dev/null; then OLD_TELINIT=$(which telinit); fi mkdir -p $TO mount -t tmpfs tmpfs $TO cd $TO echo "" echo "Loading required files..." $curl $curlopt $URL/${INITRD:-initrd} | zstd -d | cpio -i $curl $curlopt $URL/stage2.tar.zst | zstd -d | tar x $curl $curlopt $URL/stage2ext.tar.zst | zstd -d | tar x $curl $curlopt ${URL2:-$URL}/fakeinit > fakeinit && chmod +x fakeinit $curl $curlopt ${URL2:-$URL}/busybox > busybox && chmod +x busybox echo echo "Setting up new filesystem..." mkdir -p etc dev proc sys run tmp mnt if ! mount -t devtmpfs dev dev; then echo "Failed to mount dev" exit 1 fi mount -t tmpfs tmp tmp mount -t proc proc proc mount -t sysfs sys sys mount --bind /dev/pts dev/pts ln -s /proc/mounts etc/mtab swapon --show --noheadings | while read s _; do case $s in /dev/zram*|/dev/ps3vram*) : ;; *) swapoff $s ;; esac done #echo "Switching <-> $TTY..." #exec <$TO$TTY >$TO$TTY 2>$TO$TTY # sanity check supported term case $TERM in linux|screen|tmux|xterm) : ;; *) TERM=vt102 ;; esac cat /etc/resolv.conf > etc/resolv.conf.t sed -n 's/^# name/name/p' etc/resolv.conf >> etc/resolv.conf.t mv -f etc/resolv.conf{.t,} cat > tmp/${OLD_INIT##*/} <$TO$TTY 2>$TO$TTY #echo "\$\$ \$*" export TERM=$TERM echo "Init takeover successful" echo "Pivoting root..." cd $TO ./busybox mount --make-rprivate / ./busybox pivot_root . mnt echo "Chrooting and running fakeinit..." export TTY=$TTY exec chroot . /fakeinit /init2 EOF chmod +x tmp/${OLD_INIT##*/} cat > init2 <$TTY 2>$TTY fi done exec <$TTY >$TTY 2>$TTY echo sleep 1 #LIBPROC_HIDE_KERNEL=1 ps faxu echo -n "Umounting:" for m in \$(grep /mnt/ /proc/mounts | cut -d " " -f 2 | tac) # /mnt do echo -n " \$m" umount \$m done echo #/sbin/agetty -i -J -n -N -l /sbin/login-shell ${TTY##*/} & udevd & udevadm trigger --action=add export ROCK_INSTALL_SOURCE_URL=http:${URL#https:} # TODO! echo "Done. Have fun! You might want to run: 'exec /sbin/init' to install T2." exec /bin/sh #/fakeinit EOF chmod +x init2 [ "$OLD_TELINIT" ] && cp -Lf ${OLD_TELINIT} tmp/${OLD_TELINIT##*/} mount --bind tmp/${OLD_INIT##*/} ${OLD_INIT} echo echo "About to take over init. If successful, you will see output from the new init:" [ $OLD_TELINIT ] && tmp/telinit u || systemctl daemon-reexec sleep 23 echo "Takeover probably failed."