OSDN Git Service

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