OSDN Git Service

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