OSDN Git Service

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