OSDN Git Service

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