OSDN Git Service

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