OSDN Git Service

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