#!/usr/bin/bash # vim:fdm=marker:fmr={,} # shellcheck disable=2154 # This program is free software, provided under the GNU GPL # Written by Nathaniel Maia for use in Archlabs # Some ideas and code has been taken from other installers # AIF, Cnichi, Calamares, The Arch Wiki.. Credit where credit is due VER="1.8.38" # version DIST="ArchLabs" # distributor MNT="/mnt" # install mountpoint # bulk default values { ROOT_PART="" # root partition BOOT_PART="" # boot partition BOOT_DEVICE="" # device used for BIOS grub install BOOTLDR="" # bootloader selected EXTRA_MNT="" # holder for additional partitions while mounting EXTRA_MNTS="" # when an extra partition is mounted append it's info SWAP_PART="" # swap partition or file path SWAP_SIZE="" # when using a swapfile use this size NEWUSER="" # username for the primary user USER_PASS="" # password for the primary user ROOT_PASS="" # root password LOGIN_WM="" # default login session LOGIN_TYPE="" # login manager can be lightdm or xinit INSTALL_WMS="" # space separated list of chosen wm/de KERNEL="" # kernel can be linux or linux-lts PACKAGES="" # list of all packages to install including WM_PACKAGES USER_PKGS="" # packages selected by the user during install WM_PACKAGES="" # full list of packages added during wm/de choice MYSHELL="" # login shell for root and the primary user UCODE="" # cpu manufacturer microcode filename (if any) HOOKS="shutdown" # list of additional HOOKS to add in /etc/mkinitcpio.conf FONT="ter-i16n" # font used in the linux console LUKS="" # empty when not using luks encryption LUKS_DEV="" # boot parameter string for LUKS LUKS_PART="" # partition used for encryption LUKS_PASS="" # encryption password LUKS_UUID="" # encrypted partition UUID LUKS_NAME="" # name used for encryption LVM="" # empty when not using lvm VGROUP_MB=0 # available space in volume group LVM_PARTS=() # partitions used for volume group WARN=false # issued mounting/partitioning warning SEP_BOOT=false # separate boot partition for BIOS AUTOLOGIN=false # enable autologin for xinit CONFIG_DONE=false # basic configuration is finished BROADCOM_WL=false # fixes for broadcom cards eg. BCM4352 CHECKED_NET=false # have we checked the network connection already # sane baseline BASE_PKGS="archlabs-scripts archlabs-skel-base archlabs-themes archlabs-dARK archlabs-icons archlabs-wallpapers " BASE_PKGS+="base-devel xorg xorg-drivers sudo git gvfs gtk3 gtk-engines gtk-engine-murrine pavucontrol tumbler " BASE_PKGS+="playerctl ffmpeg gstreamer libmad libmatroska gst-libav gst-plugins-base gst-plugins-good" # sane extras for window managers WM_BASE_PKGS="arandr archlabs-networkmanager-dmenu xdg-user-dirs nitrogen polkit-gnome volumeicon xclip exo " WM_BASE_PKGS+="xdotool compton gnome-keyring dunst feh gsimplecal xfce4-power-manager xfce4-settings laptop-detect" ERR="/tmp/errlog" # error log used internally DBG="/tmp/debuglog" # debug log when passed -d RUN="/run/archiso/bootmnt/arch/boot" # path for live /boot BT="$DIST Installer - v$VER" # backtitle used for dialogs VM="$(dmesg | grep -i "hypervisor")" # is the system a vm AUTO_ROOT_PART="" # values from auto partition AUTO_BOOT_PART="" # } # giant ugly container :P { # amount of RAM in the system in Mb SYS_MEM="$(awk '/MemTotal/ { print int($2 / 1024)"M" }' /proc/meminfo)" # parsed string of locales from /etc/locale.gen LOCALES="$(awk '/\.UTF-8/ { gsub(/# .*|#/, "") if ($1) { print $1 " -" } }' /etc/locale.gen)" # parsed string of linux console keyboard mappings CMAPS="$(find /usr/share/kbd/keymaps -name '*.map.gz' | awk '{ gsub(/\.map\.gz|.*\//, "") print $1 " -" }' | sort)" # make sure these are defined for some dialog size calculation [[ $LINES ]] || LINES=$(tput lines) [[ $COLUMNS ]] || COLUMNS=$(tput cols) # various associative arrays # { # command used to install each bootloader declare -A BCMDS=( [grub]="grub-install --recheck --force" [syslinux]="syslinux-install_update -iam" [systemd-boot]="bootctl --path=/boot install" ) # boot partition mount points for each bootloader declare -A BMNTS=( [BIOS-grub]="/boot" [UEFI-grub]="/boot/efi" [BIOS-syslinux]="/boot" [UEFI-systemd-boot]="/boot" ) # bootloader options with respective boot partition mountpoint declare -A BOOTLDRS=( [BIOS]="grub ${BMNTS[BIOS-grub]} syslinux ${BMNTS[BIOS-syslinux]}" [UEFI]="systemd-boot ${BMNTS[UEFI-systemd-boot]} grub ${BMNTS[UEFI-grub]}" ) # match the wm name with the actual session name used for xinit declare -A WM_SESSIONS=( [dwm]='dwm' [i3-gaps]='i3' [bspwm]='bspwm' [plasma]='startkde' [xfce4]='startxfce4' [gnome]='gnome-session' [fluxbox]='startfluxbox' [openbox]='openbox-session' [cinnamon]='cinnamon-session' ) # additional packages installed for each wm/de declare -A WM_EXT=( [gnome]="" [fluxbox]="menumaker" [plasma]="kdebase-meta" [bspwm]="sxhkd archlabs-skel-bspwm rofi archlabs-polybar" [i3-gaps]="i3status perl-anyevent-i3 archlabs-skel-i3-gaps rofi archlabs-polybar" [openbox]="obconf archlabs-skel-openbox jgmenu archlabs-polybar tint2 conky rofi lxmenu-data" [xfce4]="xfce4-goodies xfce4-pulseaudio-plugin network-manager-applet volumeicon rofi archlabs-skel-xfce4" ) # files the user can edit during the final stage of install declare -A EDIT_FILES=( [login]="" [fstab]="/etc/fstab" [sudoers]="/etc/sudoers" [crypttab]="/etc/crypttab" [pacman]="/etc/pacman.conf" [console]="/etc/vconsole.conf" [mkinitcpio]="/etc/mkinitcpio.conf" [hostname]="/etc/hostname /etc/hosts" [bootloader]="/boot/loader/entries/$DIST.conf" [locale]="/etc/locale.conf /etc/default/locale" [keyboard]="/etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard" ) # PKG_EXT: if you add a package to $PACKAGES in any dialog # and it uses/requires some additional packages, # you can add them here to keep it simple: [package]="extra" # duplicates are removed with `uniq` before install declare -A PKG_EXT=( [vlc]="qt4" [mpd]="mpc" [mupdf]="mupdf-tools" [qt5ct]="qt5-styleplugins" [vlc]="qt5ct qt5-styleplugins" [zathura]="zathura-pdf-poppler" [noto-fonts]="noto-fonts-emoji" [cairo-dock]="cairo-dock-plug-ins" [qutebrowser]="qt5ct qt5-styleplugins" [qbittorrent]="qt5ct qt5-styleplugins" [transmission-qt]="qt5ct qt5-styleplugins" [kdenlive]="kdebase-meta dvdauthor frei0r-plugins breeze breeze-gtk qt5ct qt5-styleplugins" ) # mkfs command to format a partition as a given file system declare -A FS_CMDS=( [f2fs]="mkfs.f2fs" [jfs]="mkfs.jfs -q" [xfs]="mkfs.xfs -f" [ntfs]="mkfs.ntfs -q" [ext2]="mkfs.ext2 -q" [ext3]="mkfs.ext3 -q" [ext4]="mkfs.ext4 -q" [vfat]="mkfs.vfat -F32" [nilfs2]="mkfs.nilfs2 -q" [reiserfs]="mkfs.reiserfs -q" ) # mount options for a given file system declare -A FS_OPTS=( [vfat]="" [ntfs]="" [ext2]="" [ext3]="" [jfs]="discard - off errors=continue - off errors=panic - off nointegrity - off" [reiserfs]="acl - off nolog - off notail - off replayonly - off user_xattr - off" [ext4]="discard - off dealloc - off nofail - off noacl - off relatime - off noatime - off nobarrier - off nodelalloc - off" [xfs]="discard - off filestreams - off ikeep - off largeio - off noalign - off nobarrier - off norecovery - off noquota - off wsync - off" [nilfs2]="discard - off nobarrier - off errors=continue - off errors=panic - off order=relaxed - off order=strict - off norecovery - off" [f2fs]="data_flush - off disable_roll_forward - off disable_ext_identify - off discard - off fastboot - off flush_merge - off inline_xattr - off inline_data - off inline_dentry - off no_heap - off noacl - off nobarrier - off noextent_cache - off noinline_data - off norecovery - off" ) # } # } main() { [[ $SEL ]] || SEL=0 (( SEL < 11 )) && (( SEL++ )) tput civis SEL=$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PrepTitle " --default-item $SEL \ --cancel-label 'Exit' --menu "$_PrepBody" 0 0 0 \ "1" "$_PrepShowDev" \ "2" "$_PrepParts" \ "3" "$_PrepLUKS" \ "4" "$_PrepLVM" \ "5" "$_PrepMount" \ "6" "$_PrepUser" \ "7" "$_PrepConfig" \ "8" "$_PrepWM" \ "9" "$_PrepPkg" \ "10" "$_PrepShow" \ "11" "$_PrepInstall") if [[ $WARN != true && $SEL =~ (2|5) ]]; then WARN=true; msgbox "$_PrepTitle" "$_WarnMount" fi case $SEL in 1) device_tree ;; 2) partition || (( SEL-- )) ;; 3) luks_menu || (( SEL-- )) ;; 4) lvm_menu || (( SEL-- )) ;; 5) mnt_menu || (( SEL-- )) ;; 6) prechecks 0 && { mkuser || (( SEL-- )); } ;; 7) prechecks 1 && { cfg_menu || (( SEL-- )); } ;; 8) prechecks 2 && { select_sessions || (( SEL-- )); } ;; 9) prechecks 2 && { select_packages || (( SEL-- )); } ;; 10) prechecks 2 && show_cfg ;; 11) prechecks 2 && install ;; *) yesno "$_CloseInst" "$_CloseInstBody" && die esac } ############################################################################### # dialog menus show_cfg() { local cmd="${BCMDS[$BOOTLDR]}" local mnt="${BMNTS[$SYS-$BOOTLDR]}" local pkgs="${USER_PKGS# }" pkgs="${pkgs% }" pkgs="${pkgs% } ${PACKAGES# }" [[ $INSTALL_WMS == *dwm* ]] && pkgs="dwm st dmenu ${pkgs# }" pkgs="${pkgs# }" pkgs="${pkgs% }" msgbox "$_PrepTitle" " ---------- PARTITION CONFIGURATION ------------ Root: ${ROOT_PART:-None} Boot: ${BOOT_PART:-${BOOT_DEVICE:-None}} Swap: ${SWAP_PART:-None} Size: ${SWAP_SIZE:-None} LVM: ${LVM:-None} LUKS: ${LUKS:-None} Extra Mounts: ${EXTRA_MNTS:-${EXTRA_MNT:-None}} Mkinit Hooks: ${HOOKS:-None} ---------- BOOTLOADER CONFIGURATION ----------- Bootloader: ${BOOTLDR:-None} Mountpoint: ${mnt:-None} Command: ${cmd:-None} ------------ SYSTEM CONFIGURATION ------------- Locale: ${LOCALE:-None} Keymap: ${KEYMAP:-None} Hostname: ${HOSTNAME:-None} Timezone: ${ZONE:-None}/${SUBZONE:-None} ------------ USER CONFIGURATION -------------- User: ${NEWUSER:-None} Shell: ${MYSHELL:-None} Session: ${LOGIN_WM:-None} Autologin: ${AUTOLOGIN:-None} Login Method: ${LOGIN_TYPE:-None} ------------ PACKAGES AND MIRRORS ------------- Kernel: ${KERNEL:-None} Sessions: ${INSTALL_WMS:-None} Mirrors: ${MIRROR_CMD:-None} Packages: $(print4 "${pkgs:-None}") " } cfg_menu() { tput civis if ! MYSHELL="$(menubox "$_ShellTitle" "$_ShellBody" '/usr/bin/zsh' '-' '/bin/bash' '-' '/usr/bin/mksh' '-')"; then return 1 fi tput cnorm if ! HOSTNAME="$(getinput "$_ConfHost" "$_HostNameBody" "${DIST,,}")"; then return 1 fi tput civis if ! LOCALE="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_ConfLocale " --menu "$_LocaleBody" 0 0 $((LINES - 20)) $LOCALES)"; then return 1 fi select_timezone || return 1 if ! KERNEL="$(menubox "$_KernelTitle" "$_KernelBody" 'linux' '-' 'linux-lts' '-')"; then return 1 fi select_mirrorcmd || return 1 CONFIG_DONE=true return 0 } mkuser() { tput cnorm local values if ! values="$(dialog --stdout --no-cancel --separator ':|~|:' \ --ok-label "Submit" --backtitle "$BT" --title " $_UserTitle " \ --insecure --mixedform "$_UserBody" 0 0 0 \ "$_Username" 1 1 "$NEWUSER" 1 $((${#_Username} + 2)) $COLUMNS 0 0 \ "$_Password" 2 1 "" 2 $((${#_Password} + 2)) $COLUMNS 0 1 \ "$_Password2" 3 1 "" 3 $((${#_Password2} + 2)) $COLUMNS 0 1 \ "$_RootBody" 6 1 "" 6 $((${#_RootBody} + 1)) $COLUMNS 0 2 \ "$_Password" 8 1 "" 8 $((${#_Password} + 2)) $COLUMNS 0 1 \ "$_Password2" 9 1 "" 9 $((${#_Password2} + 2)) $COLUMNS 0 1)"; then return 1 fi local user pass pass2 rpass rpass2 user="$(awk -F':|~|:' '{print $1}' <<< "$values")" pass="$(awk -F':|~|:' '{print $2}' <<< "$values")" pass2="$(awk -F':|~|:' '{print $3}' <<< "$values")" rpass="$(awk -F':|~|:' '{print $5}' <<< "$values")" rpass2="$(awk -F':|~|:' '{print $6}' <<< "$values")" # both root passwords are empty, so use the user passwords instead [[ $rpass == "" && $rpass2 == "" ]] && { rpass="$pass"; rpass2="$pass2"; } # make sure a username was entered and that the passwords match if [[ ${#user} -eq 0 || $user =~ \ |\' || $user =~ [^a-z0-9] ]]; then msgbox "$_UserErrTitle" "$_UserErrBody" mkuser || return 1 elif [[ $pass == "" ]]; then msgbox "$_ErrTitle" "$_UserPassEmpty\n$_TryAgain" mkuser || return 1 elif [[ "$pass" != "$pass2" ]]; then msgbox "$_ErrTitle" "$_UserPassErr\n$_TryAgain" mkuser || return 1 elif [[ "$rpass" != "$rpass2" ]]; then msgbox "$_ErrTitle" "$_RootPassErr\n$_TryAgain" mkuser || return 1 fi NEWUSER="$user" USER_PASS="$pass" ROOT_PASS="$rpass" return 0 } select_keymap() { tput civis KEYMAP="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PrepLayout " --menu "$_XMapBody" 0 0 $((LINES - 20)) \ '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' \ 'es' 'Spanish' 'latam' 'Spanish' 'br' 'Portuguese' 'pt' 'Portuguese' 'ma' 'Arabic' \ 'sy' 'Arabic' 'ara' 'Arabic' 'ua' 'Ukrainian' 'cz' 'Czech' 'ru' 'Russian' \ 'sk' 'Slovak' 'nl' 'Dutch' 'it' 'Italian' 'hu' 'Hungarian' 'cn' 'Chinese' \ 'tw' 'Taiwanese' 'vn' 'Vietnamese' 'kr' 'Korean' 'jp' 'Japanese' 'th' 'Thai' \ 'la' 'Lao' 'pl' 'Polish' 'se' 'Swedish' 'is' 'Icelandic' 'fi' 'Finnish' \ 'dk' 'Danish' 'be' 'Belgian' 'in' 'Indian' 'al' 'Albanian' 'am' 'Armenian' \ 'bd' 'Bangla' 'ba' 'Bosnian' 'bg' 'Bulgarian' 'dz' 'Berber' 'mm' 'Burmese' \ 'hr' 'Croatian' 'gr' 'Greek' 'il' 'Hebrew' 'ir' 'Persian' 'iq' 'Iraqi' \ 'af' 'Afghani' 'fo' 'Faroese' 'ge' 'Georgian' 'ee' 'Estonian' 'kg' 'Kyrgyz' \ 'kz' 'Kazakh' 'lt' 'Lithuanian' 'mt' 'Maltese' 'mn' 'Mongolian' 'ro' 'Romanian' \ 'no' 'Norwegian' 'rs' 'Serbian' 'si' 'Slovenian' 'tj' 'Tajik' 'lk' 'Sinhala' \ 'tr' 'Turkish' 'uz' 'Uzbek' 'ie' 'Irish' 'pk' 'Urdu' 'mv' 'Dhivehi' \ 'np' 'Nepali' 'et' 'Amharic' 'sn' 'Wolof' 'ml' 'Bambara' 'tz' 'Swahili' \ 'ke' 'Swahili' 'bw' 'Tswana' 'ph' 'Filipino' 'my' 'Malay' 'tm' 'Turkmen' \ 'id' 'Indonesian' 'bt' 'Dzongkha' 'lv' 'Latvian' 'md' 'Moldavian' 'mao' 'Maori' \ 'by' 'Belarusian' 'az' 'Azerbaijani' 'mk' 'Macedonian' 'kh' 'Khmer' 'epo' 'Esperanto' \ 'me' 'Montenegrin')" [[ $KEYMAP ]] || return 1 if [[ $CMAPS == *"$KEYMAP"* ]]; then CMAP="$KEYMAP" else CMAP="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_CMapTitle " --menu "$_CMapBody" 0 0 $((LINES - 17)) $CMAPS)" [[ $CMAP ]] || return 1 fi if [[ $DISPLAY && $TERM != 'linux' ]]; then setxkbmap $KEYMAP >/dev/null 2>&1 else loadkeys $CMAP >/dev/null 2>&1 fi return 0 } select_timezone() { # create associative array for SUBZONES[zone] local f="/usr/share/zoneinfo/zone.tab" declare -A SUBZONES for i in America Australia Asia Atlantic Africa Europe Indian Pacific Arctic Antarctica; do SUBZONES[$i]="$(awk '/'"$i"'\// {gsub(/'"$i"'\//, ""); print $3, $1}' $f | sort)" done tput civis if ! ZONE="$(menubox "$_TimeZTitle" "$_TimeZBody" \ 'America' '-' 'Australia' '-' 'Asia' '-' 'Atlantic' '-' 'Africa' '-' \ 'Europe' '-' 'Indian' '-' 'Pacific' '-' 'Arctic' '-' 'Antarctica' '-')"; then return 1 fi if ! SUBZONE="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_TimeZTitle " --menu "$_TimeSubZBody" 0 0 $((LINES - 17)) ${SUBZONES[$ZONE]})"; then return 1 fi yesno "$_TimeZTitle" "$_TimeZQ $ZONE/$SUBZONE?\n" || select_timezone } select_sessions() { LOGIN_CHOICES="" tput civis INSTALL_WMS="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ --title " $_WMChoice " --checklist "$_WMChoiceBody\n" 0 0 0 \ "i3-gaps" "A fork of i3wm with more features including gaps" off \ "openbox" "A lightweight, powerful, and highly configurable stacking wm" off \ "bspwm" "A tiling wm that represents windows as the leaves of a binary tree" off \ "dwm" "A fork of dwm, with more layouts and features" off \ "fluxbox" "A lightweight and highly-configurable window manager" off \ "gnome" "A desktop environment that aims to be simple and easy to use" off \ "cinnamon" "A desktop environment combining traditional desktop with modern effects" off \ "plasma" "A kde software project currently comprising a full desktop environment" off \ "xfce4" "A lightweight and modular desktop environment based on gtk+2/3" off)" [[ $INSTALL_WMS ]] || return 1 WM_NUM=$(awk '{print NF}' <<< "$INSTALL_WMS") WM_PACKAGES="${INSTALL_WMS/dwm/}" # remove dwm from package list WM_PACKAGES="${WM_PACKAGES// / }" # remove double spaces WM_PACKAGES="${WM_PACKAGES# }" # remove leading space for wm in $INSTALL_WMS; do LOGIN_CHOICES+="$wm - " [[ ${WM_EXT[$wm]} && $WM_PACKAGES != *"${WM_EXT[$wm]}"* ]] && WM_PACKAGES+=" ${WM_EXT[$wm]}" done select_login || return 1 if [[ $LOGIN_TYPE == 'xinit' ]]; then if [[ $WM_NUM -eq 1 ]]; then LOGIN_WM="${WM_SESSIONS[$INSTALL_WMS]}" else LOGIN_WM="$(menubox "$_WMLogin" "$_WMLoginBody" $LOGIN_CHOICES)" || return 1 LOGIN_WM="${WM_SESSIONS[$LOGIN_WM]}" fi yesno "$_WMLogin" "$_AutoLoginBody\n" && AUTOLOGIN=true || AUTOLOGIN=false else AUTOLOGIN=false fi # add unique wm packages to main package list for i in $WM_PACKAGES; do [[ $PACKAGES == *$i* ]] || PACKAGES+=" ${WM_PACKAGES# }" done return 0 } select_login() { LOGIN_TYPE="$(menubox "$_WMLogin" "$_LoginTypeBody" \ "xinit" "Console login without a display manager" \ "lightdm" "Lightweight display manager with a gtk greeter")" if [[ $LOGIN_TYPE ]]; then return 1 elif [[ $LOGIN_TYPE == 'lightdm' ]]; then WM_PACKAGES+=" lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice" EDIT_FILES[login]="/etc/lightdm/lightdm.conf /etc/lightdm/lightdm-gtk-greeter.conf" else PACKAGES="${PACKAGES// lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice/}" WM_PACKAGES="${WM_PACKAGES// lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice/}" WM_PACKAGES+=" xorg-xinit" EDIT_FILES[login]="/home/$NEWUSER/.xinitrc /home/$NEWUSER/.xprofile" fi } select_mirrorcmd() { local c local key="5f29642060ab983b31fdf4c2935d8c56" if hash reflector >/dev/null 2>&1; then MIRROR_CMD="reflector --score 100 -l 50 -f 5 --sort rate --verbose" yesno "$_MirrorTitle" "$_MirrorSetup" && return 0 c="$(json 'country_name' "$(json 'ip' "check&?access_key=${key}&fields=ip")?access_key=${key}&fields=country_name")" MIRROR_CMD="reflector --country $c --fastest 5 --sort rate --verbose" tput cnorm MIRROR_CMD="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ --title " $_MirrorTitle " --inputbox "$_MirrorCmd\n --score n Limit the list to the n servers with the highest score. --latest n Limit the list to the n most recently synchronized servers. --fastest n Return the n fastest mirrors that meet the other criteria. --sort {age,rate,country,score,delay} 'age': Last server synchronization; 'rate': Download rate; 'country': Server location; 'score': MirrorStatus score; 'delay': MirrorStatus delay.\n" 0 0 "$MIRROR_CMD")" elif hash rankmirrors >/dev/null 2>&1; then infobox "$_MirrorTitle" "\nQuerying mirrors near your location\n" c="$(json 'country_code' "$(json 'ip' "check&?access_key=${key}&fields=ip")?access_key=${key}&fields=country_code")" local w="https://www.archlinux.org/mirrorlist" if [[ $c ]]; then if [[ $c =~ (CA|US) ]]; then MIRROR_CMD="curl -s '$w/?country=US&country=CA&use_mirror_status=on'" else MIRROR_CMD="curl -s '$w/?country=${c}&use_mirror_status=on'" fi else MIRROR_CMD="curl -s '$w/?country=US&country=CA&country=NZ&country=GB&country=AU&use_mirror_status=on'" fi fi return 0 } edit_configs() { tput civis local choice choice=$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ --title " $_EditTitle " --menu "$_EditBody" 0 0 0 \ "keyboard" "${EDIT_FILES[keyboard]}" \ "console" "${EDIT_FILES[console]}" \ "locale" "${EDIT_FILES[locale]}" \ "hostname" "${EDIT_FILES[hostname]}" \ "sudoers" "${EDIT_FILES[sudoers]}" \ "mkinitcpio" "${EDIT_FILES[mkinitcpio]}" \ "fstab" "${EDIT_FILES[fstab]}" \ "crypttab" "${EDIT_FILES[crypttab]}" \ "bootloader" "${EDIT_FILES[bootloader]}" \ "pacman" "${EDIT_FILES[pacman]}" \ "login" "${EDIT_FILES[login]}" \ "Finished" "Unmount partitions, exit the installer, and reboot") if [[ $choice == "" || $choice == "Finished" ]]; then [[ $DEBUG == true && -r $DBG ]] && vim $DBG # when die() is passed 127 it will call: systemctl -i reboot die 127 else local exists="" for f in $(printf "%s" "${EDIT_FILES[$choice]}"); do [[ -e ${MNT}$f ]] && exists+=" ${MNT}$f" done if [[ $exists ]]; then vim -O $exists else msgbox "$_ErrTitle" "$_NoFileErr" fi fi edit_configs } ############################################################################### # package menus select_packages() { local cur=0 while true; do (( cur < 13 )) && (( cur++ )) tput civis cur=$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_Packages " --default-item $cur \ --menu "$_PackageMenu" 0 0 0 \ 1 "Web Browsers" \ 2 "Text Editors" \ 3 "File Managers" \ 4 "Terminal Emulators" \ 5 "Music & Video Players" \ 6 "Chat & Mail Clients" \ 7 "Office & Professional" \ 8 "Image & PDF Viewers" \ 9 "Additional Fonts" \ 10 "Torrent Clients" \ 11 "System Management" \ 12 "Miscellaneous" \ 13 "Return to main menu") [[ $cur && $cur -lt 13 ]] || break case $cur in 1) BROWSE_PKGS="$(select_browsers)" ;; 2) EDIT_PKGS="$(select_editors)" ;; 3) FM_PKGS="$(select_files)" ;; 4) TERM_PKGS="$(select_terms)" ;; 5) MEDIA_PKGS="$(select_media)" ;; 6) MAIL_PKGS="$(select_mail)" ;; 7) PROF_PKGS="$(select_prof)" ;; 8) VIEW_PKGS="$(select_viewers)" ;; 9) FNT_PKGS="$(select_fonts)" ;; 10) TOR_PKGS="$(select_torrent)" ;; 11) SYS_PKGS="$(select_sys)" ;; 12) EX_PKGS=" $(select_extra)" ;; esac USER_PKGS="$BROWSE_PKGS $EDIT_PKGS $FM_PKGS $TERM_PKGS $MEDIA_PKGS $MAIL_PKGS " USER_PKGS+="$PROF_PKGS $VIEW_PKGS $FNT_PKGS $TOR_PKGS $SYS_PKGS $EX_PKGS" done USER_PKGS="$BROWSE_PKGS $EDIT_PKGS $FM_PKGS $TERM_PKGS $MEDIA_PKGS $MAIL_PKGS " USER_PKGS+="$PROF_PKGS $VIEW_PKGS $FNT_PKGS $TOR_PKGS $SYS_PKGS $EX_PKGS" for i in $USER_PKGS; do [[ ${PKG_EXT[$i]} && $USER_PKGS != *"${PKG_EXT[$i]}"* ]] && USER_PKGS+=" ${PKG_EXT[$i]}" done USER_PKGS="${USER_PKGS// / }" USER_PKGS="${USER_PKGS// / }" USER_PKGS="${USER_PKGS# }" USER_PKGS="${USER_PKGS% }" return 0 } select_browsers() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "firefox" "A popular open-source graphical web browser from Mozilla" $(ofn 'firefox') \ "chromium" "an open-source graphical web browser based on the Blink rendering engine" $(ofn 'chromium') \ "opera" "A Fast and secure, free of charge web browser from Opera Software" $(ofn 'opera') \ "epiphany" "A GNOME web browser based on the WebKit rendering engine" $(ofn 'epiphany') \ "surf" "A simple web browser based on WebKit2/GTK+" $(ofn 'surf') \ "qutebrowser" "A keyboard-focused vim-like web browser based on Python and PyQt5" $(ofn 'qutebrowser'))" printf "%s" "$pkgs" } select_editors() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "neovim" "A fork of Vim aiming to improve user experience, plugins, and GUIs." $(ofn 'neovim') \ "atom" "An open-source text editor developed by GitHub that is licensed under the MIT License" $(ofn 'atom') \ "geany" "A fast and lightweight IDE" $(ofn 'geany') \ "emacs" "An extensible, customizable, self-documenting real-time display editor" $(ofn 'emacs') \ "mousepad" "A simple text editor" $(ofn 'mousepad'))" printf "%s" "$pkgs" } select_files() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "thunar" "A modern file manager for the Xfce Desktop Environment" $(ofn 'thunar') \ "pcmanfm" "A fast and lightweight file manager based in Lxde" $(ofn 'pcmanfm') \ "nautilus" "The default file manager for Gnome" $(ofn 'nautilus') \ "gparted" "A GUI frontend for creating and manipulating partition tables" $(ofn 'gparted') \ "file-roller" "Create and modify archives" $(ofn 'file-roller') \ "xarchiver" "A GTK+ frontend to various command line archivers" $(ofn 'xarchiver'))" printf "%s" "$pkgs" } select_terms() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "termite" "A minimal VTE-based terminal emulator" $(ofn 'termite') \ "rxvt-unicode" "A unicode enabled rxvt-clone terminal emulator" $(ofn 'rxvt-unicode') \ "xterm" "The standard terminal emulator for the X window system" $(ofn 'xterm') \ "alacritty" "A cross-platform, GPU-accelerated terminal emulator" $(ofn 'alacritty') \ "terminator" "Terminal emulator that supports tabs and grids" $(ofn 'terminator') \ "sakura" "A terminal emulator based on GTK and VTE" $(ofn 'sakura') \ "tilix" "A tiling terminal emulator for Linux using GTK+ 3" $(ofn 'tilix') \ "tilda" "A Gtk based drop down terminal for Linux and Unix" $(ofn 'tilda') \ "xfce4-terminal" "A terminal emulator based in the Xfce Desktop Environment" $(ofn 'xfce-terminal'))" printf "%s" "$pkgs" } select_media() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "vlc" "A free and open source cross-platform multimedia player" $(ofn 'vlc') \ "mpv" "A media player based on mplayer" $(ofn 'mpv') \ "mpd" "A flexible, powerful, server-side application for playing music" $(ofn 'mpd') \ "ncmpcpp" "An mpd client and almost exact clone of ncmpc with some new features" $(ofn 'ncmpcpp') \ "cmus" "A small, fast and powerful console music player for Unix-like operating systems" $(ofn 'cmus') \ "audacious" "A free and advanced audio player based on GTK+" $(ofn 'audacious') \ "nicotine+" "A graphical client for Soulseek" $(ofn 'nicotine+') \ "lollypop" "A new music playing application" $(ofn 'lollypop') \ "rhythmbox" "Music playback and management application" $(ofn 'rhythmbox') \ "deadbeef" "A GTK+ audio player for GNU/Linux" $(ofn 'deadbeef') \ "clementine" "A modern music player and library organizer" $(ofn 'clementine'))" printf "%s" "$pkgs" } select_mail() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "thunderbird" "Standalone mail and news reader from mozilla" $(ofn 'thunderbird') \ "geary" "A lightweight email client for the GNOME desktop" $(ofn 'geary') \ "evolution" "Manage your email, contacts and schedule" $(ofn 'evolution') \ "mutt" "Small but very powerful text-based mail client" $(ofn 'mutt') \ "hexchat" "A popular and easy to use graphical IRC client" $(ofn 'hexchat') \ "pidgin" "Multi-protocol instant messaging client" $(ofn 'pidgin') \ "weechat" "Fast, light and extensible IRC client" $(ofn 'weechat') \ "irssi" "Modular text mode IRC client" $(ofn 'irssi'))" printf "%s" "$pkgs" } select_prof() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "libreoffice-fresh" "Full featured office suite" $(ofn 'libreoffice-fresh') \ "abiword" "Fully-featured word processor" $(ofn 'abiword') \ "calligra" "A set of applications for productivity" $(ofn 'calligra') \ "gimp" "GNU Image Manipulation Program" $(ofn 'gimp') \ "inkscape" "Professional vector graphics editor" $(ofn 'inkscape') \ "krita" "Edit and paint images" $(ofn 'krita') \ "obs-studio" "Free opensource streaming/recording software" $(ofn 'obs-studio') \ "kdenlive" "A non-linear video editor for Linux using the MLT video framework" $(ofn 'kdenlive') \ "openshot" "An open-source, non-linear video editor for Linux" $(ofn 'openshot') \ "audacity" "A program that lets you manipulate digital audio waveforms" $(ofn 'audacity') \ "guvcview" "Capture video from camera devices" $(ofn 'guvcview') \ "simplescreenrecorder" "A feature-rich screen recorder" $(ofn 'simplescreenrecorder'))" printf "%s" "$pkgs" } select_fonts() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "ttf-hack" "A hand groomed and optically balanced typeface based on Bitstream Vera Mono" $(ofn 'ttf-hack') \ "ttf-anonymous-pro" "A family fixed-width fonts designed with coding in mind" $(ofn 'ttf-anonymous-pro') \ "ttf-font-awesome" "Iconic font designed for Bootstrap" $(ofn 'ttf-font-awesome') \ "ttf-fira-code" "Monospaced font with programming ligatures" $(ofn 'ttf-fira-code') \ "noto-fonts" "Google Noto fonts" $(ofn 'noto-fonts') \ "noto-fonts-cjk" "Google Noto CJK fonts (Chinese, Japanese, Korean)" $(ofn 'noto-fonts-cjk'))" printf "%s" "$pkgs" } select_viewers() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "evince" "A document viewer" $(ofn 'evince') \ "zathura" "Minimalistic document viewer" $(ofn 'zathura') \ "qpdfview" "A tabbed PDF viewer" $(ofn 'qpdfview') \ "mupdf" "Lightweight PDF and XPS viewer" $(ofn 'mupdf') \ "gpicview" "Lightweight image viewer" $(ofn 'gpicview'))" printf "%s" "$pkgs" } select_torrent() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "deluge" "A BitTorrent client written in python" $(ofn 'deluge') \ "qbittorrent" "An advanced BitTorrent client" $(ofn 'qbittorrent') \ "transmission-gtk" "Free BitTorrent client GTK+ GUI" $(ofn 'transmission-gtk') \ "transmission-qt" "Free BitTorrent client Qt GUI" $(ofn 'transmission-qt') \ "transmission-cli" "Free BitTorrent client CLI" $(ofn 'transmission-cli'))" printf "%s" "$pkgs" } select_sys() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "gnome-disk-utility" "Disk Management Utility" $(ofn 'gnome-disk-utility') \ "gnome-system-monitor" "View current processes and monitor system state" $(ofn 'gnome-system-monitor') \ "qt5ct" "GUI for managing Qt based application themes, icons, and fonts" $(ofn 'qt5ct'))" printf "%s" "$pkgs" } select_extra() { local pkgs="" pkgs="$(checkbox "$_Packages" "$_PackageBody" \ "steam" "A popular game distribution platform by Valve" $(ofn 'steam') \ "gpick" "Advanced color picker using GTK+ toolkit" $(ofn 'gpick') \ "gcolor2" "A simple GTK+2 color selector" $(ofn 'gcolor2') \ "plank" "An elegant, simple, and clean dock" $(ofn 'plank') \ "docky" "Full fledged dock for opening applications and managing windows" $(ofn 'docky') \ "cairo-dock" "Light eye-candy fully themable animated dock" $(ofn 'cairo-dock'))" printf "%s" "$pkgs" } ############################################################################### # partition menus format() { infobox "$_FSTitle" "\nRunning: ${FS_CMDS[$2]} $1\n" 1 ${FS_CMDS[$2]} "$1" >/dev/null 2>$ERR errshow "${FS_CMDS[$2]} $1" } partition() { local device if [[ $# -eq 0 ]]; then select_device 'root' || return 1 device="$DEVICE" else device="$1" fi tput civis local choice if [[ $DISPLAY ]] && hash gparted >/dev/null 2>&1; then choice="$(menubox "$_PartTitle" "$_PartBody" \ "$_PartShowTree" "Shows output from the lsblk command" \ "$_PartAuto" "Full device automatic partitioning" \ "gparted" "GUI front end to parted" \ "cfdisk" "Curses front end to fdisk" \ "parted" "GNU partition editor" \ "$_PartWipe" "Wipe data before disposal or sale of a device" \ "$_Done" "Return to the main menu")" else choice="$(menubox "$_PartTitle" "$_PartBody" \ "$_PartShowTree" "Show output of 'lsblk $device'" \ "$_PartAuto" "Full device automatic partitioning" \ "cfdisk" "Curses front end to fdisk" \ "parted" "GNU partition editor" \ "$_PartWipe" "Wipe data before disposal or sale of the device" \ "$_Done" "Return to the main menu")" fi tput civis if [[ $choice == "$_Done" || $choice == "" ]]; then return 0 elif [[ $choice != "$_PartWipe" && $choice != "$_PartAuto" && $choice != "$_PartShowTree" ]]; then clear; tput cnorm; $choice "$device"; partition "$device" elif [[ $choice == "$_PartShowTree" ]]; then msgbox "$_PartTitle" "\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT "$device")\n\n" partition "$device" elif [[ $choice == "$_PartWipe" ]]; then yesno "$_PartWipe" "$_PartBody1 $device $_PartWipeBody" && wipe -Ifrev $device partition "$device" else local root_size msg ret table boot_fs root_size=$(lsblk -lno SIZE "$device" | awk 'NR == 1 { if ($1 ~ "G") { sub(/G/, ""); print ($1 * 1000 - 512) / 1000"G" } else { sub(/M/, ""); print ($1 - 512)"M" } }') if [[ $SYS == 'BIOS' ]]; then msg="$(sed 's|vfat/fat32|ext4|' <<< "$_PartBody2")"; table="msdos"; boot_fs="ext4" else msg="$_PartBody2"; table="gpt"; boot_fs="fat32" fi if yesno "$_PrepParts" "$_PartBody1 $device $msg ($size)$_PartBody3"; then auto_partition "$device" "$table" "$boot_fs" "$root_size" || return 1 else partition "$device" fi fi return 0 } decr_count() { # remove a partition from the dialog list and decrement the number partitions left local p="$1" PARTS="$(sed "s~${p} [0-9]*[G-M]~~; s~${p} [0-9]*\.[0-9]*[G-M]~~" <<< "$PARTS")" (( COUNT > 0 )) && (( COUNT-- )) return 0 } enable_swap() { if [[ $1 == "$MNT/swapfile" && $SWAP_SIZE ]]; then fallocate -l $SWAP_SIZE $1 2>$ERR errshow "fallocate -l $SWAP_SIZE $1" chmod 600 $1 2>$ERR errshow "chmod 600 $1" fi mkswap $1 >/dev/null 2>$ERR errshow "mkswap $1" swapon $1 >/dev/null 2>$ERR errshow "swapon $1" return 0 } device_tree() { tput civis local msg if [[ $IGNORE_DEV != "" ]]; then msg="$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT | awk "!/$IGNORE_DEV/"' && /disk|part|lvm|crypt|NAME/')" else msg="$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT | awk '/disk|part|lvm|crypt|NAME/')" fi msgbox "$_PrepShowDev" "\n\n$msg\n\n" } select_device() { local dev msg [[ $1 == 'boot' ]] && msg="$_DevSelTitle for bootloader\n" || umount_dir $MNT if [[ $DEV_COUNT -eq 1 && $SYS_DEVS ]]; then # auto select without mention (it's annoying) DEVICE="$(awk '{print $1}' <<< "$SYS_DEVS")" elif (( DEV_COUNT > 1 )); then tput civis DEVICE="$(menubox "$_DevSelTitle " "${msg}$_DevSelBody" $SYS_DEVS)" || return 1 elif [[ $DEV_COUNT -lt 1 && $1 != 'boot' ]]; then msgbox "$_ErrTitle" "\nNo available devices to use.\n$_Exit"; die 1 fi # if the device selected was for bootloader, set the BOOT_DEVICE [[ $1 == 'boot' ]] && BOOT_DEVICE="$DEVICE" return 0 } confirm_mount() { local part="$1" mount="$2" [[ $mount == "$MNT" ]] && local m="/ (root)" || local m="${mount#$MNT}" if [[ $(mount) == *"$mount"* ]]; then infobox "$_MntTitle" "$_MntSucc\nPartition: $part\nMountpoint: $m\n" 1 decr_count "$part" else infobox "$_MntTitle" "$_MntFail\n$msg\n" 1 return 1 fi return 0 } check_cryptlvm() { local dev devs part="$1" devs="$(lsblk -lno NAME,FSTYPE,TYPE)" # Identify if $part is LUKS+LVM, LVM+LUKS, LVM alone, or LUKS alone if [[ $(lsblk -lno TYPE "$part") =~ 'crypt' ]]; then LUKS='encrypted' LUKS_NAME="${part#/dev/mapper/}" for dev in $(awk '/lvm/ && /crypto_LUKS/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_DEV="$LUKS_DEV cryptdevice=$dev:$LUKS_NAME" LVM='logical volume' break fi done for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')" LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" break fi done elif [[ $(lsblk -lno TYPE "$part") =~ 'lvm' ]]; then LVM='logical volume' VOLUME_NAME="${part#/dev/mapper/}" for dev in $(awk '/crypt/ && /lvm2_member/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do if grep -q "$VOLUME_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_NAME="$(sed 's~/dev/mapper/~~g' <<< "$dev")" break fi done for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')" LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME" LUKS='encrypted' break fi done fi } auto_partition() { local device="$1" table="$2" boot_fs="$3" size="$4" local dev_info="$(parted -s $device print)" infobox "$_PrepParts" "\nRemoving partitions on $device and setting table to $table\n" 2 # in case the device was previously used for swap swapoff -a # walk the partitions on the device in reverse order and delete them while read -r PART; do parted -s $device rm $PART >/dev/null 2>&1 done <<< "$(awk '/^ [1-9][0-9]?/ {print $1}' <<< "$dev_info" | sort -r)" if [[ $(awk '/Table:/ {print $3}' <<< "$dev_info") != "$table" ]]; then parted -s $device mklabel $table >/dev/null 2>&1 fi infobox "$_PrepParts" "\nCreating a 512M $fs boot partition.\n" 2 if [[ $SYS == "BIOS" ]]; then parted -s $device mkpart primary $fs 1MiB 513MiB >/dev/null 2>&1 else parted -s $device mkpart ESP $fs 1MiB 513MiB >/dev/null 2>&1 fi sleep 0.5 BOOT_DEVICE="$device" AUTO_BOOT_PART=$(lsblk -lno NAME,TYPE $device | awk 'NR == 2 {print "/dev/"$1}') if [[ $SYS == "BIOS" ]]; then mkfs.ext4 -q $AUTO_BOOT_PART >/dev/null 2>&1 else mkfs.vfat -F32 $AUTO_BOOT_PART >/dev/null 2>&1 fi infobox "$_PrepParts" "\nCreating a $size ext4 root partition.\n" 0 parted -s $device mkpart primary ext4 513MiB 100% >/dev/null 2>&1 sleep 0.5 AUTO_ROOT_PART="$(lsblk -lno NAME,TYPE $device | awk 'NR == 3 {print "/dev/"$1}')" mkfs.ext4 -q $AUTO_ROOT_PART >/dev/null 2>&1 tput civis sleep 0.5 msgbox "$_PrepParts" "\nAuto partitioning complete.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE $device)\n" } mount_partition() { local part="$1" local mountp="${MNT}$2" local fs fs="$(lsblk -lno FSTYPE $part)" mkdir -p "$mountp" if [[ $fs && ${FS_OPTS[$fs]} && $part != "$BOOT_PART" ]] && select_mount_opts "$part" "$fs"; then mount -o $MNT_OPTS "$part" "$mountp" 2>$ERR else mount "$part" "$mountp" 2>$ERR fi confirm_mount $part "$mountp" || return 1 check_cryptlvm "$part" return 0 } find_partitions() { local str="$1" local err='' # string of partitions as /TYPE/PART SIZE if [[ $IGNORE_DEV != "" ]]; then PARTS="$(lsblk -lno TYPE,NAME,SIZE | awk "/$str/"' && !'"/$IGNORE_DEV/"' { sub(/^part/, "/dev/"); sub(/^lvm|^crypt/, "/dev/mapper/") print $1$2 " " $3 }')" else PARTS="$(lsblk -lno TYPE,NAME,SIZE | awk "/$str/"' { sub(/^part/, "/dev/") sub(/^lvm|^crypt/, "/dev/mapper/") print $1$2 " " $3 }')" fi # number of partitions total if [[ $PARTS ]]; then COUNT=$(wc -l <<< "$PARTS") else COUNT=0 fi # ensure we have enough partitions for the system and action type case "$str" in 'part|lvm|crypt') [[ $COUNT -lt 1 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_PartErrBody" ;; 'part|crypt') (( COUNT < 1 )) && err="$_LvmPartErrBody" ;; 'part|lvm') (( COUNT < 2 )) && err="$_LuksPartErrBody" ;; esac # if there aren't enough partitions show the relevant error message [[ $err ]] && { msgbox "$_ErrTitle" "$err"; return 1; } return 0 } setup_boot_device() { infobox "$_PrepMount" "\nSetting device flags for: $BOOT_PART\n" 1 [[ $BOOT_PART = /dev/nvme* ]] && BOOT_DEVICE="${BOOT_PART%p[1-9]}" || BOOT_DEVICE="${BOOT_PART%[1-9]}" BOOT_PART_NUM="${BOOT_PART: -1}" if [[ $SYS == 'UEFI' ]]; then parted -s $BOOT_DEVICE set $BOOT_PART_NUM esp on >/dev/null 2>&1 else parted -s $BOOT_DEVICE set $BOOT_PART_NUM boot on >/dev/null 2>&1 fi return 0 } ############################################################################### # mounting menus mnt_menu() { # prepare partition list PARTS for dialog lvm_detect umount_dir $MNT find_partitions 'part|lvm|crypt' || { SEL=2; return 1; } select_root_partition || return 1 if [[ $SYS == "UEFI" ]]; then select_efi_partition || { BOOT_PART=""; return 1; } elif (( COUNT > 0 )); then select_boot_partition || { BOOT_PART=""; return 1; } fi setup_boot || return 1 select_swap || return 1 select_extra_partitions || return 1 return 0 } select_swap() { tput civis SWAP_PART="$(menubox "$_SelSwpSetup" "$_SelSwpBody" \ "$_SelSwpNone" "Don't allocate any swap space" \ "$_SelSwpFile" "Allocate $SYS_MEM of swap at /swapfile" \ $PARTS)" if [[ $SWAP_PART == "" || $SWAP_PART == "$_SelSwpNone" ]]; then SWAP_PART=""; return 0 elif [[ $SWAP_PART == "$_SelSwpFile" ]]; then tput cnorm local i=0 while ! [[ ${SWAP_SIZE:0:1} =~ [1-9] && ${SWAP_SIZE: -1} =~ (M|G) ]]; do (( i > 0 )) && msgbox "$_SelSwpSetup Error" "\n$_SelSwpErr $SWAP_SIZE\n" if ! SWAP_SIZE="$(getinput "$_SelSwpSetup" "$_SelSwpSize" "$SYS_MEM")"; then SWAP_PART=""; SWAP_SIZE=""; break; return 0 fi ((i++)) done enable_swap "$MNT/swapfile" SWAP_PART="/swapfile" else enable_swap "$SWAP_PART" decr_count "$SWAP_PART" SWAP_SIZE="$(lsblk -lno SIZE $SWAP_PART)" fi return 0 } select_mountpoint() { tput cnorm EXTRA_MNT="$(getinput "$_PrepMount $part" "$_ExtPartBody1" "/" nolimit)" || return 1 if [[ ${EXTRA_MNT:0:1} != "/" || ${#EXTRA_MNT} -le 1 || $EXTRA_MNT =~ \ |\' || $EXTRA_MNTS == *"$EXTRA_MNT"* ]]; then msgbox "$_ErrTitle" "$_ExtErrBody" select_mountpoint || return 1 fi return 0 } select_mount_opts() { local part="$1" local fs="$2" local title="${fs^} Mount Options" local opts="${FS_OPTS[$fs]}" tput civis is_ssd "$part" >/dev/null 2>&1 && opts=$(sed 's/discard - off/discard - on/' <<< "$opts") MNT_OPTS="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $title " --checklist "$_MntBody" 0 0 0 $opts)" [[ $MNT_OPTS ]] && MNT_OPTS="$(sed 's/ /,/g; $s/,$//' <<< "$MNT_OPTS" )" || return 1 yesno "$title" "$_MntConfBody $MNT_OPTS\n" || { select_mount_opts "$part" "$fs" || return 1; } return 0 } select_filesystem() { local part="$1" local fs cur_fs cur_fs="$(lsblk -lno FSTYPE "$part" 2>/dev/null)" local msg="\nSelect which filesystem to use for: $part\n\nCurrent: ${cur_fs:-None}\nDefault: ext4" [[ $part == $ROOT_PART && $ROOT_PART == $AUTO_ROOT_PART && $LUKS == "" && $LVM == "" ]] && return 0 tput civis if [[ $cur_fs && $part != "$ROOT_PART" ]]; then fs="$(menubox "$_FSTitle: $part" "$msg" \ "$_Skip" "-" \ "ext4" "${FS_CMDS[ext4]}" \ "ext3" "${FS_CMDS[ext3]}" \ "ext2" "${FS_CMDS[ext2]}" \ "vfat" "${FS_CMDS[vfat]}" \ "ntfs" "${FS_CMDS[ntfs]}" \ "f2fs" "${FS_CMDS[f2fs]}" \ "jfs" "${FS_CMDS[jfs]}" \ "xfs" "${FS_CMDS[xfs]}"\ "nilfs2" "${FS_CMDS[nilfs2]}" \ "reiserfs" "${FS_CMDS[reiserfs]}")" [[ $fs == "$_Skip" ]] && return 0 else fs="$(menubox "$_FSTitle: $part" "$msg" \ "ext4" "${FS_CMDS[ext4]}" \ "ext3" "${FS_CMDS[ext3]}" \ "ext2" "${FS_CMDS[ext2]}" \ "ntfs" "${FS_CMDS[ntfs]}" \ "f2fs" "${FS_CMDS[f2fs]}" \ "jfs" "${FS_CMDS[jfs]}" \ "xfs" "${FS_CMDS[xfs]}" \ "nilfs2" "${FS_CMDS[nilfs2]}" \ "reiserfs" "${FS_CMDS[reiserfs]}")" fi [[ $fs ]] || return 1 if yesno "$_FSTitle" "\nFormat $part as $fs?\n"; then format "$part" "$fs" || return 1 else select_filesystem "$part" || return 1 fi return 0 } select_efi_partition() { tput civis if (( COUNT == 1 )); then BOOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_PrepMount" "$_OnlyOne for EFI: $BOOT_PART\n" 1 elif [[ $BOOT_PART == "$AUTO_BOOT_PART" ]]; then BOOT_PART="$AUTO_BOOT_PART" return 0 # were done here else BOOT_PART="$(menubox "$_PrepMount" "$_SelUefiBody" $PARTS)" fi [[ $BOOT_PART ]] || return 1 if grep -q 'fat' <<< "$(fsck -N "$BOOT_PART")"; then local msg="$_FormUefiBody $BOOT_PART $_FormUefiBody2" if yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Skip Formatting" "no"; then format "$BOOT_PART" "vfat" sleep 1 fi else format "$BOOT_PART" "vfat" sleep 1 fi return 0 } select_boot_partition() { tput civis if [[ $AUTO_BOOT_PART == "$BOOT_PART" ]]; then BOOT_PART="$AUTO_BOOT_PART" return 0 # were done here else BOOT_PART="$(menubox "$_PrepMount" "$_SelBiosBody" "$_Skip" "-" $PARTS)" [[ $BOOT_PART == "" || $BOOT_PART == "$_Skip" ]] && { BOOT_PART=""; return 0; } fi if grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")"; then local msg="$_FormBiosBody $BOOT_PART $_FormBiosBody2" if yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Skip Formatting" "no"; then format "$BOOT_PART" "ext4" sleep 1 fi else format "$BOOT_PART" "ext4" sleep 1 fi return 0 } select_root_partition() { tput civis if (( COUNT == 1 )); then ROOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_PrepMount" "$_OnlyOne for root (/): $ROOT_PART\n" 1 elif ! ROOT_PART="$(menubox "$_PrepMount" "$_SelRootBody" $PARTS)"; then return 1 fi select_filesystem "$ROOT_PART" || { ROOT_PART=""; return 1; } mount_partition "$ROOT_PART" || { ROOT_PART=""; return 1; } return 0 } select_extra_partitions() { while (( COUNT > 0 )); do tput civis local part part="$(menubox "$_PrepMount " "$_ExtPartBody" "$_Done" "Return to the last menu" $PARTS)" if [[ $part == "$_Done" || $part == "" ]]; then break elif ! select_filesystem "$part"; then break; return 1 elif ! select_mountpoint; then break; return 1 elif ! mount_partition "$part" "$EXTRA_MNT"; then break; return 1 fi EXTRA_MNTS="$EXTRA_MNTS $part: $EXTRA_MNT" [[ $EXTRA_MNT == '/usr' && $HOOKS != *usr* ]] && HOOKS="usr $HOOKS" done return 0 } ############################################################################### # installation install() { clear tput cnorm install_base printf "Generating system /etc/fstab\n" genfstab -U $MNT >$MNT/etc/fstab 2>$ERR errshow 1 "genfstab -U $MNT >$MNT/etc/fstab" [[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" $MNT/etc/fstab mirrorlist_sort package_operations run_mkinitcpio install_bootloader chrun "hwclock --systohc --utc" || chrun "hwclock --systohc --utc --directisa" create_user login_manager chrun "chown -Rf $NEWUSER:users /home/$NEWUSER" edit_configs } install_base() { if [[ -e /run/archiso/sfs/airootfs/etc/skel ]]; then rsync -ahv /run/archiso/sfs/airootfs/ $MNT/ 2>$ERR errshow 1 "rsync -ahv /run/archiso/sfs/airootfs/ $MNT/" else mirrorlist_sort pacstrap $MNT base $KERNEL $UCODE $(grep -hv '^#' /usr/share/archlabs/installer/packages.txt) 2>$ERR errshow 1 "pacstrap $MNT base $KERNEL $UCODE $(grep -hv '^#' /usr/share/archlabs/installer/packages.txt)" fi printf "Removing archiso remains\n" rm -rf $MNT/etc/mkinitcpio-archiso.conf find $MNT/usr/lib/initcpio -name 'archiso*' -type f -exec rm -rf '{}' \; sed -i 's/volatile/auto/g' $MNT/etc/systemd/journald.conf if [[ $VM ]]; then printf "Removing xorg configs in /etc/X11/xorg.conf.d/ to avoid conflict in VMs\n" rm -rfv $MNT/etc/X11/xorg.conf.d/*?.conf sleep 1 elif [[ $(lspci | grep ' VGA ' | grep 'Intel') != "" ]]; then printf "Creating intel GPU 'TearFree' config in /etc/X11/xorg.conf.d/20-intel.conf\n" cat >$MNT/etc/X11/xorg.conf.d/20-intel.conf < $MNT/etc/locale.conf << EOF LANG=$LOCALE EOF cat > $MNT/etc/default/locale << EOF LANG=$LOCALE EOF sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${LOCALE}/${LOCALE}/g" $MNT/etc/locale.gen chrun "locale-gen" printf "Setting timezone: $ZONE/$SUBZONE\n" chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZONE /etc/localtime" if [[ $BROADCOM_WL == true ]]; then printf "Blacklisting modules for broadcom wireless: bmca\n" echo 'blacklist bcma' >> $MNT/etc/modprobe.d/blacklist.conf rm -f $MNT/etc/modprobe/ fi printf "Creating keyboard configurations for keymap: $KEYMAP\n" cat > $MNT/etc/X11/xorg.conf.d/00-keyboard.conf < $MNT/etc/default/keyboard < $MNT/etc/vconsole.conf < $MNT/etc/hostname << EOF $HOSTNAME EOF cat > $MNT/etc/hosts << EOF 127.0.0.1 localhost 127.0.1.1 $HOSTNAME ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters EOF } create_user() { printf "Creating user $NEWUSER, setting passwords, and setting shell\n" chrun "chpasswd <<< 'root:$ROOT_PASS'" 2>$ERR errshow 1 "Setting root password" if [[ $MYSHELL != *zsh ]]; then chrun "usermod -s $MYSHELL root" 2>$ERR errshow 1 "Setting root shell" if [[ $MYSHELL == "/usr/bin/mksh" ]]; then cp -fv $MNT/etc/skel/.mkshrc /root/.mkshrc fi fi local groups='audio,autologin,floppy,log,network,rfkill,scanner,storage,optical,power,wheel' chrun "groupadd -r autologin" chrun "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" 2>$ERR errshow 1 "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" 2>$ERR errshow 1 "Setting new users password" if [[ $USER_PKGS == *neovim* ]]; then mkdir -p $MNT/home/$NEWUSER/.config/nvim cp -fv $MNT/home/$NEWUSER/.vimrc $MNT/home/$NEWUSER/.config/nvim/init.vim cp -rfv $MNT/home/$NEWUSER/.vim/colors $MNT/home/$NEWUSER/.config/nvim/colors fi [[ $INSTALL_WMS == *dwm* ]] && suckless_install [[ $LOGIN_WM =~ (startkde|gnome-session) ]] && sed -i '/super/d' $HOME/.xprofile /root/.xprofile return 0 } setup_xinit() { if [[ -e $MNT/home/$NEWUSER/.xinitrc ]]; then sed -i "s/openbox-session/${LOGIN_WM}/g" $MNT/home/$NEWUSER/.xinitrc else printf "exec $LOGIN_WM\n" > $MNT/home/$NEWUSER/.xinitrc fi # automatic startx for login shells local loginrc case $MYSHELL in "/bin/bash") loginrc=".bash_profile" ;; "/usr/bin/mksh") loginrc=".profile" cat >> $MNT/home/$NEWUSER/.mkshrc << EOF # colors in less (manpager) export LESS_TERMCAP_mb=$'\e[01;31m' export LESS_TERMCAP_md=$'\e[01;31m' export LESS_TERMCAP_me=$'\e[0m' export LESS_TERMCAP_se=$'\e[0m' export LESS_TERMCAP_so=$'\e[01;44;33m' export LESS_TERMCAP_ue=$'\e[0m' export LESS_TERMCAP_us=$'\e[01;32m' export EDITOR=vim export MANWIDTH=100 # source shell configs for f in "\$HOME/.mksh/"*?.sh; do . "\$f" done al-info EOF ;; *) loginrc=".zprofile" esac if ! [[ ${EDIT_FILES[login]} =~ $loginrc ]]; then # add the shell login file to the edit list after install EDIT_FILES[login]+=" /home/$NEWUSER/$loginrc" fi if [[ $AUTOLOGIN == true ]]; then sed -i "s/root/${NEWUSER}/g" $SERVICE/autologin.conf cat > $MNT/home/$NEWUSER/$loginrc << EOF # ~/$loginrc # sourced by $(basename $MYSHELL) when used as a login shell # automatically run startx when logging in on tty1 [[ ! \$DISPLAY && \$XDG_VTNR -eq 1 ]] && exec startx -- vt1 EOF else rm -rf $SERVICE rm -rf $MNT/home/$NEWUSER/.{profile,zprofile,bash_profile} fi } setup_lightdm() { rm -rf $SERVICE rm -rf $MNT/home/$NEWUSER/.{xinitrc,profile,zprofile,bash_profile} chrun 'systemctl set-default graphical.target && systemctl enable lightdm.service' 2>$ERR errshow 1 "systemctl set-default graphical.target && systemctl enable lightdm.service" cat > $MNT/etc/lightdm/lightdm-gtk-greeter.conf << EOF # LightDM GTK+ Configuration [greeter] active-monitor=0 default-user-image=/usr/share/icons/ArchLabs-Dark/64x64/places/distributor-logo-archlabs.png background=/usr/share/backgrounds/archlabs/archlabs.jpg theme-name=Adwaita-dark icon-theme-name=Adwaita font-name=DejaVu Sans Mono 11 position=30%,end 50%,end EOF } login_manager() { printf "Setting up $LOGIN_TYPE\n" SERVICE="$MNT/etc/systemd/system/getty@tty1.service.d" # remove welcome message sed -i '/printf/d' $MNT/root/.zshrc # remove unneeded shell files from installation case $MYSHELL in "/bin/bash") rm -rf $MNT/home/$NEWUSER/.{zsh,mksh}* $MNT/root/.{zsh,mksh}* ;; "/usr/bin/mksh") rm -rf $MNT/home/$NEWUSER/.{zsh,bash}* $MNT/home/$NEWUSER/.inputrc $MNT/root/.{zsh,bash}* $MNT/root/.inputrc ;; "/usr/bin/zsh") rm -rf $MNT/home/$NEWUSER/.{bash,mksh}* $MNT/home/$NEWUSER/.inputrc $MNT/root/.{bash,mksh}* $MNT/root/.inputrc ;; esac if [[ $LOGIN_TYPE == 'lightdm' ]]; then setup_lightdm else setup_xinit fi } run_mkinitcpio() { local add="" [[ $LUKS && $LUKS_PASS && $SYS == 'UEFI' && $BOOTLDR == 'grub' ]] && luks_keyfile [[ $LUKS ]] && add="encrypt" [[ $LVM ]] && { [[ $add ]] && add+=" lvm2" || add+="lvm2"; } sed -i "s/block filesystems/block ${add} filesystems ${HOOKS}/g" $MNT/etc/mkinitcpio.conf chrun "mkinitcpio -p $KERNEL" 2>$ERR errshow 1 "mkinitcpio -p $KERNEL" } mirrorlist_sort() { printf "Sorting the mirrorlist\n" if hash reflector >/dev/null 2>&1; then $MIRROR_CMD --save $MNT/etc/pacman.d/mirrorlist --verbose || reflector --score 100 -l 50 -f 10 --sort rate --verbose --save $MNT/etc/pacman.d/mirrorlist else { eval $MIRROR_CMD || curl -s 'https://www.archlinux.org/mirrorlist/all/'; } | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -v -t -n 10 - > $MNT/etc/pacman.d/mirrorlist fi } package_operations() { local rmpkg="archlabs-installer" local inpkg="$BASE_PKGS $PACKAGES $USER_PKGS" [[ $MYSHELL == *mksh* ]] && inpkg+=" mksh" [[ $BOOTLDR == 'grub' ]] && inpkg+=" grub" [[ $KERNEL == 'linux-lts' ]] && { inpkg+=" linux-lts"; rmpkg+=" linux"; } [[ $INSTALL_WMS =~ (openbox|bspwm|i3-gaps|dwm) ]] && inpkg+="$WM_BASE_PKGS" [[ $INSTALL_WMS =~ ^(plasma|gnome|cinnamon)$ ]] || inpkg+=" archlabs-ksuperkey" chrun "pacman -Syyu --noconfirm" chrun "pacman -Rns $rmpkg --noconfirm" chrun "pacman -S iputils --noconfirm" chrun "pacman -S $inpkg --needed --noconfirm" sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers return 0 } suckless_install() { # install and setup dwm printf "Installing and setting up dwm\n" mkdir -pv $MNT/home/$NEWUSER/suckless for i in dwm dmenu st; do if chrun "git clone https://bitbucket.org/natemaia/$i /home/$NEWUSER/suckless/$i"; then chrun "cd /home/$NEWUSER/suckless/$i; rm -f config.h; make clean install; make clean" else printf "Failed to clone $i repo\n" fi done if [[ -d /home/$NEWUSER/suckless/dwm ]]; then printf "To configure dwm edit /home/$NEWUSER/suckless/dwm/config.h\n" printf "You can then recompile it with 'sudo make clean install'\n" sleep 2 fi } ############################################################################### # bootloader setup setup_boot() { tput civis BOOTLDR="$(menubox "$_PrepMount" "$_MntBootBody" ${BOOTLDRS[$SYS]})" [[ $BOOTLDR ]] || return 1 if [[ $BOOT_PART != "" ]]; then mount_partition "$BOOT_PART" "${BMNTS[$SYS-$BOOTLDR]}" && SEP_BOOT=true || return 1 setup_boot_device fi setup_${BOOTLDR} || return 1 } setup_grub() { local efidir="/sys/firmware/efi" # grub has by far the worst setup of the three however # the configuration is shorter due to grub-mkconfig EDIT_FILES[bootloader]="/etc/default/grub" if [[ $SYS == 'BIOS' ]]; then [[ $BOOT_DEVICE ]] || { select_device 'boot' || return 1; } BCMDS[grub]="${BCMDS[grub]} --target=i386-pc $BOOT_DEVICE" else if [[ $ROOT_PART == */dev/mapper/* && ! $LVM && ! $LUKS_PASS ]]; then luks_pass "$_LuksOpen" "" || return 1 fi BCMDS[grub]="mount -t efivarfs efivarfs $efidir/efivars || true && ${BCMDS[grub]} --bootloader-id=$DIST" grep -q $efidir/efivars <<< "$(mount)" || mount -t efivarfs efivarfs $efidir/efivars fi BCMDS[grub]="mkdir -p /run/udev /run/lvm && mount --bind /hostrun/udev /run/udev && mount --bind /hostrun/lvm /run/lvm && ${BCMDS[grub]} && grub-mkconfig -o /boot/grub/grub.cfg && sleep 1 && umount /run/udev /run/lvm" return 0 } setup_syslinux() { EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg" } setup_systemd-boot() { EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf" } prerun_grub() { local cfg="$MNT/etc/default/grub" sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g; s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" $cfg if [[ $LUKS_DEV ]]; then sed -i "s~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g; s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g" $cfg fi if [[ $SYS == 'BIOS' && $LVM && $SEP_BOOT == false ]]; then sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $cfg fi # needed for grub-probe module to work properly in the chroot # once the grub install is done these will be umounted and removed mkdir -p /run/lvm mkdir -p /run/udev mkdir -p $MNT/hostrun/lvm mkdir -p $MNT/hostrun/udev mount --bind /run/lvm $MNT/hostrun/lvm mount --bind /run/udev $MNT/hostrun/udev return 0 } prerun_systemd-boot() { # no LVM then systemd-boot uses PARTUUID [[ $ROOT_PART =~ /dev/mapper ]] || ROOT_PART_ID="PART$ROOT_PART_ID" # create the boot entry configs mkdir -p ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/entries cat > ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/loader.conf << EOF default $DIST timeout 5 editor no EOF cat > ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/entries/${DIST}.conf << EOF title $DIST Linux linux /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd %s" "/${UCODE}.img") initrd /initramfs-$KERNEL.img options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw EOF cat > ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/entries/${DIST}-fallback.conf << EOF title $DIST Linux Fallback linux /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd %s" "/${UCODE}.img") initrd /initramfs-$KERNEL-fallback.img options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw EOF # add pacman hook to update the bootloader when systemd receives an update mkdir -p $MNT/etc/pacman.d/hooks cat > $MNT/etc/pacman.d/hooks/systemd-boot.hook << EOF [Trigger] Type = Package Operation = Upgrade Target = systemd [Action] Description = Updating systemd-boot When = PostTransaction Exec = /usr/bin/bootctl update EOF # systemd-boot requires this before running bootctl systemd-machine-id-setup --root="$MNT" return 0 } prerun_syslinux() { mkdir -pv $MNT${BMNTS[$SYS-syslinux]}/syslinux cp -rfv /usr/lib/syslinux/bios/* $MNT${BMNTS[$SYS-syslinux]}/syslinux/ cat > $cfgdir/syslinux.cfg << EOF UI menu.c32 PROMPT 0 MENU TITLE $DIST Boot Menu TIMEOUT 50 DEFAULT $DIST LABEL $DIST MENU LABEL $DIST Linux LINUX ../vmlinuz-$KERNEL APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw INITRD ../initramfs-$KERNEL.img$([[ $UCODE ]] && printf "\nINITRD %s" "../${UCODE}.img") LABEL ${DIST}fallback MENU LABEL $DIST Linux Fallback LINUX ../vmlinuz-$KERNEL APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw INITRD ../initramfs-$KERNEL-fallback.img$([[ $UCODE ]] && printf "\nINITRD %s" "../${UCODE}.img") EOF return 0 } install_bootloader() { if ! [[ $ROOT_PART =~ /dev/mapper ]]; then ROOT_PART_ID="UUID=$(blkid -s PARTUUID -o value $ROOT_PART)" else ROOT_PART_ID="$ROOT_PART" fi if [[ $SYS == 'UEFI' ]]; then find ${MNT}${BMNTS[UEFI-$BOOTLDR]}/EFI/ -maxdepth 1 -mindepth 1 \ -name '[aA][rR][cC][hH][lL]abs' -type d -exec rm -rf '{}' \; >/dev/null 2>&1 find ${MNT}${BMNTS[UEFI-$BOOTLDR]}/EFI/ -maxdepth 1 -mindepth 1 \ -name '[Bb][oO][oO][tT]' -type d -exec rm -rf '{}' \; >/dev/null 2>&1 fi if [[ $BOOTLDR != 'grub' ]]; then rm -f $MNT/etc/default/grub 2>dev/null find $MNT/boot/ -name 'grub*' -exec rm -rf '{}' \; >/dev/null 2>&1 fi if [[ $BOOTLDR != 'syslinux' ]]; then find $MNT/boot/ -name 'syslinux*' -exec rm -rf '{}' \; >/dev/null 2>&1 fi prerun_$BOOTLDR printf "Installing and setting up $BOOTLDR in ${BMNTS[$SYS-$BOOTLDR]}\n" chrun "${BCMDS[$BOOTLDR]}" 2>$ERR errshow 1 "${BCMDS[$BOOTLDR]}" if [[ -d $MNT/hostrun ]]; then umount $MNT/hostrun/udev >/dev/null 2>&1 umount $MNT/hostrun/lvm >/dev/null 2>&1 rm -rf $MNT/hostrun >/dev/null 2>&1 fi if [[ $BOOTLDR == 'grub' && $SYS == 'UEFI' ]]; then local esp="${MNT}${BMNTS[$SYS-$BOOTLDR]}" mkdir -pv $esp/EFI/BOOT cp -fv $esp/EFI/$DIST/grubx64.efi $esp/EFI/BOOT/BOOTX64.EFI fi return 0 } ############################################################################### # lvm functions lvm_menu() { lvm_detect tput civis local choice choice="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PrepLVM " --menu "$_LvmMenu" 0 0 6 \ "$_LvmCreateVG" "vgcreate -f, lvcreate -L -n" \ "$_LvmDelVG" "vgremove -f" \ "$_LvMDelAll" "lvrmeove, vgremove, pvremove -f" \ "$_Back" "Return to the main menu")" case $choice in "$_LvmCreateVG") lvm_create && return 0 ;; "$_LvmDelVG") lvm_del_vg ;; "$_LvMDelAll") lvm_del_all ;; *) return 0 esac lvm_menu } lvm_detect() { PHYSICAL_VOLUMES="$(pvs -o pv_name --noheading 2>/dev/null)" VOLUME_GROUP="$(vgs -o vg_name --noheading 2>/dev/null)" VOLUMES="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)" if [[ $VOLUMES && $VOLUME_GROUP && $PHYSICAL_VOLUMES ]]; then infobox "$_PrepLVM" "$_LvmDetBody" 1 modprobe dm-mod >/dev/null 2>$ERR errshow 'modprobe dm-mod' vgscan >/dev/null 2>&1 vgchange -ay >/dev/null 2>&1 fi } lvm_show_vg() { DEL_VG="" VOL_GROUP_LIST="" for i in $(lvs --noheadings | awk '{print $2}' | uniq); do VOL_GROUP_LIST="$VOL_GROUP_LIST $i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}')" done [[ $VOL_GROUP_LIST == "" ]] && { msgbox "$_ErrTitle" "$_LvmVGErr"; return 1; } tput civis DEL_VG="$(menubox "$_PrepLVM" "$_LvmSelVGBody" $VOL_GROUP_LIST)" [[ $DEL_VG ]] } get_lv_size() { tput cnorm local ttl=" $_LvmCreateVG (LV:$VOL_COUNT) " local msg="${VOLUME_GROUP}: ${GROUP_SIZE}$GROUP_SIZE_TYPE (${VGROUP_MB}MB $_LvmLvSizeBody1).$_LvmLvSizeBody2" VOLUME_SIZE="$(getinput "$ttl" "$msg" "")" [[ $VOLUME_SIZE ]] || return 1 ERR_SIZE=0 (( ${#VOLUME_SIZE} == 0 || ${VOLUME_SIZE:0:1} == 0 )) && ERR_SIZE=1 if (( ERR_SIZE == 0 )); then local lv="$((${#VOLUME_SIZE} - 1))" for (( i=0; i= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - m)) ;; [Mm]) (( ${VOLUME_SIZE:0:$lv} >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - s)) ;; *) ERR_SIZE=1 esac fi fi fi if (( ERR_SIZE == 1 )); then msgbox "$_ErrTitle" "$_LvmLvSizeErrBody" get_lv_size || return 1 fi return 0 } lvm_volume_name() { local msg="$1" local default="volmain" (( VOL_COUNT > 1 )) && default="volextra" tput cnorm local name if ! name="$(getinput "$_LvmCreateVG (LV:$VOL_COUNT)" "$msg" "$default" nolimit)"; then return 1 fi if [[ ${name:0:1} == "/" || ${#name} -eq 0 || $name =~ \ |\' ]] || grep -q "$name" <<< "$(lsblk)"; then msgbox "$_ErrTitle" "$_LvmLvNameErrBody" lvm_volume_name "$msg" || return 1 fi VOLUME_NAME="$name" return 0 } lvm_group_name() { tput cnorm local group if ! group="$(getinput "$_LvmCreateVG" "$_LvmNameVgBody" "VolGroup" nolimit)"; then return 1 fi if [[ ${group:0:1} == "/" || ${#group} -eq 0 || $group =~ \ |\' ]] || grep -q "$group" <<< "$(lsblk)"; then msgbox "$_ErrTitle" "$_LvmNameVgErr" lvm_group_name || return 1 fi VOLUME_GROUP="$group" return 0 } lvm_extra_lvs() { while (( VOL_COUNT > 1 )); do lvm_volume_name "$_LvmLvNameBody1" || { break; return 1; } get_lv_size || { break; return 1; } lvcreate -L "$VOLUME_SIZE" "$VOLUME_GROUP" -n "$VOLUME_NAME" >/dev/null 2>$ERR errshow "lvcreate -L $VOLUME_SIZE $VOLUME_GROUP -n $VOLUME_NAME" msgbox "$_LvmCreateVG (LV:$VOL_COUNT)" "$_Done LV $VOLUME_NAME ($VOLUME_SIZE) $_LvmPvDoneBody2." ((VOL_COUNT--)) done return 0 } lvm_volume_count() { VOL_COUNT=$(dialog --cr-wrap --no-cancel --stdout \ --backtitle "$BT" --title " $_LvmCreateVG " \ --radiolist "$_LvmLvNumBody1 $VOLUME_GROUP\n$_LvmLvNumBody2" 0 0 0 \ "1" "-" off "2" "-" off "3" "-" off "4" "-" off "5" "-" off \ "6" "-" off "7" "-" off "8" "-" off "9" "-" off) [[ $VOL_COUNT ]] } lvm_partitions() { find_partitions 'part|crypt' || return 1 PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")" tput civis LVM_PARTS=($(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ --title " $_LvmCreateVG " --checklist "$_LvmPvSelBody" 0 0 0 $PARTS)) (( ${#LVM_PARTS[@]} >= 1 )) } lvm_create_group() { lvm_group_name || return 1 local msg="$_LvmPvConfBody1 $VOLUME_GROUP\n\n$_LvmPvConfBody2" while ! yesno "$_LvmCreateVG" "$msg ${LVM_PARTS[*]}\n"; do lvm_partitions || { break; return 1; } lvm_group_name || { break; return 1; } done vgcreate -f "$VOLUME_GROUP" "${LVM_PARTS[@]}" >/dev/null 2>$ERR errshow "vgcreate -f $VOLUME_GROUP ${LVM_PARTS[*]}" GROUP_SIZE=$(vgdisplay "$VOLUME_GROUP" | awk '/VG Size/ { gsub(/[^0-9.]/, "") print int($0) }') GROUP_SIZE_TYPE="$(vgdisplay "$VOLUME_GROUP" | awk '/VG Size/ { print substr($NF, 0, 1) }')" if [[ $GROUP_SIZE_TYPE == 'G' ]]; then VGROUP_MB=$((GROUP_SIZE * 1000)) else VGROUP_MB=$GROUP_SIZE fi local msg="$_LvmPvDoneBody1 $VOLUME_GROUP ($GROUP_SIZE $GROUP_SIZE_TYPE)" msgbox "$_LvmCreateVG" "$msg $_LvmPvDoneBody2\n" return 0 } lvm_create() { VOLUME_GROUP="" LVM_PARTS=() VGROUP_MB=0 umount_dir $MNT lvm_partitions || return 1 lvm_create_group || return 1 lvm_volume_count || return 1 lvm_extra_lvs || return 1 lvm_volume_name "$_LvmLvNameBody1 $_LvmLvNameBody2 (${VGROUP_MB}MB)" || return 1 lvcreate -l +100%FREE "$VOLUME_GROUP" -n "$VOLUME_NAME" >/dev/null 2>$ERR errshow "lvcreate -l +100%FREE $VOLUME_GROUP -n $VOLUME_NAME" LVM='logical volume' tput civis sleep 0.5 local msg="${_Done}$_LvmPvDoneBody1 $VOLUME_GROUP-$VOLUME_NAME (${VOLUME_SIZE:-${VGROUP_MB}MB}) $_LvmPvDoneBody2." msgbox "$_LvmCreateVG (LV:$VOL_COUNT)" "$msg\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE "${LVM_PARTS[@]}")" return 0 } lvm_del_vg() { if lvm_show_vg && yesno "$_LvmDelVG" "$_LvmDelQ"; then vgremove -f "$DEL_VG" >/dev/null 2>&1 fi return 0 } lvm_del_all() { PHYSICAL_VOLUMES="$(pvs -o pv_name --noheading 2>/dev/null)" VOLUME_GROUP="$(vgs -o vg_name --noheading 2>/dev/null)" VOLUMES="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)" if yesno "$_LvMDelAll" "$_LvmDelQ"; then for i in $VOLUMES; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1 done for i in $VOLUME_GROUP; do vgremove -f "$i" >/dev/null 2>&1 done for i in $PHYSICAL_VOLUMES; do pvremove -f "$i" >/dev/null 2>&1 done LVM='' fi return 0 } ############################################################################### # luks functions luks_menu() { tput civis local choice choice="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_PrepLUKS " \ --menu "${_LuksMenuBody}${_LuksMenuBody2}${_LuksMenuBody3}" 0 0 0 \ "$_LuksEncrypt" "cryptsetup -q luksFormat" \ "$_LuksOpen" "cryptsetup open --type luks" \ "$_LuksEncryptAdv" "cryptsetup -q -s -c luksFormat" \ "$_Back" "Return to the main menu")" case $choice in "$_LuksEncrypt") luks_default && return 0 ;; "$_LuksOpen") luks_open && return 0 ;; "$_LuksEncryptAdv") luks_keycmd && return 0 ;; *) return 0 esac luks_menu } luks_open() { LUKS_PART="" modprobe -a dm-mod dm_crypt umount_dir $MNT find_partitions 'part|crypt|lvm' || return 1 tput civis if (( COUNT == 1 )); then LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_LuksOpen" "${_OnlyOne}: $LUKS_PART\n" 1 elif ! LUKS_PART="$(menubox "$_LuksOpen" "$_LuksMenuBody" $PARTS)" || ! [[ $LUKS_PART ]]; then return 1 fi luks_pass "$_LuksOpen" "${LUKS_NAME:-cryptroot}" || return 1 infobox "$_LuksOpen" "$_LuksOpenWaitBody $LUKS_NAME $_LuksWaitBody2 $LUKS_PART\n" 0 cryptsetup open --type luks $LUKS_PART "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR errshow "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" LUKS='encrypted' luks_show return 0 } luks_pass() { local title="$1" local name="$2" local pass pass2 LUKS_PASS="" LUKS_NAME="" tput cnorm local values if [[ $name == "" ]]; then if ! values="$(dialog --stdout --no-cancel --separator ':|~|:' \ --ok-label "Submit" --backtitle "$BT" --title " $title " --insecure --mixedform \ "\nEnter the password to decrypt $ROOT_PART.\n\nThis is needed to create a keyfile." 0 0 0 \ "$_Password" 1 1 "" 1 $((${#_Password} + 2)) $COLUMNS 0 1 \ "$_Password2" 2 1 "" 2 $((${#_Password2} + 2)) $COLUMNS 0 1)"; then return 1 fi pass="$(awk -F':|~|:' '{print $1}' <<< "$values")" pass2="$(awk -F':|~|:' '{print $2}' <<< "$values")" else if ! values="$(dialog --stdout --no-cancel --separator ':|~|:' \ --ok-label "Submit" --backtitle "$BT" --title " $title " \ --insecure --mixedform "$_LuksOpenBody" 0 0 0 \ "$_Name" 1 1 "$name" 1 $((${#_Name} + 2)) $COLUMNS 0 0 \ "$_Password" 2 1 "" 2 $((${#_Password} + 2)) $COLUMNS 0 1 \ "$_Password2" 3 1 "" 3 $((${#_Password2} + 2)) $COLUMNS 0 1)"; then return 1 fi name="$(awk -F':|~|:' '{print $1}' <<< "$values")" pass="$(awk -F':|~|:' '{print $2}' <<< "$values")" pass2="$(awk -F':|~|:' '{print $3}' <<< "$values")" LUKS_NAME="$name" fi if [[ $pass == "" || "$pass" != "$pass2" ]]; then msgbox "$_ErrTitle" "$_PassErr\n$_TryAgain" luks_pass "$title" "$name" || return 1 fi LUKS_PASS="$pass" return 0 } luks_setup() { LUKS_PART="" modprobe -a dm-mod dm_crypt umount_dir $MNT find_partitions 'part|lvm' || return 1 tput civis if (( COUNT == 1 )); then LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_LuksEncrypt" "${_OnlyOne}: $LUKS_PART\n" 1 elif ! LUKS_PART="$(menubox "$_LuksEncrypt" "$_LuksEncryptBody" $PARTS)"; then return 1 elif ! luks_pass "$_LuksEncrypt" "${LUKS_NAME:-cryptroot}"; then return 1 fi return 0 } luks_default() { luks_setup || return 1 infobox "$_LuksEncrypt" "$_LuksCreateWaitBody $LUKS_NAME $_LuksWaitBody2 $LUKS_PART\n" 0 cryptsetup -q luksFormat $LUKS_PART <<< "$LUKS_PASS" 2>$ERR errshow "cryptsetup -q luksFormat $LUKS_PART" cryptsetup open $LUKS_PART "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR errshow "cryptsetup open $LUKS_PART $LUKS_NAME" LUKS='encrypted' luks_show return 0 } luks_keycmd() { if luks_setup; then tput cnorm local cipher if ! cipher="$(getinput "$_PrepLUKS" "$_LuksCipherKey" "-s 512 -c aes-xts-plain64" nolimit)"; then return 1 fi infobox "$_LuksEncryptAdv" "$_LuksCreateWaitBody $LUKS_NAME $_LuksWaitBody2 $LUKS_PART\n" 0 cryptsetup -q $cipher luksFormat $LUKS_PART <<< "$LUKS_PASS" 2>$ERR errshow "cryptsetup -q $cipher luksFormat $LUKS_PART" cryptsetup open $LUKS_PART "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR errshow "cryptsetup open $LUKS_PART $LUKS_NAME" luks_show return 0 fi return 1 } luks_show() { tput civis sleep 0.5 msgbox "$_LuksEncrypt" "${_LuksEncryptSucc}\n\n$(lsblk $LUKS_PART -o NAME,MODEL,SIZE,TYPE,FSTYPE)\n\n" } luks_keyfile() { if [[ ! -e $MNT/crypto_keyfile.bin && $LUKS_PASS && $LUKS_UUID ]]; then local n n="$(lsblk -lno NAME,UUID,TYPE | awk "/$LUKS_UUID/"' && /part|crypt|lvm/ {print $1}')" local mkkey="dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin" mkkey="$mkkey && chmod 000 /crypto_keyfile.bin" mkkey="$mkkey && cryptsetup luksAddKey /dev/$n /crypto_keyfile.bin <<< '$LUKS_PASS'" chrun "$mkkey" sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf 2>$ERR fi return 0 } ############################################################################### # helper functions ofn() { [[ $USER_PKGS == *$1* ]] && printf "on" || printf "off" } chrun() { arch-chroot $MNT /bin/bash -c "$1" } json() { curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])" } is_ssd() { local i dev=$1 # check for LVM and or LUKS for their origin devices if [[ $LUKS && ! $LVM && $dev =~ $LUKS_NAME ]]; then dev="${LUKS_PART}" elif [[ $LVM && ! $LUKS && ${#LVM_PARTS[@]} -eq 1 && ${LVM_PARTS[*]} =~ $dev ]]; then dev="${LVM_PARTS[*]}" fi dev=${dev#/dev/} [[ $dev =~ nvme ]] && dev=${dev%p[0-9]*} || dev=${dev%[0-9]*} i=$(cat /sys/block/$dev/queue/rotational 2>/dev/null) (( ${i:-1} == 0 )) } die() { (( $# >= 1 )) && local exitcode=$1 || local exitcode=0 trap - INT tput cnorm if [[ -d $MNT ]] && command cd /; then umount_dir $MNT if (( exitcode == 127 )); then umount -l /run/archiso/bootmnt sleep 0.5 systemctl -i reboot fi fi if [[ $TERM == 'linux' ]]; then # restore custom linux console 0-15 colors when not rebooting colors=("\e]P0191919" "\e]P1D15355" "\e]P2609960" "\e]P3FFCC66" "\e]P4255A9B" "\e]P5AF86C8" "\e]P62EC8D3" "\e]P7949494" "\e]P8191919" "\e]P9D15355" "\e]PA609960" "\e]PBFF9157" "\e]PC4E88CF" "\e]PDAF86C8" "\e]PE2ec8d3" "\e]PFE1E1E1" ) for col in "${colors[@]}"; do printf "$col" done fi exit $exitcode } sigint() { printf "\n^C caught, cleaning up...\n" die 1 } print4() { local str="$*" if [[ $COLUMNS -gt 110 && ${#str} -gt $((COLUMNS - 10)) ]]; then str="$(awk '{ i=2; p1=p2=p3=p4=""; p1=$1; q=int(NF / 4) for (;i<=q; i++) { p1=p1" "$i } for (;i<=q*2; i++) { p2=p2" "$i } for (;i<=q*3; i++) { p3=p3" "$i } for (;i<=NF; i++) { p4=p4" "$i } printf "%s\n %s\n %s\n %s", p1, p2, p3, p4 }' <<< "$str")" printf "%s\n" "$str" elif [[ $str ]]; then printf "%s\n" "$str" fi } system_devices() { IGNORE_DEV="$(lsblk -lno NAME,MOUNTPOINT | awk '/\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')" if [[ $IGNORE_DEV ]]; then SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')" else SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ {print "/dev/" $1 " " $2}')" fi [[ $SYS_DEVS ]] || { infobox "$_ErrTitle" "\nNo available devices...$_Exit\n"; die 1; } DEV_COUNT="$(wc -l <<< "$SYS_DEVS")" } system_identify() { local efidir="/sys/firmware/efi" if [[ $VM ]]; then UCODE="" elif grep -q 'AuthenticAMD' /proc/cpuinfo; then UCODE="amd-ucode" elif grep -q 'GenuineIntel' /proc/cpuinfo; then UCODE="intel-ucode" fi if grep -qi 'apple' /sys/class/dmi/id/sys_vendor; then modprobe -r -q efivars else modprobe -q efivarfs fi if [[ -d $efidir ]]; then SYS="UEFI" grep -q $efidir/efivars <<< "$(mount)" || mount -t efivarfs efivarfs $efidir/efivars else SYS="BIOS" fi BT="$DIST Installer - $SYS - v$VER" } load_bcm() { infobox "Broadcom Wireless Setup" "\nLoading broadcom wifi kernel modules please wait...\n" 0 rmmod wl >/dev/null 2>&1 rmmod bcma >/dev/null 2>&1 rmmod b43 >/dev/null 2>&1 rmmod ssb >/dev/null 2>&1 modprobe wl >/dev/null 2>&1 depmod -a >/dev/null 2>&1 BROADCOM_WL=true } chk_connect() { if [[ $CHECKED_NET == true ]]; then infobox "Network Connect" "\nVerifying connection\n" 1 else infobox "Network Connect" "\nChecking connection to https://www.archlinux.org\n" 1 CHECKED_NET=true fi curl -sI --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200' } net_connect() { if ! chk_connect; then if [[ $(systemctl is-active NetworkManager) == "active" ]] && hash nmtui >/dev/null 2>&1; then tput civis printf "\e]P1191919" printf "\e]P4191919" nmtui-connect printf "\e]P1D15355" printf "\e]P4255a9b" chk_connect || return 1 else return 1 fi fi return 0 } system_checks() { if [[ $(whoami) != "root" ]]; then infobox "$_ErrTitle" "$_NotRoot\n$_Exit" die 1 elif ! grep -qw 'lm' /proc/cpuinfo; then infobox "$_ErrTitle" "$_Not64Bit\n$_Exit" die 1 fi grep -q 'BCM4352' <<< "$(lspci -vnn -d 14e4:)" && load_bcm net_connect || { infobox "$_ErrTitle" "$_NoNetwork\n$_Exit" 3; die 1; } } prechecks() { if [[ $1 -ge 0 ]] && ! [[ $(lsblk -lno MOUNTPOINT) =~ $MNT ]]; then msgbox "$_ErrTitle" "$_ErrNoMount"; SEL=4; return 1 elif [[ $1 -ge 1 && ($NEWUSER == "" || $USER_PASS == "") ]]; then msgbox "$_ErrTitle" "\nYou need to create a user first.\n"; SEL=5; return 1 elif [[ $1 -ge 2 && $CONFIG_DONE != true ]]; then msgbox "$_ErrTitle" "$_ErrNoConfig"; SEL=6; return 1 fi return 0 } errshow() { local last_exit_code=$? (( last_exit_code == 0 )) && return 0 local err err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" if [[ $err ]]; then msgbox "$_ErrTitle" "\nThe command exited abnormally: $1\n\nWith the following message: $err" else msgbox "$_ErrTitle" "\nThe command exited abnormally: $1\n\nWith the no error message.\n" fi if [[ $1 == 1 ]]; then [[ -e $DBG && $TERM == 'linux' ]] && less $DBG die 1 fi } debug() { set -x exec 3>| $DBG BASH_XTRACEFD=3 DEBUG=true } umount_dir() { swapoff -a [[ -d $1 ]] && umount -R $1 >/dev/null 2>&1 return 0 } msgbox() { tput civis dialog --cr-wrap --backtitle "$BT" --title " $1 " --msgbox "$2\n" 0 0 } menubox() { local title="$1" local body="$2" shift 2 local response if ! response="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $title " --menu "$body" 0 0 0 "$@")"; then return 1 fi printf "%s" "$response" } checkbox() { local title="$1" local body="$2" shift 2 local response if ! response="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $title " --checklist "$body" 0 0 0 "$@")"; then return 1 fi printf "%s" "$response" } getinput() { local answer if [[ $# -eq 4 && $4 == 'nolimit' ]]; then answer="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $1 " --inputbox "$2" 0 0 "$3")" else answer="$(dialog --cr-wrap --max-input 63 --stdout --backtitle "$BT" --title " $1 " --inputbox "$2" 0 0 "$3")" fi local e=$? [[ $e -ne 0 || $answer == "" ]] && return 1 printf "%s" "$answer" } infobox() { local sec="$3" tput civis dialog --cr-wrap --backtitle "$BT" --title " $1 " --infobox "$2\n" 0 0 sleep ${sec:-2} } yesno() { tput civis if [[ $# -eq 5 && $5 == "no" ]]; then dialog --cr-wrap --backtitle "$BT" --defaultno --title " $1 " --yes-label "$3" --no-label "$4" --yesno "$2\n" 0 0 elif [[ $# -eq 4 ]]; then dialog --cr-wrap --backtitle "$BT" --title " $1 " --yes-label "$3" --no-label "$4" --yesno "$2\n" 0 0 else dialog --cr-wrap --backtitle "$BT" --title " $1 " --yesno "$2\n" 0 0 fi } ############################################################################### # entry point trap sigint INT for arg in "$@"; do [[ $arg =~ (--debug|-d) ]] && debug done if ! . /usr/share/archlabs/installer/lang/english.trans 2>/dev/null; then printf "Failed to source dialog text file\n" die 1 fi system_checks system_identify system_devices msgbox "$_WelTitle $DIST Installer" "$_WelBody" select_keymap || { clear; die 0; } while true; do main done