OSDN Git Service

e88e9ffab65afbed922e59c8bb41b2c9507f85c3
[pacbang-linux/installer-arch.git] / archlabs-installer
1 #!/bin/bash
2
3 # This program is free software, provided under the GNU GPL
4 # Written by Nathaniel Maia for use in Archlabs
5 # Some ideas and code reworked from other resources
6 # AIF, Cnichi, Calamares, Arch Wiki.. Credit where credit is due
7
8 VER=2.0.86
9
10 # bulk default values {
11
12 : ${DIST=ArchLabs}                 # distro name if not set
13 MNT=/mnt                           # install mountpoint
14 ANS=/tmp/ans                       # dialog answer file
15 BOOTDIR=boot                       # location to mount boot partition
16 FONT=ter-i16n                      # font used for the linux console
17 HOOKS=shutdown                     # list of additional mkinitcpio HOOKS
18 SEL=0                              # currently selected menu item
19 SYS=Unknown                        # bios type to be determined: UEFI/BIOS
20 ERR=/tmp/errlog                    # error log used internally
21 DBG=/tmp/debuglog                  # debug log when passed -d
22 RUN=/run/archiso/bootmnt/arch/boot # path for live system /boot
23 VM="$(dmesg | grep -i hypervisor)" # system running in a virtual machine
24 export DIALOGOPTS="--cr-wrap"      # see `man dialog`
25
26 BASE_PKGS="base xorg xorg-drivers sudo git gvfs gtk3 libmad libmatroska tumbler "
27 BASE_PKGS+="playerctl pulseaudio pulseaudio-alsa pavucontrol pamixer scrot xdg-user-dirs "
28 BASE_PKGS+="ffmpeg gstreamer gst-libav gst-plugins-base gst-plugins-good bash-completion xterm"
29
30 AL_BASE_PKGS="archlabs-keyring archlabs-icons archlabs-fonts archlabs-themes "
31 AL_BASE_PKGS+="archlabs-baph archlabs-wallpapers archlabs-scripts archlabs-skel-base"
32
33 WM_BASE_PKGS="arandr nitrogen polkit-gnome network-manager-applet "
34 WM_BASE_PKGS+="volumeicon xclip exo laptop-detect xdotool compton wmctrl feh "
35 WM_BASE_PKGS+="gnome-keyring dunst gsimplecal xfce4-power-manager xfce4-settings"
36
37 SYS_MEM="$(awk '/MemTotal/ {print int($2 / 1024) "M"}' /proc/meminfo)"
38 LOCALES="$(awk '/\.UTF-8/ {gsub(/# .*|#/, ""); if ($1) {print $1 " - "}}' /etc/locale.gen)"
39 CMAPS="$(find /usr/share/kbd/keymaps -name '*.map.gz' | awk '{gsub(/\.map\.gz|.*\//, ""); print $1 " - "}' | sort)"
40
41 [[ $LINES ]] || LINES=$(tput lines)
42 [[ $COLUMNS ]] || COLUMNS=$(tput cols)
43
44 # }
45
46 # commands used to install each bootloader, however most get modified during runtime {
47 declare -A BCMDS=(
48 [refind-efi]='refind-install'
49 [grub]='grub-install --recheck --force' [syslinux]='syslinux-install_update -i -a -m'
50 [efistub]='efibootmgr -v -d /dev/sda -p 1 -c -l' [systemd-boot]='bootctl --path=/boot install'
51 ) # }
52
53 # executable name for each wm/de used in ~/.xinitrc {
54 declare -A WM_SESSIONS=(
55 [dwm]='dwm' [i3-gaps]='i3' [bspwm]='bspwm' [awesome]='awesome' [plasma]='startkde' [xfce4]='startxfce4'
56 [gnome]='gnome-session' [fluxbox]='startfluxbox' [openbox]='openbox-session' [cinnamon]='cinnamon-session'
57 ) # }
58
59 # packages installed for each wm/de, most are depends of the skel packages {
60 declare -A WM_EXT=(
61 [dwm]='' [gnome]='' [cinnamon]='gnome-terminal' [plasma]='kdebase-meta'
62 [awesome]='archlabs-skel-awesome' [bspwm]='archlabs-skel-bspwm' [fluxbox]='archlabs-skel-fluxbox'
63 [i3-gaps]='archlabs-skel-i3-gaps' [openbox]='archlabs-skel-openbox' [xfce4]='archlabs-skel-xfce4 xfce4-goodies'
64 ) # }
65
66 # files offered for editing after install is complete {
67 declare -A EDIT_FILES=(
68 [login]='' # login is populated once we know the username and shell
69 [fstab]='/etc/fstab' [sudoers]='/etc/sudoers' [crypttab]='/etc/crypttab' [pacman]='/etc/pacman.conf'
70 [console]='/etc/vconsole.conf' [mkinitcpio]='/etc/mkinitcpio.conf' [hostname]='/etc/hostname /etc/hosts'
71 [bootloader]="/boot/loader/entries/$DIST.conf"  # ** based on bootloader
72 [locale]='/etc/locale.conf /etc/default/locale' [keyboard]='/etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard'
73 ) # }
74
75 # mkfs command flags for filesystem formatting {
76 declare -A FS_CMD_FLAGS=(
77 [f2fs]='' [jfs]='-q' [xfs]='-f' [ntfs]='-q' [ext2]='-q' [ext3]='-q' [ext4]='-q' [vfat]='-F32' [nilfs2]='-q' [reiserfs]='-q'
78 ) # }
79
80 # mount options for each filesystem {
81 declare -A FS_OPTS=(
82 [vfat]='' [ntfs]='' [ext2]='' [ext3]=''  # NA
83 [jfs]='discard errors=continue errors=panic nointegrity'
84 [reiserfs]='acl nolog notail replayonly user_xattr off'
85 [ext4]='discard dealloc nofail noacl relatime noatime nobarrier nodelalloc'
86 [xfs]='discard filestreams ikeep largeio noalign nobarrier norecovery noquota wsync'
87 [nilfs2]='discard nobarrier errors=continue errors=panic order=relaxed order=strict norecovery'
88 [f2fs]='discard fastboot flush_merge data_flush inline_xattr inline_data noinline_data inline_dentry no_heap noacl nobarrier norecovery noextent_cache disable_roll_forward disable_ext_identify'
89 ) # }
90
91 # packages installed for each login option {
92 declare -A LOGIN_PKGS=(
93 [xinit]='xorg-xinit' [ly]='archlabs-ly' [gdm]='gdm' [sddm]='sddm'
94 [lightdm]='lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice'
95 ) # }
96
97 # extras installed for user selected packages {
98 # if a package requires additional packages that aren't already dependencies
99 # they can be added here eg. [package]="extra"
100 declare -A PKG_EXT=(
101 [vlc]='qt4' [mpd]='mpc' [mupdf]='mupdf-tools'
102 [rxvt-unicode]='urxvt-pearls' [zathura]='zathura-pdf-poppler' [noto-fonts]='noto-fonts-emoji' [cairo-dock]='cairo-dock-plug-ins' [qt5ct]='qt5-styleplugins'
103 [vlc]='qt5ct qt5-styleplugins' [qutebrowser]='qt5ct qt5-styleplugins' [qbittorrent]='qt5ct qt5-styleplugins' [transmission-qt]='qt5ct qt5-styleplugins'
104 [bluez]='bluez-libs bluez-utils bluez-tools bluez-plugins bluez-hid2hci' [kdenlive]='kdebase-meta dvdauthor frei0r-plugins breeze breeze-gtk qt5ct qt5-styleplugins'
105 ) # }
106
107 # dialog text variables {
108 # Basics (somewhat in order)
109 _welcome="\nThis will help you get $DIST installed and setup on your system.\n\nIf you are unsure about a section the default option will be listed or\nthe first selected item will be the default.\n\n\nMenu Navigation:\n\n - Select items with the arrow keys or the option number.\n - Use [Space] to toggle check boxes and [Enter] to accept.\n - Switch between fields using [Tab] or the arrow keys.\n - Use [Page Up] and [Page Down] to jump whole pages\n - Press the highlighted key of an option to select it.\n"
110 _keymap="\nPick which keymap to use for the system from the list below\n\nThis is used once a graphical environment is running (Xorg).\n\nSystem default: us"
111 _vconsole="\nSelect the console keymap, the console is the tty shell you reach before starting a graphical environment (Xorg).\n\nIts keymap is seperate from the one used by the graphical environments, though many do use the same such as 'us' English.\n\nSystem default: us"
112 _device="\nSelect a device to use from the list below.\n\nDevices (/dev) are the available drives on the system. /sda, /sdb, /sdc ..."
113 _resize="\nSelect a new filesystem size in MB, a new partition will be created from the free space but will be left unformatted.\nThe lowest size is just enough to fit the currently in use space on the partition while the default is set to split the free space evenly.\n\nUse Tab or the arrow keys move the cursor between the buttons and the value, when the cursor is on the value, you can edit it by:\n\n - left/right cursor movement to select a digit to modify\n - +/-  characters to increment/decrement the digit by one\n - 0 through 9 to set the digit to the given value\n\nSome keys are also recognized in all cursor positions:\n\n - Home/End set the value to its maximum or minimum\n - Pageup/Pagedown increment the value so that the slider moves by one column."
114 _mount="\nUse [Space] to toggle mount options from below, press [Enter] when done to confirm selection.\n\nNot selecting any and confirming will run an automatic mount."
115 _warn="\nIMPORTANT:\n\nChoose carefully when editing, formatting, and mounting partitions or your DATA MAY BE LOST.\n\nTo mount a partition without formatting it, select 'skip' when prompted to choose a filesystem during the mounting stage.\nThis can only be used for partitions that already contain a filesystem and cannot be the root (/) partition, it needs to be formatted before install.\n"
116 _part="\nFull device auto partitioning is available for beginners otherwise cfdisk is recommended.\n\n  - All systems will require a root partition (8G or greater).\n  - UEFI or BIOS using LUKS without LVM require a separate boot partition (100-512M)."
117 _uefi="\nSelect the EFI boot partition (/boot), required for UEFI boot.\n\nIt's usually the first partition on the device, 100-512M, and will be formatted as vfat/fat32 if not already."
118 _bios="\nDo you want to use a separate boot partition? (optional)\n\nIt's usually the first partition on the device, 100-512M, and will be formatted as ext3/4 if not already."
119 _biosluks="\nSelect the boot partition (/boot), required for LUKS.\n\nIt's usually the first partition on the device, 100-512M, and will be formatted as ext3/4 if not already."
120 _format="is already formatted correctly.\n\nFor a clean install, previously existing partitions should be reformatted, however this removes ALL data (bootloaders) on the partition so choose carefully.\n\nDo you want to reformat the partition?\n"
121 _swapsize="\nEnter the size of the swapfile in megabytes (M) or gigabytes (G).\n\neg. 100M will create a 100 megabyte swapfile, while 10G will create a 10 gigabyte swapfile.\n\nFor ease of use and as an example it is filled in to match the size of your system memory (RAM).\n\nMust be greater than 1, contain only whole numbers, and end with either M or G."
122 _expart="\nYou can now choose any additional partitions you want mounted, you'll be asked for a mountpoint after.\n\nSelect 'done' to finish the mounting step and begin unpacking the base system in the background."
123 _exmnt="\nWhere do you want the partition mounted?\n\nEnsure the name begins with a slash (/).\nExamples include: /usr, /home, /var, etc."
124 _user="\nEnter a name and password for the new user account.\n\nThe name must not use capital letters, contain any periods (.), end with a hyphen (-), or include any colons (:)\n\nNOTE: Use [Up], [Down], or [Tab] to switch between fields, and [Enter] to accept."
125 _hostname="\nEnter a hostname for the new system.\n\nA hostname is used to identify systems on the network.\n\nIt's restricted to alphanumeric characters (a-z, A-Z, 0-9).\nIt can contain hyphens (-) BUT NOT at the beggining or end."
126 _locale="\nLocale determines the system language and currency formats.\n\nThe format for locale names is languagecode_COUNTRYCODE\n\neg. en_US is: english United States\n    en_GB is: english Great Britain"
127 _timez="\nThe time zone is used to set the system clock.\n\nSelect your country or continent from the list below"
128 _timesubz="\nSelect the nearest city to you or one with the same time zone.\n\nTIP: Pressing the first letter of the city name repeatedly will navigate between entries beggining with that letter."
129 _sessions="\nUse [Space] to toggle available sessions, use [Enter] to accept the selection and continue.\n\nA basic package set will be installed for compatibility and functionality."
130 _login="\nSelect which of your session choices to use for the initial login.\n\nYou can be change this later by editing your ~/.xinitrc"
131 _autologin="\nDo you want autologin enabled for USER?\n\nIf so the following two files will be created (disable autologin by removing them):\n\n - /home/USER/RC (run startx when logging in on tty1)\n - /etc/systemd/system/getty@tty1.service.d/autologin.conf (login USER without password)\n"
132 _packages="\nUse [Space] to move a package into the selected area and press [Enter] to accept the selection.\n\nPackages may be installed by your DE/WM (if any), or for the packages you select."
133 _edit="\nBefore exiting you can select configuration files to review/change.\n\nIf you need to make other changes with the drives still mounted, use Ctrl-z to pause the installer, when finished type 'fg' and [Enter] to resume the installer, if you want to avoid the automatic reboot using Ctrl-c will cleanly exit."
134
135 # LUKS
136 _luksnew="Basic LUKS Encryption"
137 _luksadv="Advanced LUKS Encryption"
138 _luksopen="Open Existing LUKS Partition"
139 _luksmenu="\nA seperate boot partition without encryption or logical volume management (LVM) is required (except BIOS systems using grub).\n\nBasic uses the default encryption settings, and is recommended for beginners. Advanced allows cypher and key size parameters to be entered manually."
140 _luksomenu="\nEnter a name and password for the encrypted device.\n\nIt is not necessary to prefix the name with /dev/mapper/,an example has been provided."
141 _lukskey="Once the specified flags have been amended, they will automatically be used with the 'cryptsetup -q luksFormat /dev/...' command.\n\nNOTE: Do not specify any additional flags such as -v (--verbose) or -y (--verify-passphrase)."
142
143 # LVM
144 _lvmmenu="\nLogical volume management (LVM) allows 'virtual' hard drives (volume groups) and partitions (logical volumes) to be created from existing device partitions.\n\nA volume group must be created first, then one or more logical volumes within it.\n\nLVM can also be used with an encrypted partition to create multiple logical volumes (e.g. root and home) within it."
145 _lvmnew="Create Volume Group and Volume(s)"
146 _lvmdel="Delete an Existing Volume Group"
147 _lvmdelall="Delete ALL Volume Group(s) and Volume(s)"
148 _lvmvgname="\nEnter a name for the volume group (VG) being created from the partition(s) selected."
149 _lvmlvname="\nEnter a name for the logical volume (LV) being created.\n\nThis is similar to setting a label for a partition."
150 _lvmlvsize="\nEnter what size you want the logical volume (LV) to be in megabytes (M) or gigabytes (G).\n\neg. 100M will create a 100 megabyte volume, 10G will create a 10 gigabyte volume."
151 _lvmdelask="\nConfirm deletion of volume group(s) and logical volume(s).\n\nDeleting a volume group, will delete all logical volumes within it.\n"
152
153 # Errors
154 _errexpart="\nCannot mount partition due to a problem with the mountpoint.\n\nEnsure it begins with a slash (/) followed by atleast one character.\n"
155 _errpart="\nYou need create the partiton(s) first.\n\n\nBIOS systems require at least one partition (ROOT).\n\nUEFI systems require at least two (ROOT and EFI).\n"
156 _lukserr="\nA minimum of two partitions are required for encryption:\n\n 1. root (/) - standard or LVM.\n 2. boot (/boot) - standard (unless using LVM on BIOS systems).\n"
157 _lvmerr="\nThere are no viable partitions available to use for LVM, a minimum of one is required.\n\nIf LVM is already in use, deactivating it will allow the partition(s) to be used again.\n"
158 _lvmerrvgname="\nInvalid name entered.\n\nThe volume group name may be alpha-numeric, but may not contain spaces, start with a '/', or already be in use.\n"
159 _lvmerlvname="\nInvalid name entered.\n\nThe logical volume (LV) name may be alpha-numeric, but may not contain spaces or be preceded with a '/'\n"
160 _lvmerrlvsize="\nInvalid value Entered.\n\nMust be a numeric value with 'M' (megabytes) or 'G' (gigabytes) at the end.\n\neg. 400M, 10G, 250G, etc...\n\nThe value may also not be equal to or greater than the remaining size of the volume group.\n"
161
162 # }
163
164 ###############################################################################
165 # selection menus
166 # main is the entry point which calls functions including outside of its block
167 # once those functions finished they always are returned here with the
168 # exception of install_main(), it exits upon completion
169
170 main()
171 {
172         (( SEL < 12 )) && (( SEL++ ))
173         tput civis
174         dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " Prepare " --default-item $SEL --cancel-label 'Exit' --menu "$_prep" 0 0 0 \
175                 1 "Device tree (optional)" \
176                 2 "Partitioning (optional)" \
177                 3 "LUKS setup (optional)" \
178                 4 "LVM setup (optional)" \
179                 5 "Mount partitions" \
180                 6 "System bootloader" \
181                 7 "User and password" \
182                 8 "System configuration" \
183                 9 "Select WM/DE (optional)" \
184                 10 "Select Packages (optional)" \
185                 11 "View configuration (optional)" \
186                 12 "Start the installation" 2>"$ANS"
187
188         read -r SEL < "$ANS"
189         [[ -z $WARN && $SEL =~ (2|5) ]] && { msg "Data Warning" "$_warn"; WARN=true; }
190         case $SEL in
191                 1) part_show ;;
192                 2) part_menu || (( SEL-- )) ;;
193                 3) luks_menu || (( SEL-- )) ;;
194                 4) lvm_menu || (( SEL-- )) ;;
195                 5) mount_menu || (( SEL-- )) ;;
196                 6) prechecks 0 && { select_boot || (( SEL-- )); } ;;
197                 7) prechecks 1 && { select_mkuser || (( SEL-- )); } ;;
198                 8) prechecks 2 && { select_config || (( SEL-- )); } ;;
199                 9) prechecks 3 && { select_sessions || (( SEL-- )); } ;;
200                 10) prechecks 3 && { select_packages || (( SEL-- )); } ;;
201                 11) prechecks 3 && select_show ;;
202                 12) prechecks 3 && install_main ;;
203                 *) yesno "Exit" "\nUnmount partitions (if any) and exit the installer?\n" && die 0
204         esac
205 }
206
207 select_boot()
208 {
209         if [[ $SYS == 'BIOS' ]]; then
210                 dlg BOOTLDR menu "BIOS Bootloader" "\nSelect which bootloader to use." \
211                         "grub"     "The Grand Unified Bootloader, standard among many Linux distributions" \
212                         "syslinux" "A collection of boot loaders for booting drives, CDs, or over the network" || return 1
213         else
214                 dlg BOOTLDR menu "UEFI Bootloader" "\nSelect which bootloader to use." \
215                         "systemd-boot" "A simple UEFI boot manager which executes configured EFI images" \
216                         "grub"         "The Grand Unified Bootloader, standard among many Linux distributions" \
217                         "refind-efi"   "A UEFI boot manager that aims to be platform neutral and simplify multi-boot" \
218                         "efistub"      "Boot the kernel image directly (no chainloading support)" \
219                         "syslinux"     "A collection of boot loaders for booting drives, CDs, or over the network (no chainloading support)" || return 1
220         fi
221         setup_${BOOTLDR}
222 }
223
224 select_show()
225 {
226         local pkgs="${USER_PKGS//  / } ${PACKAGES//  / }"
227         [[ $INSTALL_WMS == *dwm* ]] && pkgs="dwm st dmenu $pkgs"
228         pkgs="${pkgs//  / }" pkgs="${pkgs# }"
229         msg "Show Configuration" "
230 ---------- PARTITION CONFIGURATION ------------
231
232   Root Part:      $ROOT_PART
233   Boot Part:      ${BOOT_PART:-none}
234   Boot Device:    ${BOOT_DEV:-none}
235   Swap Part/File: ${SWAP_PART:-none}
236   Swap Size:      ${SWAP_SIZE:-none}
237   Extra Mounts:   ${EXMNTS:-none}
238   Mkinit Hooks:   ${HOOKS:-none}
239
240   LVM used:   ${LVM:-none}
241   LUKS used:  ${LUKS:-none}
242
243 ------------ SYSTEM CONFIGURATION -------------
244
245   Locale:   ${MYLOCALE:-none}
246   Keymap:   ${KEYMAP:-none}
247   Hostname: ${MYHOST:-none}
248   Timezone: ${ZONE:-none}/${SUBZ:-none}
249
250 ------------ USER CONFIGURATION ---------------
251
252   Username:      ${NEWUSER:-none}
253   Login Shell:   ${MYSHELL:-none}
254   Login Session: ${LOGIN_WM:-none}
255   Autologin:     ${AUTOLOGIN:-none}
256   Login Type:    ${LOGIN_TYPE:-none}
257
258 ----------- PACKAGE CONFIGURATION -------------
259
260   Kernel:     ${KERNEL:-none}
261   Bootloader: ${BOOTLDR:-none}
262   Packages:   ${pkgs:-none}
263 "
264 }
265
266 select_login()
267 {
268         [[ $INSTALL_WMS ]] || return 0
269
270         AUTOLOGIN='' # no autologin unless using xinit
271
272         dlg LOGIN_TYPE menu "Login Management" "\nSelect what kind of login management to use." \
273                 "xinit"   "Console login without a display manager" \
274                 "ly"      "TUI display manager with a ncurses-like interface" \
275                 "lightdm" "Lightweight display manager with a gtk greeter" \
276                 "gdm"     "Gnome display manager" \
277                 "sddm"    "Simple desktop display manager" || return 1
278
279         case $LOGIN_TYPE in
280                 ly) EDIT_FILES[login]="/etc/ly/config.ini" ;;
281                 gdm|sddm) EDIT_FILES[login]="" ;;
282                 lightdm) EDIT_FILES[login]="/etc/lightdm/lightdm.conf /etc/lightdm/lightdm-gtk-greeter.conf" ;;
283                 xinit) EDIT_FILES[login]="/home/$NEWUSER/.xinitrc /home/$NEWUSER/.xprofile"
284                         if (( $(wc -w <<< "$INSTALL_WMS") > 1 )); then
285                                 dlg LOGIN_WM menu "Login Management" "$_login" $LOGIN_CHOICES || return 1
286                                 LOGIN_WM="${WM_SESSIONS[$LOGIN_WM]}"
287                         fi
288                         [[ -z $LOGIN_WM ]] && LOGIN_WM="${WM_SESSIONS[${INSTALL_WMS%% *}]}"
289                         yesno "Autologin" "$(sed "s|USER|$NEWUSER|g; s|RC|$LOGINRC|g" <<< "$_autologin")" && AUTOLOGIN=true || AUTOLOGIN=''
290                         ;;
291         esac
292 }
293
294 select_config()
295 {
296         typeset -i i=0
297         CONFIG_DONE=''
298
299         until [[ $CONFIG_DONE ]]; do
300                 case $i in
301                         0) dlg MYSHELL menu "Shell" "\nChoose which shell to use." \
302                                         zsh  'A very advanced and programmable command interpreter (shell) for UNIX' \
303                                         bash 'The GNU Bourne Again shell, standard in many GNU/Linux distributions' \
304                                         mksh 'The MirBSD Korn Shell - an enhanced version of the public domain ksh' || return 1
305                                 ;;
306                         1) dlg MYHOST input "Hostname" "$_hostname" "${DIST,,}" limit || { i=0; continue; } ;;
307                         2) dlg MYLOCALE menu "Locale" "$_locale" $LOCALES || { i=1; continue; } ;;
308                         3) ZONE='' SUBZ=''
309                                 until [[ $ZONE && $SUBZ ]]; do
310                                         dlg ZONE menu "Timezone" "$_timez" America - Australia - Asia - Atlantic - Africa - Europe - Indian - Pacific - Arctic - Antarctica - || break
311                                         dlg SUBZ menu "Timezone" "$_timesubz" $(awk '/'"$ZONE"'\// {gsub(/'"$ZONE"'\//, ""); print $3 " - "}' /usr/share/zoneinfo/zone.tab | sort) || continue
312                                 done
313                                 [[ $ZONE && $SUBZ ]] || { i=2; continue; } ;;
314                         4) dlg KERNEL menu "Kernel" "\nChoose which kernel to use." \
315                                         linux          'Vanilla linux kernel and modules, with a few patches applied' \
316                                         linux-lts      'Long-term support (LTS) linux kernel and modules' \
317                                         linux-zen      'A effort of kernel hackers to provide the best kernel for everyday systems' \
318                                         linux-hardened 'A security-focused linux kernel with hardening patches to mitigate exploits' || { i=3; continue; }
319                                 CONFIG_DONE=true
320                                 ;;
321                 esac
322                 (( i++ )) # progress through to the next choice
323         done
324
325         case $MYSHELL in
326                 bash) LOGINRC='.bash_profile' ;;
327                 zsh) LOGINRC='.zprofile' ;;
328                 mksh) LOGINRC='.profile' ;;
329         esac
330
331         return 0
332 }
333
334 select_mkuser()
335 {
336         NEWUSER=''
337         typeset -a ans
338
339         until [[ $NEWUSER ]]; do
340                 tput cnorm
341                 dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " User " --mixedform "$_user" 0 0 0 \
342                         "Username:"  1 1 "${ans[0]}" 1 11 "$COLUMNS" 0 0 \
343                         "Password:"  2 1 ''          2 11 "$COLUMNS" 0 1 \
344                         "Password2:" 3 1 ''          3 12 "$COLUMNS" 0 1 \
345                         "--- Root password, if left empty the user password will be used ---" 6 1 '' 6 68 "$COLUMNS" 0 2 \
346                         "Password:"  8 1 ''          8 11 "$COLUMNS" 0 1 \
347                         "Password2:" 9 1 ''          9 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
348
349                 mapfile -t ans <"$ANS"
350
351                 # root passwords empty, so use the user passwords
352                 if [[ -z "${ans[4]}" && -z "${ans[5]}" ]]; then
353                         ans[4]="${ans[1]}"
354                         ans[5]="${ans[2]}"
355                 fi
356
357                 # make sure a username was entered and that the passwords match
358                 if [[ -z ${ans[0]} || ${ans[0]} =~ \ |\' || ${ans[0]} =~ [^a-z0-9] ]]; then
359                         msg "Invalid Username" "\nInvalid user name.\n\nPlease try again.\n"; u=''
360                 elif [[ -z "${ans[1]}" || "${ans[1]}" != "${ans[2]}" ]]; then
361                         msg "Password Mismatch" "\nThe user passwords do not match.\n\nPlease try again.\n"
362                 elif [[ "${ans[4]}" != "${ans[5]}" ]]; then
363                         msg "Password Mismatch" "\nThe root passwords do not match.\n\nPlease try again.\n"
364                 else
365                         NEWUSER="${ans[0]}"
366                         USER_PASS="${ans[1]}"
367                         ROOT_PASS="${ans[4]}"
368                 fi
369         done
370         return 0
371 }
372
373 select_keymap()
374 {
375         dlg KEYMAP menu "Keyboard Layout" "$_keymap" \
376                 us English    cm    English     gb English    au English    gh English \
377                 za English    ng    English     ca French    'cd' French    gn French \
378                 tg French     fr    French      de German     at German     ch German \
379                 es Spanish    latam Spanish     br Portuguese pt Portuguese ma Arabic \
380                 sy Arabic     ara   Arabic      ua Ukrainian  cz Czech      ru Russian \
381                 sk Slovak     nl    Dutch       it Italian    hu Hungarian  cn Chinese \
382                 tw Taiwanese  vn    Vietnamese  kr Korean     jp Japanese   th Thai \
383                 la Lao        pl    Polish      se Swedish    is Icelandic 'fi' Finnish \
384                 dk Danish     be    Belgian     in Indian     al Albanian   am Armenian \
385                 bd Bangla     ba    Bosnian    'bg' Bulgarian dz Berber     mm Burmese \
386                 hr Croatian   gr    Greek       il Hebrew     ir Persian    iq Iraqi \
387                 af Afghani    fo    Faroese     ge Georgian   ee Estonian   kg Kyrgyz \
388                 kz Kazakh     lt    Lithuanian  mt Maltese    mn Mongolian  ro Romanian \
389                 no Norwegian  rs    Serbian     si Slovenian  tj Tajik      lk Sinhala \
390                 tr Turkish    uz    Uzbek       ie Irish      pk Urdu      'mv' Dhivehi \
391                 np Nepali     et    Amharic     sn Wolof      ml Bambara    tz Swahili \
392                 ke Swahili    bw    Tswana      ph Filipino   my Malay      tm Turkmen \
393                 id Indonesian bt    Dzongkha    lv Latvian    md Moldavian mao Maori \
394                 by Belarusian az    Azerbaijani mk Macedonian kh Khmer     epo Esperanto \
395                 me Montenegrin || return 1
396
397         if [[ $CMAPS == *"$KEYMAP"* ]]; then
398                 CMAP="$KEYMAP"
399         else
400                 dlg CMAP menu "Console Keymap" "$_vconsole" $CMAPS || return 1
401         fi
402
403         if [[ $TERM == 'linux' ]]; then
404                 loadkeys "$CMAP" >/dev/null 2>&1
405         else
406                 setxkbmap "$KEYMAP" >/dev/null 2>&1
407         fi
408
409         return 0
410 }
411
412 select_sessions()
413 {
414         LOGIN_CHOICES=''
415         dlg INSTALL_WMS check "Sessions" "$_sessions\n" \
416                 openbox "A lightweight, powerful, and highly configurable stacking wm" "$(ofn openbox "${INSTALL_WMS[*]}")" \
417                 i3-gaps "A fork of i3wm with more features including gaps" "$(ofn i3-gaps "${INSTALL_WMS[*]}")" \
418                 dwm "A dynamic WM for X that manages windows in tiled, floating, or monocle layouts" "$(ofn dwm "${INSTALL_WMS[*]}")" \
419                 bspwm "A tiling wm that represents windows as the leaves of a binary tree" "$(ofn bspwm "${INSTALL_WMS[*]}")" \
420                 xfce4 "A lightweight and modular desktop environment based on gtk+2/3" "$(ofn xfce4 "${INSTALL_WMS[*]}")" \
421                 awesome "A customized Awesome WM session created by @elanapan" "$(ofn awesome "${INSTALL_WMS[*]}")" \
422                 fluxbox "A lightweight and highly-configurable window manager" "$(ofn fluxbox "${INSTALL_WMS[*]}")" \
423                 plasma "A kde software project currently comprising a full desktop environment" "$(ofn plasma "${INSTALL_WMS[*]}")" \
424                 gnome "A desktop environment that aims to be simple and easy to use" "$(ofn gnome "${INSTALL_WMS[*]}")" \
425                 cinnamon "A desktop environment combining traditional desktop with modern effects" "$(ofn cinnamon "${INSTALL_WMS[*]}")"
426
427         [[ $INSTALL_WMS ]] || return 0
428
429         WM_PKGS="${INSTALL_WMS/dwm/}" # remove dwm from package list
430         WM_PKGS="${WM_PKGS//  / }"    # remove double spaces
431         WM_PKGS="${WM_PKGS# }"        # remove leading space
432
433         for i in $INSTALL_WMS; do
434                 LOGIN_CHOICES+="$i - "
435                 [[ ${WM_EXT[$i]} && $WM_PKGS != *"${WM_EXT[$i]}"* ]] && WM_PKGS+=" ${WM_EXT[$i]}"
436         done
437
438         select_login || return 1
439
440         while IFS=' ' read -r pkg; do
441                 [[ $PACKAGES != *"$pkg"* ]] && PACKAGES+=" $pkg"
442         done <<< "$WM_PKGS"
443
444         return 0
445 }
446
447 select_packages()
448 {
449         dlg USER_PKGS check " Packages " "$_packages" \
450                 abiword "A Fully-featured word processor" "$(ofn abiword "${USER_PKGS[*]}")" \
451                 alacritty "A cross-platform, GPU-accelerated terminal emulator" "$(ofn alacritty "${USER_PKGS[*]}")" \
452                 atom "An open-source text editor developed by GitHub" "$(ofn atom "${USER_PKGS[*]}")" \
453                 audacious "A free and advanced audio player based on GTK+" "$(ofn audacious "${USER_PKGS[*]}")" \
454                 audacity "A program that lets you manipulate digital audio waveforms" "$(ofn audacity "${USER_PKGS[*]}")" \
455                 blueman "GUI bluetooth device manager" "$(ofn blueman "${USER_PKGS[*]}")" \
456                 bluez "Simple CLI based bluetooth support" "$(ofn bluez "${USER_PKGS[*]}")" \
457                 cairo-dock "Light eye-candy fully themable animated dock" "$(ofn cairo-dock "${USER_PKGS[*]}")" \
458                 calligra "A set of applications for productivity" "$(ofn calligra "${USER_PKGS[*]}")" \
459                 chromium "An open-source web browser based on the Blink rendering engine" "$(ofn chromium "${USER_PKGS[*]}")" \
460                 clementine "A modern music player and library organizer" "$(ofn clementine "${USER_PKGS[*]}")" \
461                 cmus "A small, fast and powerful console music player" "$(ofn cmus "${USER_PKGS[*]}")" \
462                 deadbeef "A GTK+ audio player for GNU/Linux" "$(ofn deadbeef "${USER_PKGS[*]}")" \
463                 deluge "A BitTorrent client written in python" "$(ofn deluge "${USER_PKGS[*]}")" \
464                 emacs "An extensible, customizable, self-documenting real-time display editor" "$(ofn emacs "${USER_PKGS[*]}")" \
465                 epiphany "A GNOME web browser based on the WebKit rendering engine" "$(ofn epiphany "${USER_PKGS[*]}")" \
466                 evince "A document viewer" "$(ofn evince "${USER_PKGS[*]}")" \
467                 evolution "Manage your email, contacts and schedule" "$(ofn evolution "${USER_PKGS[*]}")" \
468                 file-roller "Create and modify archives" "$(ofn file-roller "${USER_PKGS[*]}")" \
469                 firefox "A popular open-source web browser from Mozilla" "$(ofn firefox "${USER_PKGS[*]}")" \
470                 gcolor2 "A simple GTK+2 color selector" "$(ofn gcolor2 "${USER_PKGS[*]}")" \
471                 geany "A fast and lightweight IDE" "$(ofn geany "${USER_PKGS[*]}")" \
472                 geary "A lightweight email client for the GNOME desktop" "$(ofn geary "${USER_PKGS[*]}")" \
473                 gimp "GNU Image Manipulation Program" "$(ofn gimp "${USER_PKGS[*]}")" \
474                 gnome-calculator "GNOME Scientific calculator" "$(ofn gnome-calculator "${USER_PKGS[*]}")" \
475                 gnome-disk-utility "Disk Management Utility" "$(ofn gnome-disk-utility "${USER_PKGS[*]}")" \
476                 gnome-system-monitor "View current processes and monitor system state" "$(ofn gnome-system-monitor "${USER_PKGS[*]}")" \
477                 gparted "A GUI frontend for creating and manipulating partition tables" "$(ofn gparted "${USER_PKGS[*]}")" \
478                 gpick "Advanced color picker using GTK+ toolkit" "$(ofn gpick "${USER_PKGS[*]}")" \
479                 gpicview "Lightweight image viewer" "$(ofn gpicview "${USER_PKGS[*]}")" \
480                 guvcview "Capture video from camera devices" "$(ofn guvcview "${USER_PKGS[*]}")" \
481                 hexchat "A popular and easy to use graphical IRC client" "$(ofn hexchat "${USER_PKGS[*]}")" \
482                 inkscape "Professional vector graphics editor" "$(ofn inkscape "${USER_PKGS[*]}")" \
483                 irssi "Modular text mode IRC client" "$(ofn irssi "${USER_PKGS[*]}")" \
484                 kdenlive "A popular non-linear video editor for Linux" "$(ofn kdenlive "${USER_PKGS[*]}")" \
485                 krita "Edit and paint images" "$(ofn krita "${USER_PKGS[*]}")" \
486                 libreoffice-fresh "Full featured office suite" "$(ofn libreoffice-fresh "${USER_PKGS[*]}")" \
487                 lollypop "A new music playing application" "$(ofn lollypop "${USER_PKGS[*]}")" \
488                 mousepad "A simple text editor" "$(ofn mousepad "${USER_PKGS[*]}")" \
489                 mpd "A flexible, powerful, server-side application for playing music" "$(ofn mpd "${USER_PKGS[*]}")" \
490                 mpv "A media player based on mplayer" "$(ofn mpv "${USER_PKGS[*]}")" \
491                 mupdf "Lightweight PDF and XPS viewer" "$(ofn mupdf "${USER_PKGS[*]}")" \
492                 mutt "Small but very powerful text-based mail client" "$(ofn mutt "${USER_PKGS[*]}")" \
493                 nautilus "The default file manager for Gnome" "$(ofn nautilus "${USER_PKGS[*]}")" \
494                 ncmpcpp "A mpd client and almost exact clone of ncmpc with some new features" "$(ofn ncmpcpp "${USER_PKGS[*]}")" \
495                 neovim "A fork of Vim aiming to improve user experience, plugins, and GUIs." "$(ofn neovim "${USER_PKGS[*]}")" \
496                 noto-fonts "Google Noto fonts" "$(ofn noto-fonts "${USER_PKGS[*]}")" \
497                 noto-fonts-cjk "Google Noto CJK fonts (Chinese, Japanese, Korean)" "$(ofn noto-fonts-cjk "${USER_PKGS[*]}")" \
498                 obs-studio "Free opensource streaming/recording software" "$(ofn obs-studio "${USER_PKGS[*]}")" \
499                 openshot "An open-source, non-linear video editor for Linux" "$(ofn openshot "${USER_PKGS[*]}")" \
500                 opera "A Fast and secure, free of charge web browser from Opera Software" "$(ofn opera "${USER_PKGS[*]}")" \
501                 pcmanfm "A fast and lightweight file manager based in Lxde" "$(ofn pcmanfm "${USER_PKGS[*]}")" \
502                 pidgin "Multi-protocol instant messaging client" "$(ofn pidgin "${USER_PKGS[*]}")" \
503                 plank "An elegant, simple, and clean dock" "$(ofn plank "${USER_PKGS[*]}")" \
504                 qbittorrent "An advanced BitTorrent client" "$(ofn qbittorrent "${USER_PKGS[*]}")" \
505                 qpdfview "A tabbed PDF viewer" "$(ofn qpdfview "${USER_PKGS[*]}")" \
506                 qt5ct "GUI for managing Qt based application themes, icons, and fonts" "$(ofn qt5ct "${USER_PKGS[*]}")" \
507                 qutebrowser "A keyboard-focused vim-like web browser based on Python and PyQt5" "$(ofn qutebrowser "${USER_PKGS[*]}")" \
508                 rhythmbox "A Music playback and management application" "$(ofn rhythmbox "${USER_PKGS[*]}")" \
509                 rxvt-unicode "A unicode enabled rxvt-clone terminal emulator" "$(ofn rxvt-unicode "${USER_PKGS[*]}")" \
510                 sakura "A terminal emulator based on GTK and VTE" "$(ofn sakura "${USER_PKGS[*]}")" \
511                 simple-scan "Simple scanning utility" "$(ofn simple-scan "${USER_PKGS[*]}")" \
512                 simplescreenrecorder "A feature-rich screen recorder" "$(ofn simplescreenrecorder "${USER_PKGS[*]}")" \
513                 steam "A popular game distribution platform by Valve" "$(ofn steam "${USER_PKGS[*]}")" \
514                 surf "A simple web browser based on WebKit2/GTK+" "$(ofn surf "${USER_PKGS[*]}")" \
515                 terminator "Terminal emulator that supports tabs and grids" "$(ofn terminator "${USER_PKGS[*]}")" \
516                 termite "A minimal VTE-based terminal emulator" "$(ofn termite "${USER_PKGS[*]}")" \
517                 thunar "A modern file manager for the Xfce Desktop Environment" "$(ofn thunar "${USER_PKGS[*]}")" \
518                 thunderbird "Standalone mail and news reader from mozilla" "$(ofn thunderbird "${USER_PKGS[*]}")" \
519                 tilda "A GTK based drop down terminal for Linux and Unix" "$(ofn tilda "${USER_PKGS[*]}")" \
520                 tilix "A tiling terminal emulator for Linux using GTK+ 3" "$(ofn tilix "${USER_PKGS[*]}")" \
521                 transmission-cli "Free BitTorrent client CLI" "$(ofn transmission-cli "${USER_PKGS[*]}")" \
522                 transmission-gtk "GTK+ Front end for transmission" "$(ofn transmission-gtk "${USER_PKGS[*]}")" \
523                 transmission-qt "Qt Front end for transmission" "$(ofn transmission-qt "${USER_PKGS[*]}")" \
524                 ttf-anonymous-pro "A family fixed-width fonts designed with code in mind" "$(ofn ttf-anonymous-pro "${USER_PKGS[*]}")" \
525                 ttf-fira-code "Monospaced font with programming ligatures" "$(ofn ttf-fira-code "${USER_PKGS[*]}")" \
526                 ttf-font-awesome "Iconic font designed for Bootstrap" "$(ofn ttf-font-awesome "${USER_PKGS[*]}")" \
527                 ttf-hack "A hand groomed typeface based on Bitstream Vera Mono" "$(ofn ttf-hack "${USER_PKGS[*]}")" \
528                 vlc "A free and open source cross-platform multimedia player" "$(ofn vlc "${USER_PKGS[*]}")" \
529                 weechat "Fast, light and extensible IRC client" "$(ofn weechat "${USER_PKGS[*]}")" \
530                 xapps "Common library for X-Apps project" "$(ofn xapps "${USER_PKGS[*]}")" \
531                 xarchiver "A GTK+ frontend to various command line archivers" "$(ofn xarchiver "${USER_PKGS[*]}")" \
532                 xed "A small and lightweight text editor. X-Apps Project." "$(ofn xed "${USER_PKGS[*]}")" \
533                 xfce4-terminal "A terminal emulator based in the Xfce Desktop Environment" "$(ofn xfce4-terminal "${USER_PKGS[*]}")" \
534                 xreader "Document viewer for files like PDF and Postscript. X-Apps Project." "$(ofn xed "${USER_PKGS[*]}")" \
535                 zathura "Minimalistic document viewer" "$(ofn zathura "${USER_PKGS[*]}")"
536
537         if [[ $USER_PKGS ]]; then    # add any needed PKG_EXT to the list
538                 for i in $USER_PKGS; do
539                         [[ ${PKG_EXT[$i]} && $USER_PKGS != *"${PKG_EXT[$i]}"* ]] && USER_PKGS+=" ${PKG_EXT[$i]}"
540                 done
541         fi
542
543         return 0
544 }
545
546 ###############################################################################
547 # partitioning menus
548 # non-essential partitioning helpers called by the user when using the optional
549 # partition menu and selecting a device to edit
550
551 part_menu()
552 {
553         no_bg_install || return 0
554         local device choice devhash
555         devhash="$(lsblk -f | base64)"
556         umount_dir "$MNT"
557         part_device || return 1
558         device="$DEVICE"
559
560         while :; do
561                 choice=""
562                 dlg choice menu "Edit Partitions" "$_part\n\n$(lsblk -no NAME,MODEL,SIZE,TYPE,FSTYPE $device)" \
563                         "auto"   "Whole device automatic partitioning" \
564                         "shrink" "Shrink an existing ext or ntfs partition" \
565                         "cfdisk" "Curses based variant of fdisk" \
566                         "parted" "GNU partition editor" \
567                         "fdisk"  "Dialog-driven creation and manipulation of partitions" \
568                         "done"   "Return to the main menu"
569
570                 if [[ -z $choice || $choice == 'done' ]]; then
571                         return 0
572                 elif [[ $choice == 'shrink' ]]; then
573                         part_shrink "$device"
574                 elif [[ $choice == 'auto' ]]; then
575                         local root_size txt table boot_fs
576                         root_size=$(lsblk -lno SIZE "$device" | awk 'NR == 1 {
577                                 if ($1 ~ "G") {
578                                         sub(/G/, "")
579                                         print ($1 * 1000 - 512) / 1000 "G"
580                                 } else {
581                                         sub(/M/, "")
582                                         print ($1 - 512) "M"
583                                 }
584                         }')
585                         txt="\nWARNING:\n\nALL data on $device will be destroyed and the following partitions will be created\n\n- "
586                         if [[ $SYS == 'BIOS' ]]; then
587                                 table="msdos" boot_fs="ext4"
588                                 txt+="An $boot_fs boot partition with the boot flag enabled (512M)\n- "
589                         else
590                                 table="gpt" boot_fs="fat32"
591                                 txt+="A $boot_fs efi boot partition (512M)\n- "
592                         fi
593                         txt+="An ext4 partition using all remaining space ($root_size)\n\nDo you want to continue?\n"
594                         yesno "Auto Partition" "$txt" && part_auto "$device" "$table" "$boot_fs" "$root_size"
595                 else
596                         clear
597                         tput cnorm
598                         $choice "$device"
599                 fi
600                 if [[ $devhash != "$(lsblk -f | base64)" ]]; then
601                         msg "Probing Partitions" "\nInforming kernel of partition changes using partprobe\n" 0
602                         partprobe >/dev/null 2>&1
603                         [[ $choice == 'auto' ]] && return
604                 fi
605         done
606 }
607
608 part_show()
609 {
610         local txt
611         if [[ $IGNORE_DEV ]]; then
612                 txt="$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT | awk "!/$IGNORE_DEV/"' && /disk|part|lvm|crypt|NAME/')"
613         else
614                 txt="$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT | awk '/disk|part|lvm|crypt|NAME/')"
615         fi
616         msg "Device Tree" "\n\n$txt\n\n"
617 }
618
619 part_auto()
620 {
621         local device="$1" table="$2" boot_fs="$3" size="$4" dev_info=""
622
623         msg "Auto Partition" "\nRemoving partitions on $device and setting table to $table\n" 1
624
625         dev_info="$(parted -s "$device" print)"
626
627         swapoff -a
628         while read -r PART; do
629                 parted -s "$device" rm "$PART" >/dev/null 2>&1
630         done <<< "$(awk '/^ [1-9][0-9]?/ {print $1}' <<< "$dev_info" | sort -r)"
631
632         [[ $(awk '/Table:/ {print $3}' <<< "$dev_info") != "$table" ]] && parted -s "$device" mklabel "$table" >/dev/null 2>&1
633
634         msg "Auto Partition" "\nCreating a 512M $boot_fs boot partition.\n" 1
635         if [[ $SYS == "BIOS" ]]; then
636                 parted -s "$device" mkpart primary "$boot_fs" 1MiB 513MiB >/dev/null 2>&1
637         else
638                 parted -s "$device" mkpart ESP "$boot_fs" 1MiB 513MiB >/dev/null 2>&1
639         fi
640
641         sleep 0.5
642         BOOT_DEV="$device"
643         AUTO_BOOT_PART=$(lsblk -lno NAME,TYPE "$device" | awk 'NR==2 {print "/dev/" $1}')
644
645         if [[ $SYS == "BIOS" ]]; then
646                 mkfs.ext4 -q "$AUTO_BOOT_PART" >/dev/null 2>&1
647         else
648                 mkfs.vfat -F32 "$AUTO_BOOT_PART" >/dev/null 2>&1
649         fi
650
651         msg "Auto Partition" "\nCreating a $size ext4 root partition.\n" 0
652         parted -s "$device" mkpart primary ext4 513MiB 100% >/dev/null 2>&1
653         sleep 0.5
654         AUTO_ROOT_PART="$(lsblk -lno NAME,TYPE "$device" | awk 'NR==3 {print "/dev/" $1}')"
655         mkfs.ext4 -q "$AUTO_ROOT_PART" >/dev/null 2>&1
656         sleep 0.5
657         msg "Auto Partition" "\nProcess complete.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE "$device")\n"
658 }
659
660 part_shrink()
661 {
662         part=""
663         typeset -i size num
664         local device="$1" fs=""
665
666         part_find "${device##*/}[^ ]" || return 1
667         (( COUNT == 1 )) && part="$(awk '{print $1}' <<< "${PARTS[@]}" )"
668
669         if (( COUNT == 1 )) || dlg part menu "Resize" "\nWhich partition on $device do you want to resize?" $PARTS; then
670                 fs=$(lsblk -lno FSTYPE "$part")
671                 case "$fs" in
672                         ext*|ntfs)
673                                 msg "Resize" "\nGathering device size info.\n" 0
674                                 num="${part: -1}"
675                                 end=$(parted -s "$device" unit KiB print | awk '/^\s*'"$num"'/ {print $3}')                    # part size in KiB
676                                 devsize=$(parted -s "$device" unit KiB print | awk '/Disk '"${device//\//\\/}"':/ {print $3}') # whole device size in KiB
677                                 mount "$part" "$MNT" >/dev/null 2>&1; sleep 0.5
678                                 min=$(df --output=used --block-size=MiB "$part" | awk 'NR == 2 {print int($1) + 256}')
679                                 max=$(df --output=avail --block-size=MiB "$part" | awk 'NR == 2 {print int($1)}')
680                                 umount_dir "$MNT"
681                                 tput cnorm
682                                 if dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " Resize: $part " --rangebox "$_resize" 17 "$COLUMNS" "$min" "$max" $((max / 2)) 2>$ANS; then
683                                         size=$(< "$ANS")
684                                         size=$((size * 1024))
685                                 else
686                                         return 1
687                                 fi
688                                 clear
689                                 case "$fs" in
690                                         ntfs)
691                                                 if ntfsresize -fc "$part"; then
692                                                         ntfsresize -ff --size $(( (size * 1024) / 1000 ))k "$part" 2>$ERR # k=10^3 bytes
693                                                         errshow "ntfsresize -f -s $(( (size * 1024) / 1000 ))k $part" || return 1
694                                                 else
695                                                         msg "Resize" "\nThe ntfs partition $part cannot be resized because it is scheduled for a consistency check.\n\nTo do a consistency check in windows open command prompt as admin and run:\n\n\tchkdsk /f /r /x\n"
696                                                         return 1
697                                                 fi
698                                                 ;;
699                                         *)
700                                                 e2fsck -f "$part"; sleep 0.5
701                                                 resize2fs -f "$part" ${size}K 2>$ERR # K=2^10 bytes
702                                                 errshow "resize2fs -f $part ${size}K" || return 1
703                                                 ;;
704                                 esac
705                                 sleep 0.5
706                                 parted "$device" resizepart "$num" ${size}KiB || return 1
707                                 (( size++ ))
708                                 sleep 0.5
709                                 if [[ $devsize == "$end" ]]; then
710                                         parted -s "$device" mkpart primary ext4 ${size}KiB 100% 2>$ERR
711                                         errshow "parted -s $device mkpart primary ext4 ${size}KiB 100%" || return 1
712                                 else
713                                         parted -s "$device" mkpart primary ext4 ${size}KiB ${end}KiB 2>$ERR
714                                         errshow "parted -s $device mkpart primary ext4 ${size}KiB ${end}KiB" || return 1
715                                 fi
716                                 msg "Resize Complete" "\n$part has been successfully resized to $((size / 1024))M.\n" 1
717                                 ;;
718                         "") msg "No Filesystem" "\nFor unformatted partitions, cfdisk can be used in the partition menu.\n" ;;
719                         *) msg "Invalid Filesystem: $fs" "\nResizing only supports ext and ntfs.\n" ;;
720                 esac
721         fi
722 }
723
724 ###############################################################################
725 # partition management functions
726 # these are helpers for use by other functions to do essential setup/teardown
727
728 part_find()
729 {
730         local regexp="$1" err=''
731
732         # string of partitions as /TYPE/PART SIZE.. eg. /dev/sda1 256G
733         if [[ $IGNORE_DEV ]]; then
734                 PARTS="$(lsblk -lno TYPE,NAME,SIZE | awk "/$regexp/"' && !'"/$IGNORE_DEV/"' {sub(/^part/, "/dev/"); sub(/^lvm|^crypt/, "/dev/mapper/"); print $1$2, $3}')"
735         else
736                 PARTS="$(lsblk -lno TYPE,NAME,SIZE | awk "/$regexp/"' {sub(/^part/, "/dev/"); sub(/^lvm|^crypt/, "/dev/mapper/"); print $1$2 " " $3}')"
737         fi
738
739         # ensure we have enough partitions for the system and action were trying to do
740         COUNT=$(wc -l <<< "$PARTS")
741         case "$regexp" in
742                 'part|lvm|crypt') [[ $COUNT -lt 1 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_errpart" ;;
743                 'part|crypt') (( COUNT < 1 )) && err="$_lvmerr" ;;
744                 'part|lvm') (( COUNT < 2 )) && err="$_lukserr" ;;
745         esac
746
747         if [[ $err ]]; then
748                  msg "Not Enough Partitions" "$err" 2
749                  return 1
750         fi
751
752         return 0
753 }
754
755 part_swap()
756 {
757         if [[ $1 == "$MNT/swapfile" && $SWAP_SIZE ]]; then
758                 fallocate -l $SWAP_SIZE "$1" 2>$ERR
759                 errshow "fallocate -l $SWAP_SIZE $1"
760                 chmod 600 "$1" 2>$ERR
761                 errshow "chmod 600 $1"
762         fi
763         mkswap "$1" >/dev/null 2>$ERR
764         errshow "mkswap $1"
765         swapon "$1" >/dev/null 2>$ERR
766         errshow "swapon $1"
767         return 0
768 }
769
770 part_mount()
771 {
772         local part="$1" mountp="${MNT}$2" fs=""
773         fs="$(lsblk -lno FSTYPE "$part")"
774         mkdir -p "$mountp"
775
776         if [[ $fs && ${FS_OPTS[$fs]} && $part != "$BOOT_PART" && $part != "$AUTO_ROOT_PART" ]] && select_mntopts "$fs"; then
777                 mount -o "$MNT_OPTS" "$part" "$mountp" >/dev/null 2>&1
778         else
779                 mount "$part" "$mountp" >/dev/null 2>&1
780         fi
781
782         part_mountconf "$part" "$mountp" || return 1
783         part_cryptlv "$part"
784
785         return 0
786 }
787
788 part_format()
789 {
790         local part="$1" fs="$2" delay="$3"
791
792         msg "Format" "\nFormatting $part as $fs\n" 0
793         mkfs.$fs ${FS_CMD_FLAGS[$fs]} "$part" >/dev/null 2>$ERR
794         errshow "mkfs.$fs ${FS_CMD_FLAGS[$fs]} "$part"" || return 1
795         FORMATTED+="$part "
796         sleep $delay
797 }
798
799 part_device()
800 {
801         if [[ $DEV_COUNT -eq 1 && $SYS_DEVS ]]; then
802                 DEVICE="$(awk '{print $1}' <<< "$SYS_DEVS")"
803         elif (( DEV_COUNT > 1 )); then
804                 if [[ $1 ]]; then
805                         dlg DEVICE menu "Boot Device" "\nSelect the device to use for bootloader install." $SYS_DEVS
806                 else
807                         dlg DEVICE menu "Select Device" "$_device" $SYS_DEVS
808                 fi
809                 [[ $DEVICE ]] || return 1
810         elif [[ $DEV_COUNT -lt 1 && ! $1 ]]; then
811                 msg "Device Error" "\nNo available devices.\n\nExiting..\n" 2
812                 die 1
813         fi
814
815         [[ $1 ]] && BOOT_DEV="$DEVICE"
816
817         return 0
818 }
819
820 part_bootdev()
821 {
822         BOOT_DEV="${BOOT_PART%[1-9]}"
823         BOOT_PART_NUM="${BOOT_PART: -1}"
824         [[ $BOOT_PART = /dev/nvme* ]] && BOOT_DEV="${BOOT_PART%p[1-9]}"
825         if [[ $SYS == 'UEFI' ]]; then
826                 parted -s $BOOT_DEV set $BOOT_PART_NUM esp on >/dev/null 2>&1
827         else
828                 parted -s $BOOT_DEV set $BOOT_PART_NUM boot on >/dev/null 2>&1
829         fi
830         return 0
831 }
832
833 part_cryptlv()
834 {
835         local part="$1" devs=""
836         devs="$(lsblk -lno NAME,FSTYPE,TYPE)"
837
838         # Identify if $part is LUKS+LVM, LVM+LUKS, LVM alone, or LUKS alone
839         if lsblk -lno TYPE "$part" | grep -q 'crypt'; then
840                 LUKS='encrypted'
841                 LUKS_NAME="${part#/dev/mapper/}"
842                 for dev in $(awk '/lvm/ && /crypto_LUKS/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do
843                         if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
844                                 LUKS_DEV="$LUKS_DEV cryptdevice=$dev:$LUKS_NAME"
845                                 LVM='logical volume'
846                                 break
847                         fi
848                 done
849                 for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do
850                         if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
851                                 LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')"
852                                 LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
853                                 break
854                         fi
855                 done
856         elif lsblk -lno TYPE "$part" | grep -q 'lvm'; then
857                 LVM='logical volume'
858                 VNAME="${part#/dev/mapper/}"
859                 for dev in $(awk '/crypt/ && /lvm2_member/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do
860                         if lsblk -lno NAME "$dev" | grep -q "$VNAME"; then
861                                 LUKS_NAME="${dev/\/dev\/mapper\//}"
862                                 break
863                         fi
864                 done
865                 for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do
866                         if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
867                                 LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')"
868                                 LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
869                                 LUKS='encrypted'
870                                 break
871                         fi
872                 done
873         fi
874 }
875
876 part_countdec()
877 {
878         for pt; do
879                 if (( COUNT > 0 )); then
880                         PARTS="$(sed "/${pt//\//\\/}/d" <<< "$PARTS")"
881                         (( COUNT-- ))
882                 fi
883         done
884 }
885
886 part_mountconf()
887 {
888         if grep -qw "$1" /proc/mounts; then
889                 msg "Mount Success" "\nPartition $1 mounted at $2\n" 1
890                 part_countdec "$1"
891                 return 0
892         else
893                 msg "Mount Fail" "\nPartition $1 failed to mount at $2\n" 2
894                 return 1
895         fi
896 }
897
898 ###############################################################################
899 # mounting menus
900 # mount_menu is the entry point which calls all other functions
901 # once finished it returns to the main menu: main()
902
903 mount_menu()
904 {
905         no_bg_install || return 0
906         lvm_detect
907         umount_dir "$MNT"
908         part_find 'part|lvm|crypt' || { SEL=2; return 1; }
909         [[ $LUKS && $LUKS_PART ]] && part_countdec $LUKS_PART
910         [[ $LVM && $LVM_PARTS ]] && part_countdec $LVM_PARTS
911         select_root_partition || { ROOT_PART=''; return 1; }
912         select_boot_partition || { BOOT_PART=''; return 1; }
913         if [[ $BOOT_PART ]]; then
914                 part_mount "$BOOT_PART" "/$BOOTDIR" && SEP_BOOT=true || return 1
915                 part_bootdev
916         fi
917         select_swap || return 1
918         select_extra_partitions || return 1
919         install_background
920         return 0
921 }
922
923 select_swap()
924 {
925         local pts dev size isize
926
927         while read -r dev size; do  # walk partition list and skip ones that are too small/big for swap
928                 size_t="${size: -1:1}"
929                 isize=${size:0:-1}
930                 isize=${isize%.*}
931                 [[ $size_t =~ [KT] || ($size_t == 'G' && $isize -gt 16) || ($size_t == 'M' && $isize -lt 100) ]] || pts+="$dev $size "
932         done <<< "$PARTS"
933
934         dlg SWAP_PART menu "Swap Setup" "\nSelect whether to use a swapfile, swap partition, or none." \
935                 "none" "Don't allocate any swap space" \
936                 "swapfile" "Allocate $SYS_MEM at /swapfile" \
937                 $pts
938
939         if [[ -z $SWAP_PART || $SWAP_PART == "none" ]]; then
940                 SWAP_PART=''
941                 return 0
942         elif [[ $SWAP_PART == "swapfile" ]]; then
943                 local i=0
944                 until [[ ${SWAP_SIZE:0:1} =~ [1-9] && ${SWAP_SIZE: -1} =~ (M|G) ]]; do
945                         (( i > 0 )) && msg "Swap Size Error" "\nSwap size must be 1(M|G) or greater, and can only contain whole numbers\n\nSize entered: $SWAP_SIZE\n" 2
946                         dlg SWAP_SIZE input "Swap Setup" "$_swapsize" "$SYS_MEM" || { SWAP_PART=''; SWAP_SIZE=''; return 1; }
947                         (( i++ ))
948                 done
949                 part_swap "$MNT/$SWAP_PART"
950                 SWAP_PART="/$SWAP_PART"
951         elif [[ $PARTS == *"$SWAP_PART"* ]]; then
952                 part_swap $SWAP_PART
953                 part_countdec $SWAP_PART
954                 SWAP_SIZE="$(lsblk -lno SIZE $SWAP_PART)"
955         else
956                 return 1
957         fi
958
959         return 0
960 }
961
962 select_mntopts()
963 {
964         local fs="$1" opts=''
965         local title="${fs^} Mount Options"
966
967         for i in ${FS_OPTS[$fs]}; do
968                 opts+="$i - off "
969         done
970
971         until [[ $MNT_OPTS ]]; do
972                 dlg MNT_OPTS check "$title" "$_mount" $opts
973                 [[ $MNT_OPTS ]] || return 1
974                 MNT_OPTS="${MNT_OPTS// /,}"
975                 yesno "$title" "\nConfirm the following options: $MNT_OPTS\n" || MNT_OPTS=''
976         done
977
978         return 0
979 }
980
981 select_mountpoint()
982 {
983         EXMNT=''
984         until [[ $EXMNT ]]; do
985                 dlg EXMNT input "Extra Mount $part" "$_exmnt" "/" || return 1
986                 if [[ ${EXMNT:0:1} != "/" || ${#EXMNT} -le 1 || $EXMNT =~ \ |\' || $EXMNTS == *"$EXMNT"* ]]; then
987                         msg "Mountpoint Error" "$_errexpart"
988                         EXMNT=''
989                 fi
990         done
991         msg "Mount Extra" "\nMounting Finished\n\n\nNo extra partitions available to mount, returning to main menu.\n" 2
992         return 0
993 }
994
995 select_filesystem()
996 {
997         local part="$1" fs='' cur=''
998         local txt="\nSelect which filesystem to use for: $part\n\nDefault:  ext4"
999         cur="$(lsblk -lno FSTYPE "$part" 2>/dev/null)"
1000
1001         # bail early if the partition was created in part_auto()
1002         [[ $cur && $part == "$AUTO_ROOT_PART" ]] && return 0
1003
1004         until [[ $fs ]]; do
1005                 if [[ $cur && $FORMATTED == *"$part"* ]]; then
1006                         dlg fs menu "Filesystem" "$txt\nCurrent:  $cur" skip - ext4 - ext3 - ext2 - vfat - ntfs - f2fs - jfs - xfs - nilfs2 - reiserfs - || return 1
1007                 else
1008                         dlg fs menu "Filesystem" "$txt" ext4 - ext3 - ext2 - vfat - ntfs - f2fs - jfs - xfs - nilfs2 - reiserfs - || return 1
1009                 fi
1010                 [[ $fs == 'skip' ]] && return 0
1011                 yesno "Filesystem" "\nFormat $part as $fs?\n" || fs=''
1012         done
1013         part_format "$part" "$fs" 0
1014 }
1015
1016 select_boot_partition()
1017 {
1018         local pts dev size isize ptcount=0
1019
1020         if [[ -z $BOOT_PART ]]; then
1021                 if [[ $AUTO_BOOT_PART && -z $LVM && -z $LUKS ]]; then
1022                         BOOT_PART="$AUTO_BOOT_PART"
1023                         return 0
1024                 else
1025                         while read -r dev size; do  # walk partition list and skip ones that are too small/big for boot
1026                                 size_t="${size: -1:1}"
1027                                 isize=${size:0:-1}
1028                                 isize=${isize%.*}
1029                                 [[ $size_t =~ [KT] || ($size_t == 'G' && $isize -gt 2) || ($size_t == 'M' && $isize -lt 100) ]] || { pts+="$dev $size "; (( ptcount++ )); }
1030                         done <<< "$PARTS"
1031
1032                         case "$SYS" in
1033                                 UEFI)
1034                                         case "$ptcount" in
1035                                                 0) msg "EFI Boot Partition" "\nNo partitions available that meet size requirements!!\n\nReturning to the main menu.\n" 2; return 1 ;;
1036                                                 1) msg "EFI Boot Partition" "\nOnly one partition available that meets size requirements.\n" 1; BOOT_PART="$(awk 'NF > 0 {print $1}' <<< "$pts")" ;;
1037                                                 *) dlg BOOT_PART menu "EFI Partition" "$_uefi" $pts ;;
1038                                         esac
1039                                         [[ $BOOT_PART ]] || return 1
1040                                         ;;
1041                                 BIOS)
1042                                         (( ptcount == 0 )) && return 0
1043                                         if [[ $LUKS && ! $LVM ]]; then
1044                                                 dlg BOOT_PART menu "Boot Partition" "$_biosluks" $pts
1045                                                 [[ $BOOT_PART ]] || return 1
1046                                         else
1047                                                 dlg BOOT_PART menu "Boot Partition" "$_bios" "skip" "no separate boot" $pts
1048                                                 [[ -z $BOOT_PART || $BOOT_PART == "skip" ]] && { BOOT_PART=''; return 0; }
1049                                         fi
1050                                         ;;
1051                         esac
1052                 fi
1053         fi
1054
1055         if ([[ $SYS == 'BIOS' ]] && grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")") || ([[ $SYS == 'UEFI' ]] && grep -q 'fat' <<< "$(fsck -N "$BOOT_PART")"); then
1056                 yesno "Format Boot Partition" "\nIMPORTANT:\n\nThe boot partition $BOOT_PART $_format" "Format $BOOT_PART" "Skip Formatting" 1 || return 0
1057         fi
1058
1059         case "$SYS" in
1060                 UEFI) part_format "$BOOT_PART" "vfat" 2 || return 1 ;;
1061                 BIOS) part_format "$BOOT_PART" "ext4" 2 || return 1 ;;
1062         esac
1063         return 0
1064 }
1065
1066 select_root_partition()
1067 {
1068         if [[ -z $ROOT_PART ]]; then
1069                 if [[ $AUTO_ROOT_PART && -z $LVM && -z $LUKS ]]; then
1070                         ROOT_PART="$AUTO_ROOT_PART"
1071                         msg "Mount Menu" "\nUsing partitions created during automatic format.\n" 2
1072                         part_mount "$ROOT_PART" || { ROOT_PART=''; return 1; }
1073                         return 0  # we're done here
1074                 else
1075                         local pts dev size isize ptcount=0
1076
1077                         # walk partition list and skip ones that are too small for / (root)
1078                         while read -r dev size; do
1079                                 size_t="${size: -1:1}"  # size type eg. K, M, G, T
1080                                 isize=${size:0:-1}      # remove trailing size type character
1081                                 isize=${isize%.*}       # remove any decimal (round down)
1082                                 [[ $size_t =~ [MK] || ($size_t == 'G' && $isize -lt 4) ]] || { pts+="$dev $size "; (( ptcount++ )); }
1083                         done <<< "$PARTS"
1084
1085                         if (( ptcount == 1 )); then  # only one available device
1086                                 msg "Root Partition (/)" "\nOnly one partition available that meets size requirements.\n" 2
1087                                 ROOT_PART="$(awk 'NF > 0 {print $1}' <<< "$pts")"
1088                         else
1089                                 dlg ROOT_PART menu "Mount Root" "\nSelect the root (/) partition, this is where $DIST will be installed.\n\nDevices smaller than 8G will not be shown here." $pts
1090                         fi
1091                 fi
1092         fi
1093
1094         if [[ -z $ROOT_PART ]] || ! select_filesystem "$ROOT_PART" || ! part_mount "$ROOT_PART"; then
1095                 ROOT_PART=''
1096                 return 1
1097         fi
1098         return 0
1099 }
1100
1101 select_extra_partitions()
1102 {
1103         local part dev size
1104
1105         # walk partition list and skip ones that are too small to be usable
1106         while read -r dev size; do
1107                 [[ ${size: -1:1} =~ [KM] ]] && part_countdec "$dev"
1108         done <<< "$PARTS"
1109
1110         while (( COUNT > 0 )); do
1111                 part=''
1112                 dlg part menu 'Mount Extra' "$_expart" 'done' 'finish mounting step' $PARTS || break
1113                 if [[ $part == 'done' ]]; then
1114                         break
1115                 elif select_filesystem "$part" && select_mountpoint && part_mount "$part" "$EXMNT"; then
1116                         EXMNTS+="$part: $EXMNT "
1117                         [[ $EXMNT == '/usr' && $HOOKS != *usr* ]] && HOOKS+=" usr"
1118                 else
1119                         return 1
1120                 fi
1121         done
1122         return 0
1123 }
1124
1125 ###############################################################################
1126 # installation
1127 # main is the entry point which calls all other install functions, once
1128 # complete it shows a dialog to edit files on the new system before reboot
1129
1130 install_main()
1131 {
1132         install_base
1133         genfstab -U "$MNT" >"$MNT/etc/fstab" 2>$ERR 2>&1
1134         errshow 1 "genfstab -U $MNT >$MNT/etc/fstab"
1135         [[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" "$MNT/etc/fstab"
1136         install_packages
1137         install_mkinitcpio
1138         install_boot
1139         chrun "hwclock --systohc --utc" || chrun "hwclock --systohc --utc --directisa"
1140         install_user
1141         install_login
1142         chrun "chown -Rf $NEWUSER:users /home/$NEWUSER"
1143
1144         while :; do
1145                 dlg choice menu "Finalization" "$_edit" \
1146                         finished   "exit the installer and reboot" \
1147                         keyboard   "${EDIT_FILES[keyboard]}" \
1148                         console    "${EDIT_FILES[console]}" \
1149                         locale     "${EDIT_FILES[locale]}" \
1150                         hostname   "${EDIT_FILES[hostname]}" \
1151                         sudoers    "${EDIT_FILES[sudoers]}" \
1152                         mkinitcpio "${EDIT_FILES[mkinitcpio]}" \
1153                         fstab      "${EDIT_FILES[fstab]}" \
1154                         crypttab   "${EDIT_FILES[crypttab]}" \
1155                         bootloader "${EDIT_FILES[bootloader]}" \
1156                         pacman     "${EDIT_FILES[pacman]}" \
1157                         login      "${EDIT_FILES[login]}"
1158
1159                 if [[ -z $choice || $choice == 'finished' ]]; then
1160                         [[ $DEBUG == true && -r $DBG ]] && ${EDITOR:-vim} $DBG
1161                         clear && die 127
1162                 else
1163                         local exists=''
1164                         for f in ${EDIT_FILES[$choice]}; do
1165                                 [[ -e ${MNT}$f ]] && exists+=" ${MNT}$f"
1166                         done
1167                         if [[ $exists ]]; then
1168                                 ${EDITOR:-vim} -O $exists
1169                         else
1170                                 msg "File Missing" "\nThe file(s) selected do not exist:\n\n${EDIT_FILES[$choice]}\n"
1171                         fi
1172                 fi
1173         done
1174 }
1175
1176 install_base()
1177 {
1178         clear
1179         tput cnorm
1180         while kill -0 $BG_PID 2>/dev/null; do
1181                 clear; printf "\nA background install process is still running...\n"; sleep 1
1182         done
1183         trap - EXIT
1184         unset BG_PID
1185
1186         rm -rf "$MNT/etc/mkinitcpio-archiso.conf"
1187         find "$MNT/usr/lib/initcpio" -name 'archiso*' -type f -delete
1188         sed -i 's/#\(Storage=\)volatile/\1auto/' "$MNT/etc/systemd/journald.conf"
1189         find "$MNT/boot" -name '*-ucode.img' -delete
1190
1191         [[ $DIST != "ArchLabs" ]] || sed -i "s/ArchLabs/$DIST/g" "$MNT/etc/"{lsb-release,os-release}
1192
1193         if [[ $VM ]]; then
1194                 echo "Virtual machine detected, removing xorg configs"
1195                 find "$MNT/etc/X11/xorg.conf.d/" -name '*.conf' -delete -printf "remove %p\n"
1196         elif lspci | grep ' VGA ' | grep -q 'Intel'; then
1197                 echo "Creating Intel Tear Free config /etc/X11/xorg.conf.d/20-intel.conf"
1198                 cat > "$MNT/etc/X11/xorg.conf.d/20-intel.conf" <<- EOF
1199                 Section "Device"
1200                     Identifier  "Intel Graphics"
1201                     Driver      "intel"
1202                     Option      "TearFree" "true"
1203                 EndSection
1204                 EOF
1205         elif lspci | grep ' VGA ' | grep -q 'AMD/ATI.*RX'; then # newer RX cards can use the amdgpu driver
1206                 echo "Creating AMD Tear Free config /etc/X11/xorg.conf.d/20-amdgpu.conf"
1207                 cat > "$MNT/etc/X11/xorg.conf.d/20-amdgpu.conf" <<- EOF
1208                 Section "Device"
1209                     Identifier  "AMD Graphics"
1210                     Driver      "amdgpu"
1211                     Option      "TearFree" "true"
1212                 EndSection
1213                 EOF
1214         elif lspci | grep ' VGA ' | grep -q 'AMD/ATI.*HD [2-6][0-9]*'; then # older HD 2xxx-6xxx cards must use the radeon driver
1215                 echo "Creating Radeon Tear Free config /etc/X11/xorg.conf.d/20-radeon.conf"
1216                 cat > "$MNT/etc/X11/xorg.conf.d/20-radeon.conf" <<- EOF
1217                 Section "Device"
1218                     Identifier  "AMD Graphics"
1219                     Driver      "radeon"
1220                     Option      "TearFree" "on"
1221                 EndSection
1222                 EOF
1223         fi
1224
1225         if [[ -e /run/archiso/sfs/airootfs ]]; then
1226                 cp -vf "$RUN/x86_64/vmlinuz" "$MNT/boot/vmlinuz-linux" 2>$ERR 2>&1
1227                 errshow 1 "cp -vf $RUN/x86_64/vmlinuz $MNT/boot/vmlinuz-linux"
1228         fi
1229         [[ -d /etc/netctl ]] && cp -rfv /etc/netctl "$MNT/etc/"
1230         [[ -f /etc/resolv.conf ]] && cp -fv /etc/resolv.conf "$MNT/etc/"
1231         [[ -e /etc/NetworkManager/system-connections ]] && cp -rvf /etc/NetworkManager/system-connections "$MNT/etc/NetworkManager/"
1232
1233         echo "LANG=$MYLOCALE" > "$MNT/etc/locale.conf"
1234         cp -fv "$MNT/etc/locale.conf" "$MNT/etc/default/locale"
1235         sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${MYLOCALE}/${MYLOCALE}/g" "$MNT/etc/locale.gen"
1236         chrun "locale-gen"
1237         chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZ /etc/localtime"
1238
1239         cat > "$MNT/etc/X11/xorg.conf.d/00-keyboard.conf" <<- EOF
1240         # Use localectl(1) to instruct systemd-localed to update it.
1241         Section "InputClass"
1242             Identifier      "system-keyboard"
1243             MatchIsKeyboard "on"
1244             Option          "XkbLayout" "$KEYMAP"
1245         EndSection
1246         EOF
1247
1248         cat > "$MNT/etc/default/keyboard" <<- EOF
1249         # KEYBOARD CONFIGURATION FILE
1250         # Consult the keyboard(5) manual page.
1251         XKBMODEL=""
1252         XKBLAYOUT="$KEYMAP"
1253         XKBVARIANT=""
1254         XKBOPTIONS=""
1255         BACKSPACE="guess"
1256         EOF
1257         printf "KEYMAP=%s\nFONT=%s\n" "$CMAP" "$FONT" > "$MNT/etc/vconsole.conf"
1258         echo "$MYHOST" > "$MNT/etc/hostname"
1259         cat > "$MNT/etc/hosts" <<- EOF
1260         127.0.0.1       localhost
1261         127.0.1.1       $MYHOST
1262         ::1                     localhost ip6-localhost ip6-loopback
1263         ff02::1         ip6-allnodes
1264         ff02::2         ip6-allrouters
1265         EOF
1266 }
1267
1268 install_boot()
1269 {
1270         echo "Installing $BOOTLDR"
1271
1272         if [[ $ROOT_PART == /dev/mapper* ]]; then
1273                 ROOT_PART_ID="$ROOT_PART"
1274         else
1275                 local uuid_type="UUID"
1276                 [[ $BOOTLDR =~ (systemd-boot|refind-efi|efistub) ]] && uuid_type="PARTUUID"
1277                 ROOT_PART_ID="$uuid_type=$(blkid -s $uuid_type -o value $ROOT_PART)"
1278         fi
1279
1280         if [[ $SYS == 'UEFI' ]]; then
1281                 # remove our old install and generic BOOT/ dir
1282                 echo "Removing conflicting boot directories"
1283                 find "$MNT/$BOOTDIR/EFI/" -maxdepth 1 -mindepth 1 -iname "$DIST" -type d -delete -printf "remove %p\n"
1284                 find "$MNT/$BOOTDIR/EFI/" -maxdepth 1 -mindepth 1 -iname 'BOOT' -type d -delete -printf "remove %p\n"
1285         fi
1286
1287         prerun_$BOOTLDR
1288         chrun "${BCMDS[$BOOTLDR]}" 2>$ERR 2>&1
1289         errshow 1 "${BCMDS[$BOOTLDR]}"
1290
1291         if [[ -d $MNT/hostrun ]]; then
1292                 echo "Unmounting chroot directories"
1293                 # cleanup the bind mounts we made earlier for the grub-probe module
1294                 umount_dir "$MNT/hostrun/"{udev,lvm}
1295                 rm -rf "$MNT/hostrun" >/dev/null 2>&1
1296         fi
1297
1298         if [[ $SYS == 'UEFI' ]]; then
1299                 # some UEFI firmware requires a generic esp/BOOT/BOOTX64.EFI
1300                 mkdir -pv "$MNT/$BOOTDIR/EFI/BOOT"
1301                 case "$BOOTLDR" in
1302                         grub) cp -fv "$MNT/$BOOTDIR/EFI/$DIST/grubx64.efi" "$MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI" ;;
1303                         syslinux) cp -rf "$MNT/$BOOTDIR/EFI/syslinux/"* "$MNT/$BOOTDIR/EFI/BOOT/" && cp -f "$MNT/$BOOTDIR/EFI/syslinux/syslinux.efi" "$MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI" ;;
1304                         refind-efi) sed -i '/#extra_kernel_version_strings/ c extra_kernel_version_strings linux-hardened,linux-zen,linux-lts,linux' "$MNT/$BOOTDIR/EFI/refind/refind.conf"
1305                                 cp -fv "$MNT/$BOOTDIR/EFI/refind/refind_x64.efi" "$MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI" ;;
1306                 esac
1307         fi
1308
1309         return 0
1310 }
1311
1312 install_user()
1313 {
1314         local groups='audio,floppy,log,network,rfkill,scanner,storage,optical,power,wheel'
1315
1316         rm -f "$MNT/root/.zshrc"  # remove welcome message from root zshrc
1317
1318         chrun "chpasswd <<< 'root:$ROOT_PASS'" 2>$ERR 2>&1
1319         errshow 1 "set root password"
1320         if [[ $MYSHELL != 'zsh' ]]; then # root uses zsh by default
1321                 chrun "usermod -s /bin/$MYSHELL root" 2>$ERR 2>&1
1322                 errshow 1 "usermod -s /bin/$MYSHELL root"
1323                 # copy the default mkshrc to /root if it was selected
1324                 [[ $MYSHELL == 'mksh' ]] && cp -fv "$MNT/etc/skel/.mkshrc" "$MNT/root/.mkshrc"
1325         fi
1326
1327         echo "Creating new user $NEWUSER and setting password"
1328         chrun "useradd -m -u 1000 -g users -G $groups -s /bin/$MYSHELL $NEWUSER" 2>$ERR 2>&1
1329         errshow 1 "useradd -m -u 1000 -g users -G $groups -s /bin/$MYSHELL $NEWUSER"
1330         chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" 2>$ERR 2>&1
1331         errshow 1 "set $NEWUSER password"
1332
1333         chrun "pamixer --unmute; gtk-update-icon-cache /usr/share/icons/ArchLabs-Dark /usr/share/icons/ArchLabs-Light /usr/share/icons/ArchLabs"
1334
1335         if [[ $INSTALL_WMS == *dwm* ]];then
1336                 install_suckless "/home/$NEWUSER" chroot
1337                 [[ $INSTALL_WMS == 'dwm' ]] && rm -rf "$MNT/home/$NEWUSER/.config/xfce4"
1338         fi
1339
1340         [[ $INSTALL_WMS != *bspwm* && $INSTALL_WMS != *openbox* ]] && rm -rf "$MNT/home/$NEWUSER/.config/"{jgmenu,tint2}
1341         [[ $USER_PKGS != *geany* ]] && rm -rf "$MNT/home/$NEWUSER/.config/geany"
1342         [[ $MYSHELL != 'bash' ]] && rm -rf "$MNT/home/$NEWUSER/.bash"*
1343         [[ $MYSHELL != 'zsh' ]] && rm -rf "$MNT/home/$NEWUSER/.z"*
1344
1345         # remove some commands from ~/.xprofile when using KDE or Gnome as the login session
1346         if [[ $LOGIN_WM =~ (startkde|gnome-session) || ($LOGIN_TYPE != 'xinit' && $WM_PKGS =~ (plasma|gnome)) ]]; then
1347                 sed -i '/super/d; /nitrogen/d; /compton/d' "$MNT/home/$NEWUSER/.xprofile" "$MNT/root/.xprofile"
1348         elif [[ $LOGIN_WM == 'dwm' ]]; then # and dwm
1349                 sed -i '/super/d; /compton/d' "$MNT/home/$NEWUSER/.xprofile" "$MNT/root/.xprofile"
1350         fi
1351
1352         # create user home directories (Music, Documents, Downloads, etc..)
1353         chrun 'xdg-user-dirs-update'
1354
1355         return 0
1356 }
1357
1358 install_login()
1359 {
1360         local serv="$MNT/etc/systemd/system/getty@tty1.service.d"
1361         echo "Setting up $LOGIN_TYPE"
1362         case $LOGIN_TYPE in
1363                 ly|sddm|gdm|lightdm)
1364                         if [[ $LOGIN_WM == *dwm* ]]; then # dwm doesn't include an xsession file for display managers
1365                                 mkdir -p "$MNT/usr/share/xsessions"
1366                                 cat >"$MNT/usr/share/xsessions/dwm.desktop" <<- EOF
1367                                 [Desktop Entry]
1368                                 Encoding=UTF-8
1369                                 Name=Dwm
1370                                 Comment=Dynamic Window Manager
1371                                 Exec=dwm
1372                                 Type=XSession
1373                                 EOF
1374                         fi
1375                         rm -rf "$serv" "$MNT/home/$NEWUSER/.xinitrc"
1376                         chrun "systemctl enable $LOGIN_TYPE.service" 2>$ERR 2>&1
1377                         errshow 1 "systemctl enable $LOGIN_TYPE.service"
1378                         ${LOGIN_TYPE}_config
1379                         ;;
1380                 xinit)
1381                         if [[ $INSTALL_WMS ]]; then
1382                                 sed -i "/exec/ c exec ${LOGIN_WM}" "$MNT/home/$NEWUSER/.xinitrc"
1383                         elif [[ -e $MNT/home/$NEWUSER/.xinitrc ]]; then
1384                                 sed -i '/exec/d' "$MNT/home/$NEWUSER/.xinitrc"
1385                                 return 0
1386                         fi
1387                         if [[ $AUTOLOGIN ]]; then
1388                                 sed -i "s/root/${NEWUSER}/g" $serv/autologin.conf
1389                                 cat > "$MNT/home/$NEWUSER/$LOGINRC" <<- EOF
1390                                 # automatically run startx when logging in on tty1
1391                                 [ -z "\$DISPLAY" ] && [ \$XDG_VTNR -eq 1 ] && startx
1392                                 EOF
1393                         else
1394                                 rm -rf $serv
1395                         fi
1396                         ;;
1397         esac
1398 }
1399
1400 install_packages()
1401 {
1402         local rmpkg=""
1403         local inpkg="$PACKAGES $USER_PKGS $AL_BASE_PKGS "
1404
1405         if pacman -Qq archlabs-installer >/dev/null 2>&1; then
1406                 rmpkg+="archlabs-installer "
1407         fi
1408
1409         if [[ $MYSHELL == 'zsh' ]]; then
1410                 inpkg+="zsh-completions "
1411         else
1412                 rmpkg+="zsh "
1413         fi
1414
1415         if [[ $INSTALL_WMS == 'dwm' ]]; then # dwm only needs a very limited package set
1416                 inpkg+="nitrogen polkit-gnome gnome-keyring dunst "
1417         else
1418                 [[ $INSTALL_WMS =~ (plasma|gnome|cinnamon) ]] || inpkg+="archlabs-ksuperkey "
1419                 [[ $INSTALL_WMS =~ (openbox|bspwm|i3-gaps|fluxbox) ]] && inpkg+="$WM_BASE_PKGS "
1420         fi
1421
1422         # update and install crucial packages first to avoid issues
1423         chrun "pacman -Syyu $KERNEL $BASE_PKGS base-devel ${LOGIN_PKGS[$LOGIN_TYPE]} $MYSHELL --noconfirm --needed" 2>$ERR 2>&1
1424         errshow 1 "pacman -Syyu $KERNEL $BASE_PKGS base-devel ${LOGIN_PKGS[$LOGIN_TYPE]} $MYSHELL --noconfirm --needed"
1425
1426         # remove the packages we don't want on the installed system
1427         [[ $rmpkg ]] && chrun "pacman -Rnsc $rmpkg --noconfirm"
1428
1429         # reinstalling iputils fixes the network issue for non-root users
1430         chrun "pacman -S iputils $UCODE --noconfirm"
1431
1432         # install the packages chosen throughout the install
1433         chrun "pacman -S $inpkg --needed --noconfirm" 2>$ERR 2>&1
1434         errshow 1 "pacman -S $inpkg --needed --noconfirm"
1435
1436         # bootloader packages
1437         if [[ $BOOTLDR == 'grub' ]]; then
1438                 [[ $SYS == 'UEFI' ]] && local efib="efibootmgr"
1439                 chrun "pacman -S os-prober grub $efib --needed --noconfirm" 2>$ERR 2>&1
1440                 errshow 1 "pacman -S os-prober grub $efib --needed --noconfirm"
1441         elif [[ $BOOTLDR == 'refind-efi' ]]; then
1442                 chrun "pacman -S refind-efi efibootmgr --needed --noconfirm" 2>$ERR 2>&1
1443                 errshow 1 "pacman -S refind-efi efibootmgr --needed --noconfirm"
1444         elif [[ $SYS == 'UEFI' ]]; then
1445                 chrun "pacman -S efibootmgr --needed --noconfirm" 2>$ERR 2>&1
1446                 errshow 1 "pacman -S efibootmgr --needed --noconfirm"
1447         fi
1448
1449         # allow members of the wheel group to run commands as root
1450         sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" "$MNT/etc/sudoers"
1451
1452         return 0
1453 }
1454
1455 install_mkinitcpio()
1456 {
1457         local add=''
1458         [[ $LUKS ]] && add="encrypt"
1459         [[ $LVM ]] && { [[ $add ]] && add+=" lvm2" || add+="lvm2"; }
1460         sed -i "s/block filesystems/block ${add} filesystems ${HOOKS}/g" "$MNT/etc/mkinitcpio.conf"
1461         chrun "mkinitcpio -p $KERNEL" 2>$ERR 2>&1
1462         errshow 1 "mkinitcpio -p $KERNEL"
1463 }
1464
1465 install_mirrorlist()
1466 {
1467         if hash reflector >/dev/null 2>&1; then
1468                 reflector --verbose --score 80 -l 40 -f 5 --sort rate --save "$1"
1469         elif hash rankmirrors >/dev/null 2>&1; then
1470                 echo "Sorting mirrorlist"
1471                 local key="access_key=5f29642060ab983b31fdf4c2935d8c56"
1472                 ip_add="$(curl -fsSL "http://api.ipstack.com/check&?$key&fields=ip" | python -c "import sys, json; print(json.load(sys.stdin)['ip'])")"
1473                 country="$(curl -fsSL "http://api.ipstack.com/$ip_add?$key&fields=country_code" | python -c "import sys, json; print(json.load(sys.stdin)['country_code'])")"
1474                 if [[ "$country" ]]; then
1475                         if [[ $country =~ (CA|US) ]]; then
1476                                 # use both CA and US mirrors for CA or US countries
1477                                 mirror="https://www.archlinux.org/mirrorlist/?country=US&country=CA&use_mirror_status=on"
1478                         elif [[ $country =~ (AU|NZ) ]]; then
1479                                 # use both AU and NZ mirrors for AU or NZ countries
1480                                 mirror="https://www.archlinux.org/mirrorlist/?country=AU&country=NZ&use_mirror_status=on"
1481                         else
1482                                 mirror="https://www.archlinux.org/mirrorlist/?country=${country}&use_mirror_status=on"
1483                         fi
1484                 else # no country code so just grab all mirrors, will be a very slow sort but we don't have other options
1485                         mirror="https://www.archlinux.org/mirrorlist/?country=all&use_mirror_status=on"
1486                 fi
1487                 curl -fsSL "$mirror" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 6 - >"$1"
1488         fi
1489
1490         return 0
1491 }
1492
1493 install_background()
1494 {
1495         ( rsync -a /run/archiso/sfs/airootfs/ "$MNT/" && install_mirrorlist "$MNT/etc/pacman.d/mirrorlist" >/dev/null 2>&1 ) &
1496         BG_PID=$!
1497         trap "kill $BG_PID 2>/dev/null" EXIT
1498 }
1499
1500 install_suckless()
1501 {
1502         local dir="$1/suckless"
1503         shift
1504
1505         if [[ $2 == 'chroot' ]]; then
1506                 chrun "mkdir -pv '$dir'"
1507                 for i in dwm dmenu st; do
1508                         if chrun "git clone 'https://git.suckless.org/$i' '$dir/$i'"; then
1509                                 chrun "cd '$dir/$i' && make PREFIX=/usr install"
1510                         else
1511                                 printf "failed to clone %s repo\n" "$i"
1512                         fi
1513                 done
1514         else
1515                 mkdir -pv "$dir"
1516                 for i in dwm dmenu st; do
1517                         if git clone "https://git.suckless.org/$i" "$dir/$i"; then
1518                                 cd "$dir/$i" && make PREFIX=/usr install
1519                         else
1520                                 printf "failed to clone %s repo\n" "$i"
1521                         fi
1522                 done
1523         fi
1524 }
1525
1526 ###############################################################################
1527 # display manager config
1528 # these are called based on which DM is chosen after it is installed
1529 # additional config can be  handled here, for now only lightdm has one
1530
1531 lightdm_config()
1532 {
1533         cat > "$MNT/etc/lightdm/lightdm-gtk-greeter.conf" <<- EOF
1534         [greeter]
1535         default-user-image=/usr/share/icons/ArchLabs-Dark/64x64/places/distributor-logo-archlabs.png
1536         background=/usr/share/backgrounds/archlabs/archlabs.jpg
1537         theme-name=Adwaita-dark
1538         icon-theme-name=Adwaita
1539         font-name=DejaVu Sans Mono 11
1540         position=30%,end 50%,end
1541         EOF
1542 }
1543
1544 ly_config()
1545 {
1546         :
1547 }
1548
1549 gdm_config()
1550 {
1551         :
1552 }
1553
1554 sddm_config()
1555 {
1556         :
1557 }
1558
1559 ###############################################################################
1560 # bootloader setup
1561 # prerun_* set up the configs needed before actually running the commands
1562 # setup_* are run after selecting a bootloader and build the command used later
1563 # they can also be used for further user input as these run before control is taken away
1564
1565 setup_grub()
1566 {
1567         EDIT_FILES[bootloader]="/etc/default/grub"
1568
1569         if [[ $SYS == 'BIOS' ]]; then
1570                 [[ $BOOT_DEV ]] || { part_device 1 || return 1; }
1571                 BCMDS[grub]="grub-install --recheck --force --target=i386-pc $BOOT_DEV"
1572         else
1573                 BCMDS[grub]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1574                 grub-install --recheck --force --target=x86_64-efi --efi-directory=/$BOOTDIR --bootloader-id=$DIST"
1575                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1576         fi
1577
1578         BCMDS[grub]="mkdir -p /run/udev /run/lvm &&
1579                 mount --bind /hostrun/udev /run/udev &&
1580                 mount --bind /hostrun/lvm /run/lvm &&
1581                 ${BCMDS[grub]} &&
1582                 grub-mkconfig -o /boot/grub/grub.cfg &&
1583                 sleep 1 && umount /run/udev /run/lvm"
1584
1585         return 0
1586 }
1587
1588 prerun_grub()
1589 {
1590         sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g; s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" "$MNT/etc/default/grub"
1591
1592         if [[ $LUKS_DEV ]]; then
1593                 sed -i "s~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g; s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g" "$MNT/etc/default/grub" 2>$ERR 2>&1
1594                 errshow 1 "sed -i 's~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g; s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g' $MNT/etc/default/grub"
1595         fi
1596
1597         if [[ $SYS == 'BIOS' && $LVM && -z $SEP_BOOT ]]; then
1598                 sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" "$MNT/etc/default/grub" 2>$ERR 2>&1
1599                 errshow 1 "sed -i 's/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g' $MNT/etc/default/grub"
1600         fi
1601
1602         # setup for os-prober module
1603         mkdir -p /run/{lvm,udev} "$MNT/hostrun/"{lvm,udev}
1604         mount --bind /run/lvm "$MNT/hostrun/lvm"
1605         mount --bind /run/udev "$MNT/hostrun/udev"
1606
1607         return 0
1608 }
1609
1610 setup_efistub()
1611 {
1612         EDIT_FILES[bootloader]=""
1613 }
1614
1615 prerun_efistub()
1616 {
1617         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1618                 efibootmgr -v -d $BOOT_DEV -p $BOOT_PART_NUM -c -L '${DIST} Linux' -l /vmlinuz-${KERNEL} \
1619                 -u 'root=$ROOT_PART_ID rw $([[ $UCODE ]] && printf 'initrd=\%s.img ' "$UCODE")initrd=\initramfs-${KERNEL}.img'"
1620 }
1621
1622 setup_syslinux()
1623 {
1624         if [[ $SYS == 'BIOS' ]]; then
1625                 EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg"
1626         else
1627                 EDIT_FILES[bootloader]="/boot/EFI/syslinux/syslinux.cfg"
1628                 BCMDS[syslinux]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1629                 efibootmgr -v -c -d $BOOT_DEV -p $BOOT_PART_NUM -l /EFI/syslinux/syslinux.efi -L $DIST"
1630         fi
1631 }
1632
1633 prerun_syslinux()
1634 {
1635         local c="$MNT/boot/syslinux"
1636         local s="/usr/lib/syslinux/bios"
1637         local d=".."
1638         if [[ $SYS == 'UEFI' ]]; then
1639                  c="$MNT/boot/EFI/syslinux"
1640                  s="/usr/lib/syslinux/efi64"
1641                  d='';
1642         fi
1643         mkdir -pv "$c" 2>$ERR 2>&1
1644         errshow 1 "mkdir -pv $c"
1645         cp -rfv "$s/"* "$c/" 2>$ERR 2>&1
1646         errshow 1 "cp -rfv $s/* $c/"
1647         cp -fv "$RUN/syslinux/splash.png" "$c/" 2>$ERR 2>&1
1648         errshow 0 "cp -fv $RUN/syslinux/splash.png $c/"
1649         cat > "$c/syslinux.cfg" <<- EOF
1650         UI vesamenu.c32
1651         MENU TITLE $DIST Boot Menu
1652         MENU BACKGROUND splash.png
1653         TIMEOUT 50
1654         DEFAULT $DIST
1655
1656         # see: https://www.syslinux.org/wiki/index.php/Comboot/menu.c32
1657         MENU WIDTH 78
1658         MENU MARGIN 4
1659         MENU ROWS 4
1660         MENU VSHIFT 10
1661         MENU TIMEOUTROW 13
1662         MENU TABMSGROW 14
1663         MENU CMDLINEROW 14
1664         MENU HELPMSGROW 16
1665         MENU HELPMSGENDROW 29
1666         MENU COLOR border       30;44   #40ffffff #a0000000 std
1667         MENU COLOR title        1;36;44 #9033ccff #a0000000 std
1668         MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
1669         MENU COLOR unsel        37;44   #50ffffff #a0000000 std
1670         MENU COLOR help         37;40   #c0ffffff #a0000000 std
1671         MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
1672         MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
1673         MENU COLOR msg07        37;40   #90ffffff #a0000000 std
1674         MENU COLOR tabmsg       31;40   #30ffffff #00000000 std
1675
1676         LABEL $DIST
1677         MENU LABEL $DIST Linux
1678         LINUX $d/vmlinuz-$KERNEL
1679         APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1680         INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL.img
1681
1682         LABEL ${DIST}fallback
1683         MENU LABEL $DIST Linux Fallback
1684         LINUX $d/vmlinuz-$KERNEL
1685         APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1686         INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL-fallback.img
1687         EOF
1688         return 0
1689 }
1690
1691 setup_refind-efi()
1692 {
1693         EDIT_FILES[bootloader]="/boot/refind_linux.conf"
1694         BCMDS[refind-efi]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1; refind-install"
1695 }
1696
1697 prerun_refind-efi()
1698 {
1699         cat > "$MNT/boot/refind_linux.conf" <<- EOF
1700         "$DIST Linux"          "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1701                                                 printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1702                                                 printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL.img"
1703         "$DIST Linux Fallback" "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1704                                                 printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1705                                                 printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL-fallback.img"
1706         EOF
1707         mkdir -p "$MNT/etc/pacman.d/hooks"
1708         cat > "$MNT/etc/pacman.d/hooks/refind.hook" <<- EOF
1709         [Trigger]
1710         Operation = Upgrade
1711         Type = Package
1712         Target = refind-efi
1713
1714         [Action]
1715         Description = Updating rEFInd on ESP
1716         When = PostTransaction
1717         Exec = /usr/bin/refind-install
1718         EOF
1719 }
1720
1721 setup_systemd-boot()
1722 {
1723         EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf"
1724         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1; bootctl --path=/boot install"
1725 }
1726
1727 prerun_systemd-boot()
1728 {
1729         mkdir -p "$MNT/boot/loader/entries"
1730         cat > "$MNT/boot/loader/loader.conf" <<- EOF
1731         default  $DIST
1732         timeout  5
1733         editor   no
1734         EOF
1735         cat > "$MNT/boot/loader/entries/$DIST.conf" <<- EOF
1736         title   $DIST Linux
1737         linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1738         initrd  /initramfs-$KERNEL.img
1739         options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1740         EOF
1741         cat > "$MNT/boot/loader/entries/$DIST-fallback.conf" <<- EOF
1742         title   $DIST Linux Fallback
1743         linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1744         initrd  /initramfs-$KERNEL-fallback.img
1745         options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1746         EOF
1747         mkdir -p "$MNT/etc/pacman.d/hooks"
1748         cat > "$MNT/etc/pacman.d/hooks/systemd-boot.hook" <<- EOF
1749         [Trigger]
1750         Type = Package
1751         Operation = Upgrade
1752         Target = systemd
1753
1754         [Action]
1755         Description = Updating systemd-boot
1756         When = PostTransaction
1757         Exec = /usr/bin/bootctl update
1758         EOF
1759         systemd-machine-id-setup --root="$MNT"
1760         return 0
1761 }
1762
1763 ###############################################################################
1764 # lvm functions
1765
1766 lvm_menu()
1767 {
1768         no_bg_install || return 1
1769         lvm_detect
1770         local choice
1771         while :; do
1772                 dlg choice menu "Logical Volume Management" "$_lvmmenu" \
1773                         "$_lvmnew"    "vgcreate -f, lvcreate -L -n" \
1774                         "$_lvmdel"    "vgremove -f" \
1775                         "$_lvmdelall" "lvrmeove, vgremove, pvremove -f" \
1776                         "Back"        "Return to the main menu"
1777                 case "$choice" in
1778                         "$_lvmnew") lvm_create && break ;;
1779                         "$_lvmdel") lvm_delgroup && yesno "$_lvmdel" "$_lvmdelask" && vgremove -f "$DEL_VG" >/dev/null 2>&1 ;;
1780                         "$_lvmdelall") lvm_del_all ;;
1781                         *) break ;;
1782                 esac
1783         done
1784
1785         return 0
1786 }
1787
1788 lvm_detect()
1789 {
1790         local v pv
1791         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1792         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1793         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1794
1795         if [[ $VGROUP && $v && $pv ]]; then
1796                 msg "LVM Setup" "\nActivating existing logical volume management.\n" 0
1797                 modprobe dm-mod >/dev/null 2>$ERR
1798                 errshow 'modprobe dm-mod'
1799                 vgscan >/dev/null 2>&1
1800                 vgchange -ay >/dev/null 2>&1
1801         fi
1802 }
1803
1804 lvm_create()
1805 {
1806         VGROUP='' LVM_PARTS='' VGROUP_MB=0
1807         umount_dir "$MNT"
1808         lvm_mkgroup || return 1
1809         local txt="\nThe last (or only) logical volume will automatically use all remaining space in the volume group."
1810         dlg VOL_COUNT menu "$_lvmnew" "\nSelect the number of logical volumes (LVs) to create in: $VGROUP\n$txt" 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -
1811         [[ $VOL_COUNT ]] || return 1
1812         lvm_extra_lvs || return 1
1813         lvm_volume_name "$_lvmlvname\nNOTE: This LV will use up all remaining space in the volume group (${VGROUP_MB}MB)" || return 1
1814         msg "$_lvmnew (LV:$VOL_COUNT)" "\nCreating volume $VNAME from remaining space in $VGROUP\n" 0
1815         lvcreate -l +100%FREE "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
1816         errshow "lvcreate -l +100%FREE $VGROUP -n $VNAME" || return 1
1817         LVM='logical volume'; sleep 0.5
1818         txt="\nDone, volume: $VGROUP-$VNAME (${VOLUME_SIZE:-${VGROUP_MB}MB}) has been created.\n"
1819         msg "$_lvmnew (LV:$VOL_COUNT)" "$txt\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE $LVM_PARTS)\n"
1820         return 0
1821 }
1822
1823 lvm_lv_size()
1824 {
1825         local txt="${VGROUP}: ${SIZE}$SIZE_UNIT (${VGROUP_MB}MB remaining).$_lvmlvsize"
1826
1827         while :; do
1828                 ERR_SIZE=0
1829                 dlg VOLUME_SIZE input "$_lvmnew (LV:$VOL_COUNT)" "$txt" ''
1830                 if [[ -z $VOLUME_SIZE ]]; then
1831                         ERR_SIZE=1
1832                         break # allow bailing with escape or an empty choice
1833                 elif (( ${VOLUME_SIZE:0:1} == 0 )); then
1834                         ERR_SIZE=1 # size values can't begin with '0'
1835                 else
1836                         # walk the string and make sure all but the last char are digits
1837                         local lv=$((${#VOLUME_SIZE} - 1))
1838                         for (( i=0; i<lv; i++ )); do
1839                                 [[ ${VOLUME_SIZE:$i:1} =~ [0-9] ]] || { ERR_SIZE=1; break; }
1840                         done
1841                         if (( ERR_SIZE != 1 )); then
1842                                 case ${VOLUME_SIZE:$lv:1} in
1843                                         [mMgG]) local s=${VOLUME_SIZE:0:$lv} m=$((s * 1000))
1844                                                 case ${VOLUME_SIZE:$lv:1} in
1845                                                         [Gg]) (( m >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - m)) ;;
1846                                                         [Mm]) (( ${VOLUME_SIZE:0:$lv} >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - s)) ;;
1847                                                         *) ERR_SIZE=1
1848                                                 esac ;;
1849                                         *) ERR_SIZE=1
1850                                 esac
1851                         fi
1852                 fi
1853                 if (( ERR_SIZE )); then
1854                         msg "Invalid Logical Volume Size" "$_lvmerrlvsize"
1855                 else
1856                         break
1857                 fi
1858         done
1859
1860         return $ERR_SIZE
1861 }
1862
1863 lvm_mkgroup()
1864 {
1865         local named=''
1866
1867         until [[ $named ]]; do
1868                 lvm_partitions || return 1
1869                 lvm_group_name || return 1
1870                 yesno "$_lvmnew" "\nCreate volume group: $VGROUP\n\nusing these partition(s): $LVM_PARTS\n" && named=true
1871         done
1872
1873         msg "$_lvmnew" "\nCreating volume group: $VGROUP\n" 0
1874         vgcreate -f "$VGROUP" $LVM_PARTS >/dev/null 2>$ERR
1875         errshow "vgcreate -f $VGROUP $LVM_PARTS" || return 1
1876
1877         SIZE=$(vgdisplay "$VGROUP" | awk '/VG Size/ { gsub(/[^0-9.]/, ""); print int($0) }')
1878         SIZE_UNIT="$(vgdisplay "$VGROUP" | awk '/VG Size/ { print substr($NF, 0, 1) }')"
1879
1880         if [[ $SIZE_UNIT == 'G' ]]; then
1881                 VGROUP_MB=$((SIZE * 1000))
1882         else
1883                 VGROUP_MB=$SIZE
1884         fi
1885
1886         msg "$_lvmnew" "\nVolume group $VGROUP (${SIZE}$SIZE_UNIT) successfully created\n"
1887 }
1888
1889 lvm_del_all()
1890 {
1891         local v pv
1892         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1893         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1894         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1895
1896         if [[ $VGROUP || $v || $pv ]]; then
1897                 if yesno "$_lvmdelall" "$_lvmdelask"; then
1898                         for i in $v; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1; done
1899                         for i in $VGROUP; do vgremove -f "$i" >/dev/null 2>&1; done
1900                         for i in $pv; do pvremove -f "$i" >/dev/null 2>&1; done
1901                         LVM=''
1902                 fi
1903         else
1904                 msg "Delete LVM" "\nNo LVMs to remove...\n" 2
1905                 LVM=''
1906         fi
1907 }
1908
1909 lvm_delgroup()
1910 {
1911         DEL_VG=''
1912         VOL_GROUP_LIST=''
1913
1914         for i in $(lvs --noheadings | awk '{print $2}' | uniq); do
1915                 VOL_GROUP_LIST+="$i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}') "
1916         done
1917
1918         [[ $VOL_GROUP_LIST ]] || { msg "No Groups" "\nNo volume groups found."; return 1; }
1919
1920         dlg DEL_VG menu "Logical Volume Management" "\nSelect volume group to delete.\n\nAll logical volumes within will also be deleted." $VOL_GROUP_LIST
1921         [[ $DEL_VG ]]
1922 }
1923
1924 lvm_extra_lvs()
1925 {
1926         while (( VOL_COUNT > 1 )); do
1927                 lvm_volume_name "$_lvmlvname" && lvm_lv_size || return 1
1928                 msg "$_lvmnew (LV:$VOL_COUNT)" "\nCreating a $VOLUME_SIZE volume $VNAME in $VGROUP\n" 0
1929                 lvcreate -L "$VOLUME_SIZE" "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
1930                 errshow "lvcreate -L $VOLUME_SIZE $VGROUP -n $VNAME" || return 1
1931                 msg "$_lvmnew (LV:$VOL_COUNT)" "\nDone, logical volume (LV) $VNAME ($VOLUME_SIZE) has been created.\n"
1932                 (( VOL_COUNT-- ))
1933         done
1934         return 0
1935 }
1936
1937 lvm_partitions()
1938 {
1939         part_find 'part|crypt' || return 1
1940         PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")"
1941         dlg LVM_PARTS check "$_lvmnew" "\nSelect the partition(s) to use for the physical volume (PV)." $PARTS
1942         [[ $LVM_PARTS ]]
1943 }
1944
1945 lvm_group_name()
1946 {
1947         VGROUP=''
1948         until [[ $VGROUP ]]; do
1949                 dlg VGROUP input "$_lvmnew" "$_lvmvgname" "lvgroup"
1950                 if [[ -z $VGROUP ]]; then
1951                         return 1
1952                 elif [[ ${VGROUP:0:1} == "/" || $VGROUP =~ \ |\' ]] || vgdisplay | grep -q "$VGROUP"; then
1953                         msg "LVM Name Error" "$_lvmerrvgname"
1954                         VGROUP=''
1955                 fi
1956         done
1957         return 0
1958 }
1959
1960 lvm_volume_name()
1961 {
1962         VNAME=''
1963         local txt="$1" default="mainvolume"
1964         (( VOL_COUNT > 1 )) && default="extvolume$VOL_COUNT"
1965         until [[ $VNAME ]]; do
1966                 dlg VNAME input "$_lvmnew (LV:$VOL_COUNT)" "\n$txt" "$default"
1967                 if [[ -z $VNAME ]]; then
1968                         return 1
1969                 elif [[ ${VNAME:0:1} == "/" || $VNAME =~ \ |\' ]] || lsblk | grep -q "$VNAME"; then
1970                         msg "LVM Name Error" "$_lvmerlvname"
1971                         VNAME=''
1972                 fi
1973         done
1974         return 0
1975 }
1976
1977 ###############################################################################
1978 # luks functions
1979
1980 luks_menu()
1981 {
1982         local choice
1983         no_bg_install || return 1
1984         dlg choice menu "LUKS Encryption" "$_luksmenu" \
1985                 "$_luksnew"  "cryptsetup -q luksFormat" \
1986                 "$_luksopen" "cryptsetup open --type luks" \
1987                 "$_luksadv"  "cryptsetup -q -s -c luksFormat" \
1988                 "Back"       "Return to the main menu"
1989
1990         case "$choice" in
1991                 "$_luksnew") luks_basic || return 1 ;;
1992                 "$_luksopen") luks_open || return 1 ;;
1993                 "$_luksadv") luks_advanced || return 1 ;;
1994         esac
1995
1996         return 0
1997 }
1998
1999 luks_open()
2000 {
2001         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2002         umount_dir "$MNT"
2003         part_find 'part|crypt|lvm' || return 1
2004
2005         if (( COUNT == 1 )); then
2006                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2007         else
2008                 dlg LUKS_PART menu "$_luksopen" "\nSelect which partition to open." $PARTS
2009         fi
2010
2011         [[ $LUKS_PART ]] || return 1
2012
2013         luks_pass "$_luksopen" || return 1
2014         msg "$_luksopen" "\nOpening encrypted partition: $LUKS_NAME\n\nUsing device/volume: $LUKS_PART\n" 0
2015         cryptsetup open --type luks "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2016         errshow "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" || return 1
2017         LUKS='encrypted'; luks_show
2018         return 0
2019 }
2020
2021 luks_pass()
2022 {
2023         LUKS_PASS=''
2024         local t="$1"
2025         typeset -a ans=(cryptroot) # default name to start
2026
2027         until [[ $LUKS_PASS ]]; do
2028                 tput cnorm
2029                 dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " --mixedform "$_luksomenu" 0 0 0 \
2030                         "Name:"      1 1 "${ans[0]}" 1  7 "$COLUMNS" 0 0 \
2031                         "Password:"  2 1 ''          2 11 "$COLUMNS" 0 1 \
2032                         "Password2:" 3 1 ''          3 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2033
2034                 mapfile -t ans <"$ANS"
2035
2036                 if [[ -z "${ans[0]}" ]]; then
2037                         msg "Name Empty" "\nEncrypted device name cannot be empty.\n\nPlease try again.\n" 2
2038                 elif [[ -z "${ans[1]}" || "${ans[1]}" != "${ans[2]}" ]]; then
2039                         LUKS_NAME="${ans[0]}"
2040                         msg "Password Mismatch" "\nThe passwords entered do not match.\n\nPlease try again.\n" 2
2041                 else
2042                         LUKS_NAME="${ans[0]}"
2043                         LUKS_PASS="${ans[1]}"
2044                 fi
2045         done
2046
2047         return 0
2048 }
2049
2050 luks_show()
2051 {
2052         sleep 0.5
2053         msg "$_luksnew" "\nEncrypted partition ready for mounting.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE "$LUKS_PART")\n\n"
2054 }
2055
2056 luks_setup()
2057 {
2058         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2059         umount_dir "$MNT"
2060         part_find 'part|lvm' || return 1
2061
2062         if [[ $AUTO_ROOT_PART ]]; then
2063                 LUKS_PART="$AUTO_ROOT_PART"
2064         elif (( COUNT == 1 )); then
2065                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2066         else
2067                 dlg LUKS_PART menu "$_luksnew" "\nSelect the partition you want to encrypt." $PARTS
2068         fi
2069
2070         [[ $LUKS_PART ]] || return 1
2071         luks_pass "$_luksnew"
2072 }
2073
2074 luks_basic()
2075 {
2076         luks_setup || return 1
2077         msg "$_luksnew" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2078         cryptsetup -q luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2079         errshow "cryptsetup -q luksFormat $LUKS_PART" || return 1
2080         cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2081         errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2082         LUKS='encrypted'; luks_show
2083         return 0
2084 }
2085
2086 luks_advanced()
2087 {
2088         if luks_setup; then
2089                 local cipher
2090                 dlg cipher input "LUKS Encryption" "$_lukskey" "-s 512 -c aes-xts-plain64"
2091                 [[ $cipher ]] || return 1
2092                 msg "$_luksadv" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2093                 cryptsetup -q $cipher luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2094                 errshow "cryptsetup -q $cipher luksFormat $LUKS_PART" || return 1
2095                 cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2096                 errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2097                 luks_show
2098                 return 0
2099         fi
2100         return 1
2101 }
2102
2103 ###############################################################################
2104 # simple functions
2105 # some help avoid repetition and improve usability of some commands
2106 # others are initial setup functions used before reaching the main loop
2107
2108 ofn()
2109 {
2110         [[ "$2" == *"$1"* ]] && printf "on" || printf "off"
2111 }
2112
2113 die()
2114 {
2115         # cleanup and exit the installer cleanly with exit code $1
2116         local e="$1" # when e is 127 unmount /run/archiso/bootmnt and reboot
2117
2118         trap - INT
2119         tput cnorm
2120         if [[ -d $MNT ]]; then
2121                 umount_dir "$MNT"
2122                 if (( e == 127 )); then
2123                         umount_dir /run/archiso/bootmnt && sleep 0.5 && reboot -f
2124                 fi
2125         fi
2126         exit $e
2127 }
2128
2129 dlg()
2130 {
2131         local var="$1"   # assign output from dialog to var
2132         local dlg_t="$2" # dialog type (menu, check, input)
2133         local title="$3" # dialog title
2134         local body="$4"  # dialog message
2135         local n=0        # number of items to display for menu and check dialogs
2136
2137         shift 4  # shift off args assigned above
2138
2139         # adjust n when passed a large list
2140         local l=$((LINES - 20))
2141         (( ($# / 2) > l )) && n=$l
2142
2143         tput civis
2144         case "$dlg_t" in
2145                 menu) dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --menu "$body" 0 0 $n "$@" 2>"$ANS" || return 1 ;;
2146                 check) dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --checklist "$body" 0 0 $n "$@" 2>"$ANS" || return 1 ;;
2147                 input)
2148                         tput cnorm
2149                         local def="$1" # assign default value for input
2150                         shift
2151                         if [[ $1 == 'limit' ]]; then
2152                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --max-input 63 --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2153                         else
2154                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2155                         fi
2156                         ;;
2157         esac
2158         # if answer file isn't empty read from it into $var
2159         [[ -s "$ANS" ]] && printf -v "$var" "%s" "$(< "$ANS")"
2160 }
2161
2162 msg()
2163 {
2164         # displays a message dialog
2165         # when more than 2 args the message will disappear after sleep time ($3)
2166         local title="$1"
2167         local body="$2"
2168         shift 2
2169         tput civis
2170         if (( $# )); then
2171                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --sleep "$1" --title " $title " --infobox "$body\n" 0 0
2172         else
2173                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --msgbox "$body\n" 0 0
2174         fi
2175 }
2176
2177 live()
2178 {
2179         local ses="$1"
2180
2181         if ! select_keymap; then
2182                 clear
2183                 die 0
2184         elif ! net_connect; then
2185                 msg "Not Connected" "\nRunning live requires an active internet connection.\n\nExiting..\n" 2
2186                 die 1
2187         elif (( $(awk '/MemTotal/ {print int($2 / 1024)}' /proc/meminfo) < 2500)); then
2188                 msg "Not Enough Memory" "\nRunning live requires at least 2.5G of system memory.\n\nExiting..\n" 2
2189                 die 1
2190         else
2191                 clear
2192                 echo "Sorting mirrorlist first"
2193                 mount /run/archiso/cowspace -o remount,size=2G
2194                 install_mirrorlist "/etc/pacman.d/mirrorlist"
2195                 pacman -Syyu --noconfirm || die 1
2196                 pacman -S $BASE_PKGS $AL_BASE_PKGS xorg-xinit --needed --noconfirm || die 1
2197                 case "$ses" in
2198                         i3-gaps|openbox|fluxbox|bspwm|awesome) pacman -S "$ses" $WM_BASE_PKGS ${WM_EXT[$ses]} --needed --noconfirm || die 1 ;;
2199                         gnome|plasma|cinnamon|xfce4) pacman -S "$ses" ${WM_EXT[$ses]} --needed --noconfirm || die 1 ;;
2200                         dwm) { pacman -S git --needed --noconfirm || die 1; }; install_suckless "/root" nochroot ;;
2201                 esac
2202                 pamixer --unmute
2203                 gtk-update-icon-cache /usr/share/icons/ArchLabs-Dark /usr/share/icons/ArchLabs-Light /usr/share/icons/ArchLabs
2204                 pacman -Scc --noconfirm
2205                 rm -rf "/var/cache/pacman/pkg/"*
2206                 cp -rfT /etc/skel /root || die 1
2207                 sed -i "/exec/ c exec ${WM_SESSIONS[$ses]}" /root/.xinitrc
2208                 printf "\n%s has been set as the login session in ~/.xinitrc, to start the session simply run\n\n\tstartx\n\n" "${WM_SESSIONS[$ses]}"
2209                 die 0
2210         fi
2211 }
2212
2213 usage()
2214 {
2215         cat <<-EOF
2216         usage: $1 [-hdl] [session]
2217
2218         options:
2219             -h, --help     print this message and exit
2220             -l, --live     install and setup a live session
2221             -d, --debug    enable xtrace and log output to $DBG
2222
2223         sessions:
2224             i3-gaps  - A fork of i3wm with more features including gaps
2225             openbox  - A lightweight, powerful, and highly configurable stacking wm
2226             dwm      - A dynamic WM for X that manages windows in tiled, floating, or monocle layouts
2227             awesome  - A customized Awesome WM session created by @elanapan
2228             bspwm    - A tiling wm that represents windows as the leaves of a binary tree
2229             fluxbox  - A lightweight and highly-configurable window manager
2230             gnome    - A desktop environment that aims to be simple and easy to use
2231             cinnamon - A desktop environment combining traditional desktop with modern effects
2232             plasma   - A kde software project currently comprising a full desktop environment
2233             xfce4    - A lightweight and modular desktop environment based on gtk+2/3
2234
2235         distro name:
2236
2237             set the DIST environment variable before launching the installer eg.
2238
2239                     DIST='MyDistro' $1
2240
2241         editor used:
2242
2243             set the EDITOR environment variable before launching the installer eg.
2244
2245                     EDITOR='nano' $1
2246
2247         EOF
2248         exit 0
2249 }
2250
2251 yesno()
2252 {
2253         local title="$1" body="$2" yes='Yes' no='No'
2254         (( $# >= 3 )) && yes="$3"
2255         (( $# >= 4 )) && no="$4"
2256         tput civis
2257         if (( $# == 5 )); then
2258                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --defaultno --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2259         else
2260                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2261         fi
2262 }
2263
2264 chrun()
2265 {
2266         arch-chroot "$MNT" bash -c "$1"
2267 }
2268
2269 debug()
2270 {
2271         export PS4='| ${BASH_SOURCE} LINE:${LINENO} FUNC:${FUNCNAME[0]:+ ${FUNCNAME[0]}()} |>  '
2272         set -x
2273         exec 3>| $DBG
2274         BASH_XTRACEFD=3
2275         DEBUG=true
2276 }
2277
2278 termcol()
2279 {
2280         local colors=(
2281         "\e]P0191919" # #191919
2282         "\e]P1D15355" # #D15355
2283         "\e]P2609960" # #609960
2284         "\e]P3FFCC66" # #FFCC66
2285         "\e]P4255A9B" # #255A9B
2286         "\e]P5AF86C8" # #AF86C8
2287         "\e]P62EC8D3" # #2EC8D3
2288         "\e]P7949494" # #949494
2289         "\e]P8191919" # #191919
2290         "\e]P9D15355" # #D15355
2291         "\e]PA609960" # #609960
2292         "\e]PBFF9157" # #FF9157
2293         "\e]PC4E88CF" # #4E88CF
2294         "\e]PDAF86C8" # #AF86C8
2295         "\e]PE2ec8d3" # #2ec8d3
2296         "\e]PFE1E1E1" # #E1E1E1
2297         )
2298
2299         [[ $TERM == 'linux' ]] && printf "%b" "${colors[@]}" && clear
2300 }
2301
2302 errshow()
2303 {
2304         [ $? -eq 0 ] && return 0
2305
2306         local fatal=0 err=""
2307         err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")"
2308         [[ -z $err ]] && err="no error message was found"
2309
2310         (( $1 == 1 )) && { fatal=1; shift; }
2311
2312         local txt="\nCommand: $1\n\n\n\nError: $err\n\n"
2313
2314         if (( fatal )); then
2315                 msg "Install Error" "${txt}Errors at this stage are fatal, the install cannot continue.\n"
2316                 [[ -r $DBG && $TERM == 'linux' ]] && less "$DBG"
2317                 die 1
2318         fi
2319
2320         msg "Install Error" "${txt}Errors at this stage are non-fatal and may be fixed or ignored depending on the error.\n"
2321         return 1
2322 }
2323
2324 prechecks()
2325 {
2326         local i=1
2327
2328         if (( $1 >= 0 )) && ! grep -qw "$MNT" /proc/mounts; then
2329                 msg "Not Mounted" "\nPartition(s) must be mounted first.\n" 2
2330                 SEL=4 i=0
2331         elif [[ $1 -ge 1 && -z $BOOTLDR ]]; then
2332                 msg "No Bootloader" "\nBootloader must be selected first.\n" 2
2333                 SEL=5 i=0
2334         elif [[ $1 -ge 2 && (-z $NEWUSER || -z $USER_PASS) ]]; then
2335                 msg "No User" "\nA user must be created first.\n" 2
2336                 SEL=6 i=0
2337         elif [[ $1 -ge 3 && -z $CONFIG_DONE ]]; then
2338                 msg "Not Configured" "\nSystem configuration must be done first.\n" 2
2339                 SEL=7 i=0
2340         fi
2341         (( i )) # return code
2342 }
2343
2344 umount_dir()
2345 {
2346         mount | grep -q 'swap' && swapoff -a
2347         for dir; do
2348                 if [[ -d $dir ]] && mount | grep -q "on $dir "; then
2349                         if ! umount "$dir" 2>/dev/null; then
2350                                 sleep 0.5
2351                                 umount -f "$dir" 2>/dev/null || umount -l "$dir"
2352                         fi
2353                 fi
2354         done
2355 }
2356
2357 chk_connect()
2358 {
2359         msg "Network Connect" "\nVerifying network connection\n" 0
2360         curl -sIN --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200'
2361 }
2362
2363 net_connect()
2364 {
2365         if chk_connect; then
2366                 return 0
2367         elif hash nmtui >/dev/null 2>&1; then
2368                 tput civis
2369                 if [[ $TERM == 'linux' ]]; then
2370                         printf "%b" "\e]P1191919" "\e]P4191919"
2371                         nmtui-connect
2372                         printf "%b" "\e]P1D15355" "\e]P4255a9b"
2373                 else
2374                         nmtui-connect
2375                 fi
2376                 chk_connect
2377         elif hash wifi-menu >dev/null 2>&1; then
2378                 wifi-menu
2379                 chk_connect
2380         else
2381                 return 1
2382         fi
2383 }
2384
2385 no_bg_install()
2386 {
2387         [[ $BG_PID ]] || return 0
2388         msg "Install Running" "\nA background install process is currently running.\n" 2
2389         return 1
2390 }
2391
2392 system_devices()
2393 {
2394         IGNORE_DEV="$(lsblk -lno NAME,MOUNTPOINT | awk '/\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')"
2395
2396         if [[ $IGNORE_DEV ]]; then
2397                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')"
2398         else
2399                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ {print "/dev/" $1 " " $2}')"
2400         fi
2401
2402         if [[ -z $SYS_DEVS ]]; then
2403                 msg "Device Error" "\nNo available devices...\n\nExiting..\n" 2
2404                 die 1
2405         fi
2406
2407         DEV_COUNT=0
2408         while read -r line; do
2409                 (( DEV_COUNT++ ))
2410         done <<< "$SYS_DEVS"
2411 }
2412
2413 system_identify()
2414 {
2415         if [[ $VM ]]; then
2416                 UCODE=''
2417         # amd-ucode is not needed it's provided by linux-firmware
2418         # elif grep -q 'AuthenticAMD' /proc/cpuinfo; then
2419         #       UCODE="amd-ucode"
2420         elif grep -q 'GenuineIntel' /proc/cpuinfo; then
2421                 UCODE="intel-ucode"
2422         fi
2423
2424         modprobe -q efivarfs >/dev/null 2>&1
2425
2426         _prep="\nOnce a step is finished a step you will be returned here, if the step was successful the cursor will be advanced to the next step.\nIf a step is unsuccessful the cursor will be placed on the step required to advance (when possible).\n\nTo begin the install you should have:\n\n  - A root (/) partition mounted."
2427         if [[ -d /sys/firmware/efi/efivars ]]; then
2428                 export SYS="UEFI"
2429                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars
2430                 _prep+="\n  - An EFI boot partition mounted."
2431         else
2432                 export SYS="BIOS"
2433         fi
2434         _prep+="\n\nOnce finished mounting, a portion of the install can be done in the background while you continue configuring the system:\n"
2435         _prep+="\n  - Choose the system bootloader.\n  - Create a user and password."
2436         _prep+="\n  - Basic system configuration, kernel, shell, login, packages, etc..\n\nOnce you're happy with the choices and the required steps are complete, the main install can be started."
2437 }
2438
2439 ###############################################################################
2440 # entry point
2441
2442 # enable some nicer colours in the linux console
2443 termcol
2444
2445 if (( UID != 0 )); then
2446         msg "Not Root" "\nThis installer must be run as root or using sudo.\n\nExiting..\n" 2
2447         die 1
2448 elif ! grep -qwm 1 'lm' /proc/cpuinfo; then
2449         msg "Not x86_64 Architecture" "\nThis installer only supports x86_64 architectures.\n\nExiting..\n" 2
2450         die 1
2451 else
2452         case "$1" in
2453                 -d|--debug) debug ;;
2454                 -h|--help) usage "$0" ;;
2455                 -l|--live)
2456                         case "$2" in
2457                                 gnome|cinnamon|dwm|plasma|xfce4|i3-gaps|openbox|fluxbox|bspwm|awesome) live "$2" ;;
2458                                 *) echo "error: invalid session for -l, --live, see -h, --help"; die 1 ;;
2459                         esac ;;
2460         esac
2461 fi
2462
2463 # trap ^C to perform cleanup
2464 trap 'printf "\n^C\n" && die 1' INT
2465
2466 system_identify
2467 system_devices
2468
2469 msg "Welcome to the $DIST Installer" "$_welcome"
2470
2471 if ! select_keymap; then
2472         clear; die 0
2473 elif ! net_connect; then
2474         msg "Not Connected" "\nThis installer requires an active internet connection.\n\nExiting..\n" 2
2475         die 1
2476 fi
2477 EXMNTS=""
2478 FORMATTED=""
2479
2480 while :; do
2481         main
2482 done
2483 # vim:fdm=marker:fmr={,}