OSDN Git Service

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