OSDN Git Service

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