OSDN Git Service

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