OSDN Git Service

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