OSDN Git Service

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