OSDN Git Service

Update partition shrink function
[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.40"     # 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                                 "shrink"  "Shrink an existing ext2/3/4 or ntfs partition to make room for a new 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                                 "shrink" "Shrink an existing ext2/3/4 or ntfs partition to make room for a new 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 == 'shrink' ]]; then
739                         part_shrink "$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_shrink()
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                                 # get device size info
925                                 num="${part: -1}"
926                                 end=$(parted -s "$device" unit KiB print | awk '/^\s*'"$num"'/ {print int($3)}')                    # part size in KiB
927                                 devsize=$(parted -s "$device" unit KiB print | awk '/Disk '"${device//\//\\/}"':/ {print int($3)}') # whole device size in KiB
928
929                                 # minimum in KiB (2^10)
930                                 case "$fs" in
931                                         ext[2-4]) min=$(dumpe2fs -h "$part" |& awk '/Block count/{count=$NF} /Block size/{size=$NF} END{print int((count * size) / 1024)}') ;;
932                                         ntfs) min=$(ntfsresize -f -m "$part" | awk 'NR == 2 {print int(($NF * 1000 * 1000) / 1024)}') ;;
933                                 esac
934
935                                 # KiB -> MiB
936                                 mbmin=$((min / 1024))
937                                 mbend=$((end / 1024))
938
939                                 # get new size from user
940                                 tput cnorm
941                                 if dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " Resize: $part " --rangebox "$_resize" 17 "$COLUMNS" "$mbmin" "$mbend" $((mbend / 2)) 2>$ANS; then
942                                         (( (size = $(< "$ANS")) < mbend )) || return 1
943                                         size=$((size * 1024))
944                                 else
945                                         return 1
946                                 fi
947
948                                 clear
949                                 case "$fs" in
950                                         ext[2-4])
951                                                 e2fsck -f "$part"; sleep 0.5
952                                                 resize2fs -f "$part" ${size}K 2>$ERR # K=2^10 bytes
953                                                 errshow "resize2fs -f $part ${size}K" || return 1
954                                                 ;;
955                                         ntfs)
956                                                 ntfsresize -fc "$part" || { msg "Resize" "\nThe ntfs partition $part cannot be resized because it is scheduled for a consistency check.\n\nTo do a consistency check in windows open command prompt as admin and run:\n\n\tchkdsk /f /r /x\n"; return 1; }
957                                                 ntfsresize -ff --size $(( (size * 1024) / 1000 ))k "$part" 2>$ERR # k=10^3 bytes
958                                                 errshow "ntfsresize -f -s $(( (size * 1024) / 1000 ))k $part" || return 1
959                                                 ;;
960                                 esac
961
962                                 echo "filesystem shrunk successfully, now the partition"
963                                 sleep 0.5
964                                 parted "$device" resizepart "$num" ${size}KiB || return 1
965                                 (( size++ ))
966                                 sleep 0.5
967
968                                 if [[ $devsize == "$end" ]]; then
969                                         parted -s "$device" mkpart primary ext4 ${size}KiB 100% 2>$ERR
970                                         errshow "parted -s $device mkpart primary ext4 ${size}KiB 100%" || return 1
971                                 else
972                                         parted -s "$device" mkpart primary ext4 ${size}KiB ${end}KiB 2>$ERR
973                                         errshow "parted -s $device mkpart primary ext4 ${size}KiB ${end}KiB" || return 1
974                                 fi
975
976                                 msg "Resize Complete" "\n$part has been successfully resized to $((size / 1024))M.\n"
977                                 ;;
978                         *) msg "Invalid Filesystem: $fs" "\nResizing only supports ext and ntfs.\n\nFor unformatted partitions, cfdisk can be used.\n" ;;
979                 esac
980         fi
981 }
982
983 part_device()
984 {
985         if [[ $DEV_COUNT -eq 1 && $SYS_DEVS ]]; then
986                 DEVICE="$(awk '{print $1}' <<< "$SYS_DEVS")"
987         elif (( DEV_COUNT > 1 )); then
988                 if [[ $1 ]]; then
989                         dlg DEVICE menu "Boot Device" "\nSelect the device to use for bootloader install." $SYS_DEVS
990                 else
991                         dlg DEVICE menu "Select Device" "$_device" $SYS_DEVS
992                 fi
993                 [[ $DEVICE ]] || return 1
994         elif [[ $DEV_COUNT -lt 1 && ! $1 ]]; then
995                 msg "Device Error" "\nNo available devices.\n\nExiting..\n" 2; die 1
996         fi
997
998         [[ $1 ]] && BOOT_DEV="$DEVICE"
999
1000         return 0
1001 }
1002
1003 part_bootdev()
1004 {
1005         BOOT_DEV="${BOOT_PART%[1-9]}"
1006         BOOT_PART_NUM="${BOOT_PART: -1}"
1007         [[ $BOOT_PART = /dev/nvme* ]] && BOOT_DEV="${BOOT_PART%p[1-9]}"
1008         if [[ $SYS == 'UEFI' ]]; then
1009                 parted -s $BOOT_DEV set $BOOT_PART_NUM esp on >/dev/null 2>&1
1010         else
1011                 parted -s $BOOT_DEV set $BOOT_PART_NUM boot on >/dev/null 2>&1
1012         fi
1013         return 0
1014 }
1015
1016 part_cryptlv()
1017 {
1018         local part="$1" devs=""
1019         devs="$(lsblk -lno NAME,FSTYPE,TYPE)"
1020
1021         # Identify if $part is LUKS+LVM, LVM+LUKS, LVM alone, or LUKS alone
1022         if lsblk -lno TYPE "$part" | grep -q 'crypt'; then
1023                 LUKS='encrypted'
1024                 LUKS_NAME="${part#/dev/mapper/}"
1025
1026                 for dev in $(awk '/lvm/ && /crypto_LUKS/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do
1027                         if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
1028                                 LUKS_DEV="$LUKS_DEV cryptdevice=$dev:$LUKS_NAME"
1029                                 LVM='logical volume'
1030                                 break
1031                         fi
1032                 done
1033
1034                 for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do
1035                         if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
1036                                 LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')"
1037                                 LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
1038                                 break
1039                         fi
1040                 done
1041
1042         elif lsblk -lno TYPE "$part" | grep -q 'lvm'; then
1043                 LVM='logical volume'
1044                 VNAME="${part#/dev/mapper/}"
1045
1046                 for dev in $(awk '/crypt/ && /lvm2_member/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do
1047                         if lsblk -lno NAME "$dev" | grep -q "$VNAME"; then
1048                                 LUKS_NAME="${dev/\/dev\/mapper\//}"
1049                                 break
1050                         fi
1051                 done
1052
1053                 for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do
1054                         if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
1055                                 LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')"
1056                                 LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
1057                                 LUKS='encrypted'
1058                                 break
1059                         fi
1060                 done
1061         fi
1062 }
1063
1064 part_countdec()
1065 {
1066         for i in "$@"; do
1067                 if (( COUNT > 0 )); then
1068                         PARTS="$(sed "/${i//\//\\/}/d" <<< "$PARTS")"
1069                         (( COUNT-- ))
1070                 fi
1071         done
1072 }
1073
1074 part_mountconf()
1075 {
1076         if grep -qw "$1" /proc/mounts; then
1077                 msg "Mount Success" "\nPartition $1 mounted at $2\n" 1
1078                 part_countdec "$1"
1079                 return 0
1080         else
1081                 msg "Mount Fail" "\nPartition $1 failed to mount at $2\n" 2
1082                 return 1
1083         fi
1084 }
1085
1086 ###############################################################################
1087 # mounting menus
1088
1089 select_menu()
1090 {
1091         check_background_install || return 0
1092         lvm_detect
1093         umount_dir $MNT
1094         part_find 'part|lvm|crypt' || { SEL=2; return 1; }
1095
1096         [[ $LUKS && $LUKS_PART ]] && part_countdec $LUKS_PART
1097         [[ $LVM && $LVM_PARTS ]] && part_countdec $LVM_PARTS
1098
1099         select_root_partition || return 1
1100
1101         if [[ $SYS == 'UEFI' ]]; then
1102                 select_efi_partition || { BOOT_PART=''; return 1; }
1103         elif (( COUNT > 0 )); then
1104                 select_boot_partition || { BOOT_PART=''; return 1; }
1105         fi
1106
1107         if [[ $BOOT_PART ]]; then
1108                 part_mount "$BOOT_PART" "/$BOOTDIR" && SEP_BOOT=true || return 1
1109                 part_bootdev
1110         fi
1111
1112         select_swap || return 1
1113         select_extra_partitions || return 1
1114         install_background || return 1
1115
1116         return 0
1117 }
1118
1119 select_swap()
1120 {
1121         dlg SWAP_PART menu "Swap Setup" "\nSelect whether to use a swapfile, swap partition, or none." \
1122                 "none" "Don't allocate any swap space" \
1123                 "swapfile" "Allocate $SYS_MEM at /swapfile" \
1124                 $PARTS
1125
1126         if [[ -z $SWAP_PART || $SWAP_PART == "none" ]]; then
1127                 SWAP_PART=''
1128                 return 0
1129         elif [[ $SWAP_PART == "swapfile" ]]; then
1130                 local i=0
1131                 until [[ ${SWAP_SIZE:0:1} =~ [1-9] && ${SWAP_SIZE: -1} =~ (M|G) ]]; do
1132                         (( 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
1133                         dlg SWAP_SIZE input "Swap Setup" "$_swapsize" "$SYS_MEM" || { SWAP_PART=''; SWAP_SIZE=''; return 1; }
1134                         (( i++ ))
1135                 done
1136                 part_swap "$MNT/$SWAP_PART"
1137                 SWAP_PART="/$SWAP_PART"
1138         elif [[ $PARTS == *"$SWAP_PART"* ]]; then
1139                 part_swap $SWAP_PART
1140                 part_countdec $SWAP_PART
1141                 SWAP_SIZE="$(lsblk -lno SIZE $SWAP_PART)"
1142         else
1143                 return 1
1144         fi
1145
1146         return 0
1147 }
1148
1149 select_mntopts()
1150 {
1151         local fs="$1" opts=''
1152         local title="${fs^} Mount Options"
1153
1154         for i in ${FS_OPTS[$fs]}; do
1155                 opts+="$i - off "
1156         done
1157
1158         until [[ $MNT_OPTS ]]; do
1159                 dlg MNT_OPTS check "$title" "$_mount" $opts
1160                 [[ $MNT_OPTS ]] || return 1
1161                 MNT_OPTS="${MNT_OPTS// /,}"
1162                 yesno "$title" "\nConfirm the following options: $MNT_OPTS\n" || MNT_OPTS=''
1163         done
1164
1165         return 0
1166 }
1167
1168 select_mountpoint()
1169 {
1170         EXMNT=''
1171         until [[ $EXMNT ]]; do
1172                 dlg EXMNT input "Extra Mount $part" "$_exmnt" "/" || return 1
1173                 if [[ ${EXMNT:0:1} != "/" || ${#EXMNT} -le 1 || $EXMNT =~ \ |\' || $EXMNTS == *"$EXMNT"* ]]; then
1174                         msg "Mountpoint Error" "$_errexpart"
1175                         EXMNT=''
1176                 fi
1177         done
1178         return 0
1179 }
1180
1181 select_filesystem()
1182 {
1183         local part="$1" fs='' cur=''
1184         local txt="\nSelect which filesystem to use for: $part\n\nDefault:  ext4"
1185         cur="$(lsblk -lno FSTYPE "$part" 2>/dev/null)"
1186
1187         until [[ $fs ]]; do
1188                 if [[ $cur && ($part != "$ROOT_PART" || $FORMATTED == *"$part"*) ]]; then
1189                         dlg fs menu "Filesystem" "$txt\nCurrent:  $cur" skip - ext4 - ext3 - ext2 - vfat - ntfs - f2fs - jfs - xfs - nilfs2 - reiserfs - || return 1
1190                 else
1191                         dlg fs menu "Filesystem" "$txt" ext4 - ext3 - ext2 - vfat - ntfs - f2fs - jfs - xfs - nilfs2 - reiserfs - || return 1
1192                 fi
1193                 [[ $fs == 'skip' ]] && return 0
1194                 yesno "Filesystem" "\nFormat $part as $fs?\n" || fs=''
1195         done
1196         part_format "$part" "$fs"
1197 }
1198
1199 select_efi_partition()
1200 {
1201         if (( COUNT == 1 )); then
1202                 BOOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
1203         elif [[ $AUTO_BOOT_PART ]]; then
1204                 BOOT_PART="$AUTO_BOOT_PART"
1205                 return 0 # were done here
1206         else
1207                 dlg BOOT_PART menu "EFI Partition" "$_uefi" $PARTS
1208         fi
1209
1210         [[ $BOOT_PART ]] || return 1
1211
1212         if grep -q 'fat' <<< "$(fsck -N "$BOOT_PART")"; then
1213                 local txt="\nIMPORTANT:\n\nThe EFI partition $BOOT_PART $_format"
1214                 if yesno "Format EFI Partition" "$txt" "Format $BOOT_PART" "Skip Formatting" 1; then
1215                         part_format "$BOOT_PART" "vfat" 2
1216                 fi
1217         else
1218                 part_format "$BOOT_PART" "vfat" 2
1219         fi
1220
1221         return 0
1222 }
1223
1224 select_boot_partition()
1225 {
1226         if [[ $AUTO_BOOT_PART && ! $LVM ]]; then
1227                 BOOT_PART="$AUTO_BOOT_PART"; return 0 # were done here
1228         elif [[ $LUKS && ! $LVM ]]; then
1229                 dlg BOOT_PART menu "Boot Partition" "$_biosluks" $PARTS
1230                 [[ $BOOT_PART ]] || return 1
1231         else
1232                 dlg BOOT_PART menu "Boot Partition" "$_bios" "skip" "don't use a separate boot" $PARTS
1233                 [[ -z $BOOT_PART || $BOOT_PART == "skip" ]] && { BOOT_PART=''; return 0; }
1234         fi
1235
1236         if grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")"; then
1237                 local txt="\nIMPORTANT:\n\nThe boot partition $BOOT_PART $_format"
1238                 if yesno "Format Boot Partition" "$txt" "Format $BOOT_PART" "Skip Formatting" 1; then
1239                         part_format "$BOOT_PART" "ext4" 2
1240                 fi
1241         else
1242                 part_format "$BOOT_PART" "ext4" 2
1243         fi
1244         return 0
1245 }
1246
1247 select_root_partition()
1248 {
1249         if [[ $AUTO_ROOT_PART && -z $LVM && -z $LUKS ]]; then
1250                 ROOT_PART="$AUTO_ROOT_PART"
1251         elif (( COUNT == 1 )); then
1252                 ROOT_PART="$(awk 'NR==1 {print $1}' <<< "$PARTS")"
1253         else
1254                 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
1255         fi
1256         [[ $ROOT_PART ]] || return 1
1257
1258         select_filesystem "$ROOT_PART" || { ROOT_PART=''; return 1; }
1259         part_mount "$ROOT_PART" || { ROOT_PART=''; return 1; }
1260
1261         return 0
1262 }
1263
1264 select_extra_partitions()
1265 {
1266         local part
1267         while (( COUNT > 0 )); do
1268                 part=''
1269                 dlg part menu 'Mount Extra' "$_expart" 'done' 'finish mounting step' $PARTS || break
1270                 if [[ $part == 'done' ]]; then
1271                         break
1272                 elif select_filesystem "$part" && select_mountpoint && part_mount "$part" "$EXMNT"; then
1273                         EXMNTS+="$part: $EXMNT "
1274                         [[ $EXMNT == '/usr' && $HOOKS != *usr* ]] && HOOKS="usr $HOOKS"
1275                 else
1276                         return 1
1277                 fi
1278         done
1279         return 0
1280 }
1281
1282 ###############################################################################
1283 # installation
1284
1285 install_main()
1286 {
1287         clear
1288         tput cnorm
1289         install_base
1290         genfstab -U $MNT >$MNT/etc/fstab 2>$ERR
1291         errshow 1 "genfstab -U $MNT >$MNT/etc/fstab"
1292         [[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" $MNT/etc/fstab
1293         install_packages
1294         install_mkinitcpio
1295         install_boot
1296         chrun "hwclock --systohc --utc" || chrun "hwclock --systohc --utc --directisa"
1297         install_user
1298         install_xinit
1299         chrun "chown -Rf $NEWUSER:users /home/$NEWUSER"
1300         sleep 1
1301
1302         while true; do
1303                 dlg choice menu "Finalization" "$_edit" \
1304                         finished   "exit the installer and reboot" \
1305                         keyboard   "${EDIT_FILES[keyboard]}" \
1306                         console    "${EDIT_FILES[console]}" \
1307                         locale     "${EDIT_FILES[locale]}" \
1308                         hostname   "${EDIT_FILES[hostname]}" \
1309                         sudoers    "${EDIT_FILES[sudoers]}" \
1310                         mkinitcpio "${EDIT_FILES[mkinitcpio]}" \
1311                         fstab      "${EDIT_FILES[fstab]}" \
1312                         crypttab   "${EDIT_FILES[crypttab]}" \
1313                         bootloader "${EDIT_FILES[bootloader]}" \
1314                         pacman     "${EDIT_FILES[pacman]}" \
1315                         login      "${EDIT_FILES[login]}"
1316
1317                 if [[ -z $choice || $choice == 'finished' ]]; then
1318                         [[ $DEBUG == true && -r $DBG ]] && vim $DBG
1319                         die 127
1320                 else
1321                         local exists=''
1322                         for f in ${EDIT_FILES[$choice]}; do
1323                                 [[ -e ${MNT}$f ]] && exists+=" ${MNT}$f"
1324                         done
1325                         if [[ $exists ]]; then
1326                                 vim -O $exists
1327                         else
1328                                 msg "File Missing" "\nThe file(s) selected do not exist:\n\n${EDIT_FILES[$choice]}\n"
1329                         fi
1330                 fi
1331         done
1332 }
1333
1334 install_base()
1335 {
1336         if [[ $RSYNC_PID || $MIRROR_PID ]]; then
1337                 while kill -0 "$RSYNC_PID" 2>/dev/null || kill -0 "$MIRROR_PID" 2>/dev/null; do
1338                         clear; printf "\nOne or more background install processes are still running...\n"; sleep 1
1339                 done
1340                 trap - EXIT
1341                 unset RSYNC_PID MIRROR_PID
1342         elif [[ -d /run/archiso/sfs/airootfs/etc/skel ]]; then
1343                 rsync -ahv /run/archiso/sfs/airootfs/ $MNT/ 2>$ERR
1344                 errshow 1 "rsync -ahv /run/archiso/sfs/airootfs/ $MNT/"
1345                 install_mirrorlist "$MNT/etc/pacman.d/mirrorlist"
1346                 chrun "pacman -Syyu --noconfirm && pacman -S $BASE_PKGS --needed --noconfirm"
1347         else
1348                 mkdir -p /etc/pacman.d/mirrorlist
1349                 install_mirrorlist "/etc/pacman.d/mirrorlist"
1350                 pacstrap $MNT base $ISO_BASE 2>$ERR
1351                 errshow 1 "pacstrap $MNT base $KERNEL $ISO_BASE"
1352                 mkdir -p $MNT/etc/pacman.d/mirrorlist
1353                 cp -f "/etc/pacman.d/mirrorlist" "$MNT/etc/pacman.d/mirrorlist"
1354                 chrun "pacman -Syyu --noconfirm && pacman -S $BASE_PKGS --needed --noconfirm"
1355         fi
1356
1357         rm -rf $MNT/etc/mkinitcpio-archiso.conf
1358         find $MNT/usr/lib/initcpio -name 'archiso*' -type f -delete
1359         sed -i 's/volatile/auto/g' $MNT/etc/systemd/journald.conf
1360         find $MNT/boot -name '*-ucode.img' -delete
1361
1362         if [[ $VM ]]; then
1363                 find $MNT/etc/X11/xorg.conf.d/ -name '*.conf' -delete
1364         elif lspci | grep ' VGA ' | grep -q 'Intel'; then
1365                 cat > $MNT/etc/X11/xorg.conf.d/20-intel.conf << EOF
1366 Section "Device"
1367         Identifier  "Intel Graphics"
1368         Driver      "intel"
1369         Option      "TearFree" "true"
1370 EndSection
1371 EOF
1372         fi
1373
1374         [[ -e /run/archiso/sfs/airootfs && $KERNEL == 'linux' ]] && cp -vf $RUN/x86_64/vmlinuz $MNT/boot/vmlinuz-linux
1375
1376         cp -fv /etc/resolv.conf $MNT/etc/
1377         [[ -e /etc/NetworkManager/system-connections ]] && cp -rvf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/
1378
1379         echo "LANG=$MYLOCALE" > $MNT/etc/locale.conf
1380         cp -f $MNT/etc/locale.conf $MNT/etc/default/locale
1381         sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${MYLOCALE}/${MYLOCALE}/g" $MNT/etc/locale.gen
1382         chrun "locale-gen"
1383         chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZ /etc/localtime"
1384
1385         if [[ $BROADCOM_WL ]]; then
1386                 echo 'blacklist bcma' >> $MNT/etc/modprobe.d/blacklist.conf
1387                 rm -f $MNT/etc/modprobe/
1388         fi
1389
1390         cat > $MNT/etc/X11/xorg.conf.d/00-keyboard.conf << EOF
1391 # Use localectl(1) to instruct systemd-localed to update it.
1392 Section "InputClass"
1393         Identifier      "system-keyboard"
1394         MatchIsKeyboard "on"
1395         Option          "XkbLayout" "$KEYMAP"
1396 EndSection
1397 EOF
1398         cat > $MNT/etc/default/keyboard << EOF
1399 # KEYBOARD CONFIGURATION FILE
1400 # Consult the keyboard(5) manual page.
1401 XKBMODEL=""
1402 XKBLAYOUT="$KEYMAP"
1403 XKBVARIANT=""
1404 XKBOPTIONS=""
1405 BACKSPACE="guess"
1406 EOF
1407         printf "KEYMAP=%s\nFONT=%s\n" "$CMAP" "$FONT" > $MNT/etc/vconsole.conf
1408         echo "$MYHOST" > $MNT/etc/hostname
1409         cat > $MNT/etc/hosts << EOF
1410 127.0.0.1       localhost
1411 127.0.1.1       $MYHOST
1412 ::1                     localhost ip6-localhost ip6-loopback
1413 ff02::1         ip6-allnodes
1414 ff02::2         ip6-allrouters
1415 EOF
1416 }
1417
1418 install_boot()
1419 {
1420         if [[ $ROOT_PART == /dev/mapper* ]]; then
1421                 ROOT_PART_ID="$ROOT_PART"
1422         else
1423                 local uuid_type="UUID"
1424                 [[ $BOOTLDR =~ (systemd-boot|refind-efi|efistub) ]] && uuid_type="PARTUUID"
1425                 ROOT_PART_ID="$uuid_type=$(blkid -s $uuid_type -o value $ROOT_PART)"
1426         fi
1427
1428         if [[ $SYS == 'UEFI' ]]; then
1429                 find $MNT/$BOOTDIR/EFI/ -maxdepth 1 -mindepth 1 -iname "$DIST" -type d -delete
1430                 find $MNT/$BOOTDIR/EFI/ -maxdepth 1 -mindepth 1 -iname 'boot' -type d -delete
1431         fi
1432
1433         prerun_$BOOTLDR
1434         chrun "${BCMDS[$BOOTLDR]}" 2>$ERR
1435         errshow 1 "${BCMDS[$BOOTLDR]}"
1436
1437         if [[ -d $MNT/hostrun ]]; then
1438                 umount $MNT/hostrun/udev >/dev/null 2>&1
1439                 umount $MNT/hostrun/lvm >/dev/null 2>&1
1440                 rm -rf $MNT/hostrun >/dev/null 2>&1
1441         fi
1442
1443         if [[ $SYS == 'UEFI' ]]; then
1444                 # some UEFI firmware requires a generic esp/BOOT/BOOTX64.EFI
1445                 mkdir -pv $MNT/$BOOTDIR/EFI/BOOT
1446                 if [[ $BOOTLDR == 'grub' ]]; then
1447                         cp -fv $MNT/$BOOTDIR/EFI/$DIST/grubx64.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
1448                 elif [[ $BOOTLDR == 'syslinux' ]]; then
1449                         cp -rf $MNT/$BOOTDIR/EFI/syslinux/* $MNT/$BOOTDIR/EFI/BOOT/
1450                         cp -f $MNT/$BOOTDIR/EFI/syslinux/syslinux.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
1451                 elif [[ $BOOTLDR == 'refind-efi' ]]; then
1452                         sed -i '/#extra_kernel_version_strings/ c extra_kernel_version_strings linux-hardened,linux-zen,linux-lts,linux' $MNT/$BOOTDIR/EFI/refind/refind.conf
1453                         cp -fv $MNT/$BOOTDIR/EFI/refind/refind_x64.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
1454                 fi
1455         fi
1456
1457         return 0
1458 }
1459
1460 install_user()
1461 {
1462         chrun "chpasswd <<< 'root:$ROOT_PASS'" 2>$ERR
1463         errshow 1 "set root password"
1464         if [[ $MYSHELL != *zsh ]]; then
1465                 chrun "usermod -s $MYSHELL root" 2>$ERR
1466                 errshow 1 "usermod -s $MYSHELL root"
1467                 [[ $MYSHELL == "/usr/bin/mksh" ]] && cp -fv $MNT/etc/skel/.mkshrc /root/.mkshrc
1468         fi
1469
1470         local groups='audio,autologin,floppy,log,network,rfkill,scanner,storage,optical,power,wheel'
1471
1472         chrun "groupadd -r autologin" 2>$ERR
1473         errshow 1 "groupadd -r autologin"
1474         chrun "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" 2>$ERR
1475         errshow 1 "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER"
1476         chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" 2>$ERR
1477         errshow 1 "set $NEWUSER password"
1478
1479         if [[ $USER_PKGS == *neovim* ]]; then
1480                 mkdir -p $MNT/home/$NEWUSER/.config/nvim
1481                 cp -fv $MNT/home/$NEWUSER/.vimrc $MNT/home/$NEWUSER/.config/nvim/init.vim
1482                 cp -rfv $MNT/home/$NEWUSER/.vim/colors $MNT/home/$NEWUSER/.config/nvim/colors
1483         fi
1484
1485         [[ $INSTALL_WMS == *dwm* ]] && install_suckless
1486         [[ $LOGIN_WM =~ (startkde|gnome-session) ]] && sed -i '/super/d' $MNT/home/$NEWUSER/.xprofile /root/.xprofile
1487
1488         return 0
1489 }
1490
1491 install_xinit()
1492 {
1493         SERVICE="$MNT/etc/systemd/system/getty@tty1.service.d"
1494         sed -i '/printf/d' $MNT/root/.zshrc
1495
1496         if [[ -e $MNT/home/$NEWUSER/.xinitrc ]] && grep -q 'exec' $MNT/home/$NEWUSER/.xinitrc; then
1497                 sed -i "/exec/ c exec ${LOGIN_WM}" $MNT/home/$NEWUSER/.xinitrc
1498         else
1499                 printf "exec %s\n" "$LOGIN_WM" >> $MNT/home/$NEWUSER/.xinitrc
1500         fi
1501
1502         [[ ${EDIT_FILES[login]} == *"$LOGINRC"* ]] || EDIT_FILES[login]+=" /home/$NEWUSER/$LOGINRC"
1503
1504         if [[ $AUTOLOGIN ]]; then
1505                 sed -i "s/root/${NEWUSER}/g" $SERVICE/autologin.conf
1506                 cat > $MNT/home/$NEWUSER/$LOGINRC <<EOF
1507 # ~/$LOGINRC
1508 # sourced by ${MYSHELL##*/} when used as a login shell
1509
1510 # automatically run startx when logging in on tty1
1511 [[ -z \$DISPLAY && \$XDG_VTNR -eq 1 ]] && exec startx -- vt1
1512 EOF
1513         else
1514                 rm -rf $SERVICE
1515                 rm -rf $MNT/home/$NEWUSER/.{profile,zprofile,bash_profile}
1516         fi
1517 }
1518
1519 install_packages()
1520 {
1521         local rmpkg=""
1522         local inpkg="$PACKAGES $USER_PKGS"
1523
1524         if pacman -Qsq 'archlabs-installer' >/dev/null 2>&1; then
1525                 rmpkg+=" archlabs-installer"
1526         elif [[ -e "$MNT/usr/bin/archlabs-installer" ]]; then
1527                 rm -f "$MNT/usr/bin/archlabs-installer"
1528         fi
1529
1530         [[ $UCODE ]] && inpkg+=" $UCODE"
1531         [[ $MYSHELL == *mksh ]] && inpkg+=" mksh"
1532         [[ $KERNEL == 'linux' ]] || { inpkg+=" $KERNEL"; rmpkg+=" linux"; }
1533         [[ $inpkg =~ (term|urxvt|tilix|alacritty|sakura|tilda|plasma|cinnamon) || $INSTALL_WMS == *dwm* ]] || inpkg+=" xterm"
1534         [[ $MYSHELL == '/usr/bin/zsh' ]] && inpkg+=" zsh-completions"
1535         [[ $INSTALL_WMS =~ ^(plasma|gnome|cinnamon)$ ]] || inpkg+=" archlabs-ksuperkey"
1536         [[ $INSTALL_WMS =~ (openbox|bspwm|i3-gaps|dwm|fluxbox) ]] && inpkg+=" $WM_BASE_PKGS"
1537
1538         chrun "pacman -Syyu --noconfirm"
1539         [[ $rmpkg ]] && chrun "pacman -Rns $rmpkg --noconfirm"
1540         chrun "pacman -S iputils --noconfirm"
1541         chrun "pacman -S $inpkg --needed --noconfirm" 2>$ERR
1542         errshow 1 "pacman -S $inpkg --needed --noconfirm"
1543
1544         if [[ $BOOTLDR == 'grub' ]]; then
1545                 if [[ $SYS == 'BIOS' ]]; then
1546                         chrun "pacman -S grub os-prober --needed --noconfirm" 2>$ERR
1547                         errshow 1 "pacman -S grub os-prober --needed --noconfirm"
1548                 else
1549                         chrun "pacman -S grub os-prober efibootmgr --needed --noconfirm" 2>$ERR
1550                         errshow 1 "pacman -S grub os-prober efibootmgr --needed --noconfirm"
1551                 fi
1552         elif [[ $BOOTLDR == 'refind-efi' ]]; then
1553                 chrun "pacman -S refind-efi efibootmgr --needed --noconfirm" 2>$ERR
1554                 errshow 1 "pacman -S refind-efi efibootmgr --needed --noconfirm"
1555         elif [[ $SYS == 'UEFI' ]]; then
1556                 chrun "pacman -S efibootmgr --needed --noconfirm" 2>$ERR
1557                 errshow 1 "pacman -S efibootmgr --needed --noconfirm"
1558         fi
1559         sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers
1560         return 0
1561 }
1562
1563 install_suckless()
1564 {
1565         mkdir -pv $MNT/home/$NEWUSER/suckless
1566
1567         for i in dwm dmenu st; do
1568                 if chrun "git clone https://git.suckless.org/$i /home/$NEWUSER/suckless/$i"; then
1569                         chrun "cd /home/$NEWUSER/suckless/$i; make PREFIX=/usr install; make clean; rm config.h"
1570                 else
1571                         printf "failed to clone %s repo\n" "$i"
1572                 fi
1573         done
1574
1575         if [[ -d $MNT/home/$NEWUSER/suckless/dwm && -x $MNT/usr/bin/dwm ]]; then
1576                 printf "To configure dwm edit %s\n" "/home/$NEWUSER/suckless/dwm/config.h"
1577                 printf "You can then recompile it with 'sudo make clean install'\n"
1578                 sleep 2
1579         fi
1580 }
1581
1582 install_mkinitcpio()
1583 {
1584         local add=''
1585
1586         # luks keyfile creation
1587         # currently not used due to not prompting for passphrase on startup
1588         # [[ $LUKS_UUID && $LUKS_PASS && $SYS == 'UEFI' && $BOOTLDR == 'grub' ]] && luks_keyfile
1589
1590         [[ $LUKS ]] && add="encrypt"
1591         [[ $LVM ]] && { [[ $add ]] && add+=" lvm2" || add+="lvm2"; }
1592         sed -i "s/block filesystems/block ${add} filesystems ${HOOKS}/g" $MNT/etc/mkinitcpio.conf
1593         chrun "mkinitcpio -p $KERNEL" 2>$ERR
1594         errshow 1 "mkinitcpio -p $KERNEL"
1595 }
1596
1597 install_mirrorlist()
1598 {
1599         mfile="$1"
1600
1601         if hash reflector >/dev/null 2>&1; then
1602                 reflector --score 120 -l 50 -f 5 --sort rate --save "$mfile"
1603         elif hash rankmirrors >/dev/null 2>&1; then
1604                 i="$(json 'ip' "check&?access_key=5f29642060ab983b31fdf4c2935d8c56&fields=ip")"
1605                 c="$(json 'country_code' "${i}?access_key=5f29642060ab983b31fdf4c2935d8c56&fields=country_code")"
1606                 if [[ $c && $c =~ (CA|US) ]]; then
1607                         m="https://www.archlinux.org/mirrorlist/?country=US&country=CA&use_mirror_status=on"
1608                 elif [[ $c ]]; then
1609                         m="https://www.archlinux.org/mirrorlist/?country=${c}&use_mirror_status=on"
1610                 else
1611                         m="https://www.archlinux.org/mirrorlist/?country=US&country=CA&country=NZ&country=GB&country=AU&use_mirror_status=on"
1612                 fi
1613                 curl -s "$m" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -t -n 10 - | tail -n 10 >"$mfile"
1614         fi
1615         return 0
1616 }
1617
1618 install_background()
1619 {
1620         if [[ -d /run/archiso/sfs/airootfs/etc/skel ]] && grep -qw "$MNT" /proc/mounts && (grep -qw "$MNT/$BOOTDIR" /proc/mounts || [[ $SYS == 'BIOS' && -z $LUKS ]]); then
1621                 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
1622                 rsync -a /run/archiso/sfs/airootfs/ $MNT/ &
1623                 RSYNC_PID=$!
1624                 ( install_mirrorlist "$MNT/etc/pacman.d/mirrorlist" && chrun "pacman -Syyu $BASE_PKGS --needed --noconfirm" ) >/dev/null 2>&1 &
1625                 MIRROR_PID=$!
1626                 trap "kill $RSYNC_PID 2>/dev/null; kill $MIRROR_PID 2>/dev/null" EXIT
1627         fi
1628         return 0
1629 }
1630
1631 ###############################################################################
1632 # bootloader setup
1633
1634 setup_grub()
1635 {
1636         EDIT_FILES[bootloader]="/etc/default/grub"
1637
1638         if [[ $SYS == 'BIOS' ]]; then
1639                 [[ $BOOT_DEV ]] || { part_device 1 || return 1; }
1640                 BCMDS[grub]="grub-install --recheck --force --target=i386-pc $BOOT_DEV"
1641         else
1642                 if [[ $ROOT_PART == */dev/mapper/* && -z $LVM && -z $LUKS_PASS ]]; then
1643                         luks_pass "$_luksopen" 1 || return 1
1644                 fi
1645                 BCMDS[grub]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1646                 grub-install --recheck --force --target=x86_64-efi --efi-directory=/$BOOTDIR --bootloader-id=$DIST"
1647                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1648         fi
1649
1650         BCMDS[grub]="mkdir -p /run/udev /run/lvm &&
1651                 mount --bind /hostrun/udev /run/udev &&
1652                 mount --bind /hostrun/lvm /run/lvm &&
1653                 ${BCMDS[grub]} &&
1654                 grub-mkconfig -o /boot/grub/grub.cfg &&
1655                 sleep 1 && umount /run/udev /run/lvm"
1656
1657         return 0
1658 }
1659
1660 prerun_grub()
1661 {
1662         sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g;
1663         s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" $MNT/etc/default/grub
1664
1665         if [[ $LUKS_DEV ]]; then
1666                 sed -i "s~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g;
1667                 s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g" $MNT/etc/default/grub 2>$ERR
1668                 errshow 1 "sed -i 's~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g;
1669                 s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g' $MNT/etc/default/grub"
1670         fi
1671
1672         if [[ $SYS == 'BIOS' && $LVM && -z $SEP_BOOT ]]; then
1673                 sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $MNT/etc/default/grub 2>$ERR
1674                 errshow 1 "sed -i 's/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g' $MNT/etc/default/grub"
1675         fi
1676
1677         # setup for os-prober module
1678         mkdir -p /run/{lvm,udev}
1679         mkdir -p $MNT/hostrun/{lvm,udev}
1680         mount --bind /run/lvm $MNT/hostrun/lvm
1681         mount --bind /run/udev $MNT/hostrun/udev
1682
1683         return 0
1684 }
1685
1686 setup_efistub()
1687 {
1688         EDIT_FILES[bootloader]=""
1689 }
1690
1691 prerun_efistub()
1692 {
1693         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1694                 efibootmgr -v -d $BOOT_DEV -p $BOOT_PART_NUM -c -L '${DIST} Linux' -l /vmlinuz-${KERNEL} \
1695                 -u 'root=$ROOT_PART_ID rw $([[ $UCODE ]] && printf 'initrd=\%s.img ' "$UCODE")initrd=\initramfs-${KERNEL}.img'"
1696         return 0
1697 }
1698
1699 setup_syslinux()
1700 {
1701         if [[ $SYS == 'BIOS' ]]; then
1702                 EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg"
1703         else
1704                 EDIT_FILES[bootloader]="/boot/EFI/syslinux/syslinux.cfg"
1705                 BCMDS[syslinux]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1706                 efibootmgr -v -c -d $BOOT_DEV -p $BOOT_PART_NUM -l /EFI/syslinux/syslinux.efi -L $DIST"
1707
1708         fi
1709 }
1710
1711 prerun_syslinux()
1712 {
1713         local c="$MNT/boot/syslinux" s="/usr/lib/syslinux/bios" d=".."
1714         [[ $SYS == 'UEFI' ]] && { c="$MNT/boot/EFI/syslinux"; s="/usr/lib/syslinux/efi64/"; d=''; }
1715
1716         mkdir -pv "$c" && cp -rfv $s/* "$c/" && cp -f "$RUN/syslinux/splash.png" "$c/"
1717         cat > "$c/syslinux.cfg" << EOF
1718 UI vesamenu.c32
1719 MENU TITLE $DIST Boot Menu
1720 MENU BACKGROUND splash.png
1721 TIMEOUT 50
1722 DEFAULT $DIST
1723
1724 # see: https://www.syslinux.org/wiki/index.php/Comboot/menu.c32
1725 MENU WIDTH 78
1726 MENU MARGIN 4
1727 MENU ROWS 4
1728 MENU VSHIFT 10
1729 MENU TIMEOUTROW 13
1730 MENU TABMSGROW 14
1731 MENU CMDLINEROW 14
1732 MENU HELPMSGROW 16
1733 MENU HELPMSGENDROW 29
1734 MENU COLOR border       30;44   #40ffffff #a0000000 std
1735 MENU COLOR title        1;36;44 #9033ccff #a0000000 std
1736 MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
1737 MENU COLOR unsel        37;44   #50ffffff #a0000000 std
1738 MENU COLOR help         37;40   #c0ffffff #a0000000 std
1739 MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
1740 MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
1741 MENU COLOR msg07        37;40   #90ffffff #a0000000 std
1742 MENU COLOR tabmsg       31;40   #30ffffff #00000000 std
1743
1744 LABEL $DIST
1745 MENU LABEL $DIST Linux
1746 LINUX $d/vmlinuz-$KERNEL
1747 APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1748 INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL.img
1749
1750 LABEL ${DIST}fallback
1751 MENU LABEL $DIST Linux Fallback
1752 LINUX $d/vmlinuz-$KERNEL
1753 APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1754 INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL-fallback.img
1755 $([[ $SYS == 'BIOS' ]] && printf "\n%s" "# examples of chainloading other bootloaders
1756
1757 #LABEL grub2
1758 #MENU LABEL Grub2
1759 #COM32 chain.c32
1760 #APPEND file=$d/grub/boot.img
1761
1762 #LABEL windows
1763 #MENU LABEL Windows
1764 #COM32 chain.c32
1765 #APPEND hd0 3")
1766 EOF
1767         return 0
1768 }
1769
1770 setup_refind-efi()
1771 {
1772         EDIT_FILES[bootloader]="/boot/refind_linux.conf"
1773         BCMDS[refind-efi]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1774                 refind-install"
1775 }
1776
1777 prerun_refind-efi()
1778 {
1779         cat > $MNT/boot/refind_linux.conf << EOF
1780 "$DIST Linux"          "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1781                         printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1782                         printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL.img"
1783 "$DIST Linux Fallback" "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1784                         printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1785                         printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL-fallback.img"
1786 EOF
1787         mkdir -p $MNT/etc/pacman.d/hooks
1788         cat > $MNT/etc/pacman.d/hooks/refind.hook << EOF
1789 [Trigger]
1790 Operation = Upgrade
1791 Type = Package
1792 Target = refind-efi
1793
1794 [Action]
1795 Description = Updating rEFInd on ESP
1796 When = PostTransaction
1797 Exec = /usr/bin/refind-install
1798 EOF
1799 }
1800
1801 setup_systemd-boot()
1802 {
1803         EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf"
1804         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1805                 bootctl --path=/boot install"
1806 }
1807
1808 prerun_systemd-boot()
1809 {
1810         mkdir -p $MNT/boot/loader/entries
1811         cat > $MNT/boot/loader/loader.conf << EOF
1812 default  $DIST
1813 timeout  5
1814 editor   no
1815 EOF
1816         cat > $MNT/boot/loader/entries/$DIST.conf << EOF
1817 title   $DIST Linux
1818 linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1819 initrd  /initramfs-$KERNEL.img
1820 options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1821 EOF
1822         cat > $MNT/boot/loader/entries/$DIST-fallback.conf << EOF
1823 title   $DIST Linux Fallback
1824 linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1825 initrd  /initramfs-$KERNEL-fallback.img
1826 options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1827 EOF
1828         mkdir -p $MNT/etc/pacman.d/hooks
1829         cat > $MNT/etc/pacman.d/hooks/systemd-boot.hook << EOF
1830 [Trigger]
1831 Type = Package
1832 Operation = Upgrade
1833 Target = systemd
1834
1835 [Action]
1836 Description = Updating systemd-boot
1837 When = PostTransaction
1838 Exec = /usr/bin/bootctl update
1839 EOF
1840         systemd-machine-id-setup --root="$MNT"
1841         return 0
1842 }
1843
1844 ###############################################################################
1845 # lvm functions
1846
1847 lvm_menu()
1848 {
1849         check_background_install || return 1
1850         lvm_detect
1851         local choice
1852         dlg choice menu "Logical Volume Management" "$_lvmmenu" \
1853                 "$_lvmnew"    "vgcreate -f, lvcreate -L -n" \
1854                 "$_lvmdel"    "vgremove -f" \
1855                 "$_lvmdelall" "lvrmeove, vgremove, pvremove -f" \
1856                 "back"        "return to the main menu"
1857
1858         case "$choice" in
1859                 "$_lvmnew") lvm_create || return 1 ;;
1860                 "$_lvmdel") lvm_delgroup && yesno "$_lvmdel" "$_lvmdelask" && vgremove -f "$DEL_VG" >/dev/null 2>&1 ;;
1861                 "$_lvmdelall") lvm_del_all ;;
1862         esac
1863
1864         return 0
1865 }
1866
1867 lvm_detect()
1868 {
1869         local v pv
1870         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1871         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1872         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1873
1874         if [[ $VGROUP && $v && $pv ]]; then
1875                 msg "Logical Volume Management" "\nActivating existing logical volume management (LVM).\n\n" 1
1876                 modprobe dm-mod >/dev/null 2>$ERR
1877                 errshow 'modprobe dm-mod'
1878                 vgscan >/dev/null 2>&1
1879                 vgchange -ay >/dev/null 2>&1
1880         fi
1881 }
1882
1883 lvm_create()
1884 {
1885         VGROUP='' LVM_PARTS='' VGROUP_MB=0
1886         umount_dir $MNT
1887         lvm_mkgroup || return 1
1888         local txt="\nThe last (or only) logical volume will automatically use all remaining space in the volume group."
1889         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
1890         [[ $VOL_COUNT ]] || return 1
1891         lvm_extra_lvs || return 1
1892         lvm_volume_name "$_lvmlvname\nNOTE: This LV will use up all remaining space in the volume group (${VGROUP_MB}MB)" || return 1
1893         lvcreate -l +100%FREE "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
1894         errshow "lvcreate -l +100%FREE $VGROUP -n $VNAME" || return 1
1895         LVM='logical volume'; sleep 0.5
1896         txt="\nDone, volume: $VGROUP-$VNAME (${VOLUME_SIZE:-${VGROUP_MB}MB}) has been created.\n"
1897         msg "$_lvmnew (LV:$VOL_COUNT)" "$txt\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE $LVM_PARTS)\n"
1898         return 0
1899 }
1900
1901 get_lv_size()
1902 {
1903         local txt="${VGROUP}: ${SIZE}$SIZE_UNIT (${VGROUP_MB}MB remaining).$_lvmlvsize"
1904
1905         while true; do
1906                 ERR_SIZE=0
1907                 dlg VOLUME_SIZE input "$_lvmnew (LV:$VOL_COUNT)" "$txt" ''
1908                 if [[ -z $VOLUME_SIZE ]]; then
1909                         ERR_SIZE=1
1910                         break # allow bailing with escape or an empty choice
1911                 elif (( ${VOLUME_SIZE:0:1} == 0 )); then
1912                         ERR_SIZE=1 # size values can't begin with '0'
1913                 else
1914                         # walk the string and make sure all but the last char are digits
1915                         local lv=$((${#VOLUME_SIZE} - 1))
1916                         for (( i=0; i<lv; i++ )); do
1917                                 [[ ${VOLUME_SIZE:$i:1} =~ [0-9] ]] || { ERR_SIZE=1; break; }
1918                         done
1919                         if (( ERR_SIZE != 1 )); then
1920                                 case ${VOLUME_SIZE:$lv:1} in
1921                                         [mMgG]) local s=${VOLUME_SIZE:0:$lv} m=$((s * 1000))
1922                                                 case ${VOLUME_SIZE:$lv:1} in
1923                                                         [Gg]) (( m >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - m)) ;;
1924                                                         [Mm]) (( ${VOLUME_SIZE:0:$lv} >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - s)) ;;
1925                                                         *) ERR_SIZE=1
1926                                                 esac ;;
1927                                         *) ERR_SIZE=1
1928                                 esac
1929                         fi
1930                 fi
1931                 if (( ERR_SIZE )); then
1932                         msg "Invalid Logical Volume Size" "$_lvmerrlvsize"
1933                 else
1934                         break
1935                 fi
1936         done
1937
1938         return $ERR_SIZE
1939 }
1940
1941 lvm_mkgroup()
1942 {
1943         local named=''
1944         local txt="\nConfirm creation of volume group: $VGROUP\n\nwith the following partition(s):"
1945
1946         until [[ $named ]]; do
1947                 lvm_partitions || return 1
1948                 lvm_group_name || return 1
1949                 yesno "$_lvmnew" "$txt $LVM_PARTS\n" && named=true
1950         done
1951
1952         vgcreate -f "$VGROUP" $LVM_PARTS >/dev/null 2>$ERR
1953         errshow "vgcreate -f $VGROUP $LVM_PARTS" || return 1
1954
1955         SIZE=$(vgdisplay "$VGROUP" | awk '/VG Size/ { gsub(/[^0-9.]/, ""); print int($0) }')
1956         SIZE_UNIT="$(vgdisplay "$VGROUP" | awk '/VG Size/ { print substr($NF, 0, 1) }')"
1957
1958         if [[ $SIZE_UNIT == 'G' ]]; then
1959                 VGROUP_MB=$((SIZE * 1000))
1960         else
1961                 VGROUP_MB=$SIZE
1962         fi
1963
1964         msg "$_lvmnew" "\nVolume group: $VGROUP ($SIZE $SIZE_UNIT) has been created\n"
1965         return 0
1966 }
1967
1968 lvm_del_all()
1969 {
1970         local v pv
1971         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1972         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1973         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1974
1975         if [[ $VGROUP || $v || $pv ]]; then
1976                 if yesno "$_lvmdelall" "$_lvmdelask"; then
1977                         for i in $v; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1; done
1978                         for i in $VGROUP; do vgremove -f "$i" >/dev/null 2>&1; done
1979                         for i in $pv; do pvremove -f "$i" >/dev/null 2>&1; done
1980                         LVM=''
1981                 fi
1982         else
1983                 msg "Delete LVM" "\nNo LVMs to remove...\n" 2
1984                 LVM=''
1985         fi
1986 }
1987
1988 lvm_delgroup()
1989 {
1990         DEL_VG=''
1991         VOL_GROUP_LIST=''
1992
1993         for i in $(lvs --noheadings | awk '{print $2}' | uniq); do
1994                 VOL_GROUP_LIST+="$i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}') "
1995         done
1996         [[ $VOL_GROUP_LIST ]] || { msg "No Groups" "\nNo volume groups found."; return 1; }
1997
1998         dlg DEL_VG menu "Logical Volume Management" "\nSelect volume group to delete.\n\nAll logical volumes within will also be deleted." $VOL_GROUP_LIST
1999         [[ $DEL_VG ]]
2000 }
2001
2002 lvm_extra_lvs()
2003 {
2004         while (( VOL_COUNT > 1 )); do
2005                 lvm_volume_name "$_lvmlvname" && get_lv_size || return 1
2006                 lvcreate -L "$VOLUME_SIZE" "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
2007                 errshow "lvcreate -L $VOLUME_SIZE $VGROUP -n $VNAME" || return 1
2008                 msg "$_lvmnew (LV:$VOL_COUNT)" "\nDone, logical volume (LV) $VNAME ($VOLUME_SIZE) has been created.\n"
2009                 (( VOL_COUNT-- ))
2010         done
2011         return 0
2012 }
2013
2014 lvm_partitions()
2015 {
2016         part_find 'part|crypt' || return 1
2017         PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")"
2018         dlg LVM_PARTS check "$_lvmnew" "\nSelect the partition(s) to use for the physical volume (PV)." $PARTS
2019         [[ $LVM_PARTS ]]
2020 }
2021
2022 lvm_group_name()
2023 {
2024         VGROUP=''
2025         until [[ $VGROUP ]]; do
2026                 dlg VGROUP input "$_lvmnew" "$_lvmvgname" "mygroup"
2027                 if [[ -z $VGROUP ]]; then
2028                         return 1
2029                 elif [[ ${VGROUP:0:1} == "/" || $VGROUP =~ \ |\' ]] || lsblk | grep -q "$VGROUP"; then
2030                         msg "LVM Name Error" "$_lvmerrvgname"
2031                         VGROUP=''
2032                 fi
2033         done
2034         return 0
2035 }
2036
2037 lvm_volume_name()
2038 {
2039         VNAME=''
2040         local txt="$1" default="mainvolume"
2041         (( VOL_COUNT > 1 )) && default="extravolume$VOL_COUNT"
2042         until [[ $VNAME ]]; do
2043                 dlg VNAME input "$_lvmnew (LV:$VOL_COUNT)" "\n$txt" "$default"
2044                 if [[ -z $VNAME ]]; then
2045                         return 1
2046                 elif [[ ${VNAME:0:1} == "/" || $VNAME =~ \ |\' ]] || lsblk | grep -q "$VNAME"; then
2047                         msg "LVM Name Error" "$_lvmerlvname"
2048                         VNAME=''
2049                 fi
2050         done
2051         return 0
2052 }
2053
2054 ###############################################################################
2055 # luks functions
2056
2057 luks_menu()
2058 {
2059         local choice
2060         check_background_install || return 1
2061         dlg choice menu "LUKS Encryption" "$_luksmenu" \
2062                 "$_luksnew"  "cryptsetup -q luksFormat" \
2063                 "$_luksopen" "cryptsetup open --type luks" \
2064                 "$_luksadv"  "cryptsetup -q -s -c luksFormat" \
2065                 "back"       "Return to the main menu"
2066
2067         case "$choice" in
2068                 "$_luksnew") luks_basic || return 1 ;;
2069                 "$_luksopen") luks_open || return 1 ;;
2070                 "$_luksadv") luks_advanced || return 1 ;;
2071         esac
2072
2073         return 0
2074 }
2075
2076 luks_open()
2077 {
2078         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2079         umount_dir $MNT
2080         part_find 'part|crypt|lvm' || return 1
2081
2082         if (( COUNT == 1 )); then
2083                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2084         else
2085                 dlg LUKS_PART menu "$_luksopen" "\nSelect which partition to open." $PARTS
2086         fi
2087
2088         [[ $LUKS_PART ]] || return 1
2089
2090         luks_pass "$_luksopen" || return 1
2091         msg "$_luksopen" "\nOpening encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2092         cryptsetup open --type luks "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2093         errshow "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" || return 1
2094         LUKS='encrypted'; luks_show
2095         return 0
2096 }
2097
2098 luks_pass()
2099 {
2100         LUKS_PASS=''
2101         local t="$1" op="$2" v='' p='' p2=''
2102
2103         until [[ $LUKS_PASS ]]; do
2104                 i=0
2105                 tput cnorm
2106                 if [[ $op ]]; then
2107                         dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " \
2108                                 --mixedform "\nEnter the password to decrypt $ROOT_PART\n\nThis is needed to create a keyfile." 0 0 0 \
2109                                 "Password:"  1 1 '' 1 11 "$COLUMNS" 0 1 \
2110                                 "Password2:" 2 1 '' 2 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2111
2112                 else
2113                         dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " --mixedform "$_luksomenu" 0 0 0 \
2114                                 "Name:"      1 1 "${LUKS_NAME:-cryptroot}" 1  7 "$COLUMNS" 0 0 \
2115                                 "Password:"  2 1 ''                        2 11 "$COLUMNS" 0 1 \
2116                                 "Password2:" 3 1 ''                        3 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2117
2118                 fi
2119
2120                 while read -r line; do
2121                         if [[ $op ]]; then
2122                                 case $i in
2123                                         0) p="$line" ;;
2124                                         1) p2="$line" ;;
2125                                 esac
2126                         else
2127                                 case $i in
2128                                         0) n="$line" ;;
2129                                         1) p="$line" ;;
2130                                         2) p2="$line" ;;
2131                                 esac
2132                         fi
2133                         (( i++ ))
2134                 done < "$ANS"
2135
2136                 if [[ -z $op && -z $n ]]; then
2137                         msg "Name Empty" "\nEncrypted device name cannot be empty.\n\nPlease try again.\n" 2
2138                 elif [[ -z $p || "$p" != "$p2" ]]; then
2139                         [[ $op ]] || LUKS_NAME="$n"
2140                         msg "Password Mismatch" "\nThe passwords entered do not match.\n\nPlease try again.\n" 2
2141                 else
2142                         [[ $op ]] || LUKS_NAME="$n"
2143                         LUKS_PASS="$p"
2144                 fi
2145         done
2146
2147         return 0
2148 }
2149
2150 luks_show()
2151 {
2152         sleep 0.5
2153         msg "$_luksnew" "\nEncrypted partition opened and ready for mounting.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE "$LUKS_PART")\n\n"
2154 }
2155
2156 luks_setup()
2157 {
2158         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2159         umount_dir $MNT
2160         part_find 'part|lvm' || return 1
2161
2162         if [[ $AUTO_ROOT_PART ]]; then
2163                 LUKS_PART="$AUTO_ROOT_PART"
2164         elif (( COUNT == 1 )); then
2165                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2166         else
2167                 dlg LUKS_PART menu "$_luksnew" "\nSelect the partition you want to encrypt." $PARTS
2168         fi
2169
2170         [[ $LUKS_PART ]] || return 1
2171         luks_pass "$_luksnew"
2172 }
2173
2174 luks_basic()
2175 {
2176         luks_setup || return 1
2177         msg "$_luksnew" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2178         cryptsetup -q luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2179         errshow "cryptsetup -q luksFormat $LUKS_PART" || return 1
2180         cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2181         errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2182         LUKS='encrypted'; luks_show
2183         return 0
2184 }
2185
2186 luks_keyfile()
2187 {
2188         if [[ ! -e $MNT/crypto_keyfile.bin ]]; then
2189                 # printf "Creating LUKS keyfile /crypto_keyfile.bin\n"
2190                 local n
2191                 n="$(lsblk -lno NAME,UUID,TYPE | awk "/$LUKS_UUID/"' && /part|crypt|lvm/ {print $1}')"
2192                 local mkkey="dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin"
2193                 mkkey="$mkkey && chmod 000 /crypto_keyfile.bin"
2194                 mkkey="$mkkey && cryptsetup luksAddKey /dev/$n /crypto_keyfile.bin <<< '$LUKS_PASS'"
2195                 chrun "$mkkey"
2196                 sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf 2>$ERR
2197         fi
2198
2199         return 0
2200 }
2201
2202 luks_advanced()
2203 {
2204         if luks_setup; then
2205                 local cipher
2206                 dlg cipher input "LUKS Encryption" "$_lukskey" "-s 512 -c aes-xts-plain64"
2207                 [[ $cipher ]] || return 1
2208                 msg "$_luksadv" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2209                 cryptsetup -q $cipher luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2210                 errshow "cryptsetup -q $cipher luksFormat $LUKS_PART" || return 1
2211                 cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2212                 errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2213                 luks_show
2214                 return 0
2215         fi
2216         return 1
2217 }
2218
2219 ###############################################################################
2220 # helper functions
2221
2222 ofn()
2223 {
2224         [[ "$2" == *"$1"* ]] && printf "on" || printf "off"
2225 }
2226
2227 die()
2228 {
2229         local exitcode="$1"
2230         trap - INT
2231         trap - TSTP
2232         tput cnorm
2233         if [[ -d $MNT ]] && command cd /; then
2234                 umount_dir $MNT
2235                 if (( exitcode == 127 )); then
2236                         umount -l /run/archiso/bootmnt
2237                         sleep 0.5
2238                         systemctl -i reboot
2239                 fi
2240         fi
2241
2242         # restore custom linux console 0-15 colors when not rebooting
2243         if [[ $TERM == 'linux' ]]; then
2244                 colors=("\e]P0191919" "\e]P1D15355" "\e]P2609960" "\e]P3FFCC66"
2245                 "\e]P4255A9B" "\e]P5AF86C8" "\e]P62EC8D3" "\e]P7949494" "\e]P8191919" "\e]P9D15355"
2246                 "\e]PA609960" "\e]PBFF9157" "\e]PC4E88CF" "\e]PDAF86C8" "\e]PE2ec8d3" "\e]PFE1E1E1")
2247                 printf "%b" "${colors[@]}" && clear && unset col
2248         fi
2249
2250         exit "$exitcode"
2251 }
2252
2253 dlg()
2254 {
2255         local var="$1" dialog_type="$2" title="$3" body="$4" n=0
2256         shift 4
2257         (( ($# / 2) > SHL )) && n=$SHL
2258         
2259         tput civis
2260         case "$dialog_type" in
2261                 menu)
2262                         dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --menu "$body" 0 0 $n "$@" 2>"$ANS" || return 1
2263                         ;;
2264                 check)
2265                         dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --checklist "$body" 0 0 $n "$@" 2>"$ANS" || return 1
2266                         ;;
2267                 input)
2268                         tput cnorm
2269                         local def="$1"
2270                         shift
2271                         if [[ $1 == 'limit' ]]; then
2272                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --max-input 63 --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2273                         else
2274                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2275                         fi
2276                         ;;
2277         esac
2278         [[ -s "$ANS" ]] && printf -v "$var" "%s" "$(< "$ANS")"
2279 }
2280
2281 msg()
2282 {
2283         local title="$1" body="$2"
2284         tput civis
2285         if (( $# == 3 )); then
2286                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --sleep "$3" --title " $title " --infobox "$body\n" 0 0
2287         else
2288                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --msgbox "$body\n" 0 0
2289         fi
2290 }
2291
2292 json()
2293 {
2294         curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])"
2295 }
2296
2297 yesno()
2298 {
2299         local title="$1" body="$2" yes='Yes' no='No'
2300         (( $# >= 3 )) && local yes="$3"
2301         (( $# >= 4 )) && local no="$4"
2302         tput civis
2303         if (( $# == 5 )); then
2304                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --defaultno --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2305         else
2306                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2307         fi
2308 }
2309
2310 chrun()
2311 {
2312         arch-chroot "$MNT" bash -c "$1"
2313 }
2314
2315 debug()
2316 {
2317         export PS4='| ${BASH_SOURCE} LINE:${LINENO} FUNC:${FUNCNAME[0]:+ ${FUNCNAME[0]}()} |>  '
2318         set -x
2319         exec 3>| $DBG
2320         BASH_XTRACEFD=3
2321         DEBUG=true
2322 }
2323
2324 sigint()
2325 {
2326         printf "\n^C caught, cleaning up...\n"
2327         die 1
2328 }
2329
2330 print4()
2331 {
2332         local str="$*"
2333         if [[ $COLUMNS -ge 110 && ${#str} -gt $((COLUMNS - 30)) ]]; then
2334                 str="$(awk '{
2335                         i = 2; p1 = p2 = p3 = p4 = ""; p1 = $1; q = int(NF / 4)
2336                         for (; i <= q; i++) { p1 = p1 " " $i }
2337                         for (; i <= q * 2; i++) { p2 = p2 " " $i }
2338                         for (; i <= q * 3; i++) { p3 = p3 " " $i }
2339                         for (; i <= NF; i++) { p4 = p4 " " $i }
2340                         printf "%s\n           %s\n           %s\n           %s", p1, p2, p3, p4
2341                 }' <<< "$str")"
2342                 printf "%s\n" "$str"
2343         elif [[ $str ]]; then
2344                 printf "%s\n" "$str"
2345         fi
2346 }
2347
2348 errshow()
2349 {
2350         exit_code="$?"
2351         (( exit_code == 0 )) && return 0
2352
2353         local fatal=0 err=""
2354         err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")"
2355         (( $1 == 1 )) && { fatal=1; shift; }
2356         local txt="\nThe command exited abnormally:\n\n$1\n\n"
2357
2358         if [[ $err ]]; then
2359                 txt+="With the following message:\n\n$err\n\n"
2360         else
2361                 txt+="With no error message:\n\n"
2362         fi
2363
2364         if (( fatal == 1 )); then
2365                 txt+="Errors at this stage are fatal and the install cannot continue.\n"
2366         else
2367                 txt+="Errors at this stage are non-fatal and can be either fixed or ignored depending on the error.\n"
2368         fi
2369
2370         msg "Install Error" "$txt"
2371
2372         if (( fatal == 1 )); then
2373                 [[ -r $DBG && $TERM == 'linux' ]] && less "$DBG"
2374                 die 1
2375         fi
2376
2377         return 1
2378 }
2379
2380 load_bcm()
2381 {
2382         msg "Broadcom Wireless Setup" "\nDetected chipset is Broadcom BCM4352\n\nDisabling bcma/b43 modules and loading wl module.\n" 1
2383         { rmmod wl; rmmod bcma; rmmod b43; rmmod ssb; modprobe wl; depmod -a; } >/dev/null 2>&1
2384         BROADCOM_WL=true
2385 }
2386
2387 prechecks()
2388 {
2389         if [[ $1 -ge 0 ]] && ! grep -qw "$MNT" /proc/mounts; then
2390                 msg "Not Mounted" "\nPartition(s) must be mounted first.\n" 2
2391                 SEL=4
2392                 return 1
2393         elif [[ $1 -ge 1 && -z $BOOTLDR ]]; then
2394                 msg "No Bootloader" "\nBootloader must be selected first.\n" 2
2395                 SEL=5
2396                 return 1
2397         elif [[ $1 -ge 2 && (-z $NEWUSER || -z $USER_PASS) ]]; then
2398                 msg "No User" "\nA user must be created first.\n" 2
2399                 SEL=6
2400                 return 1
2401         elif [[ $1 -ge 3 && -z $CONFIG_DONE ]]; then
2402                 msg "Not Configured" "\nSystem configuration must be done first.\n" 2
2403                 SEL=7
2404                 return 1
2405         fi
2406         return 0
2407 }
2408
2409 umount_dir()
2410 {
2411         swapoff -a
2412         [[ -d $1 ]] && umount -R "$1" >/dev/null 2>&1
2413         return 0
2414 }
2415
2416 chk_connect()
2417 {
2418         msg "Network Connect" "\nVerifying network connection\n" 1
2419         curl -sIN --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200'
2420 }
2421
2422 net_connect()
2423 {
2424         if chk_connect; then
2425                 return 0
2426         else
2427                 if hash nmtui >/dev/null 2>&1; then
2428                         tput civis
2429                         [[ $TERM == 'linux' ]] && printf "%b" "\e]P1191919" "\e]P4191919"
2430                         nmtui-connect
2431                         [[ $TERM == 'linux' ]] && printf "%b" "\e]P1D15355" "\e]P4255a9b"
2432                         chk_connect
2433                 else
2434                         return 1
2435                 fi
2436         fi
2437 }
2438
2439 system_devices()
2440 {
2441         IGNORE_DEV="$(lsblk -lno NAME,MOUNTPOINT | awk '/\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')"
2442
2443         if [[ $IGNORE_DEV ]]; then
2444                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')"
2445         else
2446                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ {print "/dev/" $1 " " $2}')"
2447         fi
2448
2449         if [[ -z $SYS_DEVS ]]; then
2450                 msg "Device Error" "\nNo available devices...\n\nExiting..\n" 2
2451                 die 1
2452         fi
2453
2454         DEV_COUNT=0
2455         while read -r line; do
2456                 (( DEV_COUNT++ ))
2457         done <<< "$SYS_DEVS"
2458 }
2459
2460 system_identify()
2461 {
2462         if [[ $VM ]]; then
2463                 UCODE=''
2464         elif grep -q 'AuthenticAMD' /proc/cpuinfo; then
2465                 UCODE="amd-ucode"
2466         elif grep -q 'GenuineIntel' /proc/cpuinfo; then
2467                 UCODE="intel-ucode"
2468         fi
2469
2470         modprobe -q efivarfs >/dev/null 2>&1
2471
2472         _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."
2473         if [[ -d /sys/firmware/efi/efivars ]]; then
2474                 export SYS="UEFI"
2475                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars
2476                 _prep+="\n  - An EFI boot partition mounted."
2477         else
2478                 export SYS="BIOS"
2479         fi
2480         _prep+="\n\nOnce finished mounting, a portion of the install can be done in the background while you continue configuring the system:\n"
2481         _prep+="\n  - Choose the system bootloader.\n  - Create a user and password."
2482         _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."
2483 }
2484
2485 check_background_install()
2486 {
2487         [[ $RSYNC_PID || $MIRROR_PID ]] || return 0
2488         msg "Install Running" "\nA background install process is currently running.\n" 2
2489         return 1
2490 }
2491
2492 ###############################################################################
2493 # entry point
2494
2495 if (( UID != 0 )); then
2496         msg "Not Root" "\nThis installer must be run as root or using sudo.\n\nExiting..\n" 2
2497         die 1
2498 elif ! grep -qwm 1 'lm' /proc/cpuinfo; then
2499         msg "Not x86_64 Architecture" "\nThis installer only supports x86_64 architectures.\n\nExiting..\n" 2
2500         die 1
2501 elif [[ $1 =~ (-d|--debug) ]]; then
2502         debug
2503 fi
2504
2505 # trap ^C to perform cleanup
2506 trap sigint INT
2507
2508 system_identify
2509 system_devices
2510
2511 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"
2512
2513 select_keymap || { clear; die 0; }
2514
2515 if lspci -vnn -d 14e4: | grep -q 'BCM4352'; then
2516         load_bcm
2517 fi
2518
2519 if ! net_connect; then
2520         msg "Not Connected" "\nThis installer requires an active internet connection.\n\nExiting..\n" 2
2521         die 1
2522 fi
2523
2524 while true; do
2525         select_main
2526 done
2527
2528 # vim:fdm=marker:fmr={,}