OSDN Git Service

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