OSDN Git Service

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