OSDN Git Service

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