OSDN Git Service

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