OSDN Git Service

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