OSDN Git Service

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