OSDN Git Service

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