OSDN Git Service

12574a17e1896928c78be53c569ea8792cf8dd54
[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                         fi
1398                 done
1399                 trap - EXIT
1400                 unset RSYNC_PID MIRROR_PID
1401         else
1402                 rsync -ahv /run/archiso/sfs/airootfs/ $MNT/ 2>$ERR
1403                 errshow 1 "rsync -ahv /run/archiso/sfs/airootfs/ $MNT/"
1404                 install_mirrorlist "$MNT/etc/pacman.d/mirrorlist"
1405                 chrun "pacman -Syyu --noconfirm && pacman -S $BASE_PKGS --needed --noconfirm"
1406         fi
1407
1408         rm -rf $MNT/etc/mkinitcpio-archiso.conf
1409         find $MNT/usr/lib/initcpio -name 'archiso*' -type f -delete
1410         sed -i 's/volatile/auto/g' $MNT/etc/systemd/journald.conf
1411         find $MNT/boot -name '*-ucode.img' -delete
1412
1413         if [[ $VM ]]; then
1414                 find $MNT/etc/X11/xorg.conf.d/ -name '*.conf' -delete
1415         elif lspci | grep ' VGA ' | grep -q 'Intel'; then
1416                 cat > $MNT/etc/X11/xorg.conf.d/20-intel.conf <<- EOF
1417                 Section "Device"
1418                     Identifier  "Intel Graphics"
1419                     Driver      "intel"
1420                     Option      "TearFree" "true"
1421                 EndSection
1422                 EOF
1423         fi
1424
1425         [[ -e /run/archiso/sfs/airootfs && $KERNEL == 'linux' ]] && cp -vf $RUN/x86_64/vmlinuz $MNT/boot/vmlinuz-linux
1426
1427         cp -fv /etc/resolv.conf $MNT/etc/
1428         [[ -e /etc/NetworkManager/system-connections ]] && cp -rvf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/
1429
1430         echo "LANG=$MYLOCALE" > $MNT/etc/locale.conf
1431         cp -f $MNT/etc/locale.conf $MNT/etc/default/locale
1432         sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${MYLOCALE}/${MYLOCALE}/g" $MNT/etc/locale.gen
1433         chrun "locale-gen"
1434         chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZ /etc/localtime"
1435
1436         if [[ $BROADCOM_WL ]]; then
1437                 echo 'blacklist bcma' >> $MNT/etc/modprobe.d/blacklist.conf
1438                 rm -f $MNT/etc/modprobe/
1439         fi
1440
1441         cat > $MNT/etc/X11/xorg.conf.d/00-keyboard.conf <<- EOF
1442         # Use localectl(1) to instruct systemd-localed to update it.
1443         Section "InputClass"
1444             Identifier      "system-keyboard"
1445             MatchIsKeyboard "on"
1446             Option          "XkbLayout" "$KEYMAP"
1447         EndSection
1448         EOF
1449
1450         cat > $MNT/etc/default/keyboard <<- EOF
1451         # KEYBOARD CONFIGURATION FILE
1452         # Consult the keyboard(5) manual page.
1453         XKBMODEL=""
1454         XKBLAYOUT="$KEYMAP"
1455         XKBVARIANT=""
1456         XKBOPTIONS=""
1457         BACKSPACE="guess"
1458         EOF
1459         printf "KEYMAP=%s\nFONT=%s\n" "$CMAP" "$FONT" > $MNT/etc/vconsole.conf
1460         echo "$MYHOST" > $MNT/etc/hostname
1461         cat > $MNT/etc/hosts <<- EOF
1462         127.0.0.1       localhost
1463         127.0.1.1       $MYHOST
1464         ::1                     localhost ip6-localhost ip6-loopback
1465         ff02::1         ip6-allnodes
1466         ff02::2         ip6-allrouters
1467         EOF
1468 }
1469
1470 install_boot()
1471 {
1472         if [[ $ROOT_PART == /dev/mapper* ]]; then
1473                 ROOT_PART_ID="$ROOT_PART"
1474         else
1475                 local uuid_type="UUID"
1476                 [[ $BOOTLDR =~ (systemd-boot|refind-efi|efistub) ]] && uuid_type="PARTUUID"
1477                 ROOT_PART_ID="$uuid_type=$(blkid -s $uuid_type -o value $ROOT_PART)"
1478         fi
1479
1480         if [[ $SYS == 'UEFI' ]]; then
1481                 # our old installs
1482                 find $MNT/$BOOTDIR/EFI/ -maxdepth 1 -mindepth 1 -iname "$DIST" -type d -delete
1483                 # generic BOOT/ dir
1484                 find $MNT/$BOOTDIR/EFI/ -maxdepth 1 -mindepth 1 -iname 'BOOT' -type d -delete
1485         fi
1486
1487         prerun_$BOOTLDR
1488         chrun "${BCMDS[$BOOTLDR]}" 2>$ERR
1489         errshow 1 "${BCMDS[$BOOTLDR]}"
1490
1491         if [[ -d $MNT/hostrun ]]; then
1492                 # cleanup the bind mounts we made earlier for the grub-probe module
1493                 umount_dir $MNT/hostrun/{udev,lvm}
1494                 rm -rf $MNT/hostrun >/dev/null 2>&1
1495         fi
1496
1497         if [[ $SYS == 'UEFI' ]]; then
1498                 # some UEFI firmware requires a generic esp/BOOT/BOOTX64.EFI
1499                 mkdir -pv $MNT/$BOOTDIR/EFI/BOOT
1500                 if [[ $BOOTLDR == 'grub' ]]; then
1501                         cp -fv $MNT/$BOOTDIR/EFI/$DIST/grubx64.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
1502                 elif [[ $BOOTLDR == 'syslinux' ]]; then
1503                         cp -rf $MNT/$BOOTDIR/EFI/syslinux/* $MNT/$BOOTDIR/EFI/BOOT/
1504                         cp -f $MNT/$BOOTDIR/EFI/syslinux/syslinux.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
1505                 elif [[ $BOOTLDR == 'refind-efi' ]]; then
1506                         sed -i '/#extra_kernel_version_strings/ c extra_kernel_version_strings linux-hardened,linux-zen,linux-lts,linux' $MNT/$BOOTDIR/EFI/refind/refind.conf
1507                         cp -fv $MNT/$BOOTDIR/EFI/refind/refind_x64.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
1508                 fi
1509         fi
1510
1511         return 0
1512 }
1513
1514 install_user()
1515 {
1516         rm -f $MNT/root/.zshrc  # remove welcome message from root zshrc
1517         chrun "chpasswd <<< 'root:$ROOT_PASS'" 2>$ERR
1518         errshow 1 "set root password"
1519         if [[ $MYSHELL != "/usr/bin/zsh" ]]; then # root uses zsh by default, change it if something else was chosen
1520                 chrun "usermod -s $MYSHELL root" 2>$ERR
1521                 errshow 1 "usermod -s $MYSHELL root"
1522                 # copy the default mkshrc to /root if mksh was picked
1523                 [[ $MYSHELL == '/usr/bin/mksh' ]] && cp -fv $MNT/etc/skel/.mkshrc $MNT/root/.mkshrc
1524         fi
1525
1526         local groups='audio,autologin,floppy,log,network,rfkill,scanner,storage,optical,power,wheel'
1527
1528         chrun "groupadd -r autologin" 2>$ERR
1529         errshow 1 "groupadd -r autologin"
1530         chrun "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" 2>$ERR
1531         errshow 1 "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER"
1532         chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" 2>$ERR
1533         errshow 1 "set $NEWUSER password"
1534
1535         # if neovim was picked copy vim configs to ~/.config/nvim
1536         if [[ $USER_PKGS == *neovim* ]]; then
1537                 mkdir -p $MNT/home/$NEWUSER/.config/nvim
1538                 cp -fv $MNT/home/$NEWUSER/.vimrc $MNT/home/$NEWUSER/.config/nvim/init.vim
1539                 cp -rfv $MNT/home/$NEWUSER/.vim/colors $MNT/home/$NEWUSER/.config/nvim/colors
1540         fi
1541
1542         case $INSTALL_WMS in *awesome*) install_awesome ;; *dwm*) install_suckless ;; esac
1543
1544         [[ $WM_PKGS == *xfce* ]] && echo 'volumeicon &' >> $MNT/home/$NEWUSER/.xprofile
1545
1546         # remove some commands from ~/.xprofile when using KDE or Gnome as the login session
1547         if [[ $LOGIN_WM =~ (startkde|gnome-session) || ($LOGIN_TYPE == 'lightdm' && $WM_PKGS =~ (plasma|gnome)) ]]; then
1548                 sed -i '/super/d' $MNT/home/$NEWUSER/.xprofile $MNT/root/.xprofile
1549                 sed -i '/nitrogen/d' $MNT/home/$NEWUSER/.xprofile $MNT/root/.xprofile
1550                 sed -i '/al-compositor/d' $MNT/home/$NEWUSER/.xprofile $MNT/root/.xprofile
1551                 sed -i '/compton/d' $MNT/home/$NEWUSER/.xprofile $MNT/root/.xprofile
1552         fi
1553         
1554         return 0
1555 }
1556
1557 install_xinit()
1558 {
1559
1560         if [[ $INSTALL_WMS ]]; then
1561                 if [[ -e $MNT/home/$NEWUSER/.xinitrc ]] && grep -q 'exec' "$MNT/home/$NEWUSER/.xinitrc"; then
1562                         sed -i "/exec/ c exec ${LOGIN_WM}" "$MNT/home/$NEWUSER/.xinitrc"
1563                 elif [[ $INSTALL_WMS ]]; then
1564                         printf "exec %s\n" "$LOGIN_WM" >> "$MNT/home/$NEWUSER/.xinitrc"
1565                 else
1566                         printf "exec %s\n" "$LOGIN_WM" >> "$MNT/home/$NEWUSER/.xinitrc"
1567                 fi
1568         elif [[ -e $MNT/home/$NEWUSER/.xinitrc ]]; then
1569                 # no sessions available so remove the exec from ~/.xinitrc and return
1570                 sed -i '/exec/d' "$MNT/home/$NEWUSER/.xinitrc"
1571                 return 0
1572         fi
1573
1574         [[ ${EDIT_FILES[login]} == *"$LOGINRC"* ]] || EDIT_FILES[login]+=" /home/$NEWUSER/$LOGINRC"
1575
1576         if [[ $AUTOLOGIN ]]; then
1577                 sed -i "s/root/${NEWUSER}/g" $SERVICE/autologin.conf
1578                 cat > "$MNT/home/$NEWUSER/$LOGINRC" <<- EOF
1579                 # ~/$LOGINRC
1580                 # sourced by ${MYSHELL##*/} when used as a login shell
1581
1582                 # automatically run startx when logging in on tty1
1583                 [ -z \$DISPLAY ] && [ \$XDG_VTNR -eq 1 ] && exec startx
1584                 EOF
1585         else
1586                 rm -rf $SERVICE
1587         fi
1588 }
1589
1590 install_login()
1591 {
1592         SERVICE="$MNT/etc/systemd/system/getty@tty1.service.d"
1593         install_${LOGIN_TYPE:-xinit}
1594 }
1595
1596 install_lightdm()
1597 {
1598         rm -rf "$SERVICE" "$MNT/home/$NEWUSER"/.{xinitrc,profile,zprofile,bash_profile}
1599         chrun 'systemctl enable lightdm.service' 2>$ERR
1600         errshow 1 "systemctl enable lightdm.service"
1601         cat > $MNT/etc/lightdm/lightdm-gtk-greeter.conf <<- EOF
1602         # LightDM GTK+ Configuration
1603
1604         [greeter]
1605         default-user-image=/usr/share/icons/ArchLabs-Dark/64x64/places/distributor-logo-archlabs.png
1606         background=/usr/share/backgrounds/archlabs/archlabs.jpg
1607         theme-name=Adwaita-dark
1608         icon-theme-name=Adwaita
1609         font-name=DejaVu Sans Mono 11
1610         position=30%,end 50%,end
1611         EOF
1612 }
1613
1614 install_awesome()
1615 {
1616         # downloads and sets up @elenapan's awesome WM config hosted on github
1617         if chrun "git clone https://github.com/elenapan/archlabs-awesome /home/$NEWUSER/archlabs-awesome"; then
1618                 cp -rT "/mnt/home/$NEWUSER/archlabs-awesome" "/mnt/home/$NEWUSER"
1619                 cp -rT "/mnt/home/$NEWUSER/archlabs-awesome" /mnt/etc/skel
1620                 rm -rf "/home/$NEWUSER/"{.git,archlabs-awesome,screenshots}
1621                 printf "You will need to install pamac separately using: 'baph -i pamac-aur'\n"
1622                 sleep 3
1623         else
1624                 printf "failed to clone awesome config repo\n"
1625         fi
1626 }
1627
1628 install_packages()
1629 {
1630         local rmpkg=""
1631         local inpkg="$BASE_PKGS ${LOGIN_PKGS[$LOGIN_TYPE]} $PACKAGES $USER_PKGS $UCODE "
1632
1633         if pacman -Qsq 'archlabs-installer' >/dev/null 2>&1; then
1634                 rmpkg+="archlabs-installer "
1635         elif [[ -e "$MNT/usr/bin/archlabs-installer" ]]; then
1636                 rm -f "$MNT/usr/bin/archlabs-installer"
1637         fi
1638
1639         # add extra packages chosen throughout the install
1640         if [[ $MYSHELL == '/usr/bin/zsh' ]]; then
1641                 inpkg+="zsh-completions "
1642         else
1643                 rmpkg+="zsh "
1644                 [[ $MYSHELL == '/usr/bin/mksh' ]] && inpkg+="mksh "
1645         fi
1646         if [[ $KERNEL != 'linux' ]]; then
1647                 inpkg+="$KERNEL "
1648                 rmpkg+="linux "
1649         fi
1650
1651         # gnome, plasma, and cinnamon have their own superkey bind and ksuperkey conflicts
1652         [[ $INSTALL_WMS =~ ^(plasma|gnome|cinnamon)$ ]] || inpkg+="archlabs-ksuperkey "
1653
1654         # window manager only packages
1655         [[ $INSTALL_WMS =~ (openbox|bspwm|i3-gaps|dwm|fluxbox) ]] && inpkg+="$WM_BASE_PKGS "
1656
1657         # ensure a terminal gets installed if one wasn't chosen or a DE that installs one
1658         [[ $inpkg =~ (term|urxvt|tilix|alacritty|sakura|tilda|plasma|cinnamon) || $INSTALL_WMS == *dwm* ]] || inpkg+=" xterm"
1659
1660         # update first to avoid issues
1661         chrun "pacman -Syyu --noconfirm"
1662
1663         # remove the packages we don't want on the installed system
1664         [[ $rmpkg ]] && chrun "pacman -Rns $rmpkg --noconfirm"
1665
1666         # reinstalling iputils fixes the network issue for non-root users
1667         chrun "pacman -S iputils --noconfirm"
1668
1669         # install the packages chosen throughout the install
1670         chrun "pacman -S $inpkg --needed --noconfirm" 2>$ERR
1671         errshow 1 "pacman -S $inpkg --needed --noconfirm"
1672
1673         # bootloader packages
1674         if [[ $BOOTLDR == 'grub' ]]; then
1675                 [[ $SYS == 'UEFI' ]] && local efib="efibootmgr"
1676                 chrun "pacman -S os-prober grub $efib --needed --noconfirm" 2>$ERR
1677                 errshow 1 "pacman -S os-prober grub $efib --needed --noconfirm"
1678         elif [[ $BOOTLDR == 'refind-efi' ]]; then
1679                 chrun "pacman -S refind-efi efibootmgr --needed --noconfirm" 2>$ERR
1680                 errshow 1 "pacman -S refind-efi efibootmgr --needed --noconfirm"
1681         elif [[ $SYS == 'UEFI' ]]; then
1682                 chrun "pacman -S efibootmgr --needed --noconfirm" 2>$ERR
1683                 errshow 1 "pacman -S efibootmgr --needed --noconfirm"
1684         fi
1685
1686         # allow members of the wheel group to run commands as root
1687         sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers
1688
1689         return 0
1690 }
1691
1692 install_suckless()
1693 {
1694         mkdir -pv "$MNT/home/$NEWUSER/suckless"
1695
1696         for i in dwm dmenu st; do
1697                 if chrun "git clone https://git.suckless.org/$i /home/$NEWUSER/suckless/$i"; then
1698                         chrun "cd /home/$NEWUSER/suckless/$i; make PREFIX=/usr install; make clean; rm config.h"
1699                 else
1700                         printf "failed to clone %s repo\n" "$i"
1701                 fi
1702         done
1703
1704         if [[ -x $MNT/usr/bin/dwm ]]; then
1705                 printf "To configure dwm edit %s\n" "/home/$NEWUSER/suckless/dwm/config.h"
1706                 printf "You can then recompile it with 'sudo make clean install'\n"
1707                 sleep 2
1708         fi
1709 }
1710
1711 install_mkinitcpio()
1712 {
1713         local add=''
1714
1715         # luks keyfile creation
1716         # currently not used due to not prompting for passphrase on startup
1717         # [[ $LUKS_UUID && $LUKS_PASS && $SYS == 'UEFI' && $BOOTLDR == 'grub' ]] && luks_keyfile
1718
1719         [[ $LUKS ]] && add="encrypt"
1720         [[ $LVM ]] && { [[ $add ]] && add+=" lvm2" || add+="lvm2"; }
1721         sed -i "s/block filesystems/block ${add} filesystems ${HOOKS}/g" $MNT/etc/mkinitcpio.conf
1722         chrun "mkinitcpio -p $KERNEL" 2>$ERR
1723         errshow 1 "mkinitcpio -p $KERNEL"
1724 }
1725
1726 install_mirrorlist()
1727 {
1728         local mfile="$1"  # output mirrorlist file
1729
1730         if hash reflector >/dev/null 2>&1; then
1731                 reflector --score 80 -l 40 -f 5 --sort rate --save "$mfile"
1732         elif hash rankmirrors >/dev/null 2>&1; then
1733                 local key="access_key=5f29642060ab983b31fdf4c2935d8c56"
1734                 ip_add="$(curl -fsSL "http://api.ipstack.com/check&?$key&fields=ip" | python -c "import sys, json; print(json.load(sys.stdin)['ip'])")"
1735                 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'])")"
1736                 if [[ "$country" ]]; then
1737                         if [[ $country =~ (CA|US) ]]; then
1738                                 # use both CA and US mirrors for CA or US countries
1739                                 mirror="https://www.archlinux.org/mirrorlist/?country=US&country=CA&use_mirror_status=on"
1740                         elif [[ $country =~ (AU|NZ) ]]; then
1741                                 # use both AU and NZ mirrors for AU or NZ countries
1742                                 mirror="https://www.archlinux.org/mirrorlist/?country=AU&country=NZ&use_mirror_status=on"
1743                         else
1744                                 mirror="https://www.archlinux.org/mirrorlist/?country=${country}&use_mirror_status=on"
1745                         fi
1746                 else # no country code so just grab all mirrors, will be a very slow sort but we don't have other options
1747                         mirror="https://www.archlinux.org/mirrorlist/?country=all&use_mirror_status=on"
1748                 fi
1749                 curl -fsSL "$mirror" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 6 - >"$mfile"
1750         fi
1751
1752         return 0
1753 }
1754
1755 install_background()
1756 {
1757         yesno "Background Install" "\nBegin install in the background?\n" || return 0
1758
1759         rsync -a /run/archiso/sfs/airootfs/ $MNT/ &
1760         RSYNC_PID=$!
1761
1762         mkdir -p $MNT/var/lib/pacman # can help with pacman errors
1763         ( install_mirrorlist "$MNT/etc/pacman.d/mirrorlist" && sleep 1 && chrun "pacman -Syyu $BASE_PKGS --needed --noconfirm" >> /tmp/bg_out 2>&1 ) &
1764         MIRROR_PID=$!
1765
1766         # end the background processes before exiting
1767         trap "kill $RSYNC_PID $MIRROR_PID 2>/dev/null" EXIT
1768 }
1769
1770 ###############################################################################
1771 # bootloader setup
1772 # prerun_* set up the configs needed before actually running the commands
1773 # setup_* are run after selecting a bootloader and build the command used later
1774 # they can also be used for further user input as these run before control is taken away
1775
1776 setup_grub()
1777 {
1778         EDIT_FILES[bootloader]="/etc/default/grub"
1779
1780         if [[ $SYS == 'BIOS' ]]; then
1781                 [[ $BOOT_DEV ]] || { part_device 1 || return 1; }
1782                 BCMDS[grub]="grub-install --recheck --force --target=i386-pc $BOOT_DEV"
1783         else
1784                 # since we're not using the keyfile this is dead code
1785                 # if [[ $ROOT_PART == */dev/mapper/* && -z $LVM && -z $LUKS_PASS ]]; then
1786                 #       luks_pass "$_luksopen" 1 || return 1
1787                 # fi
1788                 BCMDS[grub]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1789                 grub-install --recheck --force --target=x86_64-efi --efi-directory=/$BOOTDIR --bootloader-id=$DIST"
1790                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1791         fi
1792
1793         BCMDS[grub]="mkdir -p /run/udev /run/lvm &&
1794                 mount --bind /hostrun/udev /run/udev &&
1795                 mount --bind /hostrun/lvm /run/lvm &&
1796                 ${BCMDS[grub]} &&
1797                 grub-mkconfig -o /boot/grub/grub.cfg &&
1798                 sleep 1 && umount /run/udev /run/lvm"
1799
1800         return 0
1801 }
1802
1803 prerun_grub()
1804 {
1805         sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g; s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" $MNT/etc/default/grub
1806
1807         if [[ $LUKS_DEV ]]; then
1808                 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
1809                 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"
1810         fi
1811
1812         if [[ $SYS == 'BIOS' && $LVM && -z $SEP_BOOT ]]; then
1813                 sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $MNT/etc/default/grub 2>$ERR
1814                 errshow 1 "sed -i 's/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g' $MNT/etc/default/grub"
1815         fi
1816
1817         # setup for os-prober module
1818         mkdir -p /run/{lvm,udev} $MNT/hostrun/{lvm,udev}
1819         mount --bind /run/lvm $MNT/hostrun/lvm
1820         mount --bind /run/udev $MNT/hostrun/udev
1821
1822         return 0
1823 }
1824
1825 setup_efistub()
1826 {
1827         EDIT_FILES[bootloader]=""
1828 }
1829
1830 prerun_efistub()
1831 {
1832         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1833                 efibootmgr -v -d $BOOT_DEV -p $BOOT_PART_NUM -c -L '${DIST} Linux' -l /vmlinuz-${KERNEL} \
1834                 -u 'root=$ROOT_PART_ID rw $([[ $UCODE ]] && printf 'initrd=\%s.img ' "$UCODE")initrd=\initramfs-${KERNEL}.img'"
1835 }
1836
1837 setup_syslinux()
1838 {
1839         if [[ $SYS == 'BIOS' ]]; then
1840                 EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg"
1841         else
1842                 EDIT_FILES[bootloader]="/boot/EFI/syslinux/syslinux.cfg"
1843                 BCMDS[syslinux]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1844                 efibootmgr -v -c -d $BOOT_DEV -p $BOOT_PART_NUM -l /EFI/syslinux/syslinux.efi -L $DIST"
1845         fi
1846 }
1847
1848 prerun_syslinux()
1849 {
1850         local c="$MNT/boot/syslinux" s="/usr/lib/syslinux/bios" d=".."
1851         [[ $SYS == 'UEFI' ]] && { c="$MNT/boot/EFI/syslinux"; s="/usr/lib/syslinux/efi64/"; d=''; }
1852
1853         mkdir -pv "$c" && cp -rfv $s/* "$c/" && cp -f "$RUN/syslinux/splash.png" "$c/"
1854         cat > "$c/syslinux.cfg" <<- EOF
1855         UI vesamenu.c32
1856         MENU TITLE $DIST Boot Menu
1857         MENU BACKGROUND splash.png
1858         TIMEOUT 50
1859         DEFAULT $DIST
1860
1861         # see: https://www.syslinux.org/wiki/index.php/Comboot/menu.c32
1862         MENU WIDTH 78
1863         MENU MARGIN 4
1864         MENU ROWS 4
1865         MENU VSHIFT 10
1866         MENU TIMEOUTROW 13
1867         MENU TABMSGROW 14
1868         MENU CMDLINEROW 14
1869         MENU HELPMSGROW 16
1870         MENU HELPMSGENDROW 29
1871         MENU COLOR border       30;44   #40ffffff #a0000000 std
1872         MENU COLOR title        1;36;44 #9033ccff #a0000000 std
1873         MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
1874         MENU COLOR unsel        37;44   #50ffffff #a0000000 std
1875         MENU COLOR help         37;40   #c0ffffff #a0000000 std
1876         MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
1877         MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
1878         MENU COLOR msg07        37;40   #90ffffff #a0000000 std
1879         MENU COLOR tabmsg       31;40   #30ffffff #00000000 std
1880
1881         LABEL $DIST
1882         MENU LABEL $DIST Linux
1883         LINUX $d/vmlinuz-$KERNEL
1884         APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1885         INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL.img
1886
1887         LABEL ${DIST}fallback
1888         MENU LABEL $DIST Linux Fallback
1889         LINUX $d/vmlinuz-$KERNEL
1890         APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1891         INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL-fallback.img
1892         EOF
1893         return 0
1894 }
1895
1896 setup_refind-efi()
1897 {
1898         EDIT_FILES[bootloader]="/boot/refind_linux.conf"
1899         BCMDS[refind-efi]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1900                 refind-install"
1901 }
1902
1903 prerun_refind-efi()
1904 {
1905         cat > $MNT/boot/refind_linux.conf <<- EOF
1906         "$DIST Linux"          "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1907                                                 printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1908                                                 printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL.img"
1909         "$DIST Linux Fallback" "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1910                                                 printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1911                                                 printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL-fallback.img"
1912         EOF
1913         mkdir -p $MNT/etc/pacman.d/hooks
1914         cat > $MNT/etc/pacman.d/hooks/refind.hook <<- EOF
1915         [Trigger]
1916         Operation = Upgrade
1917         Type = Package
1918         Target = refind-efi
1919
1920         [Action]
1921         Description = Updating rEFInd on ESP
1922         When = PostTransaction
1923         Exec = /usr/bin/refind-install
1924         EOF
1925 }
1926
1927 setup_systemd-boot()
1928 {
1929         EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf"
1930         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1931                 bootctl --path=/boot install"
1932 }
1933
1934 prerun_systemd-boot()
1935 {
1936         mkdir -p $MNT/boot/loader/entries
1937         cat > $MNT/boot/loader/loader.conf <<- EOF
1938         default  $DIST
1939         timeout  5
1940         editor   no
1941         EOF
1942         cat > $MNT/boot/loader/entries/$DIST.conf <<- EOF
1943         title   $DIST Linux
1944         linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1945         initrd  /initramfs-$KERNEL.img
1946         options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1947         EOF
1948         cat > $MNT/boot/loader/entries/$DIST-fallback.conf <<- EOF
1949         title   $DIST Linux Fallback
1950         linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1951         initrd  /initramfs-$KERNEL-fallback.img
1952         options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1953         EOF
1954         mkdir -p $MNT/etc/pacman.d/hooks
1955         cat > $MNT/etc/pacman.d/hooks/systemd-boot.hook <<- EOF
1956         [Trigger]
1957         Type = Package
1958         Operation = Upgrade
1959         Target = systemd
1960
1961         [Action]
1962         Description = Updating systemd-boot
1963         When = PostTransaction
1964         Exec = /usr/bin/bootctl update
1965         EOF
1966         systemd-machine-id-setup --root="$MNT"
1967         return 0
1968 }
1969
1970 ###############################################################################
1971 # lvm functions
1972
1973 lvm_menu()
1974 {
1975         is_bg_install || return 1
1976         lvm_detect
1977         local choice
1978         dlg choice menu "Logical Volume Management" "$_lvmmenu" \
1979                 "$_lvmnew"    "vgcreate -f, lvcreate -L -n" \
1980                 "$_lvmdel"    "vgremove -f" \
1981                 "$_lvmdelall" "lvrmeove, vgremove, pvremove -f" \
1982                 "back"        "return to the main menu"
1983
1984         case "$choice" in
1985                 "$_lvmnew") lvm_create || return 1 ;;
1986                 "$_lvmdel") lvm_delgroup && yesno "$_lvmdel" "$_lvmdelask" && vgremove -f "$DEL_VG" >/dev/null 2>&1 ;;
1987                 "$_lvmdelall") lvm_del_all ;;
1988         esac
1989
1990         return 0
1991 }
1992
1993 lvm_detect()
1994 {
1995         local v pv
1996         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1997         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1998         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1999
2000         if [[ $VGROUP && $v && $pv ]]; then
2001                 msg "Logical Volume Management" "\nActivating existing logical volume management (LVM).\n\n" 1
2002                 modprobe dm-mod >/dev/null 2>$ERR
2003                 errshow 'modprobe dm-mod'
2004                 vgscan >/dev/null 2>&1
2005                 vgchange -ay >/dev/null 2>&1
2006         fi
2007 }
2008
2009 lvm_create()
2010 {
2011         VGROUP='' LVM_PARTS='' VGROUP_MB=0
2012         umount_dir $MNT
2013         lvm_mkgroup || return 1
2014         local txt="\nThe last (or only) logical volume will automatically use all remaining space in the volume group."
2015         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
2016         [[ $VOL_COUNT ]] || return 1
2017         lvm_extra_lvs || return 1
2018         lvm_volume_name "$_lvmlvname\nNOTE: This LV will use up all remaining space in the volume group (${VGROUP_MB}MB)" || return 1
2019         lvcreate -l +100%FREE "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
2020         errshow "lvcreate -l +100%FREE $VGROUP -n $VNAME" || return 1
2021         LVM='logical volume'; sleep 0.5
2022         txt="\nDone, volume: $VGROUP-$VNAME (${VOLUME_SIZE:-${VGROUP_MB}MB}) has been created.\n"
2023         msg "$_lvmnew (LV:$VOL_COUNT)" "$txt\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE $LVM_PARTS)\n"
2024         return 0
2025 }
2026
2027 get_lv_size()
2028 {
2029         local txt="${VGROUP}: ${SIZE}$SIZE_UNIT (${VGROUP_MB}MB remaining).$_lvmlvsize"
2030
2031         while :; do
2032                 ERR_SIZE=0
2033                 dlg VOLUME_SIZE input "$_lvmnew (LV:$VOL_COUNT)" "$txt" ''
2034                 if [[ -z $VOLUME_SIZE ]]; then
2035                         ERR_SIZE=1
2036                         break # allow bailing with escape or an empty choice
2037                 elif (( ${VOLUME_SIZE:0:1} == 0 )); then
2038                         ERR_SIZE=1 # size values can't begin with '0'
2039                 else
2040                         # walk the string and make sure all but the last char are digits
2041                         local lv=$((${#VOLUME_SIZE} - 1))
2042                         for (( i=0; i<lv; i++ )); do
2043                                 [[ ${VOLUME_SIZE:$i:1} =~ [0-9] ]] || { ERR_SIZE=1; break; }
2044                         done
2045                         if (( ERR_SIZE != 1 )); then
2046                                 case ${VOLUME_SIZE:$lv:1} in
2047                                         [mMgG]) local s=${VOLUME_SIZE:0:$lv} m=$((s * 1000))
2048                                                 case ${VOLUME_SIZE:$lv:1} in
2049                                                         [Gg]) (( m >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - m)) ;;
2050                                                         [Mm]) (( ${VOLUME_SIZE:0:$lv} >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - s)) ;;
2051                                                         *) ERR_SIZE=1
2052                                                 esac ;;
2053                                         *) ERR_SIZE=1
2054                                 esac
2055                         fi
2056                 fi
2057                 if (( ERR_SIZE )); then
2058                         msg "Invalid Logical Volume Size" "$_lvmerrlvsize"
2059                 else
2060                         break
2061                 fi
2062         done
2063
2064         return $ERR_SIZE
2065 }
2066
2067 lvm_mkgroup()
2068 {
2069         local named=''
2070         local txt="\nConfirm creation of volume group: $VGROUP\n\nwith the following partition(s):"
2071
2072         until [[ $named ]]; do
2073                 lvm_partitions || return 1
2074                 lvm_group_name || return 1
2075                 yesno "$_lvmnew" "$txt $LVM_PARTS\n" && named=true
2076         done
2077
2078         vgcreate -f "$VGROUP" $LVM_PARTS >/dev/null 2>$ERR
2079         errshow "vgcreate -f $VGROUP $LVM_PARTS" || return 1
2080
2081         SIZE=$(vgdisplay "$VGROUP" | awk '/VG Size/ { gsub(/[^0-9.]/, ""); print int($0) }')
2082         SIZE_UNIT="$(vgdisplay "$VGROUP" | awk '/VG Size/ { print substr($NF, 0, 1) }')"
2083
2084         if [[ $SIZE_UNIT == 'G' ]]; then
2085                 VGROUP_MB=$((SIZE * 1000))
2086         else
2087                 VGROUP_MB=$SIZE
2088         fi
2089
2090         msg "$_lvmnew" "\nVolume group: $VGROUP ($SIZE $SIZE_UNIT) has been created\n"
2091         return 0
2092 }
2093
2094 lvm_del_all()
2095 {
2096         local v pv
2097         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
2098         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
2099         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
2100
2101         if [[ $VGROUP || $v || $pv ]]; then
2102                 if yesno "$_lvmdelall" "$_lvmdelask"; then
2103                         for i in $v; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1; done
2104                         for i in $VGROUP; do vgremove -f "$i" >/dev/null 2>&1; done
2105                         for i in $pv; do pvremove -f "$i" >/dev/null 2>&1; done
2106                         LVM=''
2107                 fi
2108         else
2109                 msg "Delete LVM" "\nNo LVMs to remove...\n" 2
2110                 LVM=''
2111         fi
2112 }
2113
2114 lvm_delgroup()
2115 {
2116         DEL_VG=''
2117         VOL_GROUP_LIST=''
2118
2119         for i in $(lvs --noheadings | awk '{print $2}' | uniq); do
2120                 VOL_GROUP_LIST+="$i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}') "
2121         done
2122         [[ $VOL_GROUP_LIST ]] || { msg "No Groups" "\nNo volume groups found."; return 1; }
2123
2124         dlg DEL_VG menu "Logical Volume Management" "\nSelect volume group to delete.\n\nAll logical volumes within will also be deleted." $VOL_GROUP_LIST
2125         [[ $DEL_VG ]]
2126 }
2127
2128 lvm_extra_lvs()
2129 {
2130         while (( VOL_COUNT > 1 )); do
2131                 lvm_volume_name "$_lvmlvname" && get_lv_size || return 1
2132                 lvcreate -L "$VOLUME_SIZE" "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
2133                 errshow "lvcreate -L $VOLUME_SIZE $VGROUP -n $VNAME" || return 1
2134                 msg "$_lvmnew (LV:$VOL_COUNT)" "\nDone, logical volume (LV) $VNAME ($VOLUME_SIZE) has been created.\n"
2135                 (( VOL_COUNT-- ))
2136         done
2137         return 0
2138 }
2139
2140 lvm_partitions()
2141 {
2142         part_find 'part|crypt' || return 1
2143         PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")"
2144         dlg LVM_PARTS check "$_lvmnew" "\nSelect the partition(s) to use for the physical volume (PV)." $PARTS
2145         [[ $LVM_PARTS ]]
2146 }
2147
2148 lvm_group_name()
2149 {
2150         VGROUP=''
2151         until [[ $VGROUP ]]; do
2152                 dlg VGROUP input "$_lvmnew" "$_lvmvgname" "mygroup"
2153                 if [[ -z $VGROUP ]]; then
2154                         return 1
2155                 elif [[ ${VGROUP:0:1} == "/" || $VGROUP =~ \ |\' ]] || lsblk | grep -q "$VGROUP"; then
2156                         msg "LVM Name Error" "$_lvmerrvgname"
2157                         VGROUP=''
2158                 fi
2159         done
2160         return 0
2161 }
2162
2163 lvm_volume_name()
2164 {
2165         VNAME=''
2166         local txt="$1" default="mainvolume"
2167         (( VOL_COUNT > 1 )) && default="extravolume$VOL_COUNT"
2168         until [[ $VNAME ]]; do
2169                 dlg VNAME input "$_lvmnew (LV:$VOL_COUNT)" "\n$txt" "$default"
2170                 if [[ -z $VNAME ]]; then
2171                         return 1
2172                 elif [[ ${VNAME:0:1} == "/" || $VNAME =~ \ |\' ]] || lsblk | grep -q "$VNAME"; then
2173                         msg "LVM Name Error" "$_lvmerlvname"
2174                         VNAME=''
2175                 fi
2176         done
2177         return 0
2178 }
2179
2180 ###############################################################################
2181 # luks functions
2182
2183 luks_menu()
2184 {
2185         local choice
2186         is_bg_install || return 1
2187         dlg choice menu "LUKS Encryption" "$_luksmenu" \
2188                 "$_luksnew"  "cryptsetup -q luksFormat" \
2189                 "$_luksopen" "cryptsetup open --type luks" \
2190                 "$_luksadv"  "cryptsetup -q -s -c luksFormat" \
2191                 "back"       "Return to the main menu"
2192
2193         case "$choice" in
2194                 "$_luksnew") luks_basic || return 1 ;;
2195                 "$_luksopen") luks_open || return 1 ;;
2196                 "$_luksadv") luks_advanced || return 1 ;;
2197         esac
2198
2199         return 0
2200 }
2201
2202 luks_open()
2203 {
2204         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2205         umount_dir $MNT
2206         part_find 'part|crypt|lvm' || return 1
2207
2208         if (( COUNT == 1 )); then
2209                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2210         else
2211                 dlg LUKS_PART menu "$_luksopen" "\nSelect which partition to open." $PARTS
2212         fi
2213
2214         [[ $LUKS_PART ]] || return 1
2215
2216         luks_pass "$_luksopen" || return 1
2217         msg "$_luksopen" "\nOpening encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2218         cryptsetup open --type luks "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2219         errshow "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" || return 1
2220         LUKS='encrypted'; luks_show
2221         return 0
2222 }
2223
2224 luks_pass()
2225 {
2226         LUKS_PASS=''
2227         local t="$1" op="$2" v='' p='' p2=''
2228
2229         until [[ $LUKS_PASS ]]; do
2230                 i=0
2231                 tput cnorm
2232                 if [[ $op ]]; then
2233                         dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " \
2234                                 --mixedform "\nEnter the password to decrypt $ROOT_PART\n\nThis is needed to create a keyfile." 0 0 0 \
2235                                 "Password:"  1 1 '' 1 11 "$COLUMNS" 0 1 \
2236                                 "Password2:" 2 1 '' 2 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2237
2238                 else
2239                         dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " --mixedform "$_luksomenu" 0 0 0 \
2240                                 "Name:"      1 1 "${LUKS_NAME:-cryptroot}" 1  7 "$COLUMNS" 0 0 \
2241                                 "Password:"  2 1 ''                        2 11 "$COLUMNS" 0 1 \
2242                                 "Password2:" 3 1 ''                        3 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2243
2244                 fi
2245
2246                 while read -r line; do
2247                         if [[ $op ]]; then
2248                                 case $i in
2249                                         0) p="$line" ;;
2250                                         1) p2="$line" ;;
2251                                 esac
2252                         else
2253                                 case $i in
2254                                         0) n="$line" ;;
2255                                         1) p="$line" ;;
2256                                         2) p2="$line" ;;
2257                                 esac
2258                         fi
2259                         (( i++ ))
2260                 done < "$ANS"
2261
2262                 if [[ -z $op && -z $n ]]; then
2263                         msg "Name Empty" "\nEncrypted device name cannot be empty.\n\nPlease try again.\n" 2
2264                 elif [[ -z $p || "$p" != "$p2" ]]; then
2265                         [[ $op ]] || LUKS_NAME="$n"
2266                         msg "Password Mismatch" "\nThe passwords entered do not match.\n\nPlease try again.\n" 2
2267                 else
2268                         [[ $op ]] || LUKS_NAME="$n"
2269                         LUKS_PASS="$p"
2270                 fi
2271         done
2272
2273         return 0
2274 }
2275
2276 luks_show()
2277 {
2278         sleep 0.5
2279         msg "$_luksnew" "\nEncrypted partition opened and ready for mounting.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE "$LUKS_PART")\n\n"
2280 }
2281
2282 luks_setup()
2283 {
2284         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2285         umount_dir $MNT
2286         part_find 'part|lvm' || return 1
2287
2288         if [[ $AUTO_ROOT_PART ]]; then
2289                 LUKS_PART="$AUTO_ROOT_PART"
2290         elif (( COUNT == 1 )); then
2291                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2292         else
2293                 dlg LUKS_PART menu "$_luksnew" "\nSelect the partition you want to encrypt." $PARTS
2294         fi
2295
2296         [[ $LUKS_PART ]] || return 1
2297         luks_pass "$_luksnew"
2298 }
2299
2300 luks_basic()
2301 {
2302         luks_setup || return 1
2303         msg "$_luksnew" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2304         cryptsetup -q luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2305         errshow "cryptsetup -q luksFormat $LUKS_PART" || return 1
2306         cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2307         errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2308         LUKS='encrypted'; luks_show
2309         return 0
2310 }
2311
2312 luks_keyfile()
2313 {
2314         if [[ ! -e $MNT/crypto_keyfile.bin ]]; then
2315                 # printf "Creating LUKS keyfile /crypto_keyfile.bin\n"
2316                 local n
2317                 n="$(lsblk -lno NAME,UUID,TYPE | awk "/$LUKS_UUID/"' && /part|crypt|lvm/ {print $1}')"
2318                 local mkkey="dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin"
2319                 mkkey="$mkkey && chmod 000 /crypto_keyfile.bin"
2320                 mkkey="$mkkey && cryptsetup luksAddKey /dev/$n /crypto_keyfile.bin <<< '$LUKS_PASS'"
2321                 chrun "$mkkey"
2322                 sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf 2>$ERR
2323         fi
2324
2325         return 0
2326 }
2327
2328 luks_advanced()
2329 {
2330         if luks_setup; then
2331                 local cipher
2332                 dlg cipher input "LUKS Encryption" "$_lukskey" "-s 512 -c aes-xts-plain64"
2333                 [[ $cipher ]] || return 1
2334                 msg "$_luksadv" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2335                 cryptsetup -q $cipher luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2336                 errshow "cryptsetup -q $cipher luksFormat $LUKS_PART" || return 1
2337                 cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2338                 errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2339                 luks_show
2340                 return 0
2341         fi
2342         return 1
2343 }
2344
2345 ###############################################################################
2346 # simple functions
2347 # some help avoid repetition and improve usability of some commands
2348 # others are initial setup functions used before reaching the main loop
2349
2350 ofn()
2351 {
2352         # does $2 contain $1
2353         [[ "$2" == *"$1"* ]] && printf "on" || printf "off"
2354 }
2355
2356 die()
2357 {
2358         # cleanup and exit the installer cleanly with exit code $1
2359         # when ecode is 127 unmount /run/archiso/bootmnt and reboot
2360         local ecode="$1"
2361
2362         trap - INT
2363         tput cnorm
2364         if [[ -d $MNT ]] && command cd /; then
2365                 umount_dir $MNT
2366                 if (( ecode == 127 )); then
2367                         umount_dir /run/archiso/bootmnt
2368                         sleep 0.5
2369                         reboot -f
2370                 fi
2371         fi
2372         termcol
2373         exit "$ecode"
2374 }
2375
2376 dlg()
2377 {
2378         local var="$1"   # assign output from dialog to var
2379         local dlg_t="$2" # display a dialog of type dlg_t (menu, check, input)
2380         local title="$3" # dialog title
2381         local body="$4"  # dialog message
2382         local n=0        # number of items to display
2383
2384         shift 4  # shift off args assigned above
2385
2386         # when passed a large amount of arguments (menu list) adjust menu height
2387         (( ($# / 2) > SHL )) && n=$SHL
2388         
2389         tput civis
2390         case "$dlg_t" in
2391                 menu) dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --menu "$body" 0 0 $n "$@" 2>"$ANS" || return 1 ;;
2392                 check) dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --checklist "$body" 0 0 $n "$@" 2>"$ANS" || return 1 ;;
2393                 input)
2394                         tput cnorm
2395                         local def="$1" # assign default value for input
2396                         shift
2397                         if [[ $1 == 'limit' ]]; then
2398                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --max-input 63 --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2399                         else
2400                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2401                         fi
2402                         ;;
2403         esac
2404         # if answer file isn't empty read from it into $var
2405         [[ -s "$ANS" ]] && printf -v "$var" "%s" "$(< "$ANS")"
2406 }
2407
2408 msg()
2409 {
2410         # displays a message dialog
2411         # when more than 2 args the message will disappear after sleep time ($3)
2412         local title="$1"
2413         local body="$2"
2414         shift 2
2415         tput civis
2416         if (( $# )); then
2417                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --sleep "$1" --title " $title " --infobox "$body\n" 0 0
2418         else
2419                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --msgbox "$body\n" 0 0
2420         fi
2421 }
2422
2423 yesno()
2424 {
2425         local title="$1" body="$2" yes='Yes' no='No'
2426         (( $# >= 3 )) && yes="$3"
2427         (( $# >= 4 )) && no="$4"
2428         tput civis
2429         if (( $# == 5 )); then
2430                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --defaultno --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2431         else
2432                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2433         fi
2434 }
2435
2436 chrun()
2437 {
2438         arch-chroot "$MNT" bash -c "$1"
2439 }
2440
2441 debug()
2442 {
2443         export PS4='| ${BASH_SOURCE} LINE:${LINENO} FUNC:${FUNCNAME[0]:+ ${FUNCNAME[0]}()} |>  '
2444         set -x
2445         exec 3>| $DBG
2446         BASH_XTRACEFD=3
2447         DEBUG=true
2448 }
2449
2450 sigint()
2451 {
2452         printf "\n^C caught, cleaning up...\n"
2453         die 1
2454 }
2455
2456 termcol()
2457 {
2458         local colors=(
2459         "\e]P0191919" # #191919
2460         "\e]P1D15355" # #D15355
2461         "\e]P2609960" # #609960
2462         "\e]P3FFCC66" # #FFCC66
2463         "\e]P4255A9B" # #255A9B
2464         "\e]P5AF86C8" # #AF86C8
2465         "\e]P62EC8D3" # #2EC8D3
2466         "\e]P7949494" # #949494
2467         "\e]P8191919" # #191919
2468         "\e]P9D15355" # #D15355
2469         "\e]PA609960" # #609960
2470         "\e]PBFF9157" # #FF9157
2471         "\e]PC4E88CF" # #4E88CF
2472         "\e]PDAF86C8" # #AF86C8
2473         "\e]PE2ec8d3" # #2ec8d3
2474         "\e]PFE1E1E1" # #E1E1E1
2475         )
2476
2477         [[ $TERM == 'linux' ]] && printf "%b" "${colors[@]}" && clear
2478 }
2479
2480 errshow()
2481 {
2482         [ $? -eq 0 ] && return 0
2483
2484         local fatal=0 err=""
2485         err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")"
2486
2487         (( $1 == 1 )) && { fatal=1; shift; }
2488
2489         local txt="\nThe command exited abnormally:\n\n$1\n\n"
2490
2491         if [[ $err ]]; then
2492                 txt+="With the following message:\n\n$err\n\n"
2493         else
2494                 txt+="With no error message:\n\n"
2495         fi
2496
2497         if (( fatal )); then
2498                 txt+="Errors at this stage are fatal and the install cannot continue.\n"
2499         else
2500                 txt+="Errors at this stage are non-fatal and can be either fixed or ignored depending on the error.\n"
2501         fi
2502         msg "Install Error" "$txt"
2503
2504         if (( fatal )); then
2505                 [[ -r $DBG && $TERM == 'linux' ]] && less "$DBG"
2506                 die 1
2507         fi
2508
2509         return 1
2510 }
2511
2512 load_bcm()
2513 {
2514         msg "Broadcom Wireless Setup" "\nDetected chipset is Broadcom BCM4352\n\nDisabling bcma/b43 modules and loading wl module.\n" 1
2515         { rmmod wl; rmmod bcma; rmmod b43; rmmod ssb; modprobe wl; depmod -a; } >/dev/null 2>&1
2516         BROADCOM_WL=true
2517 }
2518
2519 prechecks()
2520 {
2521         local i=1
2522
2523         if (( $1 >= 0 )) && ! grep -qw "$MNT" /proc/mounts; then
2524                 msg "Not Mounted" "\nPartition(s) must be mounted first.\n" 2
2525                 SEL=4 i=0
2526         elif [[ $1 -ge 1 && -z $BOOTLDR ]]; then
2527                 msg "No Bootloader" "\nBootloader must be selected first.\n" 2
2528                 SEL=5 i=0
2529         elif [[ $1 -ge 2 && (-z $NEWUSER || -z $USER_PASS) ]]; then
2530                 msg "No User" "\nA user must be created first.\n" 2
2531                 SEL=6 i=0
2532         elif [[ $1 -ge 3 && -z $CONFIG_DONE ]]; then
2533                 msg "Not Configured" "\nSystem configuration must be done first.\n" 2
2534                 SEL=7 i=0
2535         fi
2536         (( i )) # return code
2537 }
2538
2539 umount_dir()
2540 {
2541         mount | grep -q 'swap' && swapoff -a
2542         for dir; do
2543                 [[ -d $dir && "$(mount | grep "on $dir ")" ]] || continue
2544                 umount "$dir" 2>/dev/null || { sleep 0.5; umount -f "$dir" 2>/dev/null || umount -l "$dir"; }
2545         done
2546 }
2547
2548 chk_connect()
2549 {
2550         msg "Network Connect" "\nVerifying network connection\n" 1
2551         curl -sIN --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200'
2552 }
2553
2554 net_connect()
2555 {
2556         if chk_connect; then
2557                 return 0
2558         else
2559                 if hash nmtui >/dev/null 2>&1; then
2560                         tput civis
2561                         if [[ $TERM == 'linux' ]]; then
2562                                 printf "%b" "\e]P1191919" "\e]P4191919"
2563                                 nmtui-connect
2564                                 printf "%b" "\e]P1D15355" "\e]P4255a9b"
2565                         else
2566                                 nmtui-connect
2567                         fi
2568                         chk_connect
2569                 else
2570                         return 1
2571                 fi
2572         fi
2573 }
2574
2575 is_bg_install()
2576 {
2577         [[ $RSYNC_PID || $MIRROR_PID ]] || return 0
2578         msg "Install Running" "\nA background install process is currently running.\n" 2
2579         return 1
2580 }
2581
2582 system_devices()
2583 {
2584         IGNORE_DEV="$(lsblk -lno NAME,MOUNTPOINT | awk '/\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')"
2585
2586         if [[ $IGNORE_DEV ]]; then
2587                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')"
2588         else
2589                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ {print "/dev/" $1 " " $2}')"
2590         fi
2591
2592         if [[ -z $SYS_DEVS ]]; then
2593                 msg "Device Error" "\nNo available devices...\n\nExiting..\n" 2
2594                 die 1
2595         fi
2596
2597         DEV_COUNT=0
2598         while read -r line; do
2599                 (( DEV_COUNT++ ))
2600         done <<< "$SYS_DEVS"
2601 }
2602
2603 system_identify()
2604 {
2605         if [[ $VM ]]; then
2606                 UCODE=''
2607         elif grep -q 'AuthenticAMD' /proc/cpuinfo; then
2608                 UCODE="amd-ucode"
2609         elif grep -q 'GenuineIntel' /proc/cpuinfo; then
2610                 UCODE="intel-ucode"
2611         fi
2612
2613         modprobe -q efivarfs >/dev/null 2>&1
2614
2615         _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."
2616         if [[ -d /sys/firmware/efi/efivars ]]; then
2617                 export SYS="UEFI"
2618                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars
2619                 _prep+="\n  - An EFI boot partition mounted."
2620         else
2621                 export SYS="BIOS"
2622         fi
2623         _prep+="\n\nOnce finished mounting, a portion of the install can be done in the background while you continue configuring the system:\n"
2624         _prep+="\n  - Choose the system bootloader.\n  - Create a user and password."
2625         _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."
2626 }
2627
2628 ###############################################################################
2629 # entry point
2630
2631 # enable some nicer colours in the linux console
2632 termcol
2633
2634 if (( UID != 0 )); then
2635         msg "Not Root" "\nThis installer must be run as root or using sudo.\n\nExiting..\n" 2
2636         die 1
2637 elif ! grep -qwm 1 'lm' /proc/cpuinfo; then
2638         msg "Not x86_64 Architecture" "\nThis installer only supports x86_64 architectures.\n\nExiting..\n" 2
2639         die 1
2640 elif [[ $1 =~ (-d|--debug) ]]; then
2641         debug
2642 fi
2643
2644 # trap ^C to perform cleanup
2645 trap sigint INT
2646
2647 system_identify
2648 system_devices
2649
2650 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"
2651
2652 select_keymap || { clear; die 0; }
2653
2654 # try to fix problematic broadcom wireless chipset before network check
2655 lspci -vnn -d 14e4: | grep -q 'BCM4352' && load_bcm
2656
2657 net_connect || { msg "Not Connected" "\nThis installer requires an active internet connection.\n\nExiting..\n" 2; die 1; }
2658
2659 while :; do
2660         main
2661 done
2662
2663 # vim:fdm=marker:fmr={,}