mirror of
https://github.com/jiangcuo/pve-iso-builder.git
synced 2025-09-12 20:31:30 +00:00
432 lines
13 KiB
Bash
Executable File
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 PVE, but the installation will likely fail."
|
|
/bin/echo "[WARN] check your installation medium and the downloaded ISO"
|
|
PRODUCT="PVE"
|
|
PRODUCTLONG="Proxmox VE"
|
|
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="/proxmox.iso"
|
|
|
|
if [ -f $initrdisoimage ]; then
|
|
# this is useful for PXE boot
|
|
echo "found proxmox 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 proxmox 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 Proxmox 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
|