OSDN Git Service

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