#!/usr/bin/env bash # vim:ft=sh:fdm=marker:fmr={,} # Written by Nathaniel Maia for use in Archlabs # Some ideas and code were taken from other installers # AIF, ABIF, Calamares.. Credit where credit is due # This program is free software, provided under the GNU GPL # dry run performing no action, used to check for syntax errors # set -n # no unbound variables # set -u # set verbose, print lines as they are read # set -v # immutable vaulues { readonly LIVEUSER="liveuser" # Live user readonly DIST="Archlabs" # Distributor readonly VER="1.5.48" # Version readonly MNT="/mnt/install" # Mountpoint readonly ANS="/tmp/answer" # Stores answers readonly OPT="/tmp/opts" # Mount options readonly ERR="/tmp/errlog" # Error logging readonly LOG="/tmp/log" # set -x logging # } # mutable values and prep { EFI="/boot/efi" # EFI system partition mountpoint EFI_PARTITION="NONE" VG_MB=0 SEPERATE_BOOT=0 MKINIT_HOOKS="shutdown" # timezones used for manual setup if automatic fails FULLZONES="$(awk '{print $3}' < /usr/share/zoneinfo/zone.tab | grep ".*/.*" | sort -ud)" ZONES="" for i in $(awk -F'/' '{print $1}' <<< "$FULLZONES" | uniq); do ZONES="$ZONES $i -" done # list of available countries for reflector COUNTRIES="$(reflector --list-countries | awk '{print $1}' | grep -v '[0-9]')" COUNTRY="" for i in $COUNTRIES; do COUNTRY="$COUNTRY $i -" done # parsed locales from /etc/locale.gen FULL_LOCALES="$(grep -v "# " /etc/locale.gen | sed 's/#//g; s/ UTF-8//g' | grep .UTF-8)" LOCALES="" for i in $FULL_LOCALES; do LOCALES="$LOCALES $i -" done # parsed console maps from /usr/share/kbd/keymaps FULL_CONSOLE_MAPS="$(ls -R /usr/share/kbd/keymaps | grep "map.gz" | sed 's/\.map\.gz//g' | sort)" CONSOLE_MAPS="" for i in $FULL_CONSOLE_MAPS; do CONSOLE_MAPS="$CONSOLE_MAPS $i -" done # raw set of available keyboard maps and their language FULL_XORG_MAPS=("us English" "cm English" "gb English" "au English" "gh English" "za English" "ng English" "ca French" "cd French" "gn French" "tg French" "fr French" "de German" "at German" "ch German" "gr Greek" "hu Hungarian" "by Belarusian" "is Icelandic" "cn Chinese" "ua Ukrainian" "cz Czech" "ru Russian" "dk Danish" "it Italian" "br Portuguese" "pt Portuguese" "in Indian" "se Swedish" "ara Arabic" "al Albanian" "am Armenian" "be Belgian" "bd Bangla" "ba Bosnian" "az Azerbaijani" "bg Bulgarian" "dz Berber" "ma Arabic" "mm Burmese" "hr Croatian" "il Hebrew" "ee Estonian" "ir Persian" "iq Iraqi" "fo Faroese" "fi Finnish" "ge Georgian" "kg Kyrgyz" "kh Khmer" "kz Kazakh" "la Lao" "latam Spanish" "lt Lithuanian" "me Montenegrin" "mk Macedonian" "mt Maltese" "mn Mongolian" "no Norwegian" "pl Polish" "ro Romanian" "rs Serbian" "si Slovenian" "sk Slovak" "es Spanish" "sy Arabic" "tj Tajik" "lk Sinhala" "th Thai" "tr Turkish" "tw Taiwanese" "uz Uzbek" "vn Vietnamese" "kr Korean" "ie Irish" "pk Urdu" "mv Dhivehi" "epo Esperanto" "np Nepali" "et Amharic" "sn Wolof" "ml Bambara" "tz Swahili" "ke Swahili" "bw Tswana" "ph Filipino" "id Indonesian" "my Malay" "nec_vndr/jp Japanese" "jp Japanese" "tm Turkmen" "bt Dzongkha" "nl Dutch" "lv Latvian" "md Moldavian" "mao Maori" "af Afghani") XORG_MAPS="" for i in "${FULL_XORG_MAPS[@]}"; do XORG_MAPS="$XORG_MAPS $i" done # } ###################################################################### ## Utility and Check Functions ## ###################################################################### chroot_cmd() { arch-chroot $MNT /bin/bash -c "$1" } show_devices() { lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT | grep "disk\|part\|lvm\|crypt\|NAME\|TYPE\|SIZE" >/tmp/.devlist dialog --backtitle "$BT" --title " $_PrepShowDev " --textbox /tmp/.devlist 0 0 } set_debug() { local cmd echo "" >$LOG set -x exec 3>| $LOG BASH_XTRACEFD=3 if hash st &>/dev/null; then st -e tail -f $LOG & elif hash termite &>/dev/null; then termite -e tail -f $LOG & else xterm -e tail -f $LOG & fi } cleanup() { [[ -e $ANS ]] && rm -rf $ANS [[ -e $OPT ]] && rm -rf $OPT [[ -e $ERR ]] && rm -rf $ERR [[ -e $LOG ]] && rm -rf $LOG return 0 } select_language() { dialog --backtitle "$DIST Installer - (x86_64)" --title " Select Language " --menu \ "\nLanguage - sprache - taal - språk - lingua - idioma - nyelv - língua\n" 0 0 10 \ "1" "English (en_**)" \ "2" "Español (es_ES)" \ "3" "Português [Brasil] (pt_BR)" \ "4" "Português (pt_PT)" \ "5" "Français (fr_FR)" \ "6" "Russkiy (ru_RU)" \ "7" "Italiano (it_IT)" \ "8" "Nederlands (nl_NL)" \ "9" "Magyar (hu_HU)" \ "10" "Chinese (zh_CN)" 2> $ANS # always source the english translation first. # it acts as a set of fallback values should any other # translations be missing or lack values source /installer/english.trans 2>/dev/null case "$(cat $ANS)" in 1) LOC="en_US.UTF-8" ;; 2) source /installer/spanish.trans 2>/dev/null && LOC="es_ES.UTF-8" ;; 3) source /installer/p_brasil.trans 2>/dev/null && LOC="pt_BR.UTF-8" ;; 4) source /installer/portuguese.trans 2>/dev/null && LOC="pt_PT.UTF-8" ;; 5) source /installer/french.trans 2>/dev/null && LOC="fr_FR.UTF-8" ;; 6) source /installer/russian.trans 2>/dev/null && LOC="ru_RU.UTF-8" ;; 7) source /installer/italian.trans 2>/dev/null && LOC="it_IT.UTF-8" ;; 8) source /installer/dutch.trans 2>/dev/null && LOC="nl_NL.UTF-8" ;; 9) source /installer/hungarian.trans 2>/dev/null && LOC="hu_HU.UTF-8" ;; 10) source /installer/chinese.trans 2>/dev/null && LOC="zh_CN.UTF-8" ;; *) clear && exit 0 esac sed -i "s/#en_US.UTF-8/en_US.UTF-8/" /etc/locale.gen [[ $LOC != "en_US.UTF-8" ]] && sed -i "s/#${LOC}/${LOC}/" /etc/locale.gen locale-gen &>/dev/null export LANG="$LOC" } identify_system() { if grep -q 'Apple' /sys/class/dmi/id/sys_vendor; then modprobe -r -q efivars || true else modprobe -q efivarfs fi if [[ -d "/sys/firmware/efi/" ]]; then if grep -q /sys/firmware/efi/efivars <<< "$(mount)"; then mount -t efivarfs efivarfs /sys/firmware/efi/efivars fi SYS="UEFI" else SYS="BIOS" fi readonly BT="$DIST Installer - $SYS (x86_64) - Version $VER" } check_requirements() { local msg cur_user cur_user="$(whoami)" if [[ $cur_user != "root" ]] || ! (ping -c 1 archlabslinux.com &>/dev/null || ping -c 1 bitbucket.org &>/dev/null || ping -c 1 github.com &>/dev/null); then [[ $cur_user != "root" ]] && msg="$_NotRoot" || msg="$_NoNetwork" infobox "$_ErrTitle" "$msg\n$_Exit" clear && exit 1 fi echo "" > $ERR return 0 } check_for_errors() { # check if last process exited with non zero if (( $? != 0 )); then # grep -wqi "$1" "$ERR" # clean escape sequences from the error message local err err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" # show the error message if any [[ $err ]] && msgbox "$_ErrTitle" "$err" local yes="Manual Fix" local no="Wipe Install" if ! yesno "$_ErrTitle" "$_ErrChoice" 0 0 --yes-label "$yes" --no-label "$no"; then for d in $MNT/?*; do if ! grep -q "boot" <<< "$d"; then rm -rf "$d" fi done fi cleanup return 1 fi return 0 } check_parts_are_mounted() { grep -q "$MNT" <<< "$(lsblk -o MOUNTPOINT)" && return 0 # partitions aren't mounted msgbox "$_ErrTitle" "$_ErrNoMount" return 1 } check_base_unpacked() { [[ -e $MNT/etc ]] && return 0 # base isn't unpacked msgbox "$_ErrTitle" "$_ErrNoBase" return 1 } check_part_is_crypt_or_lvm() { local part="$1" local fullblock fullblock="$(lsblk -lno NAME,FSTYPE,TYPE)" # Identify if $part is "crypt" (LUKS on LVM, or LUKS alone) if grep -qi "crypt" <<< "$(lsblk -lno TYPE "$part")"; then LUKS=1 LUKS_NAME="$(sed "s~^/dev/mapper/~~g" <<< "$part")" local cryptlv cryptlv="$(grep "lvm" <<< "$fullblock" | grep -i "crypto_luks" | awk '{print "/dev/mapper/"$1}' | uniq)" for i in $cryptlv; do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$i")"; then LUKS_DEV="$LUKS_DEV cryptdevice=$i:$LUKS_NAME" LVM=1 break fi done local crypt crypt="$(grep "part" <<< "$fullblock" | grep -i "crypto_luks" | awk '{print "/dev/"$1}' | uniq)" for i in $crypt; do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$i")"; then LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$i" | grep 'part' | grep -i 'crypto_luks' | awk '{print $1}')" LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" break fi done elif grep -qi "lvm" <<< "$(lsblk -lno TYPE "$part")"; then LVM=1 LV_NAME="$(sed "s~^/dev/mapper/~~g" <<< "$part")" local lvcrypt lvcrypt="$(grep "crypt" <<< "$fullblock" | grep -i "lvm2_member" | awk '{print "/dev/mapper/"$1}' | uniq)" for i in $lvcrypt; do if grep -q "$LV_NAME" <<< "$(lsblk -lno NAME "$i")"; then LUKS_NAME="$(sed 's~/dev/mapper/~~g' <<< "$i")" break fi done local crypt crypt="$(grep "part" <<< "$fullblock" | grep -i "crypto_luks" | awk '{print "/dev/"$1}' | uniq)" for i in $crypt; do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$i")"; then LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$i" | grep 'part' | grep -i 'crypto_luks' | awk '{print $1}')" LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" LUKS=1 break fi done fi } ###################################################################### ## Dialog Functions ## ###################################################################### getpass() { dialog --backtitle "$BT" --title " $1 " --insecure --passwordbox "$2" ${3:-0} ${4:-0} 2>$ANS || return 1 return 0 } # }} getinput() { dialog --backtitle "$BT" --title " $1 " --inputbox "$2" ${4:-0} ${5:-0} "$3" 2>$ANS || return 1 return 0 } # }}} msgbox() { if [[ $# -gt 4 ]]; then dialog --backtitle "$BT" --title " $1 " $5 "$6" --msgbox "$2" ${3:-0} ${4:-0} else dialog --backtitle "$BT" --title " $1 " --msgbox "$2" ${3:-0} ${4:-0} fi return 0 } # }} infobox() { local bt="${BT:-$DIST Installer - (x86_64)}" dialog --backtitle "$bt" --title " $1 " --infobox "$2" ${3:-0} ${4:-0} sleep 2 } # }} yesno() { # when additional args (--yes-label, --no-label) are passed ensure text ($6, $8) is quoted if [[ $# -eq 8 ]]; then dialog --backtitle "$BT" --title " $1 " $5 "$6" $7 "$8" --yesno "$2" $3 $4 && return 0 return 1 fi dialog --backtitle "$BT" --title " $1 " --yesno "$2" ${3:-0} ${4:-0} && return 0 return 1 } # }} ###################################################################### ## System Settings Functions ## ###################################################################### set_keymap() { KEYMAP="" console_keymap() { local map dialog --backtitle "$BT" --title " $_CMapTitle " --menu "$_CMapBody" 0 0 16 $CONSOLE_MAPS 2>$ANS map="$(cat $ANS)" map="${map:-us}" localectl set-keymap echo "KEYMAP=$map" >/tmp/vconsole.conf } xorg_keymap() { local map dialog --backtitle "$BT" --title " $_PrepLayout " --menu "$_XMapBody" 0 0 16 $XORG_MAPS 2>$ANS map="$(sed 's/_.*//' $ANS)" map="${map:-us}" KEYMAP="$map" setxkbmap "$map" 2>$ERR check_for_errors || return 1 cat >/tmp/00-keyboard.conf </tmp/keyboard </tmp/vconsole.conf else console_keymap fi } set_locale() { local locale dialog --backtitle "$BT" --title "$_ConfLocale" \ --menu "$_LocaleBody" 0 0 12 $LOCALES 2>$ANS || return 1 locale="$(cat $ANS)" infobox "$_ConfLocale" "$_GenLocale $locale\n\n" sed -i "s/en_US.UTF-8/${locale}/g" $MNT/etc/locale.conf cp -f $MNT/etc/locale.conf $MNT/etc/default/locale sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${locale}/${locale}/g" $MNT/etc/locale.gen chroot_cmd "locale-gen" >/dev/null 2>$ERR check_for_errors || return 1 set_timezone } set_timezone() { local zone subzone dialog --backtitle "$BT" --title " $_TimeZTitle " --menu "$_TimeZBody" 0 0 10 $ZONES 2>$ANS || return 1 zone="$(cat $ANS)" for i in $(grep "$zone/" <<< "$FULLZONES" | awk -F'/' '{print $2}' | sort -ud); do SUBZONES="$SUBZONES $i -" done dialog --backtitle "$BT" --title " $_TimeZTitle " --menu "$_TimeSubZBody" 0 0 10 $SUBZONES 2>$ANS || return 1 subzone="$(cat $ANS)" if [[ $zone && $subzone ]] && yesno "$_TimeZTitle" "$_TimeZQ $zone/$subzone?\n\n"; then chroot_cmd "ln -sf /usr/share/zoneinfo/$zone/$subzone /etc/localtime" 2>$ERR check_for_errors || return 1 else set_timezone fi # step done and continue to set hwclock TIMEZONE_SET="True" set_hwclock } set_hwclock() { # try setting the default setting for hwclock chroot_cmd "hwclock --systohc --utc" if (( $? != 0 )); then # when errors occur attempt handling them with a fallback chroot_cmd "hwclock --systohc --utc --directisa" # if still failing, alert the user and just continue if (( $? != 0 )); then msgbox "$_ErrTitle" "Hwclock setup and fallback attempts failed..\n\nContinuing anyway." fi fi } set_hostname() { local hostentry getinput "$_ConfHost" "$_HostNameBody" "${DIST,,}" 14 60 || return 1 hostentry="$(cat $ANS)" echo "$hostentry" >$MNT/etc/hostname cat > $MNT/etc/hosts << EOF 127.0.0.1 localhost 127.0.1.1 $hostentry ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters EOF } root_password() { getpass "$_ConfRoot" "$_RootBody" 10 40 || return 1 PASSWD="$(cat $ANS)" getpass "$_ConfRoot" "$_RootBody2" 10 40 || return 1 PASSWD2="$(cat $ANS)" if [[ "$PASSWD" == "$PASSWD2" ]]; then echo -e "$PASSWD\n$PASSWD" >/tmp/.passwd chroot_cmd "passwd root" /dev/null 2>$ERR check_for_errors || return 1 rm -f /tmp/.passwd SET_ROOT_PASSWD="True" return 0 fi # passwords don't match msgbox "$_ErrTitle" "${_PassErr}$_TryAgain" root_password } create_user() { local newuser getinput "$_UserTitle" "$_UserBody" "" || return 1 newuser="$(cat $ANS)" # bad name answer while [[ ${#newuser} -eq 0 || $newuser =~ \ |\' || $newuser =~ [^a-z0-9\ ] ]]; do getinput "$_UserTitle" "$_UserErrBody" "" 14 40 || { break; return 1; } newuser="$(cat $ANS)" done getpass "$_ConfUser" "$_UserPass $newuser" 10 40 || return 1 PASSWD="$(cat $ANS)" getpass "$_ConfUser" "$_UserPass2 $newuser" 10 40 || return 1 PASSWD2="$(cat $ANS)" # passwords don't match while [[ "$PASSWD" != "$PASSWD2" ]]; do msgbox "$_ErrTitle" "${_PassErr}$_TryAgain" 8 60 getpass "$_ConfUser" "$_UserPass $newuser" 10 40 || { break; return 1; } PASSWD="$(cat $ANS)" getpass "$_ConfUser" "$_UserPass2 $newuser" 10 40 || { break; return 1; } PASSWD2="$(cat $ANS)" done infobox "$_ConfUser" "$_UserSetBody" echo -e "$PASSWD\n$PASSWD" >/tmp/.passwd # for first user created, swap the live user account if [[ -e $MNT/home/$LIVEUSER ]]; then chroot_cmd "passwd $LIVEUSER" /dev/null 2>$ERR check_for_errors || return 1 sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers local tty_autologin if [[ -e $MNT/etc/systemd/system/autologin@.service ]]; then tty_autologin="systemd/system/autologin@.service" elif [[ -e $MNT/etc/systemd/system/getty@tty1.service.d/override.conf ]]; then tty_autologin="systemd/system/getty@tty1.service.d/override.conf" elif [[ -e $MNT/etc/systemd/system/getty@tty1.service.d/autologin.conf ]]; then tty_autologin="systemd/system/getty@tty1.service.d/autologin.conf" fi for f in group gshadow passwd shadow $tty_autologin; do sed -i "s/${LIVEUSER}/${newuser}/g" "$MNT/etc/$f" done chroot_cmd "mv /home/$LIVEUSER /home/$newuser" 2>$ERR check_for_errors || return 1 chroot_cmd "chown -R $newuser:users /home/$newuser" 2>$ERR check_for_errors || return 1 chroot_cmd "usermod -aG rfkill,wheel,autologin,network,lp,storage,power,video,audio,lp $newuser" 2>$ERR else # create new user account chroot_cmd "useradd $newuser -m -g users -G rfkill,wheel,autologin,network,lp,storage,power,video,audio,lp -s /bin/zsh" 2>$ERR check_for_errors || return 1 chroot_cmd "passwd $newuser" < /tmp/.passwd >/dev/null 2>$ERR check_for_errors || return 1 chroot_cmd "cp -R /etc/skel/ /home/$newuser" 2>$ERR check_for_errors || return 1 chroot_cmd "chown -R ${newuser}:users /home/$newuser" 2>$ERR fi check_for_errors || return 1 [[ -e /tmp/.passwd ]] && rm /tmp/.passwd # if all the needed steps are not finished, bail early [[ -z $UNPACKED_BASE || -z $TIMEZONE_SET || -z $SET_ROOT_PASSWD || -z $BOOT_DONE ]] && return 0 local yes="Exit and Reboot" local no="Go Back" yesno "$_InstFin" "$_InstFinBody" 0 0 --yes-label "$yes" --no-label "$no" && { unmount_partitions; reboot; } || return 1 } ###################################################################### ## System Partitioning Functions ## ###################################################################### confirm_mount() { local part="$1" local mntpnt="$2" # partition failed to mount properly if ! grep -q "$mntpnt" <<< "$(mount)"; then infobox "$_MntTitle" "$_MntFail" return 1 fi # mount was successful infobox "$_MntTitle" "$_MntSucc" # sed s~${part}$' -'~~ # remove mounted partition from dialog list PARTS="$(sed "s~${part} [0-9]*[G-M]~~; s~${part} [0-9]*\.[0-9]*[G-M]~~" <<< "$PARTS")" # decrement the number of partitions left, used in while loop ((PART_COUNT--)) } mount_partition() { local part="$1" local mntp="$2" mkdir -p "${MNT}$mntp" # get mount options if any if (( ${#FS_OPTS[@]} > 0 )) && select_mount_opts "$part"; then mount -o $MNT_OPTS "$part" "${MNT}$mntp" 2>$ERR else # generic auto mount mount "$part" "${MNT}$mntp" 2>$ERR fi check_for_errors || return 1 confirm_mount "$part" "${MNT}$mntp" || return 1 check_part_is_crypt_or_lvm "$part" } unmount_partitions() { swapoff -a for i in $(mount | grep "$MNT" | awk '{print $3}' | sort -r); do umount -r "$i" &>/dev/null done } find_partitions() { local str="$1" # string of partitions as: /TYPE/PART SIZE PARTS="$(lsblk -lno TYPE,NAME,SIZE | grep "$str" | sed 's|^part|/dev/|g; s|^lvm|/dev/mapper/|g; s|^crypt|/dev/mapper/|g' | awk '{print $1$2 " " $3}' | sort -u)" PART_COUNT=0 for i in $PARTS; do ((PART_COUNT++)) done # due to storing the device size, we need half the number of fields PART_COUNT=$((PART_COUNT / 2)) local err="NONE" case $str in 'part\|lvm\|crypt') ([[ $PART_COUNT -eq 0 ]] || [[$SYS == "UEFI" && $PART_COUNT -lt 2 ]]) && err="$_PartErrBody" ;; 'part\|crypt') (( PART_COUNT == 0 )) && err="$_LvmPartErrBody" ;; 'part\|lvm') (( PART_COUNT < 2 )) && err="$_LuksPartErrBody" esac if [[ $err != "NONE" ]]; then msgbox "$_ErrTitle" "$err" select_device && create_partitions "$DEVICE" || return 1 fi } create_partitions() { local choice local device="$1" dialog --backtitle "$BT" --title " $_PartTitle " --menu "$_PartBody" 0 0 5 \ "$_PartWipe" "BIOS & UEFI" \ "$_PartAuto" "BIOS & UEFI" \ "gparted" "BIOS & UEFI" \ "cfdisk" "BIOS/MBR" \ "parted" "UEFI/GPT" 2>$ANS choice="$(cat $ANS)" [[ -z $choice ]] && return 1 || clear if [[ $choice != "$_PartWipe" && $choice != "$_PartAuto" ]]; then $choice "$device" elif [[ $choice == "$_PartWipe" ]]; then wipe_device "$device" && create_partitions "$device" else auto_partition "$device" fi } auto_partition() { local device="$1" if [[ $SYS == "BIOS" ]]; then _PartBody2="will be destroyed.\n\nAn ext4 partition will be created using all available space." fi if yesno "$_PrepParts" "$_PartBody1 $device $_PartBody2 $_PartBody3"; then current_parts="$(parted -s "$device" print | awk '/^ / {print $1}' | sort -r)" for part in $current_parts; do parted -s "$device" rm $part 2>$ERR check_for_errors || { break; return 1; } done part_table="$(parted -s "$device" print | grep -i 'partition table' | awk '{print $3}')" if [[ $SYS == "BIOS" && $part_table != "msdos" ]]; then parted -s "$device" mklabel msdos 2>$ERR elif [[ $SYS == "UEFI" && $part_table != "gpt" ]]; then parted -s "$device" mklabel gpt 2>$ERR fi check_for_errors || return 1 if [[ $SYS == "BIOS" ]]; then parted -s "$device" mkpart primary ext4 1MiB 100% 2>$ERR else parted -s "$device" mkpart ESP fat32 1MiB 513MiB 2>$ERR check_for_errors || return 1 parted -s "$device" set 1 boot on 2>$ERR check_for_errors || return 1 parted -s "$device" mkpart primary ext4 513MiB 100% 2>$ERR fi check_for_errors || return 1 lsblk "$device" -o NAME,TYPE,FSTYPE,SIZE >/tmp/.devlist dialog --backtitle "$BT" --title " $_PrepParts " --textbox /tmp/.devlist 0 0 fi } wipe_device() { local device="$1" if yesno "$_PartWipe" "$_PartBody1 $device $_PartBody2 $_PartBody3"; then clear echo -e "Secure wiping $device this will take a while.." wipe -Ifre "$device" else create_partitions "$device" fi } select_swap() { local ans msg mem tot swap_part mem="$(grep MemTotal /proc/meminfo | awk '{print $2/1024}' | sed 's/\..*//')" dialog --backtitle "$BT" --title " $_PrepMount " --menu "$_SelSwpBody" 0 0 7 \ "$_Skip" "-" \ "$_SelSwpFile" "${mem}M" \ $PARTS 2>$ANS || return 0 ans="$(cat $ANS)" if [[ $ans && $ans != "$_Skip" ]]; then if [[ $ans == "$_SelSwpFile" ]]; then msg="Enter size for swapfile below\n\nM = MB, G = GB" getinput "$_SelSwpFile" "$msg" "${mem}M" 10 40 || return 0 tot="$(cat $ANS)" # bad answer while ! [[ ${tot: -1} =~ [MG] ]]; do msgbox "$_SelSwpFile" "\n$_SelSwpFile $_ErrTitle: M = MB, G = GB\n\n" 7 40 msg="Enter size for swapfile below\n\nM = MB, G = GB" getinput "$_SelSwpFile" "$msg" "${mem}M" 10 40 || { break; return 0; } tot="$(cat $ANS)" done fallocate -l "$tot" $MNT/swapfile 2>$ERR check_for_errors || return 1 chmod 600 $MNT/swapfile 2>$ERR check_for_errors || return 1 mkswap $MNT/swapfile >/dev/null 2>$ERR check_for_errors || return 1 swapon $MNT/swapfile >/dev/null 2>$ERR check_for_errors || return 1 else swap_part="$(cat $ANS)" if [[ $(lsblk -o FSTYPE "$swap_part" | grep -i "swap") != "swap" ]]; then if yesno "$_PrepMount" "\nmkswap $swap_part\n$_ContinueYN"; then mkswap "$swap_part" >/dev/null 2>$ERR check_for_errors || return 1 else return 0 fi fi swapon "$swap_part" >/dev/null 2>$ERR check_for_errors || return 1 # sed s~${swap_part}$' -'~~ # remove mounted partition from dialog list PARTS="$(sed "s~${swap_part} [0-9]*[G-M]~~; s~${swap_part} [0-9]*\.[0-9]*[G-M]~~" <<< "$PARTS")" ((PART_COUNT--)) fi fi return 0 } select_device() { local msg if [[ $1 == "bootloader" ]]; then msg="$_DevSelTitle $_DevSelBoot $ROOT_PARTITION\n" else msg="$_DevSelBody" fi DEVICE="" DEVS="$(lsblk -lno NAME,SIZE,TYPE | grep 'disk' | awk '{print "/dev/" $1 " " $2}' | sort -u)" dialog --backtitle "$BT" --title " $_DevSelTitle " --menu "$msg" 0 0 4 $DEVS 2>$ANS || return 1 [[ $1 == "bootloader" && $(cat $ANS) != "" ]] && BOOT_DEVICE="$(cat $ANS)" [[ $(cat $ANS) != "" ]] && DEVICE="$(cat $ANS)" || return 1 } select_filesystem() { local part="$1" FS_OPTS=() dialog --backtitle "$BT" --title " $_FSTitle " --menu "$_FSBody" 0 0 12 \ "$_Skip" "-" \ "ext4" "mkfs.ext4 -q" \ "ext3" "mkfs.ext3 -q" \ "ext2" "mkfs.ext2 -q" \ "vfat" "mkfs.vfat -F32" \ "btrfs" "mkfs.btrfs -f" \ "ntfs" "mkfs.ntfs -q" \ "f2fs" "mkfs.f2fs" \ "jfs" "mkfs.jfs -q" \ "nilfs2" "mkfs.nilfs2 -q" \ "reiserfs" "mkfs.reiserfs -q" \ "xfs" "mkfs.xfs -f" 2>$ANS || return 1 local choice choice="$(cat $ANS)" case $choice in "$_Skip") FS_TYPE="$_Skip" ;; ext4) FS_TYPE="mkfs.ext4 -q" FS_OPTS=(dealloc discard noacl noatime nobarrier nodelalloc) ;; ext3) FS_TYPE="mkfs.ext3 -q" ;; ext2) FS_TYPE="mkfs.ext2 -q" ;; vfat) FS_TYPE="mkfs.vfat -F32" ;; ntfs) FS_TYPE="mkfs.ntfs -q" ;; btrfs) FS_TYPE="mkfs.btrfs -f" FS_OPTS=(autodefrag "compress=zlib" "compress=lzo" "compress=no" "compress-force=zlib" "compress-force=lzo" discard noacl noatime nodatasum nospace_cache recovery skip_balance space_cache ssd ssd_spread) modprobe btrfs ;; f2fs) FS_TYPE="mkfs.f2fs" FS_OPTS=(data_flush disable_roll_forward disable_ext_identify discard fastboot flush_merge inline_xattr inline_data inline_dentry no_heap noacl nobarrier noextent_cache noinline_data norecovery) modprobe f2fs ;; jfs) FS_TYPE="mkfs.jfs -q" FS_OPTS=(discard "errors=continue" "errors=panic" nointegrity) ;; nilfs2) FS_TYPE="mkfs.nilfs2 -q" FS_OPTS=(discard nobarrier "errors=continue" "errors=panic" "order=relaxed" "order=strict" norecovery) ;; reiserfs) FS_TYPE="mkfs.reiserfs -q" FS_OPTS=(acl nolog notail replayonly user_xattr) ;; xfs) FS_TYPE="mkfs.xfs -f" FS_OPTS=(discard filestreams ikeep largeio noalign nobarrier norecovery noquota wsync) ;; *) return 1 esac if [[ $FS_TYPE != "$_Skip" ]]; then if yesno "$_FSTitle" "\nFormat $part as $choice?\n\n"; then infobox "$_FSTitle" "\nFormatting $part as $choice\n\n" $FS_TYPE $part >/dev/null 2>$ERR check_for_errors || return 1 else select_filesystem "$part" || return 1 fi fi return 0 } select_mount_opts() { echo "" >$OPT local part="$1" local list="" for i in "${FS_OPTS[@]}"; do list="$list $i - off" done local title="$(sed "s/.*\.//g; s/-.*//g" <<< "$FS_TYPE") Mount Options" dialog --backtitle "$BT" --title " $title " --checklist "$_MntBody" 0 0 7 $list 2>$OPT || return 1 MNT_OPTS="$(cat $OPT | sed 's/ /,/g; $s/,$//')" if [[ $MNT_OPTS ]]; then if ! yesno "$title" "${_MntConfBody}$MNT_OPTS" 9 45; then select_mount_opts fi return 0 fi # no mount options were chosen or cancel was pressed return 1 } select_boot_setup() { # choose mountpoint and bootloader if [[ $SYS == "UEFI" ]]; then dialog --backtitle "$BT" --title " $_PrepMount " --menu "$_MntUefiBody" 0 0 2 \ "grub" "/boot/efi" \ "systemd-boot" "/boot" 2>$ANS || return 1 BOOTLOADER="$(cat $ANS)" BOOTLOADER="${BOOTLOADER:-grub}" # $EFI only needs to be modified when not using grub # otherwise the default /boot/efi works if [[ $BOOTLOADER != "grub" ]]; then EFI="/boot" fi mkdir -p "${MNT}$EFI" mount "$EFI_PARTITION" "${MNT}$EFI" 2>$ERR check_for_errors || return 1 confirm_mount "$EFI_PARTITION" "${MNT}$EFI" || return 1 else dialog --backtitle "$BT" --title " $_InstBiosBtTitle " \ --menu "$_InstBiosBtBody" 0 0 2 \ "grub" "-" \ "syslinux" "-" 2>$ANS || return 1 BOOTLOADER="$(cat $ANS)" BOOTLOADER="${BOOTLOADER:-grub}" if [[ $BOOTLOADER == "grub" ]]; then select_device "bootloader" || return 1 BOOT_DEVICE="$DEVICE" else dialog --backtitle "$BT" --title " $_InstSysTitle " --menu "$_InstSysBody" 0 0 2 \ "syslinux-install_update -iam" "[MBR]" \ "syslinux-install_update -i" "[/]" 2>$ANS || return 1 SYSLNUX_CMD="$(cat $ANS)" SYSLNUX_CMD="${SYSLNUX_CMD:-syslinux-install_update -iam}" BOOT_DEVICE="$ROOT_PARTITION / (root)" if grep -q "iam" <<< "$SYSLNUX_CMD"; then BOOT_DEVICE="$ROOT_PARTITION MBR" fi fi fi return 0 } select_efi_partition() { dialog --backtitle "$BT" --title " $_PrepMount " \ --menu "$_SelUefiBody" 0 0 7 $PARTS 2>$ANS || return 1 EFI_PARTITION="$(cat $ANS)" BOOT_DEVICE="$EFI_PARTITION" format_efi_as_vfat() { infobox "$_FSTitle" "\nFormatting $BOOT_DEVICE as vfat/fat32.\n" mkfs.vfat -F32 "$EFI_PARTITION" >/dev/null 2>$ERR check_for_errors || return 1 } if grep -q 'fat' <<< "$(fsck -N "$EFI_PARTITION")"; then local yes="Skip Formatting" local no="Format $EFI_PARTITION" local msg="$_FormUefiBody $EFI_PARTITION $_FormUefiBody2" if ! yesno "$_PrepMount" "$msg" 0 0 --yes-label "$yes" --no-label "$no"; then format_efi_as_vfat || return 1 fi else format_efi_as_vfat || return 1 fi return 0 } select_install_partitions() { msgbox "$_PrepMount" "$_WarnMount1 '$_Skip' $_WarnMount2" lvm_detect # prepare partition list for menu unmount_partitions find_partitions 'part\|lvm\|crypt' || return 1 # select root (/) dialog --backtitle "$BT" --title "$_PrepMount" \ --menu "$_SelRootBody" 0 0 7 $PARTS 2>$ANS || return 1 ROOT_PARTITION="$(cat $ANS)" # choose filesystem select_filesystem "$ROOT_PARTITION" || return 1 # choose mount options and mount the partition mount_partition "$ROOT_PARTITION" || return 1 select_swap || return 1 if [[ $SYS == "UEFI" ]]; then select_efi_partition || return 1 fi select_boot_setup || return 1 # remaining partitions while (( PART_COUNT > 0 )); do dialog --backtitle "$BT" --title " $_PrepMount " \ --menu "$_ExtPartBody" 0 0 7 "$_Done" "-" $PARTS 2>$ANS local part="$(cat $ANS)" # return to main menu if [[ $? == 1 || $part == "$_Done" ]]; then break return 0 else select_filesystem "$part" || return 1 local title="$_PrepMount $part" local examples [[ $SYS == "UEFI" ]] && examples="/home /var" || examples="/boot /home /var" # get mountpoint getinput "$title" "${_ExtPartBody1}$examples\n" "/" || { break; return 1; } local mntp="$(cat $ANS)" # bad mountpoint while [[ ${mntp:0:1} != "/" || ${#mntp} -le 1 || $mntp =~ \ |\' ]]; do msgbox "$_ErrTitle" "$_ExtErrBody" getinput "$title" "${_ExtPartBody1}$examples\n" "/" || { break 2; return 1; } mntp="$(cat $ANS)" done # mount it mount_partition "$part" "$mntp" || return 1 # add needed hooks for certain mountpoints if [[ $mntp == "/usr" ]]; then ! grep -q "usr" <<< "$MKINIT_HOOKS" && MKINIT_HOOKS="usr $MKINIT_HOOKS" elif [[ $mntp == "/boot" ]]; then if grep -q "lvm" <<< "$(lsblk -lno TYPE "$part")"; then SEPERATE_BOOT=2 else SEPERATE_BOOT=1 fi fi ((PART_COUNT--)) fi done } ###################################################################### ## Encryption (dm_crypt) Functions ## ###################################################################### luks_pass() { getpass "$_PrepLUKS" "$_LuksPassBody" || return 1 PASSWD="$(cat $ANS)" getpass "$_PrepLUKS" "$_UserPass2 $LUKS_ROOT_NAME" || return 1 PASSWD2="$(cat $ANS)" if [[ $PASSWD != "$PASSWD2" ]]; then msgbox "$_ErrTitle" "${_PassErr}$_TryAgain" luks_pass fi LUKS_PASSWD="$PASSWD" } luks_open() { unmount_partitions find_partitions 'part\|crypt\|lvm' dialog --backtitle "$BT" --title " $_LuksOpen " \ --menu "$_LuksMenuBody" 15 50 7 $PARTS 2>$ANS || return 1 LUKS_PART="$(cat $ANS)" getinput "$_LuksOpen" "$_LuksOpenBody" "cryptroot" || return 1 LUKS_ROOT_NAME="$(cat $ANS)" if luks_pass; then infobox "$_LuksOpen" "$_LuksWaitBody $LUKS_ROOT_NAME $_LuksWaitBody2 $LUKS_PART\n" echo "$LUKS_PASSWD" | cryptsetup open --type luks "$LUKS_PART" "$LUKS_ROOT_NAME" 2>$ERR check_for_errors || return 1 luks_show fi } luks_setup() { modprobe -a dm-mod dm_crypt unmount_partitions find_partitions 'part\|lvm' dialog --backtitle "$BT" --title "$_LuksEncrypt" \ --menu "$_LuksEncryptBody" 0 0 7 $PARTS 2>$ANS || return 1 LUKS_PART="$(cat $ANS)" getinput "$_LuksEncrypt" "$_LuksOpenBody" "cryptroot" || return 1 LUKS_ROOT_NAME="$(cat $ANS)" luks_pass || return 1 } luks_default() { if luks_setup; then infobox "$_LuksEncrypt" "$_LuksWaitBody $LUKS_ROOT_NAME $_LuksWaitBody2 $LUKS_PART\n" echo "$LUKS_PASSWD" | cryptsetup -q luksFormat "$LUKS_PART" 2>$ERR check_for_errors || return 1 echo "$LUKS_PASSWD" | cryptsetup open "$LUKS_PART" "$LUKS_ROOT_NAME" 2>$ERR check_for_errors || return 1 luks_show fi } luks_key() { if luks_setup; then getinput "$_PrepLUKS" "$_LuksCipherKey" "-s 512 -c aes-xts-plain64" || return 1 local msg="$_LuksWaitBody $LUKS_ROOT_NAME $_LuksWaitBody2 $LUKS_PART\n" infobox "$_LuksEncryptAdv" "$msg" echo "$LUKS_PASSWD" | cryptsetup -q "$(cat $ANS)" luksFormat "$LUKS_PART" 2>$ERR check_for_errors || return 1 echo "$LUKS_PASSWD" | cryptsetup open "$LUKS_PART" "$LUKS_ROOT_NAME" 2>$ERR check_for_errors || return 1 luks_show fi } luks_show() { echo -e "$_LuksEncryptSucc" >/tmp/.devlist lsblk -o NAME,TYPE,FSTYPE,SIZE "$LUKS_PART" | grep "part\|crypt\|NAME\|TYPE\|FSTYPE\|SIZE" >>/tmp/.devlist dialog --backtitle "$BT" --title " $_LuksEncrypt " --textbox /tmp/.devlist 0 0 } luks_menu() { dialog --backtitle "$BT" --title " $_PrepLUKS " \ --menu "${_LuksMenuBody}${_LuksMenuBody2}${_LuksMenuBody3}" 0 0 4 \ "$_LuksOpen" "cryptsetup open --type luks" \ "$_LuksEncrypt" "cryptsetup -q luksFormat" \ "$_LuksEncryptAdv" "cryptsetup -q -s -c luksFormat" \ "$_Back" "-" 2> $ANS || return 0 case "$(cat $ANS)" in "$_LuksOpen") luks_open ;; "$_LuksEncrypt") luks_default ;; "$_LuksEncryptAdv") luks_key ;; *) return 0 esac luks_menu } luks_keyfile() { # Only used when choosing grub as bootloader. Without a keyfile, during boot # the user will be asked to enter password for decryption twice, this is annoying if [[ ! -e $MNT/crypto_keyfile.bin ]] && yesno "$_LuksKeyFileTitle" "$_LuksKeyFile"; then infobox "$_LuksKeyFileTitle" "$_LuksKeyFileCreate\n" local dev dev="$(lsblk -lno NAME,UUID,TYPE | grep "part\|crypt\|lvm" | grep "$LUKS_UUID" | awk '{print $1}')" (( LVM == 1 )) && dev="/dev/mapper/$dev" || dev="/dev/$dev" chroot_cmd "dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin" &>/dev/null chroot_cmd "chmod 000 /crypto_keyfile.bin" &>/dev/null chroot_cmd "echo '$LUKS_PASSWD' | cryptsetup luksAddKey $dev /crypto_keyfile.bin" &>/dev/null sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf &>/dev/null chroot_cmd "mkinitcpio -p linux" 2>$ERR &>/dev/null check_for_errors || return 1 fi return 0 } ###################################################################### ## Logical Volume Management Functions ## ###################################################################### lvm_detect() { LVM_PV="$(pvs -o pv_name --noheading 2>/dev/null)" LV_VG="$(vgs -o vg_name --noheading 2>/dev/null)" LV="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)" if [[ $LV && $LV_VG && $LVM_PV ]]; then infobox "$_PrepLVM" "$_LvmDetBody" modprobe dm-mod 2>$ERR check_for_errors || return 1 vgscan >/dev/null 2>&1 vgchange -ay >/dev/null 2>&1 fi } lvm_show_vg() { VG_LIST="" for i in $(lvs --noheadings | awk '{print $2}' | uniq); do VG_LIST="$VG_LIST $i $(vgdisplay "$i" | grep -i "vg size" | awk '{print $3$4}')" done if [[ -z $VG_LIST ]]; then msgbox "$_ErrTitle" "$_LvmVGErr" return 1 fi dialog --backtitle "$BT" --title " $_PrepLVM " --menu "$_LvmSelVGBody" 0 0 5 $VG_LIST 2>$ANS || return 1 return 0 } check_lv_size() { local lv="$((${#LV_SIZE} - 1))" (( ${#LV_SIZE} == 0 || ${LV_SIZE:0:1} == 0 )) && ERR_SIZE=1 || ERR_SIZE=0 if (( ERR_SIZE == 0 )); then for (( i=0; i= VG_MB )) && ERR_SIZE=1 || VG_MB=$((VG_MB - m)) ;; [Mm]) (( ${LV_SIZE:0:$lv} >= VG_MB )) && ERR_SIZE=1 || VG_MB=$((VG_MB - s)) ;; *) ERR_SIZE=1 esac fi fi fi if (( ERR_SIZE == 1 )); then msgbox "$_ErrTitle" "$_LvmLvSizeErrBody" msg="$LV_VG: ${VG_SIZE}$VG_SIZE_TYPE (${LV_VG_MB}MB $_LvmLvSizeBody1).$_LvmLvSizeBody2" getinput "$_LvmCreateVG (LV:$NUM_LVS)" "$msg" "" || { break; return 1; } LV_SIZE="$(cat $ANS)" check_lv_size fi return 0 } lvm_create() { LV_VG="" VG_PARTS="" VG_MB=0 unmount_partitions find_partitions 'part\|crypt' PARTS="$(sed 's/M\|G\|T/& off/g' <<< "$PARTS")" # get volume group name getinput "$_LvmCreateVG" "$_LvmNameVgBody" "VolGroup" || return 1 LV_VG="$(cat $ANS)" # bad answer or volume group name already taken while [[ ${LV_VG:0:1} == "/" || ${#LV_VG} -eq 0 || $LV_VG =~ \ |\' ]] || grep -q "$LV_VG" <<< "$(lsblk)"; do msgbox "$_ErrTitle" "$_LvmNameVgErr" getinput "$_LvmCreateVG" "$_LvmNameVgBody" "VolGroup" || { break; return 1; } LV_VG="$(cat $ANS)" done # choose partitions dialog --backtitle "$BT" --title "$_LvmCreateVG" --checklist "$_LvmPvSelBody" 0 0 7 $PARTS 2>$ANS [[ $(cat $ANS) != "" ]] && VG_PARTS="$(cat $ANS)" || return 1 # confirm or bail out yesno "$_LvmCreateVG" "$_LvmPvConfBody1 $LV_VG\n$_LvmPvConfBody2 $VG_PARTS" || return 1 # create it infobox "$_LvmCreateVG" "$_LvmPvActBody1 $LV_VG.\n" vgcreate -f "$LV_VG" "$VG_PARTS" >/dev/null 2>$ERR check_for_errors || return 1 VG_SIZE=$(vgdisplay "$LV_VG" | grep 'VG Size' | sed 's/\..*//; s/[^0-9]*//g') SIZE_TYPE="$(vgdisplay "$LV_VG" | grep 'VG Size' | awk '{print $4}')" # transform vg size to MB if needed [[ ${SIZE_TYPE:0:1} == "G" ]] && VG_MB=$((VG_SIZE * 1000)) || VG_MB=$VG_SIZE # finished volume group creation msgbox "$_LvmCreateVG" "$_LvmPvDoneBody1 '$LV_VG' ($VG_SIZE $SIZE_TYPE) $_LvmPvDoneBody2\n" # how many logical volumes local msg="$_LvmLvNumBody1 '$LV_VG'.\n$_LvmLvNumBody2" dialog --backtitle "$BT" --title " $_LvmCreateVG " --radiolist "$msg" 0 0 9 \ "1" "-" off \ "2" "-" off \ "3" "-" off \ "4" "-" off \ "5" "-" off \ "6" "-" off \ "7" "-" off \ "8" "-" off \ "9" "-" off 2>$ANS [[ $(cat $ANS) != "" ]] && NUM_LVS=$(cat $ANS) || return 1 # loop selected amount while (( NUM_LVS > 1 )); do local ttl=" $_LvmCreateVG (LV:$NUM_LVS) " getinput "$ttl" "$_LvmLvNameBody1" "volext" || { break; return 1; } LV_NAME="$(cat $ANS)" # bad answer while [[ ${LV_NAME:0:1} == "/" || ${#LV_NAME} -eq 0 || $LV_NAME =~ \ |\' ]] || grep -q "$LV_NAME" <<< "$(lsblk)"; do msgbox "$_ErrTitle" "$_LvmLvNameErrBody" getinput "$ttl" "$_LvmLvNameBody1" "volext" || { break 2; return 1; } LV_NAME="$(cat $ANS)" done msg="${LV_VG}: ${VG_SIZE}$SIZE_TYPE (${VG_MB}MB $_LvmLvSizeBody1).$_LvmLvSizeBody2" getinput "$ttl" "$msg" "" || { break; return 1; } LV_SIZE="$(cat $ANS)" check_lv_size || return 1 lvcreate -L "$LV_SIZE" "$LV_VG" -n "$LV_NAME" 2>$ERR check_for_errors || return 1 msgbox "$ttl" "$_Done LV $LV_NAME ($LV_SIZE) $_LvmPvDoneBody2." ((NUM_LVS--)) done # last or only msg="$_LvmLvNameBody1 $_LvmLvNameBody2 (${VG_MB}MB)." # volume name getinput "$_LvmCreateVG (LV:$NUM_LVS)" "$msg" "volhome" || return 1 LV_NAME="$(cat $ANS)" # bad volume name while [[ ${LV_NAME:0:1} == "/" || ${#LV_NAME} -eq 0 || $LV_NAME =~ \ |\' ]] || grep -q "$LV_NAME" <<< "$(lsblk)"; do msgbox "$_ErrTitle" "$_LvmLvNameErrBody" getinput "$_LvmCreateVG (LV:$NUM_LVS)" "$msg" "volhome" || { break; return 1; } LV_NAME="$(cat $ANS)" done # create it lvcreate -l +100%FREE "$LV_VG" -n "$LV_NAME" 2>$ERR check_for_errors || return 1 ((NUM_LVS--)) LVM=1 yesno "$_LvmCreateVG" "$_LvmCompBody" && show_devices return 0 } lvm_del_vg() { lvm_show_vg if yesno "$_LvmDelVG" "$_LvmDelQ"; then vgremove -f "$(cat $ANS)" >/dev/null 2>&1 fi return 0 } lvm_del_all() { LVM_PV="$(pvs -o pv_name --noheading 2>/dev/null)" LV_VG="$(vgs -o vg_name --noheading 2>/dev/null)" LV="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)" if yesno "$_LvMDelAll" "$_LvmDelQ"; then for i in $LV; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1 done for i in $LV_VG; do vgremove -f "$i" >/dev/null 2>&1 done for i in $LVM_PV; do pvremove -f "$i" >/dev/null 2>&1 done LVM=0 fi return 0 } lvm_menu() { lvm_detect dialog --backtitle "$BT" --title " $_PrepLVM " --menu "$_LvmMenu" 0 0 4 \ "$_LvmCreateVG" "vgcreate -f, lvcreate -L -n" \ "$_LvmDelVG" "vgremove -f" \ "$_LvMDelAll" "lvrmeove, vgremove, pvremove -f" \ "$_Back" "-" 2>$ANS || return 0 case "$(cat $ANS)" in "$_LvmCreateVG") lvm_create ;; "$_LvmDelVG") lvm_del_vg ;; "$_LvMDelAll") lvm_del_all ;; *) return 0 esac lvm_menu } ###################################################################### ## Installation Functions ## ###################################################################### install_main() { unpack_base_system || return 0 # user can choose to bail at this point update_mirrorlist update_system genfstab -U -p $MNT >$MNT/etc/fstab 2>$ERR check_for_errors || return 1 # remove /mnt/install prefix from /etc/fstab for swapfile [[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" $MNT/etc/fstab run_mkinitcpio || return 1 setup_bootloader || return 1 configure_menu || edit_config_menu } unpack_base_system() { [[ $UNPACKED_BASE ]] && return 0 if ! yesno "$_InstTitle" "$_BeginInst $ROOT_PARTITION\n$_ContinueYN"; then return 1 fi clear echo -e "\nUnpacking the system\n\n" rsync -a --info=progress2 /run/archiso/sfs/airootfs/ $MNT/ 2>$ERR check_for_errors || return 1 rm -rf $MNT/etc/polkit-1/rules.d/49-nopasswd_global.rules rm -rf $MNT/etc/sudoers.d/g_wheel rm -rf $MNT/etc/mkinitcpio-archiso.conf rm -rf $MNT/usr/bin/install-al rm -rf $MNT/usr/bin/al-installer rm -rf $MNT/home/$LIVEUSER/.config/keypack rm -rf $MNT/home/$LIVEUSER/bin/welcome.sh find $MNT/usr/lib/initcpio -name "archiso*" -type f -exec rm '{}' \; local openbox="$MNT/home/$LIVEUSER/.config/openbox" sed -i '/keypack/d; /welcome.sh/d; s/#$HOME/$HOME/g; s|#$HOME/.config/setup &|$HOME/.config/setup &|g' $openbox/autostart sed -i '/installer/ { N; N; d; }' $openbox/rc.xml sed -i '/item label="Welcome Screen"/ i\ ' $openbox/menu.xml sed -i '/item label="Welcome Screen"/ { N; N; N; N; N; N; N; N; N; N; N; d; }' $openbox/menu.xml sed -i 's/volatile/auto/g' $MNT/etc/systemd/journald.conf if hash al-hello &>/dev/null; then if hash st &>/dev/null; then cmd="st" elif hash termite &>/dev/null; then cmd="termite" else cmd="xterm" fi sed -i "/al-hello/ c sleep 10; ${cmd} -e al-hello &" $openbox/autostart else sed -i '/al-hello/d' $openbox/autostart fi # vmlinuz, needed for mkinitcpio cp -f /run/archiso/bootmnt/arch/boot/x86_64/vmlinuz $MNT/boot/vmlinuz-linux # config files we made during prepare_menu() cp -f /tmp/keyboard $MNT/etc/default/ cp -f /tmp/vconsole.conf $MNT/etc/ cp -f /tmp/00-keyboard.conf $MNT/etc/X11/xorg.conf.d/ # existing network config cp -f /etc/resolv.conf $MNT/etc/ cp -rf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/ UNPACKED_BASE="True" } update_system() { # update system and install needed packages infobox "$_UpdSysTitle" "$_UpdSysBody\n" chroot_cmd "pacman -Rs archlabs-installer --noconfirm" &>/dev/null chroot_cmd "pacman -Syyu --color always --noconfirm" &>/dev/null chroot_cmd "pacman -S iputils --noconfirm" &>/dev/null chroot_cmd "pacman -S base-devel git --needed --noconfirm" &>/dev/null } update_mirrorlist() { local yes="Automatic Sort" local no="Customize Command" if yesno "$_MirrorTitle" "$_MirrorSetup" 0 0 --yes-label "$yes" --no-label "$no"; then CMD="reflector --score 100 -l 50 -f 10 --sort rate" else dialog --backtitle "$BT" --title "$_MirrorTitle" --menu "$_MirrorCountry" 20 45 10 $COUNTRY 2>$ANS || return 1 COUNTRY="$(cat $ANS)" CMD="reflector --country $COUNTRY --score 100 --latest 50 --fastest 10 --sort rate" REF="\n--score n Limit the list to the n servers with the highest score. \n-l n, --latest n Limit the list to the n most recently synchronized servers. \n-f n, --fastest n Return the n fastest mirrors that meet the other criteria. \n--sort {age,rate,country,score,delay} \n 'age': last server synchronization; 'rate': download rate; \n 'country': server's location; 'score': MirrorStatus score; \n 'delay': MirrorStatus delay." getinput "$_MirrorTitle" "$_MirrorCmd\n$REF\n" "$CMD" CMD="$(cat $ANS)" fi infobox "$_MirrorTitle" "$_MirrorSort" if ! ${CMD:-reflector --score 100 -l 50 -f 10 --sort rate} --save $MNT/etc/pacman.d/mirrorlist; then infobox "$_ErrTitle" "\nAn error occurred updating the mirrorlist\n\n" mirrors fi } create_bootloader_config() { if [[ $BOOTLOADER == "grub" ]]; then sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g" $MNT/etc/default/grub 2>$ERR check_for_errors || return 1 if (( LUKS == 1 )); then sed -i 's/#GRUB_ENABLE_CRYPTODISK/GRUB_ENABLE_CRYPTODISK/g' $MNT/etc/default/grub 2>$ERR check_for_errors || return 1 if [[ $LUKS_DEV ]]; then sed -i "s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g" $MNT/etc/default/grub 2>$ERR check_for_errors || return 1 fi fi if [[ $SYS != "UEFI" ]]; then if (( LVM == 1 && SEPERATE_BOOT == 0 )) || (( SEPERATE_BOOT == 2 )); then sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $MNT/etc/default/grub 2>$ERR check_for_errors || return 1 fi fi elif [[ $BOOTLOADER == "syslinux" ]]; then local cfgdir="$MNT/boot/syslinux" [[ ! -d $cfgdir ]] && mkdir -p $cfgdir # menu would be nice, so try setting that up if [[ -e /usr/lib/syslinux/bios/vesamenu.c32 && ! -e $cfgdir/vesamenu.c32 ]]; then cp -f /usr/lib/syslinux/bios/vesamenu.c32 $cfgdir/ if [[ -e /run/archiso/bootmnt/arch/boot/syslinux/splash.png ]]; then cp -f /run/archiso/bootmnt/arch/boot/syslinux/splash.png $cfgdir/ fi fi # if the menu exists we can proceed to setup a default list if [[ -e $cfgdir/vesamenu.c32 ]]; then local menu_top="UI vesamenu.c32\nDEFAULT archlabs\nPROMPT 0\n" menu_top="$menu_top\nMENU TITLE ArchLabs\nMENU BACKGROUND splash.png\nTIMEOUT 50\n" menu_top="$menu_top\nMENU WIDTH 78\nMENU MARGIN 4\nMENU ROWS 5\nMENU VSHIFT 10" menu_top="$menu_top\nMENU TIMEOUTROW 13\nMENU TABMSGROW 11\nMENU CMDLINEROW 11" menu_top="$menu_top\nMENU HELPMSGROW 16\nMENU HELPMSGENDROW 29\n" menu_top="$menu_top\n# Refer to https://www.syslinux.org/wiki/index.php/Comboot/menu.c32\n" menu_top="$menu_top\nMENU COLOR border 30;44 #40ffffff #a0000000 std" menu_top="$menu_top\nMENU COLOR title 1;36;44 #9033ccff #a0000000 std" menu_top="$menu_top\nMENU COLOR sel 7;37;40 #e0ffffff #20ffffff all" menu_top="$menu_top\nMENU COLOR unsel 37;44 #50ffffff #a0000000 std" menu_top="$menu_top\nMENU COLOR help 37;40 #c0ffffff #a0000000 std" menu_top="$menu_top\nMENU COLOR timeout_msg 37;40 #80ffffff #00000000 std" menu_top="$menu_top\nMENU COLOR timeout 1;37;40 #c0ffffff #00000000 std" menu_top="$menu_top\nMENU COLOR msg07 37;40 #90ffffff #a0000000 std" menu_top="$menu_top\nMENU COLOR tabmsg 31;40 #30ffffff #00000000 std" else # no menu, but should display 'boot: ' and automatically boot after 5 seconds local menu_top="PROMPT 1\nTIMEOUT 50\nDEFAULT archlabs" fi cat > $cfgdir/syslinux.cfg << EOF $(echo -e "$menu_top") LABEL archlabs MENU LABEL Archlabs Linux LINUX ../vmlinuz-linux APPEND root=$ROOT_PARTITION $([[ $LUKS_DEV ]] && echo -n "$LUKS_DEV ")rw INITRD ../initramfs-linux.img LABEL archlabsfallback MENU LABEL Archlabs Linux Fallback LINUX ../vmlinuz-linux APPEND root=$ROOT_PARTITION $([[ $LUKS_DEV ]] && echo -n "$LUKS_DEV ")rw INITRD ../initramfs-linux-fallback.img LABEL hdt MENU LABEL HDT (Hardware Detection Tool) COM32 hdt.c32 #LABEL windows #MENU LABEL Windows #COM32 chain.c32 #APPEND root=/dev/sda2 rw #LABEL grub2 #MENU LABEL Grub2 #COM32 chain.c32 #APPEND file=../grub/boot.img LABEL reboot MENU LABEL Reboot COM32 reboot.c32 LABEL poweroff MENU LABEL Poweroff COM32 poweroff.c32 EOF else # remove leftover grub configs from previous installs find $MNT/boot/efi/EFI -name '[aA][rR][cC][hH][lL]abs*' -type d -exec rm '{}' \; find $MNT/boot/efi/EFI -name 'grub*' -type d -exec rm '{}' \; if grep -q "/dev/mapper/" <<< "$ROOT_PARTITION"; then ROOT_PART_ID="$ROOT_PARTITION" else ROOT_PART_ID="PARTUUID=$(blkid -s PARTUUID $ROOT_PARTITION | sed 's/.*=//g; s/"//g')" fi cat > $MNT/boot/loader/loader.conf << EOF default archlabs timeout 5 editor no EOF cat > $MNT/boot/loader/entries/archlabs.conf << EOF title Archlabs Linux linux /vmlinuz-linux initrd /initramfs-linux.img options root=$ROOT_PART_ID rw EOF for i in $(ls $MNT/boot/loader/entries/arch*.conf); do [[ $LUKS_DEV ]] && sed -i "s~rw~$LUKS_DEV rw~g" "$i" done fi return 0 } setup_bootloader() { bootloader_info() { local msg="$_InstBootloader $BOOTLOADER\n\n$_InstBootDev $BOOT_DEVICE" if [[ $SYS == "BIOS" ]]; then infobox "$_InstBiosBtTitle" "$msg\n" else infobox "$_InstUefiBtTitle" "$msg\n\nMountpoint: $EFI\n" fi } bios_bootloader_install() { if [[ $BOOTLOADER == "grub" ]]; then # must be before running grub-install or it will fail with encrypted devices create_bootloader_config || return 1 chroot_cmd "grub-install --bootloader-id=$DIST --recheck --force $BOOT_DEVICE" 2>$ERR check_for_errors || return 1 chroot_cmd "grub-mkconfig -o /boot/grub/grub.cfg" 2>$ERR check_for_errors || return 1 else chroot_cmd "pacman -Rs grub --noconfirm" &>/dev/null chroot_cmd "$SYSLNUX_CMD" 2>$ERR check_for_errors || return 1 create_bootloader_config fi return 0 } uefi_bootloader_install() { if [[ $BOOTLOADER == "grub" ]]; then # must be before running grub-install or it will fail with encrypted devices create_bootloader_config || return 1 chroot_cmd "grub-install --bootloader-id=$DIST --recheck --force" 2>$ERR check_for_errors || return 1 local efidir="${MNT}$EFI/EFI" local fallback="BOOT" for i in $(find "$efidir" -maxdepth 1 -mindepth 1 -type d); do if grep -qi "boot" <<< "$(basename $i)"; then fallback="$(basename $i)" break fi done mkdir -p $efidir/$fallback cp -f $efidir/$DIST/grubx64.efi $efidir/$fallback/bootx64.efi cp -f $efidir/$DIST/grubx64.efi $efidir/$fallback/BOOTX64.EFI chroot_cmd "grub-mkconfig -o /boot/grub/grub.cfg" 2>$ERR check_for_errors || return 1 else # remove old grub entries from previous installs chroot_cmd "pacman -Rs grub --noconfirm" &>/dev/null [[ -d $MNT/boot/efi/EFI/Archlabs ]] && rm -rf $MNT/boot/efi/EFI/{Archlabs,BOOT,boot,Boot} [[ -d $MNT/boot/EFI/Archlabs ]] && rm -rf $MNT/boot/EFI/{Archlabs,BOOT,boot,Boot} [[ -d $MNT/boot/grub ]] && rm -rf $MNT/boot/grub [[ -e $MNT/etc/default/grub ]] && rm -rf $MNT/etc/default/grub # since systemd v232 systemd-boot requires machine id to be setup first systemd-machine-id-setup --root="$MNT" 2>$ERR check_for_errors || return 1 chroot_cmd "bootctl --path=/boot install" 2>$ERR check_for_errors || return 1 create_bootloader_config fi return 0 } chroot_cmd "export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/bin/core_perl" # save repetition of basically the same command bootloader_info if [[ $SYS != "UEFI" ]]; then bios_bootloader_install || return 1 else if grep -q "/sys/firmware/efi/efivars" <<< "$(mount)"; then mount -t efivarfs efivarfs /sys/firmware/efi/efivars &>/dev/null chroot_cmd "mount -t efivarfs efivarfs /sys/firmware/efi/efivars" &>/dev/null fi uefi_bootloader_install || return 1 fi BOOT_DONE="True" [[ $BOOTLOADER == "grub" && $LUKS -eq 1 && $LUKS_PASSWD && $LUKS_UUID ]] && luks_keyfile return 0 } run_mkinitcpio() { local conf="$MNT/etc/mkinitcpio.conf" local addition # amend HOOKS in /etc/mkinitcpio.conf if (( LVM == 1 && LUKS == 0 )); then sed -i 's/block filesystems/block lvm2 filesystems/g' $conf 2>$ERR &>/dev/null addition="lvm2" elif (( LVM == 1 && LUKS == 1 )); then sed -i 's/block filesystems/block encrypt lvm2 filesystems/g' $conf 2>$ERR &>/dev/null addition="encrypt lvm2" elif (( LVM == 0 && LUKS == 1 )); then sed -i 's/block filesystems/block encrypt filesystems/g' $conf 2>$ERR &>/dev/null addition="encrypt" fi check_for_errors || return 1 sed -i "s/keyboard fsck/keyboard ${MKINIT_HOOKS} fsck/g" $conf 2>$ERR &>/dev/null check_for_errors || return 1 addition="$addition $MKINIT_HOOKS" infobox "$_RunMkinit" "$_RunMkinitBody\n\nAdded HOOKS: $addition\n" chroot_cmd "mkinitcpio -p linux" 2>$ERR &>/dev/null check_for_errors || return 1 } ###################################################################### ## Menu Interfaces ## ###################################################################### main_menu() { # automatically jump to prepare_menu() on first run if [[ $FIRST_RUN_PREP != "True" ]]; then # if prepare_menu() returns non zero begin the install if ! prepare_menu; then check_parts_are_mounted && install_main && MAIN_HIGHLIGHT=3 fi FIRST_RUN_PREP="True" fi if [[ $CURRENT_MENU != "main" ]]; then MAIN_HIGHLIGHT=1 CURRENT_MENU="main" elif (( MAIN_HIGHLIGHT < 5 )); then ((MAIN_HIGHLIGHT++)) # highlight the next choice fi dialog --backtitle "$BT" --title " $_MainTitle " \ --default-item $MAIN_HIGHLIGHT --menu "$_MainBody" 0 0 5 \ "1" "$_PrepTitle" \ "2" "$_InstTitle" \ "3" "$_ConfTitle" \ "4" "$_EditTitle" \ "5" "$_Done" 2>$ANS MAIN_HIGHLIGHT=$(cat $ANS) if [[ -n $MAIN_HIGHLIGHT ]]; then # make sure everything has been set before installing or configuring install if (( MAIN_HIGHLIGHT == 2 )) && ! check_parts_are_mounted; then return 1 elif (( MAIN_HIGHLIGHT >= 3 && MAIN_HIGHLIGHT <= 4 )); then if ! (check_parts_are_mounted && check_base_unpacked); then return 1 fi fi fi case $MAIN_HIGHLIGHT in 1) prepare_menu ;; 2) install_main ;; 3) configure_menu ;; 4) edit_config_menu ;; *) local yes="Exit Installer" local no="Go Back" if yesno "Close Installer" "$_CloseInstBody" 0 0 --yes-label "$yes" --no-label "$no"; then unmount_partitions cleanup clear exit 0 fi esac return 0 } prepare_menu() { if [[ $CURRENT_MENU != "prep" ]]; then SUB_HIGHLIGHT=1 CURRENT_MENU="prep" elif (( SUB_HIGHLIGHT < 7 )); then ((SUB_HIGHLIGHT++)) # increment the highlighted item # return non zero to main_menu() # only when we reach the end of the menu and have done required steps if [[ $SUB_HIGHLIGHT -eq 7 && -n $ROOT_PARTITION ]]; then ([[ $SYS == "UEFI" && -n $EFI_PARTITION ]] || [[ $SYS != "UEFI" ]]) && return 1 fi fi dialog --backtitle "$BT" --title " $_PrepTitle " \ --default-item $SUB_HIGHLIGHT --menu "$_PrepBody" 0 0 7 \ "1" "$_PrepLayout" \ "2" "$_PrepShowDev" \ "3" "$_PrepParts" \ "4" "$_PrepLUKS" \ "5" "$_PrepLVM" \ "6" "$_PrepMount" \ "7" "$_Back" 2>$ANS SUB_HIGHLIGHT=$(cat $ANS) case $SUB_HIGHLIGHT in 1) set_keymap ;; 2) show_devices ;; 3) unmount_partitions && select_device && create_partitions "$DEVICE" ;; 4) luks_menu ;; 5) lvm_menu ;; 6) select_install_partitions ;; *) return 0 esac # recurse back until explicitly chosen to leave prepare_menu } configure_menu() { chroot_cmd "export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/core_perl" # run through each step automatically on first run if [[ $FIRST_RUN_CONFIG != "True" ]]; then SUB_HIGHLIGHT=1 CURRENT_MENU="config" FIRST_RUN_CONFIG="True" set_hostname && set_locale && root_password # if create_user returns non zero, pass return code back to install_main() # this will return us to the edit_config_menu() create_user || return 1 # returning from another menu after first run elif [[ $CURRENT_MENU != "config" ]]; then SUB_HIGHLIGHT=1 CURRENT_MENU="config" # highlight next option elif (( SUB_HIGHLIGHT < 5 )); then ((SUB_HIGHLIGHT++)) fi dialog --backtitle "$BT" --title " $_ConfTitle " \ --default-item $SUB_HIGHLIGHT --menu "$_ConfBody" 0 0 5 \ "1" "$_ConfHost" \ "2" "$_ConfLocale" \ "3" "$_ConfRoot" \ "4" "$_ConfUser" \ "5" "$_Back" 2>$ANS SUB_HIGHLIGHT=$(cat $ANS) case $SUB_HIGHLIGHT in 1) set_hostname ;; 2) set_locale ;; 3) root_password ;; 4) create_user ;; *) return 0 esac # recurse back until we explicitly choose to exit configure_menu } edit_config_menu() { if [[ $CURRENT_MENU != "edit" ]]; then SUB_HIGHLIGHT=1 CURRENT_MENU="edit" elif (( SUB_HIGHLIGHT < 11 )); then ((SUB_HIGHLIGHT++)) fi dialog --backtitle "$BT" --title " $_EditTitle " \ --default-item $SUB_HIGHLIGHT --menu "$_EditBody" 0 0 11 \ "1" "keymap configs" \ "2" "locale configs" \ "3" "/etc/hostname" \ "4" "/etc/hosts" \ "5" "/etc/sudoers" \ "6" "/etc/mkinitcpio.conf" \ "7" "/etc/fstab" \ "8" "/etc/crypttab" \ "9" "bootloader configs" \ "10" "/etc/pacman.conf" \ "11" "$_Back" 2>$ANS SUB_HIGHLIGHT="$(cat $ANS)" local file="" local xorg_conf="etc/X11/xorg.conf.d/00-keyboard.conf" case $SUB_HIGHLIGHT in 1) [[ -e $MNT/$xorg_conf ]] && file="$MNT/$xorg_conf" [[ -e $MNT/etc/vconsole.conf ]] && file="$file $MNT/etc/vconsole.conf" [[ -e $MNT/etc/default/keyboard ]] && file="$file $MNT/etc/default/keyboard" ;; 2) [[ -e $MNT/etc/locale.conf ]] && file="$MNT/etc/locale.conf" [[ -e $MNT/etc/default/locale ]] && file="$file $MNT/etc/default/locale" ;; 3) [[ -e $MNT/etc/hostname ]] && file="$MNT/etc/hostname" ;; 4) [[ -e $MNT/etc/hosts ]] && file="$MNT/etc/hosts" ;; 5) [[ -e $MNT/etc/sudoers ]] && file="$MNT/etc/sudoers" ;; 6) [[ -e $MNT/etc/mkinitcpio.conf ]] && file="$MNT/etc/mkinitcpio.conf" ;; 7) [[ -e $MNT/etc/fstab ]] && file="$MNT/etc/fstab" ;; 8) [[ -e $MNT/etc/crypttab ]] && file="$MNT/etc/crypttab" ;; 9) if [[ $BOOTLOADER == "systemd-boot" && -e $MNT/boot/loader/entries/archlabs.conf ]]; then file="$MNT/boot/loader/entries/archlabs.conf" elif [[ $BOOTLOADER == "syslinux" && -e $MNT/boot/syslinux/syslinux.cfg ]]; then file="$MNT/boot/syslinux/syslinux.cfg" elif [[ $BOOTLOADER == "grub" && -e $MNT/etc/default/grub ]]; then file="$MNT/etc/default/grub" fi ;; 10) [[ -e $MNT/etc/pacman.conf ]] && file="$MNT/etc/pacman.conf" ;; *) return 0 esac if [[ $file != "" ]]; then if [[ $DISPLAY ]] && hash geany &>/dev/null; then geany -i $file else vim -O $file fi else msgbox "$_ErrTitle" "$_NoFileErr" fi # recurse back after each file edit finishes edit_config_menu } for arg in $@; do [[ $arg == "--debug" || $arg == "-d" ]] && set_debug done select_language check_requirements identify_system # welcome message msgbox "$_WelTitle $DIST Installer" "$_WelBody" FIRST_RUN_PREP="False" FIRST_RUN_CONFIG="False" CURRENT_MENU="main" MAIN_HIGHLIGHT=0 SUB_HIGHLIGHT=0 while true; do main_menu done