pve-iso-builder/init
2025-03-03 13:57:20 +08:00

432 lines
13 KiB
Bash
Executable File

#!/bin/sh
# (C) 2009-2023 Proxmox Server Solutions GmbH <support@proxmox.com>
export PATH=/sbin:/bin:/usr/bin:/usr/sbin
modprobe virtio-gpu
modprobe amdgpu
if [ -f /.cd-info ]; then
. /.cd-info
else
# NOTE: the only reason for not exiting now is trying to provide a shell
# for debugging this mess
/bin/echo "[ERROR] could not source .cd-info file!"
/bin/echo "[WARN] trying fallback to PXVIRT, but the installation will likely fail."
/bin/echo "[WARN] check your installation medium and the downloaded ISO"
PRODUCT="PXVIRT"
PRODUCTLONG="PXVIRT"
RELEASE="?.?"
fi
PRODUCT_LC="$(echo "$PRODUCT" | tr '[:upper:]' '[:lower:]')"
CDID_FN=".$PRODUCT_LC-cd-id.txt"
# busybox needs full paths until proc is mounted
/bin/echo "Welcome to the $PRODUCTLONG $RELEASE installer"
/bin/echo "initial setup startup"
/bin/echo "mounting proc filesystem"
/bin/mount -nt proc proc /proc
echo "mounting sys filesystem"
mount -nt sysfs sysfs /sys
if [ -d /sys/firmware/efi ]; then
echo "EFI boot mode detected, mounting efivars filesystem"
mount -nt efivarfs efivarfs /sys/firmware/efi/efivars
fi
# ensure we have a devtmpfs, so that we see the changes from the chroot /dev
# managed by udev here too, and thus normal path look ups on devices are in
# sync from the kernel root POV and the installer (ch)root POV
mount -t devtmpfs devtmpfs /dev
# ensure early that the since 5.15 enabled SYSFB_SIMPLEFB is actually usable
modprobe -q "simplefb"
parse_cmdline() {
root=
lvm2root=
proxdebug=0
# shellcheck disable=SC2013
for par in $(cat /proc/cmdline); do
case $par in
lvm2root=*)
lvm2root="${par#lvm2root=}"
;;
root=/dev/mapper/*)
lvm2root="${par#root=}"
;;
root=*)
root="${par#root=}"
;;
proxdebug)
proxdebug=1
;;
tty=*)
tty="${par#tty=}"
;;
esac
done;
}
myreboot() {
echo b > /proc/sysrq-trigger
echo "rebooting..."
sleep 100
exit 0
}
debugsh() {
setsid sh -c '/bin/sh'
}
debugsh_err_reboot() {
errmsg=$1
echo "" # try to make the message stand out more
echo "[ERROR] $errmsg"
echo "unable to continue (type exit or CTRL-D to reboot)"
debugsh
myreboot
}
echo "boot comandline: $(cat /proc/cmdline)"
parse_cmdline
# use mdev as firmware loader
echo /sbin/mdev >/proc/sys/kernel/hotplug
# initially populate /dev through /sys with cold-plugged devices
/sbin/mdev -s
DRIVERS="msdos isofs"
for mod in $DRIVERS; do
modprobe -q "$mod"
done
filenames=
# Note: skip filenames with spaces (avoid problems with bash IFS)
# Note: busybox only support -regextype 'posix-basic'
for fn in $(find /sys/devices/* -regex '^[^\ ]*/modalias'); do
filenames="$filenames $fn"
done
modlist=
load_alias() {
alias_fn=$1
alias=$(cat "${alias_fn}")
if [ -n "$alias" ]; then
for mod in $(modprobe -q -R "$alias" ); do
mod_found=0
for m in $modlist; do
if [ "$m" = "$mod" ]; then
mod_found=1
fi
done
if [ ${mod_found} -eq "0" ]; then
modlist="$modlist $mod"
fi
done
fi
}
load_mods() {
class_prefix=$1
for fn in $filenames; do
dirname=${fn%/*}
if [ -n "$class_prefix" ]; then
if [ -f "$dirname/class" ]; then
class=$(cat "$dirname/class")
class=${class:2:8}
if [ "${class_prefix}" = "${class:0:${#class_prefix}}" ]; then
load_alias "$fn"
fi
fi
else
load_alias "$fn"
fi
done
}
# for PCI Device classes and subclasses see linux-src/include/linux/pci_ids.h
# load storage drivers
load_mods 06 # PCI_BASE_CLASS_BRIDGE
load_mods 03 # PCI_BASE_CLASS_DISPLAY
# we try to have a load order, so that /dev/sda is on IDE
load_mods 0101 # PCI_CLASS_STORAGE_IDE
load_mods 0106 # PCI_CLASS_STORAGE_SATA
load_mods 0107 # PCI_CLASS_STORAGE_SAS
load_mods 0100 # PCI_CLASS_STORAGE_SCSI
load_mods 01 # PCI_BASE_CLASS_STORAGE
load_mods 02 # PCI_BASE_CLASS_NETWORK
load_mods # all other
echo "loading drivers: $modlist"
for mod in $modlist; do
modprobe "$mod"
done
stdmod="loop squashfs hfs hfsplus overlay cdrom sr_mod sd_mod usb-storage uas usbhid usbkbd hid_generic mac_hid virtio_blk"
for mod in $stdmod; do
modprobe "$mod"
done
# we have no iscsi daemon, so we need to scan iscsi device manually.
# else we cant boot from iscsi hba because devices are not detected.
for i in /sys/class/scsi_host/host*; do
host="${i##*/}"
if [ -d "$i" ] && [ -f "$i/scan" ] && [ -d "/sys/class/iscsi_host/$host" ] ; then
echo "Scanning iSCSI $host"
echo "- - -" > "$i/scan"
fi
done
if [ -n "$lvm2root" ]; then
printf '%s' "Finding device mapper major and minor numbers: "
MAJOR=$(sed -n 's/^ *\([0-9]\+\) \+misc$/\1/p' /proc/devices)
MINOR=$(sed -n 's/^ *\([0-9]\+\) \+device-mapper$/\1/p' /proc/misc)
if test -n "$MAJOR" -a -n "$MINOR" ; then
# shellcheck disable=SC2174
mkdir -p -m 755 /dev/mapper
mknod -m 600 /dev/mapper/control c "$MAJOR" "$MINOR"
fi
echo "($MAJOR,$MINOR)"
vg=${lvm2root}
vg=${vg#/dev/mapper/}
if [ "$vg" = "$1" ]; then
echo "activating all volume groups"
lvm vgchange --ignorelockingfailure -aly
else
# Split volume group from logical volume.
vg=$(echo "${vg}" | sed -e 's#\(.*\)\([^-]\)-[^-].*#\1\2#')
# Reduce padded --'s to -'s
vg=$(echo "${vg}" | sed -e 's#--#-#g')
echo "activating volume group $vg"
lvm vgchange -aly --ignorelockingfailure "${vg}"
fi
echo "create /dev/mapper/ entries using vgscan"
lvm vgscan --mknodes
echo "trying to mount lvm root: ($lvm2root)"
found=
for try in 5 4 3 2 1; do
for t in ext4 auto; do
if mount -n -t $t -r "$lvm2root" /mnt; then
found=ok
break;
fi
done
if test -n "$found"; then
break;
fi
if test $try -gt 1; then
echo "testing again in 5 seconds"
sleep 5
fi
done
elif [ -n "$root" ]; then
case $root in
/dev/*)
real_root=$root
;;
*:*)
dev_min=$((0x${root#*:}))
dev_maj=$((0x${root%:*}))
mknod /tmp/rootdev b $dev_maj $dev_min
real_root=/tmp/rootdev
;;
*)
dev_min=$((0x$root & 255))
dev_maj=$((0x$root >> 8))
mknod /tmp/rootdev b $dev_maj $dev_min
real_root=/tmp/rootdev
;;
esac
echo "trying to mount root: $real_root ($root)"
found=
for try in 5 4 3 2 1; do
for t in ext4 auto; do
if mount -n -t $t -r $real_root /mnt; then
found=ok
break;
fi
done
if test -n "$found"; then
break;
fi
if test $try -gt 1; then
echo "testing again in 5 seconds"
sleep 5
fi
done
else
cdrom=
initrdisoimage="/pxvirt.iso"
if [ -f $initrdisoimage ]; then
# this is useful for PXE boot
echo "found pxvirt ISO image inside initrd image"
if mount -t iso9660 -o loop,ro $initrdisoimage /mnt >/dev/null 2>&1; then
cdrom=$initrdisoimage
fi
else
echo "searching for block device containing the ISO $ISONAME-$RELEASE-$ISORELEASE"
reqid="$(cat "/$CDID_FN")"
echo "with ISO ID '$reqid'"
delay=1 # start out with a relatively short delay, often devices get ready quite quickly
for try in $(seq 1 9); do # 9 tries, each one second more sleep -> 45s total
for i in /sys/block/hd* /sys/block/sr* /sys/block/scd* /sys/block/sd* /sys/block/nvme*; do
# don't try to mount /all/ devices, as it produces IO, as a heuristic check all
# those which are removable and all those which are of type iso9660 (we can mount
# only those anyway) and those which have it's main partition < 1 GiB (ISO has
# normally 750MiB) this also gets USB sticks on strange systems where the firmware
# says it's not removable...
if [ -d "$i" ]; then
basedev="${i##*/}"
path="/dev/$basedev"
size="$(cat "$i/size")"
if [ "$(cat "$i/removable")" = 1 ] ||
blkid "$path" | grep -q ' TYPE="iso9660"' ||
[ "$size" -lt $(( 1024 * 1024 * 65 )) ]
then
echo "testing device '$path' for ISO"
if mount -t auto -o ro "$path" /mnt >/dev/null 2>&1; then
if [ -r "/mnt/$CDID_FN" ] && [ "X$(cat "/mnt/$CDID_FN")" = "X$reqid" ]; then
echo "found $PRODUCTLONG ISO"
cdrom=$path
break
else
echo "found ISO9660 FS but no, or wrong pxvirt cd-id, skipping"
fi
umount /mnt
fi
else
{
echo "dev $i neither removable, nor type 'iso9660' nor smallish, skipping ISO check";
echo " removable: $(cat "$i/removable"), size: $size, blkid: $(blkid "$path")";
} >> skipped-devs.txt
fi
fi
done
if test -n "$cdrom"; then
break;
fi
if test $try -gt 1; then
echo "testing again in $delay seconds"
sleep "$delay"
# gradually increase sleeps, as HW is either ready quickly or needs tens of seconds
delay=$((delay + 1))
fi
done
fi
if [ -z "$cdrom" ]; then
debugsh_err_reboot "no device with valid ISO found, please check your installation medium"
fi
fi
if [ $proxdebug -ne 0 ]; then
echo "Debugging mode (type 'exit' or press CTRL-D to continue startup)"
debugsh
fi
BASE_SQFS="/mnt/$PRODUCT_LC-base.squashfs"
INSTALLER_SQFS="/mnt/$PRODUCT_LC-installer.squashfs"
if [ -f "$INSTALLER_SQFS" ]; then
# this is a pxvirt XYZ installation CD
# hostid (gethostid(3)) is used by zfs to identify which system imported a pool last it needs
# to be present in /etc/hostid before spl.ko is loaded create it in the installer and copy it
# over to the targetdir after installation
dd if=/dev/urandom of=/etc/hostid bs=1 count=4 status=none
if ! mount -t squashfs -o ro,loop "$BASE_SQFS" /mnt/.base; then
debugsh_err_reboot "mount '$BASE_SQFS' failed"
fi
if ! mount -t squashfs -o ro,loop "$INSTALLER_SQFS" /mnt/.installer; then
debugsh_err_reboot "mount '$INSTALLER_SQFS' failed"
fi
if ! mount -t tmpfs tmpfs /mnt/.workdir; then
debugsh_err_reboot "mount overlay workdir failed"
fi
mkdir /mnt/.workdir/work
mkdir /mnt/.workdir/upper
if ! mount -t overlay -o lowerdir=/mnt/.installer:/mnt/.base,upperdir=/mnt/.workdir/upper,workdir=/mnt/.workdir/work none /mnt/.installer-mp; then
debugsh_err_reboot "mount overlayfs failed"
fi
if ! mount --bind /mnt /mnt/.installer-mp/cdrom; then
debugsh_err_reboot "bind mount cdrom failed"
fi
cp /etc/hostid /mnt/.installer-mp/etc/
cp /.cd-info /mnt/.installer-mp/ || true
if [ -x "/mnt/.installer-mp/sbin/unconfigured.sh" ]; then
mount -t devtmpfs devtmpfs /mnt/.installer-mp/dev
echo "switching root from initrd to actual installation system"
# and run the installer (via exec, so hand over PID 1)
# NOTE: the setsid/redirect dance is really required to have job control in debug shells
if ! exec switch_root -c /dev/console /mnt/.installer-mp /bin/setsid /bin/sh -c "exec /sbin/unconfigured.sh </dev/tty1 >/dev/tty1 2>&1"; then
debugsh_err_reboot "unable to switch root to installer ($?)"
fi
# NOTE: should never be reached
else
debugsh_err_reboot "unable to find installer (/sbin/unconfigured.sh)"
fi
echo "unexpected return from installer environment, trigger confused reboot now.."
cd /
# Send a SIGKILL to all processes, except for init.
kill -s KILL -1
sleep 1
umount /mnt/.installer-mp/cdrom
umount /mnt/.installer-mp
umount /mnt/.workdir/
umount /mnt/.installer
umount /mnt/.base
umount -a -l
myreboot
else
# or begin normal init for "rescue" boot
umount /sys
umount /proc
exec /sbin/switch_root -c /dev/console /mnt sbin/init
fi