OSDN Git Service

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