OSDN Git Service

Make sure the base is installed if background failed
[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.41"     # 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="b43-firmware b43-fwcutter broadcom-wl clonezilla dhclient dhcpcd ethtool wpa_supplicant "
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+="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 -Q xorg-xinit 2>/dev/null || pacman -S $BASE_PKGS --needed --noconfirm"
1542         chrun "pacman -S $inpkg --needed --noconfirm" 2>$ERR
1543         errshow 1 "pacman -S $inpkg --needed --noconfirm"
1544
1545         if [[ $BOOTLDR == 'grub' ]]; then
1546                 if [[ $SYS == 'BIOS' ]]; then
1547                         chrun "pacman -S grub os-prober --needed --noconfirm" 2>$ERR
1548                         errshow 1 "pacman -S grub os-prober --needed --noconfirm"
1549                 else
1550                         chrun "pacman -S grub os-prober efibootmgr --needed --noconfirm" 2>$ERR
1551                         errshow 1 "pacman -S grub os-prober efibootmgr --needed --noconfirm"
1552                 fi
1553         elif [[ $BOOTLDR == 'refind-efi' ]]; then
1554                 chrun "pacman -S refind-efi efibootmgr --needed --noconfirm" 2>$ERR
1555                 errshow 1 "pacman -S refind-efi efibootmgr --needed --noconfirm"
1556         elif [[ $SYS == 'UEFI' ]]; then
1557                 chrun "pacman -S efibootmgr --needed --noconfirm" 2>$ERR
1558                 errshow 1 "pacman -S efibootmgr --needed --noconfirm"
1559         fi
1560         sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers
1561         return 0
1562 }
1563
1564 install_suckless()
1565 {
1566         mkdir -pv $MNT/home/$NEWUSER/suckless
1567
1568         for i in dwm dmenu st; do
1569                 if chrun "git clone https://git.suckless.org/$i /home/$NEWUSER/suckless/$i"; then
1570                         chrun "cd /home/$NEWUSER/suckless/$i; make PREFIX=/usr install; make clean; rm config.h"
1571                 else
1572                         printf "failed to clone %s repo\n" "$i"
1573                 fi
1574         done
1575
1576         if [[ -d $MNT/home/$NEWUSER/suckless/dwm && -x $MNT/usr/bin/dwm ]]; then
1577                 printf "To configure dwm edit %s\n" "/home/$NEWUSER/suckless/dwm/config.h"
1578                 printf "You can then recompile it with 'sudo make clean install'\n"
1579                 sleep 2
1580         fi
1581 }
1582
1583 install_mkinitcpio()
1584 {
1585         local add=''
1586
1587         # luks keyfile creation
1588         # currently not used due to not prompting for passphrase on startup
1589         # [[ $LUKS_UUID && $LUKS_PASS && $SYS == 'UEFI' && $BOOTLDR == 'grub' ]] && luks_keyfile
1590
1591         [[ $LUKS ]] && add="encrypt"
1592         [[ $LVM ]] && { [[ $add ]] && add+=" lvm2" || add+="lvm2"; }
1593         sed -i "s/block filesystems/block ${add} filesystems ${HOOKS}/g" $MNT/etc/mkinitcpio.conf
1594         chrun "mkinitcpio -p $KERNEL" 2>$ERR
1595         errshow 1 "mkinitcpio -p $KERNEL"
1596 }
1597
1598 install_mirrorlist()
1599 {
1600         mfile="$1"
1601
1602         if hash reflector >/dev/null 2>&1; then
1603                 reflector --score 120 -l 50 -f 5 --sort rate --save "$mfile"
1604         elif hash rankmirrors >/dev/null 2>&1; then
1605                 i="$(json 'ip' "check&?access_key=5f29642060ab983b31fdf4c2935d8c56&fields=ip")"
1606                 c="$(json 'country_code' "${i}?access_key=5f29642060ab983b31fdf4c2935d8c56&fields=country_code")"
1607                 if [[ $c && $c =~ (CA|US) ]]; then
1608                         m="https://www.archlinux.org/mirrorlist/?country=US&country=CA&use_mirror_status=on"
1609                 elif [[ $c ]]; then
1610                         m="https://www.archlinux.org/mirrorlist/?country=${c}&use_mirror_status=on"
1611                 else
1612                         m="https://www.archlinux.org/mirrorlist/?country=US&country=CA&country=NZ&country=GB&country=AU&use_mirror_status=on"
1613                 fi
1614                 curl -s "$m" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -t -n 10 - | tail -n 10 >"$mfile"
1615         fi
1616         return 0
1617 }
1618
1619 install_background()
1620 {
1621         if [[ -d /run/archiso/sfs/airootfs/etc/skel ]] && grep -qw "$MNT" /proc/mounts && (grep -qw "$MNT/$BOOTDIR" /proc/mounts || [[ $SYS == 'BIOS' && -z $LUKS ]]); then
1622                 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
1623                 rsync -a /run/archiso/sfs/airootfs/ $MNT/ &
1624                 RSYNC_PID=$!
1625                 ( install_mirrorlist "$MNT/etc/pacman.d/mirrorlist" && chrun "pacman -Syyu $BASE_PKGS --needed --noconfirm" ) &
1626                 MIRROR_PID=$!
1627                 trap "kill $RSYNC_PID 2>/dev/null; kill $MIRROR_PID 2>/dev/null" EXIT
1628         fi
1629         return 0
1630 }
1631
1632 ###############################################################################
1633 # bootloader setup
1634
1635 setup_grub()
1636 {
1637         EDIT_FILES[bootloader]="/etc/default/grub"
1638
1639         if [[ $SYS == 'BIOS' ]]; then
1640                 [[ $BOOT_DEV ]] || { part_device 1 || return 1; }
1641                 BCMDS[grub]="grub-install --recheck --force --target=i386-pc $BOOT_DEV"
1642         else
1643                 if [[ $ROOT_PART == */dev/mapper/* && -z $LVM && -z $LUKS_PASS ]]; then
1644                         luks_pass "$_luksopen" 1 || return 1
1645                 fi
1646                 BCMDS[grub]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1647                 grub-install --recheck --force --target=x86_64-efi --efi-directory=/$BOOTDIR --bootloader-id=$DIST"
1648                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1649         fi
1650
1651         BCMDS[grub]="mkdir -p /run/udev /run/lvm &&
1652                 mount --bind /hostrun/udev /run/udev &&
1653                 mount --bind /hostrun/lvm /run/lvm &&
1654                 ${BCMDS[grub]} &&
1655                 grub-mkconfig -o /boot/grub/grub.cfg &&
1656                 sleep 1 && umount /run/udev /run/lvm"
1657
1658         return 0
1659 }
1660
1661 prerun_grub()
1662 {
1663         sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g;
1664         s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" $MNT/etc/default/grub
1665
1666         if [[ $LUKS_DEV ]]; then
1667                 sed -i "s~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g;
1668                 s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g" $MNT/etc/default/grub 2>$ERR
1669                 errshow 1 "sed -i 's~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g;
1670                 s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g' $MNT/etc/default/grub"
1671         fi
1672
1673         if [[ $SYS == 'BIOS' && $LVM && -z $SEP_BOOT ]]; then
1674                 sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $MNT/etc/default/grub 2>$ERR
1675                 errshow 1 "sed -i 's/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g' $MNT/etc/default/grub"
1676         fi
1677
1678         # setup for os-prober module
1679         mkdir -p /run/{lvm,udev}
1680         mkdir -p $MNT/hostrun/{lvm,udev}
1681         mount --bind /run/lvm $MNT/hostrun/lvm
1682         mount --bind /run/udev $MNT/hostrun/udev
1683
1684         return 0
1685 }
1686
1687 setup_efistub()
1688 {
1689         EDIT_FILES[bootloader]=""
1690 }
1691
1692 prerun_efistub()
1693 {
1694         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1695                 efibootmgr -v -d $BOOT_DEV -p $BOOT_PART_NUM -c -L '${DIST} Linux' -l /vmlinuz-${KERNEL} \
1696                 -u 'root=$ROOT_PART_ID rw $([[ $UCODE ]] && printf 'initrd=\%s.img ' "$UCODE")initrd=\initramfs-${KERNEL}.img'"
1697         return 0
1698 }
1699
1700 setup_syslinux()
1701 {
1702         if [[ $SYS == 'BIOS' ]]; then
1703                 EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg"
1704         else
1705                 EDIT_FILES[bootloader]="/boot/EFI/syslinux/syslinux.cfg"
1706                 BCMDS[syslinux]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1707                 efibootmgr -v -c -d $BOOT_DEV -p $BOOT_PART_NUM -l /EFI/syslinux/syslinux.efi -L $DIST"
1708
1709         fi
1710 }
1711
1712 prerun_syslinux()
1713 {
1714         local c="$MNT/boot/syslinux" s="/usr/lib/syslinux/bios" d=".."
1715         [[ $SYS == 'UEFI' ]] && { c="$MNT/boot/EFI/syslinux"; s="/usr/lib/syslinux/efi64/"; d=''; }
1716
1717         mkdir -pv "$c" && cp -rfv $s/* "$c/" && cp -f "$RUN/syslinux/splash.png" "$c/"
1718         cat > "$c/syslinux.cfg" << EOF
1719 UI vesamenu.c32
1720 MENU TITLE $DIST Boot Menu
1721 MENU BACKGROUND splash.png
1722 TIMEOUT 50
1723 DEFAULT $DIST
1724
1725 # see: https://www.syslinux.org/wiki/index.php/Comboot/menu.c32
1726 MENU WIDTH 78
1727 MENU MARGIN 4
1728 MENU ROWS 4
1729 MENU VSHIFT 10
1730 MENU TIMEOUTROW 13
1731 MENU TABMSGROW 14
1732 MENU CMDLINEROW 14
1733 MENU HELPMSGROW 16
1734 MENU HELPMSGENDROW 29
1735 MENU COLOR border       30;44   #40ffffff #a0000000 std
1736 MENU COLOR title        1;36;44 #9033ccff #a0000000 std
1737 MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
1738 MENU COLOR unsel        37;44   #50ffffff #a0000000 std
1739 MENU COLOR help         37;40   #c0ffffff #a0000000 std
1740 MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
1741 MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std
1742 MENU COLOR msg07        37;40   #90ffffff #a0000000 std
1743 MENU COLOR tabmsg       31;40   #30ffffff #00000000 std
1744
1745 LABEL $DIST
1746 MENU LABEL $DIST Linux
1747 LINUX $d/vmlinuz-$KERNEL
1748 APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1749 INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL.img
1750
1751 LABEL ${DIST}fallback
1752 MENU LABEL $DIST Linux Fallback
1753 LINUX $d/vmlinuz-$KERNEL
1754 APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1755 INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL-fallback.img
1756 $([[ $SYS == 'BIOS' ]] && printf "\n%s" "# examples of chainloading other bootloaders
1757
1758 #LABEL grub2
1759 #MENU LABEL Grub2
1760 #COM32 chain.c32
1761 #APPEND file=$d/grub/boot.img
1762
1763 #LABEL windows
1764 #MENU LABEL Windows
1765 #COM32 chain.c32
1766 #APPEND hd0 3")
1767 EOF
1768         return 0
1769 }
1770
1771 setup_refind-efi()
1772 {
1773         EDIT_FILES[bootloader]="/boot/refind_linux.conf"
1774         BCMDS[refind-efi]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1775                 refind-install"
1776 }
1777
1778 prerun_refind-efi()
1779 {
1780         cat > $MNT/boot/refind_linux.conf << EOF
1781 "$DIST Linux"          "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1782                         printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1783                         printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL.img"
1784 "$DIST Linux Fallback" "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
1785                         printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
1786                         printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL-fallback.img"
1787 EOF
1788         mkdir -p $MNT/etc/pacman.d/hooks
1789         cat > $MNT/etc/pacman.d/hooks/refind.hook << EOF
1790 [Trigger]
1791 Operation = Upgrade
1792 Type = Package
1793 Target = refind-efi
1794
1795 [Action]
1796 Description = Updating rEFInd on ESP
1797 When = PostTransaction
1798 Exec = /usr/bin/refind-install
1799 EOF
1800 }
1801
1802 setup_systemd-boot()
1803 {
1804         EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf"
1805         BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
1806                 bootctl --path=/boot install"
1807 }
1808
1809 prerun_systemd-boot()
1810 {
1811         mkdir -p $MNT/boot/loader/entries
1812         cat > $MNT/boot/loader/loader.conf << EOF
1813 default  $DIST
1814 timeout  5
1815 editor   no
1816 EOF
1817         cat > $MNT/boot/loader/entries/$DIST.conf << EOF
1818 title   $DIST Linux
1819 linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1820 initrd  /initramfs-$KERNEL.img
1821 options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1822 EOF
1823         cat > $MNT/boot/loader/entries/$DIST-fallback.conf << EOF
1824 title   $DIST Linux Fallback
1825 linux   /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd  %s" "/$UCODE.img")
1826 initrd  /initramfs-$KERNEL-fallback.img
1827 options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
1828 EOF
1829         mkdir -p $MNT/etc/pacman.d/hooks
1830         cat > $MNT/etc/pacman.d/hooks/systemd-boot.hook << EOF
1831 [Trigger]
1832 Type = Package
1833 Operation = Upgrade
1834 Target = systemd
1835
1836 [Action]
1837 Description = Updating systemd-boot
1838 When = PostTransaction
1839 Exec = /usr/bin/bootctl update
1840 EOF
1841         systemd-machine-id-setup --root="$MNT"
1842         return 0
1843 }
1844
1845 ###############################################################################
1846 # lvm functions
1847
1848 lvm_menu()
1849 {
1850         check_background_install || return 1
1851         lvm_detect
1852         local choice
1853         dlg choice menu "Logical Volume Management" "$_lvmmenu" \
1854                 "$_lvmnew"    "vgcreate -f, lvcreate -L -n" \
1855                 "$_lvmdel"    "vgremove -f" \
1856                 "$_lvmdelall" "lvrmeove, vgremove, pvremove -f" \
1857                 "back"        "return to the main menu"
1858
1859         case "$choice" in
1860                 "$_lvmnew") lvm_create || return 1 ;;
1861                 "$_lvmdel") lvm_delgroup && yesno "$_lvmdel" "$_lvmdelask" && vgremove -f "$DEL_VG" >/dev/null 2>&1 ;;
1862                 "$_lvmdelall") lvm_del_all ;;
1863         esac
1864
1865         return 0
1866 }
1867
1868 lvm_detect()
1869 {
1870         local v pv
1871         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1872         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1873         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1874
1875         if [[ $VGROUP && $v && $pv ]]; then
1876                 msg "Logical Volume Management" "\nActivating existing logical volume management (LVM).\n\n" 1
1877                 modprobe dm-mod >/dev/null 2>$ERR
1878                 errshow 'modprobe dm-mod'
1879                 vgscan >/dev/null 2>&1
1880                 vgchange -ay >/dev/null 2>&1
1881         fi
1882 }
1883
1884 lvm_create()
1885 {
1886         VGROUP='' LVM_PARTS='' VGROUP_MB=0
1887         umount_dir $MNT
1888         lvm_mkgroup || return 1
1889         local txt="\nThe last (or only) logical volume will automatically use all remaining space in the volume group."
1890         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
1891         [[ $VOL_COUNT ]] || return 1
1892         lvm_extra_lvs || return 1
1893         lvm_volume_name "$_lvmlvname\nNOTE: This LV will use up all remaining space in the volume group (${VGROUP_MB}MB)" || return 1
1894         lvcreate -l +100%FREE "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
1895         errshow "lvcreate -l +100%FREE $VGROUP -n $VNAME" || return 1
1896         LVM='logical volume'; sleep 0.5
1897         txt="\nDone, volume: $VGROUP-$VNAME (${VOLUME_SIZE:-${VGROUP_MB}MB}) has been created.\n"
1898         msg "$_lvmnew (LV:$VOL_COUNT)" "$txt\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE $LVM_PARTS)\n"
1899         return 0
1900 }
1901
1902 get_lv_size()
1903 {
1904         local txt="${VGROUP}: ${SIZE}$SIZE_UNIT (${VGROUP_MB}MB remaining).$_lvmlvsize"
1905
1906         while true; do
1907                 ERR_SIZE=0
1908                 dlg VOLUME_SIZE input "$_lvmnew (LV:$VOL_COUNT)" "$txt" ''
1909                 if [[ -z $VOLUME_SIZE ]]; then
1910                         ERR_SIZE=1
1911                         break # allow bailing with escape or an empty choice
1912                 elif (( ${VOLUME_SIZE:0:1} == 0 )); then
1913                         ERR_SIZE=1 # size values can't begin with '0'
1914                 else
1915                         # walk the string and make sure all but the last char are digits
1916                         local lv=$((${#VOLUME_SIZE} - 1))
1917                         for (( i=0; i<lv; i++ )); do
1918                                 [[ ${VOLUME_SIZE:$i:1} =~ [0-9] ]] || { ERR_SIZE=1; break; }
1919                         done
1920                         if (( ERR_SIZE != 1 )); then
1921                                 case ${VOLUME_SIZE:$lv:1} in
1922                                         [mMgG]) local s=${VOLUME_SIZE:0:$lv} m=$((s * 1000))
1923                                                 case ${VOLUME_SIZE:$lv:1} in
1924                                                         [Gg]) (( m >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - m)) ;;
1925                                                         [Mm]) (( ${VOLUME_SIZE:0:$lv} >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - s)) ;;
1926                                                         *) ERR_SIZE=1
1927                                                 esac ;;
1928                                         *) ERR_SIZE=1
1929                                 esac
1930                         fi
1931                 fi
1932                 if (( ERR_SIZE )); then
1933                         msg "Invalid Logical Volume Size" "$_lvmerrlvsize"
1934                 else
1935                         break
1936                 fi
1937         done
1938
1939         return $ERR_SIZE
1940 }
1941
1942 lvm_mkgroup()
1943 {
1944         local named=''
1945         local txt="\nConfirm creation of volume group: $VGROUP\n\nwith the following partition(s):"
1946
1947         until [[ $named ]]; do
1948                 lvm_partitions || return 1
1949                 lvm_group_name || return 1
1950                 yesno "$_lvmnew" "$txt $LVM_PARTS\n" && named=true
1951         done
1952
1953         vgcreate -f "$VGROUP" $LVM_PARTS >/dev/null 2>$ERR
1954         errshow "vgcreate -f $VGROUP $LVM_PARTS" || return 1
1955
1956         SIZE=$(vgdisplay "$VGROUP" | awk '/VG Size/ { gsub(/[^0-9.]/, ""); print int($0) }')
1957         SIZE_UNIT="$(vgdisplay "$VGROUP" | awk '/VG Size/ { print substr($NF, 0, 1) }')"
1958
1959         if [[ $SIZE_UNIT == 'G' ]]; then
1960                 VGROUP_MB=$((SIZE * 1000))
1961         else
1962                 VGROUP_MB=$SIZE
1963         fi
1964
1965         msg "$_lvmnew" "\nVolume group: $VGROUP ($SIZE $SIZE_UNIT) has been created\n"
1966         return 0
1967 }
1968
1969 lvm_del_all()
1970 {
1971         local v pv
1972         pv="$(pvs -o pv_name --noheading 2>/dev/null)"
1973         v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
1974         VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
1975
1976         if [[ $VGROUP || $v || $pv ]]; then
1977                 if yesno "$_lvmdelall" "$_lvmdelask"; then
1978                         for i in $v; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1; done
1979                         for i in $VGROUP; do vgremove -f "$i" >/dev/null 2>&1; done
1980                         for i in $pv; do pvremove -f "$i" >/dev/null 2>&1; done
1981                         LVM=''
1982                 fi
1983         else
1984                 msg "Delete LVM" "\nNo LVMs to remove...\n" 2
1985                 LVM=''
1986         fi
1987 }
1988
1989 lvm_delgroup()
1990 {
1991         DEL_VG=''
1992         VOL_GROUP_LIST=''
1993
1994         for i in $(lvs --noheadings | awk '{print $2}' | uniq); do
1995                 VOL_GROUP_LIST+="$i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}') "
1996         done
1997         [[ $VOL_GROUP_LIST ]] || { msg "No Groups" "\nNo volume groups found."; return 1; }
1998
1999         dlg DEL_VG menu "Logical Volume Management" "\nSelect volume group to delete.\n\nAll logical volumes within will also be deleted." $VOL_GROUP_LIST
2000         [[ $DEL_VG ]]
2001 }
2002
2003 lvm_extra_lvs()
2004 {
2005         while (( VOL_COUNT > 1 )); do
2006                 lvm_volume_name "$_lvmlvname" && get_lv_size || return 1
2007                 lvcreate -L "$VOLUME_SIZE" "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
2008                 errshow "lvcreate -L $VOLUME_SIZE $VGROUP -n $VNAME" || return 1
2009                 msg "$_lvmnew (LV:$VOL_COUNT)" "\nDone, logical volume (LV) $VNAME ($VOLUME_SIZE) has been created.\n"
2010                 (( VOL_COUNT-- ))
2011         done
2012         return 0
2013 }
2014
2015 lvm_partitions()
2016 {
2017         part_find 'part|crypt' || return 1
2018         PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")"
2019         dlg LVM_PARTS check "$_lvmnew" "\nSelect the partition(s) to use for the physical volume (PV)." $PARTS
2020         [[ $LVM_PARTS ]]
2021 }
2022
2023 lvm_group_name()
2024 {
2025         VGROUP=''
2026         until [[ $VGROUP ]]; do
2027                 dlg VGROUP input "$_lvmnew" "$_lvmvgname" "mygroup"
2028                 if [[ -z $VGROUP ]]; then
2029                         return 1
2030                 elif [[ ${VGROUP:0:1} == "/" || $VGROUP =~ \ |\' ]] || lsblk | grep -q "$VGROUP"; then
2031                         msg "LVM Name Error" "$_lvmerrvgname"
2032                         VGROUP=''
2033                 fi
2034         done
2035         return 0
2036 }
2037
2038 lvm_volume_name()
2039 {
2040         VNAME=''
2041         local txt="$1" default="mainvolume"
2042         (( VOL_COUNT > 1 )) && default="extravolume$VOL_COUNT"
2043         until [[ $VNAME ]]; do
2044                 dlg VNAME input "$_lvmnew (LV:$VOL_COUNT)" "\n$txt" "$default"
2045                 if [[ -z $VNAME ]]; then
2046                         return 1
2047                 elif [[ ${VNAME:0:1} == "/" || $VNAME =~ \ |\' ]] || lsblk | grep -q "$VNAME"; then
2048                         msg "LVM Name Error" "$_lvmerlvname"
2049                         VNAME=''
2050                 fi
2051         done
2052         return 0
2053 }
2054
2055 ###############################################################################
2056 # luks functions
2057
2058 luks_menu()
2059 {
2060         local choice
2061         check_background_install || return 1
2062         dlg choice menu "LUKS Encryption" "$_luksmenu" \
2063                 "$_luksnew"  "cryptsetup -q luksFormat" \
2064                 "$_luksopen" "cryptsetup open --type luks" \
2065                 "$_luksadv"  "cryptsetup -q -s -c luksFormat" \
2066                 "back"       "Return to the main menu"
2067
2068         case "$choice" in
2069                 "$_luksnew") luks_basic || return 1 ;;
2070                 "$_luksopen") luks_open || return 1 ;;
2071                 "$_luksadv") luks_advanced || return 1 ;;
2072         esac
2073
2074         return 0
2075 }
2076
2077 luks_open()
2078 {
2079         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2080         umount_dir $MNT
2081         part_find 'part|crypt|lvm' || return 1
2082
2083         if (( COUNT == 1 )); then
2084                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2085         else
2086                 dlg LUKS_PART menu "$_luksopen" "\nSelect which partition to open." $PARTS
2087         fi
2088
2089         [[ $LUKS_PART ]] || return 1
2090
2091         luks_pass "$_luksopen" || return 1
2092         msg "$_luksopen" "\nOpening encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2093         cryptsetup open --type luks "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2094         errshow "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" || return 1
2095         LUKS='encrypted'; luks_show
2096         return 0
2097 }
2098
2099 luks_pass()
2100 {
2101         LUKS_PASS=''
2102         local t="$1" op="$2" v='' p='' p2=''
2103
2104         until [[ $LUKS_PASS ]]; do
2105                 i=0
2106                 tput cnorm
2107                 if [[ $op ]]; then
2108                         dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " \
2109                                 --mixedform "\nEnter the password to decrypt $ROOT_PART\n\nThis is needed to create a keyfile." 0 0 0 \
2110                                 "Password:"  1 1 '' 1 11 "$COLUMNS" 0 1 \
2111                                 "Password2:" 2 1 '' 2 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2112
2113                 else
2114                         dialog --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $t " --mixedform "$_luksomenu" 0 0 0 \
2115                                 "Name:"      1 1 "${LUKS_NAME:-cryptroot}" 1  7 "$COLUMNS" 0 0 \
2116                                 "Password:"  2 1 ''                        2 11 "$COLUMNS" 0 1 \
2117                                 "Password2:" 3 1 ''                        3 12 "$COLUMNS" 0 1 2>"$ANS" || return 1
2118
2119                 fi
2120
2121                 while read -r line; do
2122                         if [[ $op ]]; then
2123                                 case $i in
2124                                         0) p="$line" ;;
2125                                         1) p2="$line" ;;
2126                                 esac
2127                         else
2128                                 case $i in
2129                                         0) n="$line" ;;
2130                                         1) p="$line" ;;
2131                                         2) p2="$line" ;;
2132                                 esac
2133                         fi
2134                         (( i++ ))
2135                 done < "$ANS"
2136
2137                 if [[ -z $op && -z $n ]]; then
2138                         msg "Name Empty" "\nEncrypted device name cannot be empty.\n\nPlease try again.\n" 2
2139                 elif [[ -z $p || "$p" != "$p2" ]]; then
2140                         [[ $op ]] || LUKS_NAME="$n"
2141                         msg "Password Mismatch" "\nThe passwords entered do not match.\n\nPlease try again.\n" 2
2142                 else
2143                         [[ $op ]] || LUKS_NAME="$n"
2144                         LUKS_PASS="$p"
2145                 fi
2146         done
2147
2148         return 0
2149 }
2150
2151 luks_show()
2152 {
2153         sleep 0.5
2154         msg "$_luksnew" "\nEncrypted partition opened and ready for mounting.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE "$LUKS_PART")\n\n"
2155 }
2156
2157 luks_setup()
2158 {
2159         modprobe -a dm-mod dm_crypt >/dev/null 2>&1
2160         umount_dir $MNT
2161         part_find 'part|lvm' || return 1
2162
2163         if [[ $AUTO_ROOT_PART ]]; then
2164                 LUKS_PART="$AUTO_ROOT_PART"
2165         elif (( COUNT == 1 )); then
2166                 LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
2167         else
2168                 dlg LUKS_PART menu "$_luksnew" "\nSelect the partition you want to encrypt." $PARTS
2169         fi
2170
2171         [[ $LUKS_PART ]] || return 1
2172         luks_pass "$_luksnew"
2173 }
2174
2175 luks_basic()
2176 {
2177         luks_setup || return 1
2178         msg "$_luksnew" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2179         cryptsetup -q luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2180         errshow "cryptsetup -q luksFormat $LUKS_PART" || return 1
2181         cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2182         errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2183         LUKS='encrypted'; luks_show
2184         return 0
2185 }
2186
2187 luks_keyfile()
2188 {
2189         if [[ ! -e $MNT/crypto_keyfile.bin ]]; then
2190                 # printf "Creating LUKS keyfile /crypto_keyfile.bin\n"
2191                 local n
2192                 n="$(lsblk -lno NAME,UUID,TYPE | awk "/$LUKS_UUID/"' && /part|crypt|lvm/ {print $1}')"
2193                 local mkkey="dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin"
2194                 mkkey="$mkkey && chmod 000 /crypto_keyfile.bin"
2195                 mkkey="$mkkey && cryptsetup luksAddKey /dev/$n /crypto_keyfile.bin <<< '$LUKS_PASS'"
2196                 chrun "$mkkey"
2197                 sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf 2>$ERR
2198         fi
2199
2200         return 0
2201 }
2202
2203 luks_advanced()
2204 {
2205         if luks_setup; then
2206                 local cipher
2207                 dlg cipher input "LUKS Encryption" "$_lukskey" "-s 512 -c aes-xts-plain64"
2208                 [[ $cipher ]] || return 1
2209                 msg "$_luksadv" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
2210                 cryptsetup -q $cipher luksFormat "$LUKS_PART" <<< "$LUKS_PASS" 2>$ERR
2211                 errshow "cryptsetup -q $cipher luksFormat $LUKS_PART" || return 1
2212                 cryptsetup open "$LUKS_PART" "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
2213                 errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
2214                 luks_show
2215                 return 0
2216         fi
2217         return 1
2218 }
2219
2220 ###############################################################################
2221 # helper functions
2222
2223 ofn()
2224 {
2225         [[ "$2" == *"$1"* ]] && printf "on" || printf "off"
2226 }
2227
2228 die()
2229 {
2230         local exitcode="$1"
2231         trap - INT
2232         trap - TSTP
2233         tput cnorm
2234         if [[ -d $MNT ]] && command cd /; then
2235                 umount_dir $MNT
2236                 if (( exitcode == 127 )); then
2237                         umount -l /run/archiso/bootmnt
2238                         sleep 0.5
2239                         systemctl -i reboot
2240                 fi
2241         fi
2242
2243         # restore custom linux console 0-15 colors when not rebooting
2244         if [[ $TERM == 'linux' ]]; then
2245                 colors=("\e]P0191919" "\e]P1D15355" "\e]P2609960" "\e]P3FFCC66"
2246                 "\e]P4255A9B" "\e]P5AF86C8" "\e]P62EC8D3" "\e]P7949494" "\e]P8191919" "\e]P9D15355"
2247                 "\e]PA609960" "\e]PBFF9157" "\e]PC4E88CF" "\e]PDAF86C8" "\e]PE2ec8d3" "\e]PFE1E1E1")
2248                 printf "%b" "${colors[@]}" && clear && unset col
2249         fi
2250
2251         exit "$exitcode"
2252 }
2253
2254 dlg()
2255 {
2256         local var="$1" dialog_type="$2" title="$3" body="$4" n=0
2257         shift 4
2258         (( ($# / 2) > SHL )) && n=$SHL
2259         
2260         tput civis
2261         case "$dialog_type" in
2262                 menu)
2263                         dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --menu "$body" 0 0 $n "$@" 2>"$ANS" || return 1
2264                         ;;
2265                 check)
2266                         dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --checklist "$body" 0 0 $n "$@" 2>"$ANS" || return 1
2267                         ;;
2268                 input)
2269                         tput cnorm
2270                         local def="$1"
2271                         shift
2272                         if [[ $1 == 'limit' ]]; then
2273                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --max-input 63 --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2274                         else
2275                                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --inputbox "$body" 0 0 "$def" 2>"$ANS" || return 1
2276                         fi
2277                         ;;
2278         esac
2279         [[ -s "$ANS" ]] && printf -v "$var" "%s" "$(< "$ANS")"
2280 }
2281
2282 msg()
2283 {
2284         local title="$1" body="$2"
2285         tput civis
2286         if (( $# == 3 )); then
2287                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --sleep "$3" --title " $title " --infobox "$body\n" 0 0
2288         else
2289                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --msgbox "$body\n" 0 0
2290         fi
2291 }
2292
2293 json()
2294 {
2295         curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])"
2296 }
2297
2298 yesno()
2299 {
2300         local title="$1" body="$2" yes='Yes' no='No'
2301         (( $# >= 3 )) && local yes="$3"
2302         (( $# >= 4 )) && local no="$4"
2303         tput civis
2304         if (( $# == 5 )); then
2305                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --defaultno --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2306         else
2307                 dialog --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
2308         fi
2309 }
2310
2311 chrun()
2312 {
2313         arch-chroot "$MNT" bash -c "$1"
2314 }
2315
2316 debug()
2317 {
2318         export PS4='| ${BASH_SOURCE} LINE:${LINENO} FUNC:${FUNCNAME[0]:+ ${FUNCNAME[0]}()} |>  '
2319         set -x
2320         exec 3>| $DBG
2321         BASH_XTRACEFD=3
2322         DEBUG=true
2323 }
2324
2325 sigint()
2326 {
2327         printf "\n^C caught, cleaning up...\n"
2328         die 1
2329 }
2330
2331 print4()
2332 {
2333         local str="$*"
2334         if [[ $COLUMNS -ge 110 && ${#str} -gt $((COLUMNS - 30)) ]]; then
2335                 str="$(awk '{
2336                         i = 2; p1 = p2 = p3 = p4 = ""; p1 = $1; q = int(NF / 4)
2337                         for (; i <= q; i++) { p1 = p1 " " $i }
2338                         for (; i <= q * 2; i++) { p2 = p2 " " $i }
2339                         for (; i <= q * 3; i++) { p3 = p3 " " $i }
2340                         for (; i <= NF; i++) { p4 = p4 " " $i }
2341                         printf "%s\n           %s\n           %s\n           %s", p1, p2, p3, p4
2342                 }' <<< "$str")"
2343                 printf "%s\n" "$str"
2344         elif [[ $str ]]; then
2345                 printf "%s\n" "$str"
2346         fi
2347 }
2348
2349 errshow()
2350 {
2351         exit_code="$?"
2352         (( exit_code == 0 )) && return 0
2353
2354         local fatal=0 err=""
2355         err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")"
2356         (( $1 == 1 )) && { fatal=1; shift; }
2357         local txt="\nThe command exited abnormally:\n\n$1\n\n"
2358
2359         if [[ $err ]]; then
2360                 txt+="With the following message:\n\n$err\n\n"
2361         else
2362                 txt+="With no error message:\n\n"
2363         fi
2364
2365         if (( fatal == 1 )); then
2366                 txt+="Errors at this stage are fatal and the install cannot continue.\n"
2367         else
2368                 txt+="Errors at this stage are non-fatal and can be either fixed or ignored depending on the error.\n"
2369         fi
2370
2371         msg "Install Error" "$txt"
2372
2373         if (( fatal == 1 )); then
2374                 [[ -r $DBG && $TERM == 'linux' ]] && less "$DBG"
2375                 die 1
2376         fi
2377
2378         return 1
2379 }
2380
2381 load_bcm()
2382 {
2383         msg "Broadcom Wireless Setup" "\nDetected chipset is Broadcom BCM4352\n\nDisabling bcma/b43 modules and loading wl module.\n" 1
2384         { rmmod wl; rmmod bcma; rmmod b43; rmmod ssb; modprobe wl; depmod -a; } >/dev/null 2>&1
2385         BROADCOM_WL=true
2386 }
2387
2388 prechecks()
2389 {
2390         if [[ $1 -ge 0 ]] && ! grep -qw "$MNT" /proc/mounts; then
2391                 msg "Not Mounted" "\nPartition(s) must be mounted first.\n" 2
2392                 SEL=4
2393                 return 1
2394         elif [[ $1 -ge 1 && -z $BOOTLDR ]]; then
2395                 msg "No Bootloader" "\nBootloader must be selected first.\n" 2
2396                 SEL=5
2397                 return 1
2398         elif [[ $1 -ge 2 && (-z $NEWUSER || -z $USER_PASS) ]]; then
2399                 msg "No User" "\nA user must be created first.\n" 2
2400                 SEL=6
2401                 return 1
2402         elif [[ $1 -ge 3 && -z $CONFIG_DONE ]]; then
2403                 msg "Not Configured" "\nSystem configuration must be done first.\n" 2
2404                 SEL=7
2405                 return 1
2406         fi
2407         return 0
2408 }
2409
2410 umount_dir()
2411 {
2412         swapoff -a
2413         [[ -d $1 ]] && umount -R "$1" >/dev/null 2>&1
2414         return 0
2415 }
2416
2417 chk_connect()
2418 {
2419         msg "Network Connect" "\nVerifying network connection\n" 1
2420         curl -sIN --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200'
2421 }
2422
2423 net_connect()
2424 {
2425         if chk_connect; then
2426                 return 0
2427         else
2428                 if hash nmtui >/dev/null 2>&1; then
2429                         tput civis
2430                         [[ $TERM == 'linux' ]] && printf "%b" "\e]P1191919" "\e]P4191919"
2431                         nmtui-connect
2432                         [[ $TERM == 'linux' ]] && printf "%b" "\e]P1D15355" "\e]P4255a9b"
2433                         chk_connect
2434                 else
2435                         return 1
2436                 fi
2437         fi
2438 }
2439
2440 system_devices()
2441 {
2442         IGNORE_DEV="$(lsblk -lno NAME,MOUNTPOINT | awk '/\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')"
2443
2444         if [[ $IGNORE_DEV ]]; then
2445                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')"
2446         else
2447                 SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ {print "/dev/" $1 " " $2}')"
2448         fi
2449
2450         if [[ -z $SYS_DEVS ]]; then
2451                 msg "Device Error" "\nNo available devices...\n\nExiting..\n" 2
2452                 die 1
2453         fi
2454
2455         DEV_COUNT=0
2456         while read -r line; do
2457                 (( DEV_COUNT++ ))
2458         done <<< "$SYS_DEVS"
2459 }
2460
2461 system_identify()
2462 {
2463         if [[ $VM ]]; then
2464                 UCODE=''
2465         elif grep -q 'AuthenticAMD' /proc/cpuinfo; then
2466                 UCODE="amd-ucode"
2467         elif grep -q 'GenuineIntel' /proc/cpuinfo; then
2468                 UCODE="intel-ucode"
2469         fi
2470
2471         modprobe -q efivarfs >/dev/null 2>&1
2472
2473         _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."
2474         if [[ -d /sys/firmware/efi/efivars ]]; then
2475                 export SYS="UEFI"
2476                 grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars
2477                 _prep+="\n  - An EFI boot partition mounted."
2478         else
2479                 export SYS="BIOS"
2480         fi
2481         _prep+="\n\nOnce finished mounting, a portion of the install can be done in the background while you continue configuring the system:\n"
2482         _prep+="\n  - Choose the system bootloader.\n  - Create a user and password."
2483         _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."
2484 }
2485
2486 check_background_install()
2487 {
2488         [[ $RSYNC_PID || $MIRROR_PID ]] || return 0
2489         msg "Install Running" "\nA background install process is currently running.\n" 2
2490         return 1
2491 }
2492
2493 ###############################################################################
2494 # entry point
2495
2496 if (( UID != 0 )); then
2497         msg "Not Root" "\nThis installer must be run as root or using sudo.\n\nExiting..\n" 2
2498         die 1
2499 elif ! grep -qwm 1 'lm' /proc/cpuinfo; then
2500         msg "Not x86_64 Architecture" "\nThis installer only supports x86_64 architectures.\n\nExiting..\n" 2
2501         die 1
2502 elif [[ $1 =~ (-d|--debug) ]]; then
2503         debug
2504 fi
2505
2506 # trap ^C to perform cleanup
2507 trap sigint INT
2508
2509 system_identify
2510 system_devices
2511
2512 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"
2513
2514 select_keymap || { clear; die 0; }
2515
2516 if lspci -vnn -d 14e4: | grep -q 'BCM4352'; then
2517         load_bcm
2518 fi
2519
2520 if ! net_connect; then
2521         msg "Not Connected" "\nThis installer requires an active internet connection.\n\nExiting..\n" 2
2522         die 1
2523 fi
2524
2525 while true; do
2526         select_main
2527 done
2528
2529 # vim:fdm=marker:fmr={,}