OSDN Git Service

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