OSDN Git Service

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