OSDN Git Service

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