OSDN Git Service

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