OSDN Git Service

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