OSDN Git Service

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