OSDN Git Service

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