OSDN Git Service

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