This repository has been archived on 2024-09-01. You can view files and clone it, but cannot push or open issues or pull requests.
al-installer/archlabs-installer
2019-03-16 21:21:49 -07:00

2598 lines
88 KiB
Bash
Executable File

#!/bin/bash
# This program is free software, provided under the GNU GPL
# Written by Nathaniel Maia for use in Archlabs
# Some ideas and code reworked from other resources
# AIF, Cnichi, Calamares, Arch Wiki.. Credit where credit is due
VER="2.0.17" # version
DIST="ArchLabs" # distributor
MNT="/mnt" # mountpoint
ANS="/tmp/ans"
# --------------------------------------------- #
# if you manually mount your partitions then you
# need to set these values to avoid issues.
# --------------------------------------------- #
# root partition, eg/ /dev/sda2
ROOT_PART=''
# boot partition, required on UEFI, eg. /dev/sda1
BOOT_PART=''
# device used for some bootloader install process, eg. /dev/sda
BOOT_DEV=''
# bootloader to use, can be:
# UEFI: grub, syslinux, efistub, systemd-boot, refind-efi
# BIOS: grub, syslinux
BOOTLDR=''
# directory to mount the boot partition (if any) for most bootloaders
# this will need to be 'boot' however a popular one for grub is 'boot/efi'
BOOTDIR='boot'
# swap partition or file path
SWAP_PART=''
# swap size, only used when creating a swapfile
SWAP_SIZE=''
# --------------------------------------------- #
# bulk default values {
EXMNT='' # holder for additional partitions while mounting
EXMNTS='' # when an extra partition is mounted append it's info
FONT="ter-i16n" # font used in the linux console
HOOKS="shutdown" # list of additional HOOKS to add in /etc/mkinitcpio.conf
LOGINRC='' # login shell rc file, eg. .zprofile, .bash_profile, .profile
LOGIN_WM='' # default login session, used only for xinit
LOGIN_TYPE='' # login manager can be: lightdm, xinit
INSTALL_WMS='' # space separated list of chosen wm and de
UCODE='' # cpu microcode (if any), eg. amd-ucode, intel-ucode
KERNEL='' # can be linux, linux-lts, linux-zen, or linux-hardened
MYSHELL='' # full path for the shell, eg. /usr/bin/zsh
WM_PKGS='' # full list of packages added during wm/de choice
PACKAGES='' # list of all packages to install including WM_PKGS
USER_PKGS='' # packages selected by the user during install
NEWUSER='' # username for the primary user
USER_PASS='' # password for the primary user
ROOT_PASS='' # root password
LUKS='' # empty when not using luks encryption
LUKS_DEV='' # boot parameter string for LUKS
LUKS_PART='' # partition used for encryption
LUKS_PASS='' # encryption password
LUKS_UUID='' # encrypted partition UUID
LUKS_NAME='' # name used for encryption
LVM='' # empty when not using lvm
VGROUP_MB=0 # available space in volume group
LVM_PARTS='' # partitions used for volume group
WARN='' # issued mounting/partitioning warning
SEP_BOOT='' # separate boot partition for BIOS
AUTOLOGIN='' # enable autologin for xinit
CONFIG_DONE='' # basic configuration is finished
BROADCOM_WL='' # fixes for broadcom cards eg. BCM4352
FORMATTED='' # partitions we formatted and should allow skipping
AUTO_ROOT_PART='' # root value from auto partition
AUTO_BOOT_PART='' # boot value from auto partition
# iso base, pacstrap when running the installer from a stock arch iso
ISO_BASE="arch-install-scripts b43-firmware b43-fwcutter broadcom-wl clonezilla dhclient dhcpcd ethtool "
ISO_BASE+="exfat-utils f2fs-tools gptfdisk vim hdparm ipw2100-fw ipw2200-fw nfs-utils nilfs-utils ntfs-3g "
ISO_BASE+="pacman-contrib parted rsync sdparm smartmontools wget wireless_tools wpa_actiond xl2tpd dialog "
ISO_BASE+="parted alsa-firmware alsa-lib alsa-plugins pulseaudio pulseaudio-alsa networkmanager "
ISO_BASE+="wireless-regdb wpa_supplicant lm_sensors lsb-release p7zip pamixer reflector unrar htop ranger "
ISO_BASE+="w3m terminus-font ttf-dejavu archlabs-keyring"
# baseline
BASE_PKGS="archlabs-skel-base archlabs-themes archlabs-dARK archlabs-icons archlabs-wallpapers "
BASE_PKGS+="base-devel xorg xorg-drivers sudo git gvfs gtk3 gtk-engines gtk-engine-murrine pavucontrol tumbler "
BASE_PKGS+="playerctl ffmpeg gstreamer libmad libmatroska gst-libav gst-plugins-base gst-plugins-good scrot"
# extras for window managers
WM_BASE_PKGS="arandr archlabs-networkmanager-dmenu xdg-user-dirs nitrogen polkit-gnome volumeicon xclip exo "
WM_BASE_PKGS+="xdotool compton wmctrl gnome-keyring dunst feh gsimplecal xfce4-power-manager xfce4-settings laptop-detect"
SEL=0 # currently selected menu item
ERR="/tmp/errlog" # error log used internally
DBG="/tmp/debuglog" # debug log when passed -d
RUN="/run/archiso/bootmnt/arch/boot" # path for live /boot
VM="$(dmesg | grep -i "hypervisor")" # is the system a vm
SYS='Unknown'
# }
# giant ugly variable container :P {
# RAM in the system in MB
SYS_MEM="$(awk '/MemTotal/ {
print int($2 / 1024)"M"
}' /proc/meminfo)"
# locales from /etc/locale.gen
LOCALES="$(awk '/\.UTF-8/ {
gsub(/# .*|#/, "")
if ($1) {
print $1 " - "
}
}' /etc/locale.gen)"
# linux console keyboard mappings
CMAPS="$(find /usr/share/kbd/keymaps -name '*.map.gz' | awk '{
gsub(/\.map\.gz|.*\//, "")
print $1 " - "
}' | sort)"
# terminal size definitions
# {
[[ $LINES ]] || LINES=$(tput lines)
[[ $COLUMNS ]] || COLUMNS=$(tput cols)
SHL=$((LINES - 20)) # }
# associative arrays
# {
# commands used to install each bootloader (most get modified during runtime) {
declare -A BCMDS=(
[refind-efi]='refind-install'
[grub]='grub-install --recheck --force'
[syslinux]='syslinux-install_update -i -a -m'
[efistub]='efibootmgr -v -d /dev/sda -p 1 -c -l'
[systemd-boot]='bootctl --path=/boot install'
) # }
# executable name for each wm/de {
declare -A WM_SESSIONS=(
[dwm]='dwm'
[i3-gaps]='i3'
[bspwm]='bspwm'
[plasma]='startkde'
[xfce4]='startxfce4'
[gnome]='gnome-session'
[fluxbox]='startfluxbox'
[openbox]='openbox-session'
[cinnamon]='cinnamon-session'
) # }
# packages installed for each wm/de {
declare -A WM_EXT=(
[gnome]=''
[plasma]='kdebase-meta'
[bspwm]='sxhkd archlabs-skel-bspwm rofi archlabs-polybar'
[fluxbox]='archlabs-skel-fluxbox archlabs-polybar jgmenu rofi lxmenu-data'
[i3-gaps]='i3status perl-anyevent-i3 archlabs-skel-i3-gaps rofi archlabs-polybar'
[openbox]='obconf archlabs-skel-openbox jgmenu archlabs-polybar tint2 conky rofi lxmenu-data'
[xfce4]='xfce4-goodies xfce4-pulseaudio-plugin network-manager-applet volumeicon rofi archlabs-skel-xfce4'
) # }
# files that can be edited after install is complete {
declare -A EDIT_FILES=(
[login]=''
[fstab]='/etc/fstab'
[sudoers]='/etc/sudoers'
[crypttab]='/etc/crypttab'
[pacman]='/etc/pacman.conf'
[console]='/etc/vconsole.conf'
[mkinitcpio]='/etc/mkinitcpio.conf'
[hostname]='/etc/hostname /etc/hosts'
[bootloader]="/boot/loader/entries/$DIST.conf"
[locale]='/etc/locale.conf /etc/default/locale'
[keyboard]='/etc/X11/xorg.conf.d/00-keyboard.conf /etc/default/keyboard'
) # }
# mkfs command for formatting each offered filesystem {
declare -A FS_CMDS=(
[f2fs]='mkfs.f2fs'
[jfs]='mkfs.jfs -q'
[xfs]='mkfs.xfs -f'
[ntfs]='mkfs.ntfs -q'
[ext2]='mkfs.ext2 -q'
[ext3]='mkfs.ext3 -q'
[ext4]='mkfs.ext4 -q'
[vfat]='mkfs.vfat -F32'
[nilfs2]='mkfs.nilfs2 -q'
[reiserfs]='mkfs.reiserfs -q'
) # }
# mount options for each offered filesystem (if any {
declare -A FS_OPTS=(
[vfat]=''
[ntfs]=''
[ext2]=''
[ext3]=''
[jfs]='discard errors=continue errors=panic nointegrity'
[reiserfs]='acl nolog notail replayonly user_xattr off'
[ext4]='discard dealloc nofail noacl relatime noatime nobarrier nodelalloc'
[xfs]='discard filestreams ikeep largeio noalign nobarrier norecovery noquota wsync'
[nilfs2]='discard nobarrier errors=continue errors=panic order=relaxed order=strict norecovery'
[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'
) # }
# PKG_EXT: if you add a package to $PACKAGES in any dialog {
# and it uses/requires some additional packages,
# you can add them here to keep it simple: [package]="extra"
# duplicates are removed with `uniq` before install
declare -A PKG_EXT=(
[vlc]='qt4'
[mpd]='mpc'
[mupdf]='mupdf-tools'
[qt5ct]='qt5-styleplugins'
[vlc]='qt5ct qt5-styleplugins'
[zathura]='zathura-pdf-poppler'
[noto-fonts]='noto-fonts-emoji'
[cairo-dock]='cairo-dock-plug-ins'
[qutebrowser]='qt5ct qt5-styleplugins'
[qbittorrent]='qt5ct qt5-styleplugins'
[transmission-qt]='qt5ct qt5-styleplugins'
[kdenlive]='kdebase-meta dvdauthor frei0r-plugins breeze breeze-gtk qt5ct qt5-styleplugins'
) # }
# }
# text variables
# {
# Basics (somewhat in order)
_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"
_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"
_user="\nEnter a name and password for the new user account.\n\nThe name must not use capital letters, contain any periods (.), end with a hyphen (-), or include any colons (:)\n\nNOTE: Use the [Up] and [Down] arrows to switch between input fields, [Tab] to toggle between input fields and the buttons, and [Enter] to accept."
_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."
_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"
_timez="\nThe time zone is used to set the system clock.\n\nSelect your country or continent from the list below"
_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."
_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."
_login="\nSelect which of your session choices to use for the initial login.\n\nYou can be change this later by editing your ~/.xinitrc"
_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."
_device="\nSelect a device to use from the list below.\n\nDevices (/dev) are the available drives on the system. /sda, /sdb, /sdc ..."
_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."
_warn="\nIMPORTANT: Please choose carefully during mounting and formatting.\n\nPartitions can be mounted without formatting by selecting skip during mounting, useful for extra or already formatted partitions.\n\nThe exception to this is the root (/) partition, it needs to be formatted before install to ensure system stability.\n"
_part="\nFull device auto partitioning is available for beginners otherwise cfdisk is recommended.\n\n - All systems will require a root partition (8G or greater).\n - UEFI (and BIOS using LUKS without LVM) require a separate boot partition (100-512M)."
_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."
_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."
_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."
_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"
_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."
_expart="\nYou can now select additional partitions you want mounted, once choosen you will be asked to enter a mountpoint.\n\nSelect 'done' to finish the mounting step and begin unpacking the base system in the background."
_exmnt="\nWhere do you want the partition mounted?\n\nEnsure the name begins with a slash (/).\nExamples include: /usr, /home, /var, etc."
_edit="\nBefore exiting you can select configuration files to review/change.\n\nIf you need to make other changes with the drives still mounted, use Ctrl-z to pause the installer, when finished type 'fg' and [Enter] or Ctrl-z again to resume the installer, if you want to avoid the automatic reboot using Ctrl-c will cleanly exit."
# LUKS
_luksnew="Basic LUKS Encryption"
_luksadv="Advanced LUKS Encryption"
_luksopen="Open Existing LUKS Partition"
_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."
_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."
_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)."
# LVM
_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."
_lvmnew="Create Volume Group and Volume(s)"
_lvmdel="Delete an Existing Volume Group"
_lvmdelall="Delete ALL Volume Group(s) and Volume(s)"
_lvmvgname="\nEnter a name for the volume group (VG) being created from the partition(s) selected."
_lvmlvname="\nEnter a name for the logical volume (LV) being created.\n\nThis is similar to setting a label for a partition."
_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."
_lvmdelask="\nConfirm deletion of volume group(s) and logical volume(s).\n\nDeleting a volume group, will delete all logical volumes within it.\n"
# Errors
_errexpart="\nCannot mount partition due to a problem with the mountpoint.\n\nEnsure it begins with a slash (/) followed by atleast one character.\n"
_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"
_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"
_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"
_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"
_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"
_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"
# }
# }
###############################################################################
# selection menus
select_main()
{
(( SEL < 12 )) && (( SEL++ ))
tput civis
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--title " Prepare " --default-item $SEL --cancel-label 'Exit' --menu "$_prep" 0 0 0 \
1 "Show lsblk output (optional)" \
2 "Edit partitions (optional)" \
3 "LUKS encryption (optional)" \
4 "Logical volume management (optional)" \
5 "Mount and format partitions" \
6 "Select system bootloader" \
7 "Create user and set password" \
8 "Configure system settings" \
9 "Select window manager or desktop (optional)" \
10 "Select additional packages (optional)" \
11 "Check configuration choices (optional)" \
12 "Start the installation" 2>"$ANS"
read -r SEL < "$ANS"
if [[ -z $WARN && $SEL =~ (2|5) ]]; then
msg "Prepare" "$_warn"
WARN=true
fi
case $SEL in
1) part_show ;;
2) part_menu || (( SEL-- )) ;;
3) luks_menu || (( SEL-- )) ;;
4) lvm_menu || (( SEL-- )) ;;
5) select_menu || (( SEL-- )) ;;
6) prechecks 0 && { select_boot || (( SEL-- )); } ;;
7) prechecks 1 && { select_mkuser || (( SEL-- )); } ;;
8) prechecks 2 && { select_config || (( SEL-- )); } ;;
9) prechecks 3 && { select_sessions || (( SEL-- )); } ;;
10) prechecks 3 && { select_packages || (( SEL-- )); } ;;
11) prechecks 3 && select_show ;;
12) prechecks 3 && install_main ;;
*) yesno "Exit" "\nUnmount partitions (if any) and exit the installer?\n" && die 0
esac
}
select_boot()
{
if [[ $SYS == 'BIOS' ]]; then
dlg BOOTLDR menu "BIOS Bootloader" "\nSelect which bootloader to use." \
"grub" "The Grand Unified Bootloader, standard among many Linux distributions" \
"syslinux" "A collection of boot loaders for booting drives, CDs, or over the network"
else
dlg BOOTLDR menu "UEFI Bootloader" "\nSelect which bootloader to use." \
"systemd-boot" "A simple UEFI boot manager which executes configured EFI images" \
"grub" "The Grand Unified Bootloader, standard among many Linux distributions" \
"refind-efi" "A UEFI boot manager that aims to be platform neutral and simplify multi-boot" \
"efistub" "Boot the kernel image directly (no chainloading support)" \
"syslinux" "A collection of boot loaders for booting drives, CDs, or over the network (no chainloading support)"
fi
[[ $BOOTLDR ]] || return 1
setup_${BOOTLDR}
}
select_show()
{
local cmd="${BCMDS[$BOOTLDR]}"
[[ $BOOT_PART ]] && local mnt="/$BOOTDIR" || local mnt="none"
local pkgs="${USER_PKGS# }"
pkgs="${pkgs% }"
pkgs="${pkgs% } ${PACKAGES# }"
pkgs="${pkgs// / }"
pkgs="${pkgs// / }"
[[ $INSTALL_WMS == *dwm* ]] && pkgs="dwm st dmenu ${pkgs# }"
pkgs="${pkgs# }"
pkgs="${pkgs% }"
pkgs="${pkgs// / }"
msg "Show Configuration" "
---------- PARTITION CONFIGURATION ------------
Root: ${ROOT_PART:-none}
Boot: ${BOOT_PART:-${BOOT_DEV:-none}}
Swap: ${SWAP_PART:-none}
Size: ${SWAP_SIZE:-none}
LVM: ${LVM:-none}
LUKS: ${LUKS:-none}
Extra: ${EXMNTS:-${EXMNT:-none}}
Hooks: ${HOOKS:-none}
---------- BOOTLOADER CONFIGURATION -----------
Bootloader: ${BOOTLDR:-none}
Mountpoint: ${mnt:-none}
Command: ${cmd:-none}
------------ SYSTEM CONFIGURATION -------------
Locale: ${MYLOCALE:-none}
Keymap: ${KEYMAP:-none}
Hostname: ${MYHOST:-none}
Timezone: ${ZONE:-none}/${SUBZ:-none}
------------ USER CONFIGURATION --------------
User: ${NEWUSER:-none}
Shell: ${MYSHELL:-none}
Session: ${LOGIN_WM:-none}
Autologin: ${AUTOLOGIN:-none}
Login Method: ${LOGIN_TYPE:-none}
------------ PACKAGES AND MIRRORS -------------
Kernel: ${KERNEL:-none}
Sessions: ${INSTALL_WMS:-none}
Mirrors: ${MIRROR_CMD:-none}
Packages: $(print4 "${pkgs:-none}")
"
}
select_login()
{
dlg LOGIN_TYPE menu "Login Management" "\nSelect which login management to use." \
"xinit" "Console login without a display manager" \
"lightdm" "Lightweight display manager with a gtk greeter"
if [[ -z $LOGIN_TYPE ]]; then
return 1
elif [[ $LOGIN_TYPE == 'lightdm' ]]; then
WM_PKGS+=" lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice"
EDIT_FILES[login]="/etc/lightdm/lightdm.conf /etc/lightdm/lightdm-gtk-greeter.conf"
AUTOLOGIN=''
else
if (( WM_NUM == 1 )); then
LOGIN_WM="${WM_SESSIONS[$INSTALL_WMS]}"
else
dlg LOGIN_WM menu "Login Management" "$_login" $LOGIN_CHOICES || return 1
LOGIN_WM="${WM_SESSIONS[$LOGIN_WM]}"
fi
local txt="\nDo you want autologin enabled for $NEWUSER?\n\nPicking yes will create the following files:\n\n - /home/$NEWUSER/$LOGINRC (run startx when logging in on tty1)\n - /etc/systemd/system/getty@tty1.service.d/autologin.conf (login $NEWUSER without password)\n\nTo disable autologin remove these files.\n"
yesno "Autologin" "$txt" && AUTOLOGIN=true || AUTOLOGIN=''
WM_PKGS+=" xorg-xinit"
EDIT_FILES[login]="/home/$NEWUSER/.xinitrc /home/$NEWUSER/.xprofile"
fi
}
select_config()
{
local i=0
CONFIG_DONE=''
until [[ $CONFIG_DONE ]]; do
case $i in
0)
dlg MYSHELL menu "Shell" "\nChoose which shell to use." \
/usr/bin/zsh 'A very advanced and programmable command interpreter (shell) for UNIX' \
/bin/bash 'The GNU Bourne Again shell, standard in many GNU/Linux distributions' \
/usr/bin/mksh 'The MirBSD Korn Shell - an enhanced version of the public domain ksh' || return 1
;;
1) dlg MYHOST input "Hostname" "$_hostname" "${DIST,,}" limit || continue ;;
2) dlg MYLOCALE menu "Locale" "$_locale" $LOCALES || continue ;;
3) ZONE='' SUBZ=''
until [[ $ZONE && $SUBZ ]]; do
dlg ZONE menu "Timezone" "$_timez" America - Australia - Asia - Atlantic - Africa - Europe - Indian - Pacific - Arctic - Antarctica - || break
dlg SUBZ menu "Timezone" "$_timesubz" $(awk '/'"$ZONE"'\// {gsub(/'"$ZONE"'\//, ""); print $3 " - "}' /usr/share/zoneinfo/zone.tab | sort) || continue
yesno "Timezone" "\nConfirm time zone: $ZONE/$SUBZ\n" || unset ZONE
done
[[ $ZONE && $SUBZ ]] || continue ;;
4)
dlg KERNEL menu "Kernel" "\nChoose which kernel to use." \
linux 'Vanilla linux kernel and modules, with a few patches applied' \
linux-lts 'Long-term support (LTS) linux kernel and modules' \
linux-zen 'A effort of kernel hackers to provide the best kernel for everyday systems' \
linux-hardened 'A security-focused linux kernel with hardening patches to mitigate exploits' || continue
;;
5) select_mirrorcmd || continue
CONFIG_DONE=true
;;
esac
(( i++ ))
done
case $MYSHELL in
'/bin/bash') LOGINRC='.bash_profile' ;;
'/usr/bin/zsh') LOGINRC='.zprofile' ;;
'/usr/bin/mksh') LOGINRC='.profile' ;;
esac
return 0
}
select_mkuser()
{
local v='' u='' p='' p2='' rp='' rp2=''
NEWUSER=''
until [[ $NEWUSER ]]; do
i=0
tput cnorm
dialog --cr-wrap --insecure --backtitle "$DIST Installer - $SYS - v$VER" \
--separator $'\n' --title " User " --mixedform "$_user" 0 0 0 \
"Username:" 1 1 "$u" 1 11 $COLUMNS 0 0 \
"Password:" 2 1 '' 2 11 $COLUMNS 0 1 \
"Password2:" 3 1 '' 3 12 $COLUMNS 0 1 \
"--- Root password, if left empty the user password will be used ---" 6 1 '' 6 68 $COLUMNS 0 2 \
"Password:" 8 1 '' 8 11 $COLUMNS 0 1 \
"Password2:" 9 1 '' 9 12 $COLUMNS 0 1 2>"$ANS" || return 1
while read -r line; do
case $i in
0) u="$line" ;;
1) p="$line" ;;
2) p2="$line" ;;
3) rp="$line" ;;
4) rp2="$line" ;;
esac
(( i++ ))
done < "$ANS"
# root passwords empty, so use the user passwords
[[ -z $rp && -z $rp2 ]] && { rp="$p"; rp2="$p2"; }
# make sure a username was entered and that the passwords match
if [[ ${#u} -eq 0 || $u =~ \ |\' || $u =~ [^a-z0-9] ]]; then
msg "Invalid Username" "\nIncorrect user name.\n\nPlease try again.\n"; u=''
elif [[ -z $p ]]; then
msg "Empty Password" "\nThe user password cannot be left empty.\n\nPlease try again.\n"
elif [[ "$p" != "$p2" ]]; then
msg "Password Mismatch" "\nThe user passwords do not match.\n\nPlease try again.\n"
elif [[ "$rp" != "$rp2" ]]; then
msg "Password Mismatch" "\nThe root passwords do not match.\n\nPlease try again.\n"
else
NEWUSER="$u"
USER_PASS="$p"
ROOT_PASS="$rp"
fi
done
return 0
}
select_keymap()
{
dlg KEYMAP menu "Keyboard Layout" "$_keymap" \
us English cm English gb English au English gh English \
za English ng English ca French 'cd' French gn French \
tg French fr French de German at German ch German \
es Spanish latam Spanish br Portuguese pt Portuguese ma Arabic \
sy Arabic ara Arabic ua Ukrainian cz Czech ru Russian \
sk Slovak nl Dutch it Italian hu Hungarian cn Chinese \
tw Taiwanese vn Vietnamese kr Korean jp Japanese th Thai \
la Lao pl Polish se Swedish is Icelandic fi Finnish \
dk Danish be Belgian in Indian al Albanian am Armenian \
bd Bangla ba Bosnian 'bg' Bulgarian dz Berber mm Burmese \
hr Croatian gr Greek il Hebrew ir Persian iq Iraqi \
af Afghani fo Faroese ge Georgian ee Estonian kg Kyrgyz \
kz Kazakh lt Lithuanian mt Maltese mn Mongolian ro Romanian \
no Norwegian rs Serbian si Slovenian tj Tajik lk Sinhala \
tr Turkish uz Uzbek ie Irish pk Urdu 'mv' Dhivehi \
np Nepali et Amharic sn Wolof ml Bambara tz Swahili \
ke Swahili bw Tswana ph Filipino my Malay tm Turkmen \
id Indonesian bt Dzongkha lv Latvian md Moldavian mao Maori \
by Belarusian az Azerbaijani mk Macedonian kh Khmer epo Esperanto \
me Montenegrin
[[ $KEYMAP ]] || return 1
if [[ $CMAPS == *"$KEYMAP"* ]]; then
CMAP="$KEYMAP"
else
dlg CMAP menu "Console Keymap" "$_vconsole" $CMAPS
[[ $CMAP ]] || return 1
fi
if [[ $DISPLAY && $TERM != 'linux' ]]; then
setxkbmap $KEYMAP >/dev/null 2>&1
else
loadkeys $CMAP >/dev/null 2>&1
fi
return 0
}
select_sessions()
{
LOGIN_CHOICES=''
dlg INSTALL_WMS check "Sessions" "$_sessions\n" \
i3-gaps "A fork of i3wm with more features including gaps" $(ofn i3-gaps "${INSTALL_WMS[*]}") \
openbox "A lightweight, powerful, and highly configurable stacking wm" $(ofn openbox "${INSTALL_WMS[*]}") \
bspwm "A tiling wm that represents windows as the leaves of a binary tree" $(ofn bspwm "${INSTALL_WMS[*]}") \
dwm "A fork of dwm, with more layouts and features" $(ofn dwm "${INSTALL_WMS[*]}") \
fluxbox "A lightweight and highly-configurable window manager" $(ofn fluxbox "${INSTALL_WMS[*]}") \
gnome "A desktop environment that aims to be simple and easy to use" $(ofn gnome "${INSTALL_WMS[*]}") \
cinnamon "A desktop environment combining traditional desktop with modern effects" $(ofn cinnamon "${INSTALL_WMS[*]}") \
plasma "A kde software project currently comprising a full desktop environment" $(ofn plasma "${INSTALL_WMS[*]}") \
xfce4 "A lightweight and modular desktop environment based on gtk+2/3" $(ofn xfce4 "${INSTALL_WMS[*]}")
[[ $INSTALL_WMS ]] || return 1
WM_NUM=0
while IFS=' ' read -r field; do
(( WM_NUM++ ))
done <<< "$INSTALL_WMS"
WM_PKGS="${INSTALL_WMS/dwm/}" # remove dwm from package list
WM_PKGS="${WM_PKGS// / }" # remove double spaces
WM_PKGS="${WM_PKGS# }" # remove leading space
for i in $INSTALL_WMS; do
LOGIN_CHOICES+="$i - "
[[ ${WM_EXT[$i]} && $WM_PKGS != *"${WM_EXT[$i]}"* ]] && WM_PKGS+=" ${WM_EXT[$i]}"
done
select_login || return 1
# add unique wm packages to main package list
for i in $WM_PKGS; do
[[ $PACKAGES == *$i* ]] || PACKAGES+=" ${i# }"
done
return 0
}
select_packages()
{
dlg USER_PKGS check " Packages " "$_packages" \
abiword "A Fully-featured word processor" $(ofn abiword "${USER_PKGS[*]}") \
alacritty "A cross-platform, GPU-accelerated terminal emulator" $(ofn alacritty "${USER_PKGS[*]}") \
atom "An open-source text editor developed by GitHub" $(ofn atom "${USER_PKGS[*]}") \
audacious "A free and advanced audio player based on GTK+" $(ofn audacious "${USER_PKGS[*]}") \
audacity "A program that lets you manipulate digital audio waveforms" $(ofn audacity "${USER_PKGS[*]}") \
cairo-dock "Light eye-candy fully themable animated dock" $(ofn cairo-dock "${USER_PKGS[*]}") \
calligra "A set of applications for productivity" $(ofn calligra "${USER_PKGS[*]}") \
chromium "An open-source web browser based on the Blink rendering engine" $(ofn chromium "${USER_PKGS[*]}") \
clementine "A modern music player and library organizer" $(ofn clementine "${USER_PKGS[*]}") \
cmus "A small, fast and powerful console music player" $(ofn cmus "${USER_PKGS[*]}") \
deadbeef "A GTK+ audio player for GNU/Linux" $(ofn deadbeef "${USER_PKGS[*]}") \
deluge "A BitTorrent client written in python" $(ofn deluge "${USER_PKGS[*]}") \
docky "Full fledged dock for opening applications and managing windows" $(ofn docky "${USER_PKGS[*]}") \
emacs "An extensible, customizable, self-documenting real-time display editor" $(ofn emacs "${USER_PKGS[*]}") \
epiphany "A GNOME web browser based on the WebKit rendering engine" $(ofn epiphany "${USER_PKGS[*]}") \
evince "A document viewer" $(ofn evince "${USER_PKGS[*]}") \
evolution "Manage your email, contacts and schedule" $(ofn evolution "${USER_PKGS[*]}") \
file-roller "Create and modify archives" $(ofn file-roller "${USER_PKGS[*]}") \
firefox "A popular open-source web browser from Mozilla" $(ofn firefox "${USER_PKGS[*]}") \
gcolor2 "A simple GTK+2 color selector" $(ofn gcolor2 "${USER_PKGS[*]}") \
geany "A fast and lightweight IDE" $(ofn geany "${USER_PKGS[*]}") \
geary "A lightweight email client for the GNOME desktop" $(ofn geary "${USER_PKGS[*]}") \
gimp "GNU Image Manipulation Program" $(ofn gimp "${USER_PKGS[*]}") \
gnome-disk-utility "Disk Management Utility" $(ofn gnome-disk-utility "${USER_PKGS[*]}") \
gnome-system-monitor "View current processes and monitor system state" $(ofn gnome-system-monitor "${USER_PKGS[*]}") \
gparted "A GUI frontend for creating and manipulating partition tables" $(ofn gparted "${USER_PKGS[*]}") \
gpick "Advanced color picker using GTK+ toolkit" $(ofn gpick "${USER_PKGS[*]}") \
gpicview "Lightweight image viewer" $(ofn gpicview "${USER_PKGS[*]}") \
guvcview "Capture video from camera devices" $(ofn guvcview "${USER_PKGS[*]}") \
hexchat "A popular and easy to use graphical IRC client" $(ofn hexchat "${USER_PKGS[*]}") \
inkscape "Professional vector graphics editor" $(ofn inkscape "${USER_PKGS[*]}") \
irssi "Modular text mode IRC client" $(ofn irssi "${USER_PKGS[*]}") \
kdenlive "A popular non-linear video editor for Linux" $(ofn kdenlive "${USER_PKGS[*]}") \
krita "Edit and paint images" $(ofn krita "${USER_PKGS[*]}") \
libreoffice-fresh "Full featured office suite" $(ofn libreoffice-fresh "${USER_PKGS[*]}") \
lollypop "A new music playing application" $(ofn lollypop "${USER_PKGS[*]}") \
mousepad "A simple text editor" $(ofn mousepad "${USER_PKGS[*]}") \
mpd "A flexible, powerful, server-side application for playing music" $(ofn mpd "${USER_PKGS[*]}") \
mpv "A media player based on mplayer" $(ofn mpv "${USER_PKGS[*]}") \
mupdf "Lightweight PDF and XPS viewer" $(ofn mupdf "${USER_PKGS[*]}") \
mutt "Small but very powerful text-based mail client" $(ofn mutt "${USER_PKGS[*]}") \
nautilus "The default file manager for Gnome" $(ofn nautilus "${USER_PKGS[*]}") \
ncmpcpp "A mpd client and almost exact clone of ncmpc with some new features" $(ofn ncmpcpp "${USER_PKGS[*]}") \
neovim "A fork of Vim aiming to improve user experience, plugins, and GUIs." $(ofn neovim "${USER_PKGS[*]}") \
nicotine+ "A graphical client for Soulseek" $(ofn nicotine+ "${USER_PKGS[*]}") \
noto-fonts "Google Noto fonts" $(ofn noto-fonts "${USER_PKGS[*]}") \
noto-fonts-cjk "Google Noto CJK fonts (Chinese, Japanese, Korean)" $(ofn noto-fonts-cjk "${USER_PKGS[*]}") \
obs-studio "Free opensource streaming/recording software" $(ofn obs-studio "${USER_PKGS[*]}") \
openshot "An open-source, non-linear video editor for Linux" $(ofn openshot "${USER_PKGS[*]}") \
opera "A Fast and secure, free of charge web browser from Opera Software" $(ofn opera "${USER_PKGS[*]}") \
pcmanfm "A fast and lightweight file manager based in Lxde" $(ofn pcmanfm "${USER_PKGS[*]}") \
pidgin "Multi-protocol instant messaging client" $(ofn pidgin "${USER_PKGS[*]}") \
plank "An elegant, simple, and clean dock" $(ofn plank "${USER_PKGS[*]}") \
qbittorrent "An advanced BitTorrent client" $(ofn qbittorrent "${USER_PKGS[*]}") \
qpdfview "A tabbed PDF viewer" $(ofn qpdfview "${USER_PKGS[*]}") \
qt5ct "GUI for managing Qt based application themes, icons, and fonts" $(ofn qt5ct "${USER_PKGS[*]}") \
qutebrowser "A keyboard-focused vim-like web browser based on Python and PyQt5" $(ofn qutebrowser "${USER_PKGS[*]}") \
rhythmbox "A Music playback and management application" $(ofn rhythmbox "${USER_PKGS[*]}") \
rxvt-unicode "A unicode enabled rxvt-clone terminal emulator" $(ofn rxvt-unicode "${USER_PKGS[*]}") \
sakura "A terminal emulator based on GTK and VTE" $(ofn sakura "${USER_PKGS[*]}") \
simplescreenrecorder "A feature-rich screen recorder" $(ofn simplescreenrecorder "${USER_PKGS[*]}") \
steam "A popular game distribution platform by Valve" $(ofn steam "${USER_PKGS[*]}") \
surf "A simple web browser based on WebKit2/GTK+" $(ofn surf "${USER_PKGS[*]}") \
terminator "Terminal emulator that supports tabs and grids" $(ofn terminator "${USER_PKGS[*]}") \
termite "A minimal VTE-based terminal emulator" $(ofn termite "${USER_PKGS[*]}") \
thunar "A modern file manager for the Xfce Desktop Environment" $(ofn thunar "${USER_PKGS[*]}") \
thunderbird "Standalone mail and news reader from mozilla" $(ofn thunderbird "${USER_PKGS[*]}") \
tilda "A GTK based drop down terminal for Linux and Unix" $(ofn tilda "${USER_PKGS[*]}") \
tilix "A tiling terminal emulator for Linux using GTK+ 3" $(ofn tilix "${USER_PKGS[*]}") \
transmission-cli "Free BitTorrent client CLI" $(ofn transmission-cli "${USER_PKGS[*]}") \
transmission-gtk "Free BitTorrent client GTK+ GUI" $(ofn transmission-gtk "${USER_PKGS[*]}") \
transmission-qt "Free BitTorrent client Qt GUI" $(ofn transmission-qt "${USER_PKGS[*]}") \
ttf-anonymous-pro "A family fixed-width fonts designed with code in mind" $(ofn ttf-anonymous-pro "${USER_PKGS[*]}") \
ttf-fira-code "Monospaced font with programming ligatures" $(ofn ttf-fira-code "${USER_PKGS[*]}") \
ttf-font-awesome "Iconic font designed for Bootstrap" $(ofn ttf-font-awesome "${USER_PKGS[*]}") \
ttf-hack "A hand groomed typeface based on Bitstream Vera Mono" $(ofn ttf-hack "${USER_PKGS[*]}") \
vlc "A free and open source cross-platform multimedia player" $(ofn vlc "${USER_PKGS[*]}") \
weechat "Fast, light and extensible IRC client" $(ofn weechat "${USER_PKGS[*]}") \
xarchiver "A GTK+ frontend to various command line archivers" $(ofn xarchiver "${USER_PKGS[*]}") \
xfce-terminal "A terminal emulator based in the Xfce Desktop Environment" $(ofn xfce-terminal "${USER_PKGS[*]}") \
xterm "The standard terminal emulator for the X window system" $(ofn xterm "${USER_PKGS[*]}") \
zathura "Minimalistic document viewer" $(ofn zathura "${USER_PKGS[*]}")
if [[ $USER_PKGS ]]; then
for i in $USER_PKGS; do
[[ ${PKG_EXT[$i]} && $USER_PKGS != *"${PKG_EXT[$i]}"* ]] && USER_PKGS="${USER_PKGS% } ${PKG_EXT[$i]}"
done
USER_PKGS="${USER_PKGS// / }"
USER_PKGS="${USER_PKGS# }"
USER_PKGS="${USER_PKGS% }"
fi
return 0
}
select_mirrorcmd()
{
local c='' key="5f29642060ab983b31fdf4c2935d8c56"
if hash reflector >/dev/null 2>&1; then
MIRROR_CMD="reflector --score 100 -l 50 -f 5 --sort rate --verbose"
yesno "Mirrorlist" "\nSort the mirrorlist automatically?\n\nTakes longer but can find faster mirrors.\n" && return 0
c="$(json 'country_name' "$(json 'ip' "check&?access_key=${key}&fields=ip")?access_key=${key}&fields=country_name")"
MIRROR_CMD="reflector --country $c --fastest 5 --sort rate --verbose"
tput cnorm
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--title " Mirrorlist " --inputbox "\nThe command below will be used to sort the mirrorlist, edit if needed.\n\n
--score n Limit the list to the n servers with the highest score.
--latest n Limit the list to the n most recently synchronized servers.
--fastest n Return the n fastest mirrors that meet the other criteria.
--sort {age,rate,country,score,delay}
'age': Last server synchronization;
'rate': Download rate;
'country': Server location;
'score': MirrorStatus score;
'delay': MirrorStatus delay.\n" 0 0 "$MIRROR_CMD" 2>"$ANS"
[ $? -eq 0 ] || return 1
read -r MIRROR_CMD < "$ANS"
elif hash rankmirrors >/dev/null 2>&1; then
msg "Mirrorlist" "\nQuerying mirrors near your location\n"
c="$(json 'country_code' "$(json 'ip' "check&?access_key=${key}&fields=ip")?access_key=${key}&fields=country_code")"
local w="https://www.archlinux.org/mirrorlist/?country="
if [[ $c ]]; then
[[ $c =~ (CA|US) ]] && MIRROR_CMD="curl -s '${w}US&country=CA&use_mirror_status=on'" || MIRROR_CMD="curl -s '${w}${c}&use_mirror_status=on'"
else
MIRROR_CMD="curl -s '${w}US&country=CA&country=NZ&country=GB&country=AU&use_mirror_status=on'"
fi
fi
return 0
}
###############################################################################
# partition menus
part_menu()
{
check_background_install || return 0
local device choice devhash="$(lsblk -f | base64)"
if [[ $# -eq 1 ]]; then
device="$1"
else
umount_dir $MNT
part_device || return 1
device="$DEVICE"
fi
while true; do
if [[ $DISPLAY && $TERM != 'linux' ]] && hash gparted >/dev/null 2>&1; then
dlg choice menu "Edit Partitions" "$_part" \
"auto" "Whole device automatic partitioning" \
"gparted" "GUI front end to parted" \
"cfdisk" "Curses based variant of fdisk" \
"parted" "GNU partition editor" \
"fdisk" "Dialog-driven program for creation and manipulation of partition tables." \
"done" "Return to the main menu"
else
dlg choice menu "Edit Partitions" "$_part" \
"auto" "Whole device automatic partitioning" \
"cfdisk" "Curses based variant of fdisk" \
"parted" "GNU partition editor" \
"fdisk" "Dialog-driven program for creation and manipulation of partition tables." \
"done" "Return to the main menu"
fi
if [[-z $choice || $choice == 'done' ]]; then
return 0
elif [[ $choice == 'auto' ]]; then
local root_size txt ret table boot_fs
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"
}
}')
txt="\nWARNING:\n\nALL data on $device will be destroyed and the following partitions will be created\n\n- "
if [[ $SYS == 'BIOS' ]]; then
table="msdos" boot_fs="ext4"
txt+="An $boot_fs boot partition with the boot flag enabled (512M)\n- "
else
table="gpt" boot_fs="fat32"
txt+="A $boot_fs efi boot partition (512M)\n- "
fi
txt+="An ext4 partition using all remaining space ($root_size)\n\nDo you want to continue?\n"
yesno "Auto Partition" "$txt" && part_auto "$device" "$table" "$boot_fs" "$root_size"
else
clear
tput cnorm
$choice "$device"
fi
# update the kernel if the partition table changes
[[ $devhash == "$(lsblk -f | base64)" ]] || partprobe >/dev/null 2>&1
done
}
part_show()
{
tput civis
local txt
if [[ $IGNORE_DEV ]]; then
txt="$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT | awk "!/$IGNORE_DEV/"' && /disk|part|lvm|crypt|NAME/')"
else
txt="$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE,MOUNTPOINT | awk '/disk|part|lvm|crypt|NAME/')"
fi
msg "Device Tree" "\n\n$txt\n\n"
}
part_swap()
{
if [[ $1 == "$MNT/swapfile" && $SWAP_SIZE ]]; then
fallocate -l $SWAP_SIZE $1 2>$ERR
errshow "fallocate -l $SWAP_SIZE $1"
chmod 600 $1 2>$ERR
errshow "chmod 600 $1"
fi
mkswap $1 >/dev/null 2>$ERR
errshow "mkswap $1"
swapon $1 >/dev/null 2>$ERR
errshow "swapon $1"
return 0
}
part_auto()
{
local device="$1" table="$2" boot_fs="$3" size="$4"
local dev_info="$(parted -s $device print)"
msg "Auto Partition" "\nRemoving partitions on $device and setting table to $table\n" 1
swapoff -a
while read -r PART; do
parted -s $device rm $PART >/dev/null 2>&1
done <<< "$(awk '/^ [1-9][0-9]?/ {print $1}' <<< "$dev_info" | sort -r)"
[[ $(awk '/Table:/ {print $3}' <<< "$dev_info") != "$table" ]] && parted -s $device mklabel $table >/dev/null 2>&1
msg "Auto Partition" "\nCreating a 512M $boot_fs boot partition.\n" 1
if [[ $SYS == "BIOS" ]]; then
parted -s $device mkpart primary $boot_fs 1MiB 513MiB >/dev/null 2>&1
else
parted -s $device mkpart ESP $boot_fs 1MiB 513MiB >/dev/null 2>&1
fi
sleep 0.5
BOOT_DEV="$device"
AUTO_BOOT_PART=$(lsblk -lno NAME,TYPE $device | awk 'NR==2 {print "/dev/" $1}')
if [[ $SYS == "BIOS" ]]; then
mkfs.ext4 -q $AUTO_BOOT_PART >/dev/null 2>&1
else
mkfs.vfat -F32 $AUTO_BOOT_PART >/dev/null 2>&1
fi
msg "Auto Partition" "\nCreating a $size ext4 root partition.\n" 0
parted -s $device mkpart primary ext4 513MiB 100% >/dev/null 2>&1
sleep 0.5
AUTO_ROOT_PART="$(lsblk -lno NAME,TYPE $device | awk 'NR==3 {print "/dev/" $1}')"
mkfs.ext4 -q $AUTO_ROOT_PART >/dev/null 2>&1
tput civis
sleep 0.5
FORMATTED+="$AUTO_BOOT_PART $AUTO_ROOT_PART "
msg "Auto Partition" "\nProcess complete.\n\n$(lsblk -o NAME,MODEL,SIZE,TYPE,FSTYPE $device)\n"
}
part_find()
{
local str="$1" err=''
# string of partitions as /TYPE/PART SIZE
if [[ $IGNORE_DEV ]]; then
PARTS="$(lsblk -lno TYPE,NAME,SIZE |
awk "/$str/"' && !'"/$IGNORE_DEV/"' {
sub(/^part/, "/dev/")
sub(/^lvm|^crypt/, "/dev/mapper/")
print $1$2, $3
}')"
else
PARTS="$(lsblk -lno TYPE,NAME,SIZE |
awk "/$str/"' {
sub(/^part/, "/dev/")
sub(/^lvm|^crypt/, "/dev/mapper/")
print $1$2 " " $3
}')"
fi
# number of partitions total
if [[ $PARTS ]]; then
COUNT=$(wc -l <<< "$PARTS")
else
COUNT=0
fi
# ensure we have enough partitions for the system and action type
case "$str" in
'part|lvm|crypt') [[ $COUNT -lt 1 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_errpart" ;;
'part|crypt') (( COUNT < 1 )) && err="$_lvmerr" ;;
'part|lvm') (( COUNT < 2 )) && err="$_lukserr" ;;
esac
# if there aren't enough partitions show the relevant error message
[[ $err ]] && { msg "Not Enough Partitions" "$err" 2; return 1; }
return 0
}
part_mount()
{
local part="$1"
local mountp="${MNT}$2"
local fs="$(lsblk -lno FSTYPE $part)"
mkdir -p "$mountp"
if [[ $fs && ${FS_OPTS[$fs]} && $part != "$BOOT_PART" ]] && select_mntopts "$fs"; then
mount -o $MNT_OPTS "$part" "$mountp" >/dev/null 2>&1
else
mount "$part" "$mountp" >/dev/null 2>&1
fi
part_mountconf $part "$mountp" || return 1
part_cryptlv "$part"
return 0
}
part_format()
{
local part="$1" fs="$2" delay="$3" fscmd="${FS_CMDS[$2]}"
msg "Format" "\nFormatting $part as $fs\n" 0
$fscmd "$part" >/dev/null 2>$ERR
errshow "$fscmd $part" || return 1
FORMATTED+="$part "
sleep ${delay:-0}
}
part_device()
{
if [[ $DEV_COUNT -eq 1 && $SYS_DEVS ]]; then
DEVICE="$(awk '{print $1}' <<< "$SYS_DEVS")"
elif (( DEV_COUNT > 1 )); then
tput civis
if [[ $1 ]]; then
dlg DEVICE menu "Boot Device" "\nSelect the device to use for bootloader install." $SYS_DEVS
else
dlg DEVICE menu "Select Device" "$_device" $SYS_DEVS
fi
[[ $DEVICE ]] || return 1
elif [[ $DEV_COUNT -lt 1 && ! $1 ]]; then
msg "Installation Error" "\nNo available devices.\n\nExiting..\n" 2; die 1
fi
[[ $1 ]] && BOOT_DEV="$DEVICE"
return 0
}
part_bootdev()
{
msg "Boot Device" "\nSetting device flags for: $BOOT_PART\n" 1
[[ $BOOT_PART = /dev/nvme* ]] && BOOT_DEV="${BOOT_PART%p[1-9]}" || BOOT_DEV="${BOOT_PART%[1-9]}"
BOOT_PART_NUM="${BOOT_PART: -1}"
if [[ $SYS == 'UEFI' ]]; then
parted -s $BOOT_DEV set $BOOT_PART_NUM esp on >/dev/null 2>&1
else
parted -s $BOOT_DEV set $BOOT_PART_NUM boot on >/dev/null 2>&1
fi
return 0
}
part_cryptlv()
{
local part="$1"
local devs="$(lsblk -lno NAME,FSTYPE,TYPE)"
# Identify if $part is LUKS+LVM, LVM+LUKS, LVM alone, or LUKS alone
if lsblk -lno TYPE "$part" | grep -q 'crypt'; then
LUKS='encrypted'
LUKS_NAME="${part#/dev/mapper/}"
for dev in $(awk '/lvm/ && /crypto_LUKS/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do
if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
LUKS_DEV="$LUKS_DEV cryptdevice=$dev:$LUKS_NAME"
LVM='logical volume'
break
fi
done
for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do
if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')"
LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
break
fi
done
elif lsblk -lno TYPE "$part" | grep -q 'lvm'; then
LVM='logical volume'
VNAME="${part#/dev/mapper/}"
for dev in $(awk '/crypt/ && /lvm2_member/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do
if lsblk -lno NAME "$dev" | grep -q "$VNAME"; then
LUKS_NAME="${dev/\/dev\/mapper\//}"
break
fi
done
for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do
if lsblk -lno NAME "$dev" | grep -q "$LUKS_NAME"; then
LUKS_UUID="$(lsblk -lno UUID,TYPE,FSTYPE "$dev" | awk '/part/ && /crypto_LUKS/ {print $1}')"
LUKS_DEV="$LUKS_DEV cryptdevice=UUID=$LUKS_UUID:$LUKS_NAME"
LUKS='encrypted'
break
fi
done
fi
}
part_countdec()
{
for i in "$@"; do
if (( COUNT > 0 )); then
PARTS="$(sed "/${i//\//\\/}/d" <<< "$PARTS")"
(( COUNT-- ))
fi
done
}
part_mountconf()
{
if grep -qw "$1" /proc/mounts; then
msg "Mount Success" "\nPartition $1 mounted at $2\n" 1
part_countdec "$1"
return 0
else
msg "Mount Fail" "\nPartition $1 failed to mount at $2\n" 2
return 1
fi
}
###############################################################################
# mounting menus
select_menu()
{
check_background_install || return 0
lvm_detect
umount_dir $MNT
part_find 'part|lvm|crypt' || { SEL=2; return 1; }
[[ $LUKS && $LUKS_PART ]] && part_countdec $LUKS_PART
[[ $LVM && $LVM_PARTS ]] && part_countdec $LVM_PARTS
select_root_partition || return 1
if [[ $SYS == 'UEFI' ]]; then
select_efi_partition || { BOOT_PART=''; return 1; }
elif (( COUNT > 0 )); then
select_boot_partition || { BOOT_PART=''; return 1; }
fi
if [[ $BOOT_PART ]]; then
part_mount "$BOOT_PART" "/$BOOTDIR" && SEP_BOOT=true || return 1
part_bootdev
fi
select_swap || return 1
select_extra_partitions || return 1
install_background || return 1
return 0
}
select_swap()
{
tput civis
dlg SWAP_PART menu "Swap Setup" "\nSelect whether to use a swapfile, swap partition, or none." \
"none" "Don't allocate any swap space" \
"swapfile" "Allocate $SYS_MEM at /swapfile" \
$PARTS
if [[ -z $SWAP_PART || $SWAP_PART == "none" ]]; then
SWAP_PART=''
return 0
elif [[ $SWAP_PART == "swapfile" ]]; then
tput cnorm
local i=0
until [[ ${SWAP_SIZE:0:1} =~ [1-9] && ${SWAP_SIZE: -1} =~ (M|G) ]]; do
(( i > 0 )) && msg "Swap Size Error" "\nSwap size must be 1(M|G) or greater, and can only contain whole numbers\n\nSize entered: $SWAP_SIZE\n" 2
dlg SWAP_SIZE input "Swap Setup" "$_swapsize" "$SYS_MEM"
[ $? -eq 0 ] || { SWAP_PART=''; SWAP_SIZE=''; return 1; }
(( i++ ))
done
part_swap "$MNT/$SWAP_PART"
SWAP_PART="/$SWAP_PART"
elif [[ $PARTS == *"$SWAP_PART"* ]]; then
part_swap $SWAP_PART
part_countdec $SWAP_PART
SWAP_SIZE="$(lsblk -lno SIZE $SWAP_PART)"
else
return 1
fi
return 0
}
select_mntopts()
{
local fs="$1" opts=''
local title="${fs^} Mount Options"
for i in ${FS_OPTS[$fs]}; do
opts+="$i - off "
done
until [[ $MNT_OPTS ]]; do
dlg MNT_OPTS check "$title" "$_mount" $opts
[[ $MNT_OPTS ]] || return 1
MNT_OPTS="${MNT_OPTS// /,}"
yesno "$title" "\nConfirm the following options: $MNT_OPTS\n" || MNT_OPTS=''
done
return 0
}
select_mountpoint()
{
EXMNT=''
tput cnorm
until [[ $EXMNT ]]; do
dlg EXMNT input "Extra Mount $part" "$_exmnt" "/"
[ $? -eq 0 ] || return 1
if [[ ${EXMNT:0:1} != "/" || ${#EXMNT} -le 1 || $EXMNT =~ \ |\' || $EXMNTS == *"$EXMNT"* ]]; then
msg "Installation Error" "$_errexpart"
EXMNT=''
fi
done
return 0
}
select_filesystem()
{
local part="$1" fs=''
local cur="$(lsblk -lno FSTYPE "$part" 2>/dev/null)"
local txt="\nSelect which filesystem to use for: $part\n\nDefault: ext4"
until [[ $fs ]]; do
if [[ $cur && ($part != "$ROOT_PART" || $FORMATTED == *"$part"*) ]]; then
dlg fs menu "Filesystem" "$txt\nCurrent: $cur" skip - ext4 - ext3 - ext2 - vfat - ntfs - f2fs - jfs - xfs - nilfs2 - reiserfs -
else
dlg fs menu "Filesystem" "$txt" ext4 - ext3 - ext2 - vfat - ntfs - f2fs - jfs - xfs - nilfs2 - reiserfs -
fi
[ $? -eq 0 ] || return 1
[[ $fs == 'skip' ]] && return 0
yesno "Filesystem" "\nFormat $part as $fs?\n" || fs=''
done
part_format "$part" "$fs"
}
select_efi_partition()
{
tput civis
if (( COUNT == 1 )); then
BOOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
elif [[ $AUTO_BOOT_PART ]]; then
BOOT_PART="$AUTO_BOOT_PART"
return 0 # were done here
else
dlg BOOT_PART menu "EFI Partition" "$_uefi" $PARTS
fi
[[ $BOOT_PART ]] || return 1
if grep -q 'fat' <<< "$(fsck -N "$BOOT_PART")"; then
local txt="\nIMPORTANT:\n\nThe EFI partition $BOOT_PART $_format"
if yesno "Format EFI Partition" "$txt" "Format $BOOT_PART" "Skip Formatting" 1; then
part_format "$BOOT_PART" "vfat" 2
fi
else
part_format "$BOOT_PART" "vfat" 2
fi
return 0
}
select_boot_partition()
{
if [[ $AUTO_BOOT_PART && ! $LVM ]]; then
BOOT_PART="$AUTO_BOOT_PART"; return 0 # were done here
elif [[ $LUKS && ! $LVM ]]; then
dlg BOOT_PART menu "Boot Partition" "$_biosluks" $PARTS
[[ $BOOT_PART ]] || return 1
else
dlg BOOT_PART menu "Boot Partition" "$_bios" "skip" "don't use a separate boot" $PARTS
[[ -z $BOOT_PART || $BOOT_PART == "skip" ]] && { BOOT_PART=''; return 0; }
fi
if grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")"; then
local txt="\nIMPORTANT:\n\nThe boot partition $BOOT_PART $_format"
if yesno "Format Boot Partition" "$txt" "Format $BOOT_PART" "Skip Formatting" 1; then
part_format "$BOOT_PART" "ext4" 2
fi
else
part_format "$BOOT_PART" "ext4" 2
fi
return 0
}
select_root_partition()
{
if [[ $AUTO_ROOT_PART && ! $LVM && ! $LUKS ]]; then
ROOT_PART="$AUTO_ROOT_PART"
elif (( COUNT == 1 )); then
ROOT_PART="$(awk 'NR==1 {print $1}' <<< "$PARTS")"
else
dlg ROOT_PART menu "Mount Root" "\nSelect the root (/) partition, this is where $DIST will be installed." $PARTS
fi
[[ $ROOT_PART ]] || return 1
select_filesystem "$ROOT_PART" || { ROOT_PART=''; return 1; }
part_mount "$ROOT_PART" || { ROOT_PART=''; return 1; }
return 0
}
select_extra_partitions()
{
local part
while (( COUNT > 0 )); do
part=''
tput civis
dlg part menu 'Mount Boot' "$_expart" 'done' 'finish mounting step' $PARTS
if [[ $part == 'done' || -z $part ]]; then
break
elif select_filesystem "$part" && select_mountpoint && part_mount "$part" "$EXMNT"; then
EXMNTS+="$part: $EXMNT "
[[ $EXMNT == '/usr' && $HOOKS != *usr* ]] && HOOKS="usr $HOOKS"
else
return 1
fi
done
return 0
}
###############################################################################
# installation
install_main()
{
clear
tput cnorm
install_base
genfstab -U $MNT >$MNT/etc/fstab 2>$ERR
errshow 1 "genfstab -U $MNT >$MNT/etc/fstab"
[[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" $MNT/etc/fstab
install_mirrorlist
install_packages
install_mkinitcpio
install_boot
chrun "hwclock --systohc --utc" || chrun "hwclock --systohc --utc --directisa"
install_user
install_login
chrun "chown -Rf $NEWUSER:users /home/$NEWUSER"
sleep 1
while true; do
dlg choice menu "Finalization" "$_edit" \
finished "exit the installer and reboot" \
keyboard "${EDIT_FILES[keyboard]}" \
console "${EDIT_FILES[console]}" \
locale "${EDIT_FILES[locale]}" \
hostname "${EDIT_FILES[hostname]}" \
sudoers "${EDIT_FILES[sudoers]}" \
mkinitcpio "${EDIT_FILES[mkinitcpio]}" \
fstab "${EDIT_FILES[fstab]}" \
crypttab "${EDIT_FILES[crypttab]}" \
bootloader "${EDIT_FILES[bootloader]}" \
pacman "${EDIT_FILES[pacman]}" \
login "${EDIT_FILES[login]}"
if [[ -z $choice || $choice == "finished" ]]; then
[[ $DEBUG == true && -r $DBG ]] && vim $DBG
die 127
else
local exists=''
for f in ${EDIT_FILES[$choice]}; do
[[ -e ${MNT}$f ]] && exists+=" ${MNT}$f"
done
if [[ $exists ]]; then
vim -O $exists
else
msg "File Error" "\nFile(s) do not exist.\n"
fi
fi
done
}
install_base()
{
if [[ $RSYNC_PID ]]; then
while kill -0 $RSYNC_PID 2>/dev/null; do
clear
printf "\nSystem base is still unpacking...\n"
sleep 1
done
trap - EXIT
unset RSYNC_PID
elif [[ -d /run/archiso/sfs/airootfs/etc/skel ]]; then
rsync -ahv /run/archiso/sfs/airootfs/ $MNT/ 2>$ERR
errshow 1 "rsync -ahv /run/archiso/sfs/airootfs/ $MNT/"
else
install_mirrorlist
pacstrap $MNT base $KERNEL $UCODE $ISO_BASE 2>$ERR
errshow 1 "pacstrap $MNT base $KERNEL $UCODE $ISO_BASE"
fi
rm -rf $MNT/etc/mkinitcpio-archiso.conf
find $MNT/usr/lib/initcpio -name 'archiso*' -type f -delete
sed -i 's/volatile/auto/g' $MNT/etc/systemd/journald.conf
if [[ $VM ]]; then
rm -rfv $MNT/etc/X11/xorg.conf.d/*?.conf
sleep 1
elif [[ "$(lspci | grep ' VGA ' | grep 'Intel')" ]]; then
cat > $MNT/etc/X11/xorg.conf.d/20-intel.conf << EOF
Section "Device"
Identifier "Intel Graphics"
Driver "intel"
Option "TearFree" "true"
EndSection
EOF
fi
if [[ -e /run/archiso/sfs/airootfs ]]; then
[[ $KERNEL == 'linux' ]] && cp -vf $RUN/x86_64/vmlinuz $MNT/boot/vmlinuz-linux
[[ $UCODE ]] && cp -vf $RUN/${UCODE/-/_}.img $MNT/boot/$UCODE.img
fi
cp -fv /etc/resolv.conf $MNT/etc/
if [[ -e /etc/NetworkManager/system-connections ]]; then
cp -rvf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/
fi
echo "LANG=$MYLOCALE" > $MNT/etc/locale.conf
echo "LANG=$MYLOCALE" > $MNT/etc/default/locale
sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${MYLOCALE}/${MYLOCALE}/g" $MNT/etc/locale.gen
chrun "locale-gen"
chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZ /etc/localtime"
if [[ $BROADCOM_WL ]]; then
echo 'blacklist bcma' >> $MNT/etc/modprobe.d/blacklist.conf
rm -f $MNT/etc/modprobe/
fi
cat > $MNT/etc/X11/xorg.conf.d/00-keyboard.conf << EOF
# Use localectl(1) to instruct systemd-localed to update it.
Section "InputClass"
Identifier "system-keyboard"
MatchIsKeyboard "on"
Option "XkbLayout" "$KEYMAP"
EndSection
EOF
cat > $MNT/etc/default/keyboard << EOF
# KEYBOARD CONFIGURATION FILE
# Consult the keyboard(5) manual page.
XKBMODEL=""
XKBLAYOUT="$KEYMAP"
XKBVARIANT=""
XKBOPTIONS=""
BACKSPACE="guess"
EOF
printf "KEYMAP=%s\nFONT=%s\n" "$CMAP" "$FONT" > $MNT/etc/vconsole.conf
echo "$MYHOST" > $MNT/etc/hostname
cat > $MNT/etc/hosts << EOF
127.0.0.1 localhost
127.0.1.1 $MYHOST
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
}
install_boot()
{
if [[ $ROOT_PART == /dev/mapper* ]]; then
ROOT_PART_ID="$ROOT_PART"
else
local uuid_type="UUID"
[[ $BOOTLDR =~ (systemd-boot|refind-efi|efistub) ]] && uuid_type="PARTUUID"
ROOT_PART_ID="$uuid_type=$(blkid -s $uuid_type -o value $ROOT_PART)"
fi
if [[ $SYS == 'UEFI' ]]; then
find $MNT/$BOOTDIR/EFI/ -maxdepth 1 -mindepth 1 -iname "$DIST" -type d -delete >/dev/null 2>&1
find $MNT/$BOOTDIR/EFI/ -maxdepth 1 -mindepth 1 -iname 'boot' -type d -delete >/dev/null 2>&1
fi
prerun_$BOOTLDR
chrun "${BCMDS[$BOOTLDR]}" 2>$ERR
errshow 1 "${BCMDS[$BOOTLDR]}"
if [[ -d $MNT/hostrun ]]; then
umount $MNT/hostrun/udev >/dev/null 2>&1
umount $MNT/hostrun/lvm >/dev/null 2>&1
rm -rf $MNT/hostrun >/dev/null 2>&1
fi
if [[ $SYS == 'UEFI' ]]; then
# some UEFI firmware requires a generic esp/BOOT/BOOTX64.EFI
mkdir -pv $MNT/$BOOTDIR/EFI/BOOT
if [[ $BOOTLDR == 'grub' ]]; then
cp -fv $MNT/$BOOTDIR/EFI/$DIST/grubx64.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
elif [[ $BOOTLDR == 'syslinux' ]]; then
cp -rf $MNT/$BOOTDIR/EFI/syslinux/* $MNT/$BOOTDIR/EFI/BOOT/
cp -f $MNT/$BOOTDIR/EFI/syslinux/syslinux.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
elif [[ $BOOTLDR == 'refind-efi' ]]; then
sed -i '/#extra_kernel_version_strings/ c extra_kernel_version_strings linux-hardened,linux-zen,linux-lts,linux' $MNT/$BOOTDIR/EFI/refind/refind.conf
cp -fv $MNT/$BOOTDIR/EFI/refind/refind_x64.efi $MNT/$BOOTDIR/EFI/BOOT/BOOTX64.EFI
fi
fi
return 0
}
install_user()
{
chrun "chpasswd <<< 'root:$ROOT_PASS'" 2>$ERR
errshow 1 "set root password"
if [[ $MYSHELL != *zsh ]]; then
chrun "usermod -s $MYSHELL root" 2>$ERR
errshow 1 "usermod -s $MYSHELL root"
if [[ $MYSHELL == "/usr/bin/mksh" ]]; then
cp -fv $MNT/etc/skel/.mkshrc /root/.mkshrc
fi
fi
local groups='audio,autologin,floppy,log,network,rfkill,scanner,storage,optical,power,wheel'
chrun "groupadd -r autologin" 2>$ERR
errshow 1 "groupadd -r autologin"
chrun "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" 2>$ERR
errshow 1 "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER"
chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" 2>$ERR
errshow 1 "set $NEWUSER password"
if [[ $USER_PKGS == *neovim* ]]; then
mkdir -p $MNT/home/$NEWUSER/.config/nvim
cp -fv $MNT/home/$NEWUSER/.vimrc $MNT/home/$NEWUSER/.config/nvim/init.vim
cp -rfv $MNT/home/$NEWUSER/.vim/colors $MNT/home/$NEWUSER/.config/nvim/colors
fi
if [[ $MYSHELL == '/usr/bin/mksh' ]]; then
cat >> $MNT/home/$NEWUSER/.mkshrc << EOF
# colors in less (manpager)
export LESS_TERMCAP_mb=$'\e[01;31m'
export LESS_TERMCAP_md=$'\e[01;31m'
export LESS_TERMCAP_me=$'\e[0m'
export LESS_TERMCAP_se=$'\e[0m'
export LESS_TERMCAP_so=$'\e[01;44;33m'
export LESS_TERMCAP_ue=$'\e[0m'
export LESS_TERMCAP_us=$'\e[01;32m'
export EDITOR=$([[ $USER_PKGS == *neovim* ]] && printf "n")vim
# source shell configs
for f in "\$HOME/.mksh/"*?.sh; do
. "\$f"
done
al-info
EOF
fi
[[ $INSTALL_WMS == *dwm* ]] && install_suckless
[[ $LOGIN_WM =~ (startkde|gnome-session) ]] && sed -i '/super/d' $HOME/.xprofile /root/.xprofile
return 0
}
install_login()
{
SERVICE="$MNT/etc/systemd/system/getty@tty1.service.d"
# remove welcome message
sed -i '/printf/d' $MNT/root/.zshrc
# remove unneeded shell files from installation
case $MYSHELL in
"/bin/bash") rm -rf $MNT/home/$NEWUSER/.{zsh,mksh}* $MNT/root/.{zsh,mksh}* ;;
"/usr/bin/mksh") rm -rf $MNT/home/$NEWUSER/.{zsh,bash}* $MNT/home/$NEWUSER/.inputrc $MNT/root/.{zsh,bash}* $MNT/root/.inputrc ;;
"/usr/bin/zsh") rm -rf $MNT/home/$NEWUSER/.{bash,mksh}* $MNT/home/$NEWUSER/.inputrc $MNT/root/.{bash,mksh}* $MNT/root/.inputrc ;;
esac
install_${LOGIN_TYPE:-xinit}
}
install_xinit()
{
if [[ -e $MNT/home/$NEWUSER/.xinitrc ]] && grep -q 'exec' $MNT/home/$NEWUSER/.xinitrc; then
sed -i "/exec/ c exec ${LOGIN_WM}" $MNT/home/$NEWUSER/.xinitrc
else
printf "exec %s\n" "$LOGIN_WM" >> $MNT/home/$NEWUSER/.xinitrc
fi
[[ ${EDIT_FILES[login]} == *"$LOGINRC"* ]] || EDIT_FILES[login]+=" /home/$NEWUSER/$LOGINRC"
if [[ $AUTOLOGIN ]]; then
sed -i "s/root/${NEWUSER}/g" $SERVICE/autologin.conf
cat > $MNT/home/$NEWUSER/$LOGINRC << EOF
# ~/$LOGINRC
# sourced by ${MYSHELL##*/} when used as a login shell
# automatically run startx when logging in on tty1
[[ -z \$DISPLAY && \$XDG_VTNR -eq 1 ]] && exec startx -- vt1
EOF
else
rm -rf $SERVICE
rm -rf $MNT/home/$NEWUSER/.{profile,zprofile,bash_profile}
fi
}
install_lightdm()
{
rm -rf $SERVICE
rm -rf $MNT/home/$NEWUSER/.{xinitrc,profile,zprofile,bash_profile}
chrun 'systemctl set-default graphical.target && systemctl enable lightdm.service' 2>$ERR
errshow 1 "systemctl set-default graphical.target && systemctl enable lightdm.service"
cat > $MNT/etc/lightdm/lightdm-gtk-greeter.conf << EOF
# LightDM GTK+ Configuration
[greeter]
active-monitor=0
default-user-image=/usr/share/icons/ArchLabs-Dark/64x64/places/distributor-logo-archlabs.png
background=/usr/share/backgrounds/archlabs/archlabs.jpg
theme-name=Adwaita-dark
icon-theme-name=Adwaita
font-name=DejaVu Sans Mono 11
position=30%,end 50%,end
EOF
}
install_packages()
{
local inpkg="$BASE_PKGS $PACKAGES $USER_PKGS"
local rmpkg=""
if pacman -Qsq 'archlabs-installer' >/dev/null 2>&1; then
rmpkg+="archlabs-installer"
elif [[ -e "$MNT/usr/bin/archlabs-installer" ]]; then
rm -f "$MNT/usr/bin/archlabs-installer"
fi
[[ $MYSHELL == *mksh ]] && inpkg+=" mksh"
[[ $KERNEL == 'linux' ]] || { inpkg+=" $KERNEL"; rmpkg+=" linux"; }
# add xterm when not installing a terminal emulator or de that provides one
[[ $inpkg =~ (term|urxvt|tilix|alacritty|sakura|tilda|plasma|cinnamon) || $INSTALL_WMS == *dwm* ]] || inpkg+=" xterm"
[[ $MYSHELL == '/usr/bin/zsh' ]] && inpkg+=" zsh-completions"
[[ $INSTALL_WMS =~ (openbox|bspwm|i3-gaps|dwm|fluxbox) ]] && inpkg+=" $WM_BASE_PKGS"
[[ $INSTALL_WMS =~ ^(plasma|gnome|cinnamon)$ ]] || inpkg+=" archlabs-ksuperkey"
chrun "pacman -Syyu --noconfirm"
chrun "pacman -Rns $rmpkg --noconfirm"
chrun "pacman -S iputils --noconfirm"
chrun "pacman -S $inpkg --needed --noconfirm"
if [[ $BOOTLDR == 'grub' ]]; then
chrun "pacman -S grub os-prober efibootmgr --needed --noconfirm"
elif [[ $BOOTLDR == 'refind-efi' ]]; then
chrun "pacman -S refind-efi efibootmgr --needed --noconfirm"
elif [[ $SYS == 'UEFI' ]]; then
chrun "pacman -S efibootmgr --needed --noconfirm"
fi
sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers
return 0
}
install_suckless()
{
mkdir -pv $MNT/home/$NEWUSER/suckless
for i in dwm dmenu st; do
if chrun "git clone https://bitbucket.org/natemaia/$i /home/$NEWUSER/suckless/$i"; then
chrun "cd /home/$NEWUSER/suckless/$i; rm -f config.h; make clean install; make clean"
else
printf "failed to clone $i repo\n"
fi
done
if [[ -d $MNT/home/$NEWUSER/suckless/dwm && -x $MNT/usr/bin/dwm ]]; then
printf "To configure dwm edit /home/$NEWUSER/suckless/dwm/config.h\n"
printf "You can then recompile it with 'sudo make clean install'\n"
sleep 2
fi
}
install_mirrorlist()
{
if hash reflector >/dev/null 2>&1; then
$MIRROR_CMD --save $MNT/etc/pacman.d/mirrorlist --verbose || reflector --score 100 -l 50 -f 10 --sort rate --verbose --save $MNT/etc/pacman.d/mirrorlist
else
{ eval $MIRROR_CMD || curl -s 'https://www.archlinux.org/mirrorlist/all/'; } |
sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -v -t -n 10 - > $MNT/etc/pacman.d/mirrorlist
fi
}
install_mkinitcpio()
{
local add=''
# [[ $LUKS_UUID && $LUKS_PASS && $SYS == 'UEFI' && $BOOTLDR == 'grub' ]] && luks_keyfile
[[ $LUKS ]] && add="encrypt"
[[ $LVM ]] && { [[ $add ]] && add+=" lvm2" || add+="lvm2"; }
sed -i "s/block filesystems/block ${add} filesystems ${HOOKS}/g" $MNT/etc/mkinitcpio.conf
chrun "mkinitcpio -p $KERNEL" 2>$ERR
errshow 1 "mkinitcpio -p $KERNEL"
}
install_background()
{
if [[ -d /run/archiso/sfs/airootfs/etc/skel ]] && grep -qw "$MNT" /proc/mounts && (grep -qw "$MNT/$BOOTDIR" /proc/mounts || [[ $SYS == 'BIOS' && -z $LUKS ]]); then
msg "Background Install" "\nThe system base will now be unpacked in the background.\n" 2
rsync -a /run/archiso/sfs/airootfs/ $MNT/ >/dev/null 2>&1 &
RSYNC_PID=$!
trap "kill $RSYNC_PID 2>/dev/null" EXIT
fi
return 0
}
###############################################################################
# bootloader setup
setup_grub()
{
EDIT_FILES[bootloader]="/etc/default/grub"
if [[ $SYS == 'BIOS' ]]; then
[[ $BOOT_DEV ]] || { part_device 1 || return 1; }
BCMDS[grub]="grub-install --recheck --force --target=i386-pc $BOOT_DEV"
else
if [[ $ROOT_PART == */dev/mapper/* && ! $LVM && ! $LUKS_PASS ]]; then
luks_pass "$_luksopen" 1 || return 1
fi
BCMDS[grub]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
grub-install --recheck --force --target=x86_64-efi --efi-directory=/$BOOTDIR --bootloader-id=$DIST"
grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
fi
BCMDS[grub]="mkdir -p /run/udev /run/lvm &&
mount --bind /hostrun/udev /run/udev &&
mount --bind /hostrun/lvm /run/lvm &&
${BCMDS[grub]} &&
grub-mkconfig -o /boot/grub/grub.cfg &&
sleep 1 && umount /run/udev /run/lvm"
return 0
}
prerun_grub()
{
sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g;
s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" $MNT/etc/default/grub
if [[ $LUKS_DEV ]]; then
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
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"
fi
if [[ $SYS == 'BIOS' && $LVM && -z $SEP_BOOT ]]; then
sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $MNT/etc/default/grub 2>$ERR
errshow 1 "sed -i 's/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g' $MNT/etc/default/grub"
fi
# setup for os-prober module
mkdir -p /run/{lvm,udev}
mkdir -p $MNT/hostrun/{lvm,udev}
mount --bind /run/lvm $MNT/hostrun/lvm
mount --bind /run/udev $MNT/hostrun/udev
return 0
}
setup_efistub()
{
EDIT_FILES[bootloader]=""
}
prerun_efistub()
{
BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
efibootmgr -v -d $BOOT_DEV -p $BOOT_PART_NUM -c -L '${DIST} Linux' -l /vmlinuz-${KERNEL} \
-u 'root=$ROOT_PART_ID rw $([[ $UCODE ]] && printf 'initrd=\%s.img ' "$UCODE")initrd=\initramfs-${KERNEL}.img'"
return 0
}
setup_syslinux()
{
if [[ $SYS == 'BIOS' ]]; then
EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg"
else
EDIT_FILES[bootloader]="/boot/EFI/syslinux/syslinux.cfg"
BCMDS[syslinux]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
efibootmgr -v -c -d $BOOT_DEV -p $BOOT_PART_NUM -l /EFI/syslinux/syslinux.efi -L $DIST"
fi
}
prerun_syslinux()
{
local c="$MNT/boot/syslinux" s="/usr/lib/syslinux/bios" d=".."
[[ $SYS == 'UEFI' ]] && { c="$MNT/boot/EFI/syslinux"; s="/usr/lib/syslinux/efi64/"; d=''; }
mkdir -pv "$c" && cp -rfv $s/* "$c/" && cp -f "$RUN/syslinux/splash.png" "$c/"
cat > "$c/syslinux.cfg" << EOF
UI vesamenu.c32
MENU TITLE $DIST Boot Menu
MENU BACKGROUND splash.png
TIMEOUT 50
DEFAULT $DIST
# see: https://www.syslinux.org/wiki/index.php/Comboot/menu.c32
MENU WIDTH 78
MENU MARGIN 4
MENU ROWS 4
MENU VSHIFT 10
MENU TIMEOUTROW 13
MENU TABMSGROW 14
MENU CMDLINEROW 14
MENU HELPMSGROW 16
MENU HELPMSGENDROW 29
MENU COLOR border 30;44 #40ffffff #a0000000 std
MENU COLOR title 1;36;44 #9033ccff #a0000000 std
MENU COLOR sel 7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel 37;44 #50ffffff #a0000000 std
MENU COLOR help 37;40 #c0ffffff #a0000000 std
MENU COLOR timeout_msg 37;40 #80ffffff #00000000 std
MENU COLOR timeout 1;37;40 #c0ffffff #00000000 std
MENU COLOR msg07 37;40 #90ffffff #a0000000 std
MENU COLOR tabmsg 31;40 #30ffffff #00000000 std
LABEL $DIST
MENU LABEL $DIST Linux
LINUX $d/vmlinuz-$KERNEL
APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL.img
LABEL ${DIST}fallback
MENU LABEL $DIST Linux Fallback
LINUX $d/vmlinuz-$KERNEL
APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
INITRD $([[ $UCODE ]] && printf "%s" "$d/$UCODE.img,")$d/initramfs-$KERNEL-fallback.img
$([[ $SYS == 'BIOS' ]] && printf "\n%s" "# examples of chainloading other bootloaders
#LABEL grub2
#MENU LABEL Grub2
#COM32 chain.c32
#APPEND file=$d/grub/boot.img
#LABEL windows
#MENU LABEL Windows
#COM32 chain.c32
#APPEND hd0 3")
EOF
return 0
}
setup_refind-efi()
{
EDIT_FILES[bootloader]="/boot/refind_linux.conf"
BCMDS[refind-efi]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
refind-install"
}
prerun_refind-efi()
{
cat > $MNT/boot/refind_linux.conf << EOF
"$DIST Linux" "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL.img"
"$DIST Linux Fallback" "root=$ROOT_PART_ID $([[ $LUKS_DEV ]] &&
printf "%s " "$LUKS_DEV")rw add_efi_memmap $([[ $UCODE ]] &&
printf "initrd=%s " "/$UCODE.img")initrd=/initramfs-$KERNEL-fallback.img"
EOF
mkdir -p $MNT/etc/pacman.d/hooks
cat > $MNT/etc/pacman.d/hooks/refind.hook << EOF
[Trigger]
Operation = Upgrade
Type = Package
Target = refind-efi
[Action]
Description = Updating rEFInd on ESP
When = PostTransaction
Exec = /usr/bin/refind-install
EOF
}
setup_systemd-boot()
{
EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf"
BCMDS[systemd-boot]="mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1
bootctl --path=/boot install"
}
prerun_systemd-boot()
{
mkdir -p $MNT/boot/loader/entries
cat > $MNT/boot/loader/loader.conf << EOF
default $DIST
timeout 5
editor no
EOF
cat > $MNT/boot/loader/entries/$DIST.conf << EOF
title $DIST Linux
linux /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd %s" "/$UCODE.img")
initrd /initramfs-$KERNEL.img
options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
EOF
cat > $MNT/boot/loader/entries/$DIST-fallback.conf << EOF
title $DIST Linux Fallback
linux /vmlinuz-${KERNEL}$([[ $UCODE ]] && printf "\ninitrd %s" "/$UCODE.img")
initrd /initramfs-$KERNEL-fallback.img
options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw
EOF
mkdir -p $MNT/etc/pacman.d/hooks
cat > $MNT/etc/pacman.d/hooks/systemd-boot.hook << EOF
[Trigger]
Type = Package
Operation = Upgrade
Target = systemd
[Action]
Description = Updating systemd-boot
When = PostTransaction
Exec = /usr/bin/bootctl update
EOF
systemd-machine-id-setup --root="$MNT"
return 0
}
###############################################################################
# lvm functions
lvm_menu()
{
check_background_install || return 1
lvm_detect
local choice
dlg choice menu "Logical Volume Management" "$_lvmmenu" \
"$_lvmnew" "vgcreate -f, lvcreate -L -n" \
"$_lvmdel" "vgremove -f" \
"$_lvmdelall" "lvrmeove, vgremove, pvremove -f" \
"back" "return to the main menu"
case "$choice" in
"$_lvmnew") lvm_create || return 1 ;;
"$_lvmdel") lvm_delgroup && yesno "$_lvmdel" "$_lvmdelask" && vgremove -f "$DEL_VG" >/dev/null 2>&1 ;;
"$_LvMDelAll") lvm_del_all ;;
esac
return 0
}
lvm_detect()
{
local pv="$(pvs -o pv_name --noheading 2>/dev/null)"
local v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
if [[ $VGROUP && $v && $pv ]]; then
msg "Logical Volume Management" "\nActivating existing logical volume management (LVM).\n\n" 1
modprobe dm-mod >/dev/null 2>$ERR
errshow 'modprobe dm-mod'
vgscan >/dev/null 2>&1
vgchange -ay >/dev/null 2>&1
fi
}
lvm_create()
{
VGROUP='' LVM_PARTS='' VGROUP_MB=0
umount_dir $MNT
lvm_mkgroup || return 1
local txt="\nThe last (or only) logical volume will automatically use all remaining space in the volume group."
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
[[ $VOL_COUNT ]] || return 1
lvm_extra_lvs || return 1
lvm_volume_name "$_lvmlvname\nNOTE: This LV will use up all remaining space in the volume group (${VGROUP_MB}MB)" || return 1
lvcreate -l +100%FREE "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
errshow "lvcreate -l +100%FREE $VGROUP -n $VNAME" || return 1
LVM='logical volume'; sleep 0.5
txt="\nDone, volume: $VGROUP-$VNAME (${VOLUME_SIZE:-${VGROUP_MB}MB}) has been created.\n"
msg "$_lvmnew (LV:$VOL_COUNT)" "$txt\n$(lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE $LVM_PARTS)\n"
return 0
}
get_lv_size()
{
local txt="${VGROUP}: ${SIZE}$SIZE_UNIT (${VGROUP_MB}MB remaining).$_lvmlvsize"
while true; do
ERR_SIZE=0
dlg VOLUME_SIZE input "$_lvmnew (LV:$VOL_COUNT)" "$txt" ''
if [[ -z $VOLUME_SIZE ]]; then
ERR_SIZE=1
break # allow bailing with escape or an empty choice
elif (( ${VOLUME_SIZE:0:1} == 0 )); then
ERR_SIZE=1 # size values can't begin with '0'
else
# walk the string and make sure all but the last char are digits
local lv=$((${#VOLUME_SIZE} - 1))
for (( i=0; i<lv; i++ )); do
[[ ${VOLUME_SIZE:$i:1} =~ [0-9] ]] || { ERR_SIZE=1; break; }
done
if (( ERR_SIZE != 1 )); then
case ${VOLUME_SIZE:$lv:1} in
[mMgG]) local s=${VOLUME_SIZE:0:$lv} m=$((s * 1000))
case ${VOLUME_SIZE:$lv:1} in
[Gg]) (( m >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - m)) ;;
[Mm]) (( ${VOLUME_SIZE:0:$lv} >= VGROUP_MB )) && ERR_SIZE=1 || VGROUP_MB=$((VGROUP_MB - s)) ;;
*) ERR_SIZE=1
esac ;;
*) ERR_SIZE=1
esac
fi
fi
(( ! ERR_SIZE )) && break || msg "Invalid Logical Volume Size" "$_lvmerrlvsize"
done
return $ERR_SIZE
}
lvm_mkgroup()
{
local named=''
local txt="\nConfirm creation of volume group: $VGROUP\n\nwith the following partition(s):"
until [[ $named ]]; do
lvm_partitions || return 1
lvm_group_name || return 1
yesno "$_lvmnew" "$txt $LVM_PARTS\n" && named=true
done
vgcreate -f "$VGROUP" $LVM_PARTS >/dev/null 2>$ERR
errshow "vgcreate -f $VGROUP $LVM_PARTS" || return 1
SIZE=$(vgdisplay "$VGROUP" | awk '/VG Size/ { gsub(/[^0-9.]/, ""); print int($0) }')
SIZE_UNIT="$(vgdisplay "$VGROUP" | awk '/VG Size/ { print substr($NF, 0, 1) }')"
if [[ $SIZE_UNIT == 'G' ]]; then
VGROUP_MB=$((SIZE * 1000))
else
VGROUP_MB=$SIZE
fi
msg "$_lvmnew" "\nVolume group: $VGROUP ($SIZE $SIZE_UNIT) has been created\n"
return 0
}
lvm_del_all()
{
local pv="$(pvs -o pv_name --noheading 2>/dev/null)"
local v="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)"
VGROUP="$(vgs -o vg_name --noheading 2>/dev/null)"
if [[ $VGROUP || $v || $pv ]]; then
if yesno "$_lvmdelall" "$_lvmdelask"; then
for i in $v; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1; done
for i in $VGROUP; do vgremove -f "$i" >/dev/null 2>&1; done
for i in $pv; do pvremove -f "$i" >/dev/null 2>&1; done
LVM=''
fi
else
msg "Delete LVM" "\nNo LVMs to remove...\n" 2
LVM=''
fi
}
lvm_delgroup()
{
DEL_VG=''
VOL_GROUP_LIST=''
for i in $(lvs --noheadings | awk '{print $2}' | uniq); do
VOL_GROUP_LIST+="$i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}') "
done
[[ $VOL_GROUP_LIST ]] || { msg "Installation Error" "\nNo volume groups found."; return 1; }
dlg DEL_VG menu "Logical Volume Management" "\nSelect volume group to delete.\n\nAll logical volumes within will also be deleted." $VOL_GROUP_LIST
[[ $DEL_VG ]]
}
lvm_extra_lvs()
{
while (( VOL_COUNT > 1 )); do
lvm_volume_name "$_lvmlvname" && get_lv_size || return 1
lvcreate -L "$VOLUME_SIZE" "$VGROUP" -n "$VNAME" >/dev/null 2>$ERR
errshow "lvcreate -L $VOLUME_SIZE $VGROUP -n $VNAME" || return 1
msg "$_lvmnew (LV:$VOL_COUNT)" "\nDone, logical volume (LV) $VNAME ($VOLUME_SIZE) has been created.\n"
(( VOL_COUNT-- ))
done
return 0
}
lvm_partitions()
{
part_find 'part|crypt' || return 1
PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")"
dlg LVM_PARTS check "$_lvmnew" "\nSelect the partition(s) to use for the physical volume (PV)." $PARTS
[[ $LVM_PARTS ]]
}
lvm_group_name()
{
VGROUP=''
until [[ $VGROUP ]]; do
dlg VGROUP input "$_lvmnew" "$_lvmvgname" "mygroup"
if [[ -z $VGROUP ]]; then
return 1
elif [[ ${VGROUP:0:1} == "/" || $VGROUP =~ \ |\' ]] || grep -q "$VGROUP" <<< "$(lsblk)"; then
msg "Installation Error" "$_lvmerrvgname"
VGROUP=''
fi
done
return 0
}
lvm_volume_name()
{
VNAME=''
local txt="$1" default="mainvolume"
(( VOL_COUNT > 1 )) && default="extravolume$VOL_COUNT"
until [[ $VNAME ]]; do
dlg VNAME input "$_lvmnew (LV:$VOL_COUNT)" "\n$txt" "$default"
if [[ -z $VNAME ]]; then
return 1
elif [[ ${VNAME:0:1} == "/" || $VNAME =~ \ |\' ]] || grep -q "$VNAME" <<< "$(lsblk)"; then
msg "Installation Error" "$_lvmerlvname"
VNAME=''
fi
done
return 0
}
###############################################################################
# luks functions
luks_menu()
{
local choice
check_background_install || return 1
dlg choice menu "LUKS Encryption" "$_luksmenu" \
"$_luksnew" "cryptsetup -q luksFormat" \
"$_luksopen" "cryptsetup open --type luks" \
"$_luksadv" "cryptsetup -q -s -c luksFormat" \
"back" "Return to the main menu"
case "$choice" in
"$_luksnew") luks_basic || return 1 ;;
"$_luksopen") luks_open || return 1 ;;
"$_luksadv") luks_advanced || return 1 ;;
esac
return 0
}
luks_open()
{
modprobe -a dm-mod dm_crypt
umount_dir $MNT
part_find 'part|crypt|lvm' || return 1
if (( COUNT == 1 )); then
LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
else
dlg LUKS_PART menu "$_luksopen" "\nSelect which partition to open." $PARTS
fi
[[ $LUKS_PART ]] || return 1
luks_pass "$_luksopen" || return 1
msg "$_luksopen" "\nOpening encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
cryptsetup open --type luks $LUKS_PART "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
errshow "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" || return 1
LUKS='encrypted'; luks_show
return 0
}
luks_pass()
{
LUKS_PASS=''
local t="$1" op="$2" v='' p='' p2=''
until [[ $LUKS_PASS ]]; do
i=0
tput cnorm
if [[ $op ]]; then
dialog --cr-wrap --insecure --backtitle "$DIST Installer - $SYS - v$VER" --separator $'\n' --title " $title " \
--mixedform "\nEnter the password to decrypt $ROOT_PART\n\nThis is needed to create a keyfile." 0 0 0 \
"Password:" 1 1 '' 1 11 $COLUMNS 0 1 \
"Password2:" 2 1 '' 2 12 $COLUMNS 0 1 2>"$ANS" || return 1
else
dialog --cr-wrap --insecure --backtitle "$DIST Installer - $SYS - v$VER" \
--separator $'\n' --title " $title " --mixedform "$_luksomenu" 0 0 0 \
"Name:" 1 1 "${LUKS_NAME:-cryptroot}" 1 7 $COLUMNS 0 0 \
"Password:" 2 1 '' 2 11 $COLUMNS 0 1 \
"Password2:" 3 1 '' 3 12 $COLUMNS 0 1 2>"$ANS" || return 1
fi
while read -r line; do
if [[ $op ]]; then
case $i in
0) p="$line" ;;
1) p2="$line" ;;
esac
else
case $i in
0) n="$line" ;;
1) p="$line" ;;
2) p2="$line" ;;
esac
fi
(( i++ ))
done < "$ANS"
if [[ -z $op && -z $n ]]; then
msg "Name Empty" "\nEncrypted device name cannot be empty.\n\nPlease try again.\n" 2
elif [[ -z $p || "$p" != "$p2" ]]; then
[[ $op ]] || LUKS_NAME="$n"
msg "Password Mismatch" "\nThe passwords entered do not match.\n\nPlease try again.\n" 2
else
[[ $op ]] || LUKS_NAME="$n"
LUKS_PASS="$p"
fi
done
return 0
}
luks_show()
{
sleep 0.5
msg "$_luksnew" "\nEncrypted partition opened and ready for mounting.\n\n$(lsblk $LUKS_PART -o NAME,MODEL,SIZE,TYPE,FSTYPE)\n\n"
}
luks_setup()
{
modprobe -a dm-mod dm_crypt
umount_dir $MNT
part_find 'part|lvm' || return 1
if [[ $AUTO_ROOT_PART ]]; then
LUKS_PART="$AUTO_ROOT_PART"
elif (( COUNT == 1 )); then
LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")"
else
dlg LUKS_PART menu "$_luksnew" "\nSelect the partition you want to encrypt." $PARTS
fi
[[ $LUKS_PART ]] || return 1
luks_pass "$_luksnew"
}
luks_basic()
{
luks_setup || return 1
msg "$_luksnew" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
cryptsetup -q luksFormat $LUKS_PART <<< "$LUKS_PASS" 2>$ERR
errshow "cryptsetup -q luksFormat $LUKS_PART" || return 1
cryptsetup open $LUKS_PART "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
LUKS='encrypted'; luks_show
return 0
}
luks_keyfile()
{
if [[ ! -e $MNT/crypto_keyfile.bin ]]; then
# printf "Creating LUKS keyfile /crypto_keyfile.bin\n"
local n
n="$(lsblk -lno NAME,UUID,TYPE | awk "/$LUKS_UUID/"' && /part|crypt|lvm/ {print $1}')"
local mkkey="dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin"
mkkey="$mkkey && chmod 000 /crypto_keyfile.bin"
mkkey="$mkkey && cryptsetup luksAddKey /dev/$n /crypto_keyfile.bin <<< '$LUKS_PASS'"
chrun "$mkkey"
sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf 2>$ERR
fi
return 0
}
luks_advanced()
{
if luks_setup; then
local cipher
dlg cipher input "LUKS Encryption" "$_lukskey" "-s 512 -c aes-xts-plain64"
[[ $cipher ]] || return 1
msg "$_luksadv" "\nCreating encrypted partition: $LUKS_NAME\n\nDevice or volume used: $LUKS_PART\n" 0
cryptsetup -q $cipher luksFormat $LUKS_PART <<< "$LUKS_PASS" 2>$ERR
errshow "cryptsetup -q $cipher luksFormat $LUKS_PART" || return 1
cryptsetup open $LUKS_PART "$LUKS_NAME" <<< "$LUKS_PASS" 2>$ERR
errshow "cryptsetup open $LUKS_PART $LUKS_NAME" || return 1
luks_show
return 0
fi
return 1
}
###############################################################################
# helper functions
ofn()
{
[[ "$2" == *"$1"* ]] && printf "on" || printf "off"
}
die()
{
local exitcode="$1"
trap - INT
tput cnorm
if [[ -d $MNT ]] && command cd /; then
umount_dir $MNT
if (( exitcode == 127 )); then
umount -l /run/archiso/bootmnt
sleep 0.5
systemctl -i reboot
fi
fi
# restore custom linux console 0-15 colors when not rebooting
if [[ $TERM == 'linux' ]]; then
colors=("\e]P0191919" "\e]P1D15355" "\e]P2609960" "\e]P3FFCC66"
"\e]P4255A9B" "\e]P5AF86C8" "\e]P62EC8D3" "\e]P7949494" "\e]P8191919" "\e]P9D15355"
"\e]PA609960" "\e]PBFF9157" "\e]PC4E88CF" "\e]PDAF86C8" "\e]PE2ec8d3" "\e]PFE1E1E1")
printf "%b" "${colors[@]}" && clear && unset col
fi
exit $exitcode
}
dlg()
{
local var="$1" btype="$2" title="$3" body="$4" n=0
shift 4
(( ($# / 2) > SHL )) && n=$SHL
case "$btype" in
menu)
tput civis
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--title " $title " --menu "$body" 0 0 $n "$@" 2>"$ANS"
;;
check)
tput civis
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--title " $title " --checklist "$body" 0 0 $n "$@" 2>"$ANS"
;;
input)
tput cnorm
local default="$1"
shift
if [[ $1 == 'limit' ]]; then
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--max-input 63 --title " $title " --inputbox "$body" 0 0 "$default" 2>"$ANS"
else
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--title " $title " --inputbox "$body" 0 0 "$default" 2>"$ANS"
fi
;;
esac
[ $? -eq 0 ] && [ -s "$ANS" ] && printf -v $var "%s" "$(< "$ANS")"
}
msg()
{
local title="$1" body="$2"
tput civis
if (( $# == 3 )); then
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" --sleep $3 --title " $title " --infobox "$body\n" 0 0
else
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" --title " $title " --msgbox "$body\n" 0 0
fi
}
json()
{
curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])"
}
yesno()
{
local title="$1" body="$2" yes='Yes' no='No'
(( $# >= 3 )) && local yes="$3"
(( $# >= 4 )) && local no="$4"
tput civis
if (( $# == 5 )); then
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" --defaultno \
--title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
else
dialog --cr-wrap --backtitle "$DIST Installer - $SYS - v$VER" \
--title " $title " --yes-label "$yes" --no-label "$no" --yesno "$body\n" 0 0
fi
}
chrun()
{
arch-chroot "$MNT" bash -c "$1"
}
debug()
{
export PS4='| ${BASH_SOURCE} LINE:${LINENO} FUNC:${FUNCNAME[0]:+ ${FUNCNAME[0]}()} |> '
set -x
exec 3>| $DBG
BASH_XTRACEFD=3
DEBUG=true
}
sigint()
{
printf "\n^C caught, cleaning up...\n"
die 1
}
print4()
{
local str="$*"
if [[ $COLUMNS -ge 110 && ${#str} -gt $((COLUMNS - 30)) ]]; then
str="$(awk '{
i = 2; p1 = p2 = p3 = p4 = ""; p1 = $1; q = int(NF / 4)
for (; i <= q; i++) { p1 = p1 " " $i }
for (; i <= q * 2; i++) { p2 = p2 " " $i }
for (; i <= q * 3; i++) { p3 = p3 " " $i }
for (; i <= NF; i++) { p4 = p4 " " $i }
printf "%s\n %s\n %s\n %s", p1, p2, p3, p4
}' <<< "$str")"
printf "%s\n" "$str"
elif [[ $str ]]; then
printf "%s\n" "$str"
fi
}
errshow()
{
[ $? -eq 0 ] && return 0
local fatal=0
local err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")"
(( $1 == 1 )) && { fatal=1; shift; }
local txt="\nThe command exited abnormally:\n\n$1\n\n"
if [[ $err ]]; then
txt+="With the following message:\n\n$err\n\n"
else
txt+="With no error message:\n\n"
fi
if (( fatal == 1 )); then
txt+="Errors at this stage are fatal and the install cannot continue.\n"
else
txt+="Errors at this stage are non-fatal and can be either fixed or ignored depending on the error.\n"
fi
msg "Install Error" "$txt"
if (( fatal == 1 )); then
[[ -r $DBG && $TERM == 'linux' ]] && less "$DBG"
die 1
fi
return 1
}
load_bcm()
{
msg "Broadcom Wireless Setup" "\nDetected chipset is Broadcom BCM4352\n\nDisabling bcma/b43 modules and loading wl module.\n" 1
{ rmmod wl; rmmod bcma; rmmod b43; rmmod ssb; modprobe wl; depmod -a; } >/dev/null 2>&1
BROADCOM_WL=true
}
prechecks()
{
if [[ $1 -ge 0 ]] && ! grep -qw "$MNT" /proc/mounts; then
msg "Not Mounted" "\nPartition(s) must be mounted first.\n" 2
SEL=4
return 1
elif [[ $1 -ge 1 && -z $BOOTLDR ]]; then
msg "No Bootloader" "\nBootloader must be selected first.\n" 2
SEL=5
return 1
elif [[ $1 -ge 2 && (-z $NEWUSER || -z $USER_PASS) ]]; then
msg "No User" "\nA user must be created first.\n" 2
SEL=6
return 1
elif [[ $1 -ge 3 && -z $CONFIG_DONE ]]; then
msg "Not Configured" "\nSystem configuration must be done first.\n" 2
SEL=7
return 1
fi
return 0
}
umount_dir()
{
swapoff -a
[[ -d $1 ]] && umount -R $1 >/dev/null 2>&1
return 0
}
chk_connect()
{
msg "Network Connect" "\nVerifying network connection\n" 1
curl -sIN --connect-timeout 5 'https://www.archlinux.org/' | sed '1q' | grep -q '200'
}
net_connect()
{
if chk_connect; then
return 0
else
if hash nmtui >/dev/null 2>&1; then
tput civis
[[ $TERM == 'linux' ]] && printf "%b" "\e]P1191919" "\e]P4191919"
nmtui-connect
[[ $TERM == 'linux' ]] && printf "%b" "\e]P1D15355" "\e]P4255a9b"
chk_connect
else
return 1
fi
fi
}
system_devices()
{
IGNORE_DEV="$(lsblk -lno NAME,MOUNTPOINT | awk '/\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')"
if [[ $IGNORE_DEV ]]; then
SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')"
else
SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE | awk '/disk/ {print "/dev/" $1 " " $2}')"
fi
if [[ -z $SYS_DEVS ]]; then
msg "Installation Error" "\nNo available devices...\n\nExiting..\n" 2
die 1
fi
DEV_COUNT=0
while read -r line; do
(( DEV_COUNT++ ))
done <<< "$SYS_DEVS"
}
system_identify()
{
if [[ $VM ]]; then
UCODE=''
elif grep -q 'AuthenticAMD' /proc/cpuinfo; then
UCODE="amd-ucode"
elif grep -q 'GenuineIntel' /proc/cpuinfo; then
UCODE="intel-ucode"
fi
if grep -qi 'apple' /sys/class/dmi/id/sys_vendor; then
modprobe -r -q efivars
else
modprobe -q efivarfs
fi
_prep="\nTo begin the install you must first have:\n\n - A root (/) partition mounted."
if [[ -d /sys/firmware/efi/efivars ]]; then
export SYS="UEFI"
grep -q /sys/firmware/efi/efivars /proc/mounts || mount -t efivarfs efivarfs /sys/firmware/efi/efivars
_prep+="\n - An EFI boot partition mounted."
else
export SYS="BIOS"
fi
_prep+="\n - The system bootloader selected.\n - A new user created and passwords set."
_prep+="\n - The system configuration finished.\n\nOnce the above are met the install can be started."
}
check_background_install()
{
[[ $RSYNC_PID ]] || return 0
msg "Install Running" "\nA background install process is currently running.\n" 2
return 1
}
###############################################################################
# entry point
if (( UID != 0 )); then
msg "Not Root" "\nThis installer must be run as root or using sudo.\n\nExiting..\n" 2
die 1
elif ! grep -qwm 1 'lm' /proc/cpuinfo; then
msg "Not x86_64 Architecture" "\nThis installer only supports x86_64 architectures.\n\nExiting..\n" 2
die 1
elif [[ $1 =~ (-d|--debug) ]]; then
debug
fi
trap sigint INT
system_identify
system_devices
msg "Welcome to the $DIST Installer" "\nThis will help you get $DIST setup on your system.\nHaving GNU/Linux experience is an asset, however we try our best to keep things simple.\n\nIf you are unsure about an option, a default will be listed or\nthe first selected option will be the default (excluding language and timezone).\n\n\nMenu Navigation:\n\n - Select items with the arrow keys or the option number.\n - Use [Space] to toggle options and [Enter] to confirm.\n - Switch between buttons using [Tab] or the arrow keys.\n - Use [Page Up] and [Page Down] to jump whole pages\n - Press the highlighted key of an option to select it.\n"
select_keymap || { clear; die 0; }
if lspci -vnn -d 14e4: | grep -q 'BCM4352'; then
load_bcm
fi
if ! net_connect; then
msg "Not Connected" "\nThis installer requires an active internet connection.\n\nExiting..\n" 2
die 1
fi
while true; do
select_main
done
# vim:fdm=marker:fmr={,}