OSDN Git Service

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