OSDN Git Service

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