#!/usr/bin/env bash LOGFILE=/tmp/void-usb-install-log GIT_REPO_BASE="https://lostcave.ddnss.de/git/BodgeMaster/void-minecraft-usb/raw/branch/master" function press_any_key { echo "Press any key to continue or Ctrl+c to abort..." read -n1 DISCARD_ME echo "" } function are_you_really_really_sure { echo "Enter the following to continue: $1" read -p "> " DISCARD_ME echo "" if [ "$1" = "$DISCARD_ME" ]; then return 0 else return 1 fi } function yesno { unset DISCARD_ME while [ -z "$DISCARD_ME" ]; do read -p "[y/n] " -n1 DISCARD_ME case "$DISCARD_ME" in y) echo "" return 0 ;; n) echo "" return 1 ;; *) echo " Please enter y for yes or n for no." unset DISCARD_ME ;; esac done } function run_in_target { echo "$@" | chroot /mnt/target sh } clear touch $LOGFILE if [ ! "$(id -u)" -eq 0 ]; then echo "Must be root to run this script!" exit 1 fi if ping -c2 repo-default.voidlinux.org >> $LOGFILE 2>&1; then : else echo "An internet connection is required to run this script." exit 1 fi echo " This script will now download and install Void Linux on your USB stick. Any data that is currently on the stick will be lost and it won’t be usable from Windows or MacOS. Before we begin, the following packages need to be installed (if not installed already): - xmirror - wget " press_any_key echo -n "Ensuring that XBPS is up-to-date... " xbps-install --yes --sync --update xbps >> $LOGFILE 2>&1 echo "done" echo -n "Installing xmirror, squashfs-tools, wget... " xbps-install --yes xmirror wget >> $LOGFILE 2>&1 echo "done" #TODO: If going down the path of extracting routines from void-installer, that should be done here #TODO: Select keyboard layout KBD_LAYOUT="de-latin1" clear echo "Select the USB stick to install to (NAME column below)... " # exclude loop devices lsblk --exclude 7 --nodeps --output NAME,SIZE,MODEL echo "" read -p "> " TARGET_DISK while [ ! -b "/dev/$TARGET_DISK" ]; do lsblk --exclude 7 --nodeps --output NAME,SIZE,MODEL echo "$TARGET_DISK is not a valid device!" read -p "> " TARGET_DISK done clear echo "Selected device:" lsblk --output NAME,SIZE,MODEL,LABEL,FSTYPE "/dev/$TARGET_DISK" echo " WARNING: The selected device will be wiped, partitioned, and formatted. ALL DATA ON IT WILL BE LOST PERMANENTLY. It will not be usable with Windows or MacOS while holding Void. " if are_you_really_really_sure "Yes, erase it."; then true else echo "Aborting." exit 1 fi if grep "$TARGET_DISK" /proc/mounts >> $LOGFILE 2>&1; then echo "The device seems to be mounted. Unmount?" if yesno; then echo -n "Attempting to unmount... " readarray -t MOUNTPOINTS <<< "$(findmnt --raw --noheadings --output SOURCE,TARGET | grep "$TARGET_DISK" | sed 's/^[^ ]* //')" for I in ${!MOUNTPOINTS[@]}; do umount -R "${MOUNTPOINTS[$I]}" >> $LOGFILE 2>&1 done if grep "$TARGET_DISK" /proc/mounts >> $LOGFILE 2>&1; then echo "failed" echo "It seems the device is still mounted. Installation cannot continue." exit 1 else echo "done" fi else echo "Aborting." exit 1 fi fi echo -n "Wiping and partitioning storage... " # new GPT # 2M BIOS GRUB # 66592 sectors ESP (first sector + 66591 sectors, the minimum to format FAT32 with default settings) # everything else one big partition echo "g n +2M n +66591 n t 1 4 t 2 1 t 3 20 w" | fdisk --wipe always --wipe-partitions always "/dev/$TARGET_DISK" >> $LOGFILE 2>&1 TARGET_PART_BIOS="$(lsblk --raw --noheadings --output PATH "/dev/$TARGET_DISK" | sed -n '2p')" TARGET_PART_EFI="$(lsblk --raw --noheadings --output PATH "/dev/$TARGET_DISK" | sed -n '3p')" TARGET_PART_BIG="$(lsblk --raw --noheadings --output PATH "/dev/$TARGET_DISK" | sed -n '4p')" echo "done" echo -n "Formatting partitions... " mkfs.vfat -F32 -n "EFIBOOT" "$TARGET_PART_EFI" >> $LOGFILE 2>&1 mkfs.f2fs -f -l "container" "$TARGET_PART_BIG" >> $LOGFILE 2>&1 echo "done" echo -n "Mounting partitions and virtual file systems... " mkdir -p /mnt/target >> $LOGFILE 2>&1 mount -t tmpfs -o size=3g,mode=755 tmpfs /mnt/target >> $LOGFILE 2>&1 # used for chroot later mkdir /mnt/target/run >> $LOGFILE 2>&1 mount --rbind /run /mnt/target/run >> $LOGFILE 2>&1 mkdir /mnt/target/proc >> $LOGFILE 2>&1 mount --rbind /proc /mnt/target/proc >> $LOGFILE 2>&1 mkdir /mnt/target/sys >> $LOGFILE 2>&1 mount --rbind /sys /mnt/target/sys >> $LOGFILE 2>&1 mkdir /mnt/target/dev >> $LOGFILE 2>&1 mount --rbind /dev /mnt/target/dev >> $LOGFILE 2>&1 # actual storage mkdir -p /mnt/target/run/void-usb/container >> $LOGFILE 2>&1 mount "$TARGET_PART_BIG" /mnt/target/run/void-usb/container >> $LOGFILE 2>&1 mkdir /mnt/target/boot >> $LOGFILE 2>&1 mkdir /mnt/target/run/void-usb/container/boot >> $LOGFILE 2>&1 mount --bind /mnt/target/run/void-usb/container/boot /mnt/target/boot >> $LOGFILE 2>&1 mkdir /mnt/target/boot/efi >> $LOGFILE 2>&1 mount "$TARGET_PART_EFI" /mnt/target/boot/efi >> $LOGFILE 2>&1 mkdir /mnt/target/home >> $LOGFILE 2>&1 mkdir /mnt/target/run/void-usb/container/home >> $LOGFILE 2>&1 mount --bind /mnt/target/run/void-usb/container/home /mnt/target/home >> $LOGFILE 2>&1 echo "done" clear echo "Storage is now prepared and ready for installation. You need to select a download mirror for Void next. The script will launch xmirror on the host/live system and determine the chosen mirror from the config file it generates. " press_any_key xmirror #TODO: also remove CPU architecture TARGET_MIRROR="$(sed 's/repository=//;s|/musl$||' /etc/xbps.d/00-repository-main.conf)" #TODO: select installation type TARGET_TYPE="x86_64" #TODO: also add CPU architecture if grep "musl" <<< "$TARGET_TYPE"; then TARGET_MIRROR="$TARGET_MIRROR/musl" fi echo -n "Copying repository keys... " mkdir -p /mnt/target/var/db/xbps/keys >> $LOGFILE 2>&1 cp /var/db/xbps/keys/* /mnt/target/var/db/xbps/keys/ >> $LOGFILE 2>&1 echo "done" clear echo "The next step is installing packages for base system components. Depending on your internet connection, your USB stick, and the phase of the moon, this may take a while. The XBPS log will be displayed for this step. " press_any_key XBPS_ARCH="$TARGET_TYPE" xbps-install --yes --sync --rootdir /mnt/target --repository "$TARGET_MIRROR" \ linux bash shadow f2fs-tools dosfstools dbus NetworkManager iana-etc \ iw wpa_supplicant util-linux which tar man-pages iproute2 iputils \ wifi-firmware traceroute grep gzip file sed gawk less coreutils findutils \ diffutils pciutils usbutils tzdata base-files ncurses mdocml procps-ng \ kbd xbps sudo ethtool kmod eudev runit-void removed-packages nano acpid \ squashfs-tools grub grub-i386-efi grub-x86_64-efi dracut xz 2>&1 | tee --append $LOGFILE echo "" echo "Adding mirror configuration." echo "repository=$TARGET_MIRROR" > /mnt/target/etc/xbps.d/00-repository-main.conf echo "Adding sudo configuration." echo "%wheel ALL=(ALL:ALL) ALL" > /mnt/target/etc/sudoers.d/wheel_as_sudo_group echo "Disabling root password." run_in_target passwd --lock root echo "Adding dracut configuration." echo '# Void USB dracut configuration hostonly="no" compress="xz" add_dracutmodules+=" void-usb " omit_dracutmodules+=" nvdimm resume "' > /mnt/target/etc/dracut.conf.d/99-void-usb.conf echo -n "Adding dracut module void-usb... " mkdir -p /mnt/target/lib/dracut/modules.d/90void-usb >> $LOGFILE 2>&1 chmod 755 /mnt/target/lib/dracut/modules.d/90void-usb >> $LOGFILE 2>&1 wget --output-document=/mnt/target/lib/dracut/modules.d/90void-usb/module-setup.sh "$GIT_REPO_BASE/dracut-modules/90void-usb/module-setup.sh" >> $LOGFILE 2>&1 chmod 744 /mnt/target/lib/dracut/modules.d/90void-usb/module-setup.sh >> $LOGFILE 2>&1 wget --output-document=/mnt/target/lib/dracut/modules.d/90void-usb/create-loop0.sh "$GIT_REPO_BASE/dracut-modules/90void-usb/create-loop0.sh" >> $LOGFILE 2>&1 chmod 744 /mnt/target/lib/dracut/modules.d/90void-usb/create-loop0.sh >> $LOGFILE 2>&1 wget --output-document=/mnt/target/lib/dracut/modules.d/90void-usb/squashfs-img.sh "$GIT_REPO_BASE/dracut-modules/90void-usb/squashfs-img.sh" >> $LOGFILE 2>&1 chmod 744 /mnt/target/lib/dracut/modules.d/90void-usb/squashfs-img.sh >> $LOGFILE 2>&1 wget --output-document=/mnt/target/lib/dracut/modules.d/90void-usb/overlay.sh "$GIT_REPO_BASE/dracut-modules/90void-usb/overlay.sh" >> $LOGFILE 2>&1 chmod 744 /mnt/target/lib/dracut/modules.d/90void-usb/overlay.sh >> $LOGFILE 2>&1 echo "done" echo -n "Adding system-image helper... " mkdir -p /mnt/target/opt/void-usb >> $LOGFILE 2>&1 chmod 755 /mnt/target/opt/void-usb >> $LOGFILE 2>&1 wget --output-document=/mnt/target/opt/void-usb/system-image "$GIT_REPO_BASE/opt/system-image" >> $LOGFILE 2>&1 chmod 744 /mnt/target/opt/void-usb/system-image >> $LOGFILE 2>&1 echo " /opt/void-usb/system-image" >> /mnt/target/etc/rc.shutdown echo "done" echo "Adding fstab." echo "# See fstab(5). # # /run/void-usb/container, /run/void-usb/overlay, and / are mounted by scripts in initramfs /run/void-usb/container/boot /boot none bind 0 0 /run/void-usb/container/home /home none bind 0 0 UUID=$(blkid --output value --match-tag UUID $TARGET_PART_EFI) /boot/efi vfat defaults,fmask=0077,dmask=0077 0 2 " > /mnt/target/etc/fstab echo "Configuring keyboard layout." if grep "#KEYMAP=" /mnt/target/etc/rc.conf; then sed -i -e 's/#KEYMAP=.*/KEYMAP="'"$KBD_LAYOUT"'"/' /mnt/target/etc/rc.conf else clear echo "WARNING: Could not locate the keymap setting in rc.conf." | tee --append $LOGFILE echo "The script will attempt to add one." | tee --append $LOGFILE echo "" press_any_key echo "" >> /mnt/target/etc/rc.conf echo "# Install script could not find keymap setting, adding one here." >> /mnt/target/etc/rc.conf echo "KEYMAP=\"$KBD_LAYOUT\"" >> /mnt/target/etc/rc.conf fi #TODO: Figure out how to configure keyboard for Xorg #TODO timezone clear echo "Assume that the hardware clock is UTC? Most Unix-like systems set the clock in your computer to UTC and add the time zone on-the-fly. Windows on the other hand usually sets the hardware clock to local time. If you want to use this stick on Windows computers, you will most likely want to answer no here. " if yesno; then if grep "#HARDWARECLOCK=" /mnt/target/etc/rc.conf; then sed -i -e 's/#HARDWARECLOCK=.*/HARDWARECLOCK="UTC"/' /mnt/target/etc/rc.conf else clear echo "WARNING: Could not locate the hardwareclock setting in rc.conf." | tee --append $LOGFILE echo "The script will attempt to add one." | tee --append $LOGFILE echo "" press_any_key echo "" >> /mnt/target/etc/rc.conf echo "# Install script could not find hardwareclock setting, adding one here." >> /mnt/target/etc/rc.conf echo 'HARDWARECLOCK="UTC"' >> /mnt/target/etc/rc.conf fi else if grep "#HARDWARECLOCK=" /mnt/target/etc/rc.conf; then sed -i -e 's/#HARDWARECLOCK=.*/HARDWARECLOCK="localtime"/' /mnt/target/etc/rc.conf else clear echo "WARNING: Could not locate the hardwareclock setting in rc.conf." | tee --append $LOGFILE echo "The script will attempt to add one." | tee --append $LOGFILE echo "" press_any_key echo "" >> /mnt/target/etc/rc.conf echo "# Install script could not find hardwareclock setting, adding one here." >> /mnt/target/etc/rc.conf echo 'HARDWARECLOCK="localtime"' >> /mnt/target/etc/rc.conf fi fi clear echo "Do you want to set a hostname? This is equivalent to giving a name to your computer - except it's a system on a USB drive. If you leave this empty, it will be set to void-usb. Allowed characters are a-zA-Z0-9 (and - in the middle). " while [ -z "$TARGET_HOSTNAME" ]; do read -p "> " TARGET_HOSTNAME if [ -z "$TARGET_HOSTNAME" ]; then TARGET_HOSTNAME="void-usb" else if grep -e "^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$" -e "^[a-zA-Z0-9]*$" <<< "$TARGET_HOSTNAME" > /dev/null 2>&1; then true else echo "Allowed characters are a-zA-Z0-9 (and - in the middle)." unset TARGET_HOSTNAME fi fi done echo "$TARGET_HOSTNAME" > /mnt/target/etc/hostname clear echo -n "Installing bootloader... " mkdir /mnt/target/boot/efi/LOADER >> $LOGFILE 2>&1 echo "GRUB has been deliberately installed to a non-standard location. This avoids default kernel hooks breaking the custom config." > /mnt/target/boot/efi/LOADER/README.TXT # no idea if any of these are even necessary/relevant but it can’t hurt... TARGET_PRELOAD_GRUB_MODULES="usb usbms uhci ehci ohci part_gpt f2fs ahci ata pata" run_in_target grub-install --target=i386-pc --boot-directory=/boot/efi/LOADER --disk-module=native --modules="'$TARGET_PRELOAD_GRUB_MODULES'" "'/dev/$TARGET_DISK'" >> $LOGFILE 2>&1 run_in_target grub-install --target=i386-efi --boot-directory=/boot/efi/LOADER --disk-module=native --efi-directory=/boot/efi --removable --no-nvram --modules="'$TARGET_PRELOAD_GRUB_MODULES'" "'/dev/$TARGET_DISK'" >> $LOGFILE 2>&1 run_in_target grub-install --target=x86_64-efi --boot-directory=/boot/efi/LOADER --disk-module=native --efi-directory=/boot/efi --removable --no-nvram --modules="'$TARGET_PRELOAD_GRUB_MODULES'" "'/dev/$TARGET_DISK'" >> $LOGFILE 2>&1 echo "done" echo -n "Adding grub-config helper... " wget --output-document=/mnt/target/opt/void-usb/grub-config "$GIT_REPO_BASE/opt/grub-config" >> $LOGFILE 2>&1 chmod 744 /mnt/target/opt/void-usb/grub-config >> $LOGFILE 2>&1 echo "done" echo -n "Adding kernel hooks... " wget --output-document=/mnt/target/etc/kernel.d/pre-install/99-void-usb "$GIT_REPO_BASE/kernel.d/pre-install/99-void-usb" >> $LOGFILE 2>&1 chmod 744 /mnt/target/etc/kernel.d/pre-install/99-void-usb >> $LOGFILE 2>&1 wget --output-document=/mnt/target/etc/kernel.d/post-install/99-void-usb "$GIT_REPO_BASE/kernel.d/post-install/99-void-usb" >> $LOGFILE 2>&1 chmod 744 /mnt/target/etc/kernel.d/post-install/99-void-usb >> $LOGFILE 2>&1 wget --output-document=/mnt/target/etc/kernel.d/post-remove/99-void-usb "$GIT_REPO_BASE/kernel.d/post-remove/99-void-usb" >> $LOGFILE 2>&1 chmod 744 /mnt/target/etc/kernel.d/post-remove/99-void-usb >> $LOGFILE 2>&1 echo "done" echo -n "Reconfiguring all installed packages... " run_in_target xbps-reconfigure -fa >> $LOGFILE 2>&1 echo "done" echo -n "Configuring runit to start some services... " run_in_target ln -s /etc/sv/acpid /etc/runit/runsvdir/default/ run_in_target ln -s /etc/sv/dbus /etc/runit/runsvdir/default/ run_in_target ln -s /etc/sv/NetworkManager /etc/runit/runsvdir/default/ echo "done" echo -n "Adding stage 2 installer... " STAGE2_DIR="/mnt/target/opt/void-usb/installer" function get_stage2_file { [ -d "$STAGE2_DIR/$(dirname $1)" ] || mkdir -p "$STAGE2_DIR/$(dirname $1)" wget --output-document="$STAGE2_DIR/$1" "$GIT_REPO_BASE/$1" >> $LOGFILE 2>&1 } get_stage2_file install-stage2.sh >> $LOGFILE 2>&1 chmod 744 "$STAGE2_DIR/install-stage2.sh" >> $LOGFILE 2>&1 get_stage2_file package_selections/sections.lst readarray -t SECTIONS < "$STAGE2_DIR/package_selections/sections.lst" for I in ${!SECTIONS[@]}; do get_stage2_file "package_selections/${SECTIONS[$I]}/description.txt" get_stage2_file "package_selections/${SECTIONS[$I]}/options.lst" readarray -t OPTIONS < "$STAGE2_DIR/package_selections/${SECTIONS[$I]}/options.lst" for J in ${!OPTIONS[@]}; do get_stage2_file "package_selections/${SECTIONS[$I]}/${OPTIONS[$J]}" done done mv /mnt/target/etc/sv/agetty-tty1/conf /mnt/target/etc/sv/agetty-tty1/conf.bak >> $LOGFILE 2>&1 echo 'if [ -x /sbin/agetty -o -x /bin/agetty ]; then if [ "${tty}" = "tty1" ]; then GETTY_ARGS="--noclear --autologin root" fi fi' > /mnt/target/etc/sv/agetty-tty1/conf [ -f /mnt/target/root/.profile ] && mv /mnt/target/root/.profile /mnt/target/root/.profile.bak >> $LOGFILE 2>&1 echo "if ps aux | grep 'installer-stage2.sh' | grep -v 'grep' > /dev/null 2>&1; then true else /opt/void-usb/installer/install-stage2.sh exit fi" > /mnt/target/root/.profile echo "done" # This is not technically necessary, but it should help to save some memory. # TODO: Be smart about this, run install stage 2 in chroot if there is enough free RAM clear echo "Stage 1 installation is now complete. The freshly installed system needs to be booted to continue with stage 2 of the installation process. The installer will now create a system image and reboot the computer. " press_any_key echo "Creating system image..." #TODO: make this work without user interaction run_in_target /opt/void-usb/system-image reboot #