#!/usr/bin/bash # vim:ft=sh:fdm=marker:fmr={,} # This program is free software, provided under the GNU GPL # Written by Nathaniel Maia for use in Archlabs # Some ideas and code were taken from other installers # AIF, ABIF, Calamares, Arch Wiki.. Credit where credit is due # dry run performing no action, good for checking syntax errors # set -n # immutable variables { readonly DIST="Archlabs" # Linux distributor readonly VER="1.6.46" # Installer version readonly LIVE="liveuser" # Live session user readonly TRN="/usr/share/archlabs-installer" # Translation path readonly MNT="/mnt/install" # Install mountpoint readonly ERR="/tmp/errlog" # Built-in error log # create a regex string of all usb devices on the system for dev in $(lsblk -lno NAME,TRAN | awk '/usb/ {print $1}'); do USB_DEVS="${dev}$([[ $USB_DEVS ]] && echo -n "|$USB_DEVS")" done # determine which device was used for booting to ignore later during partition select readonly IGNORE_DEV="$(lsblk -lno NAME,TYPE,TRAN,MOUNTPOINT | awk "/$USB_DEVS/"' && /\/run\/archiso\/bootmnt/ {sub(/[1-9]/, ""); print $1}')" readonly SYS_DEVS="$(lsblk -lno NAME,SIZE,TYPE,TRAN | awk '/disk/ && !'"/$IGNORE_DEV/"' {print "/dev/" $1 " " $2}')" readonly LOCALES="$(awk '/\.UTF-8/ {gsub(/# .*|#/, ""); if($1) print $1 " -"}' /etc/locale.gen)" readonly SYS_MEM=$(grep 'MemTotal' /proc/meminfo | awk '{print int($2 / 1024)}') readonly DEV_COUNT="$(wc -l <<< "$SYS_DEVS")" readonly KBD="$(find /usr/share/kbd/keymaps -name '*.map.gz')" readonly CONSOLE_MAPS="$(awk '{gsub(/\.map\.gz|.*\//, ""); print $1 " -"}' <<< "$KBD" | sort -r)" # create associative array for SUBZONES[zone], value is: 'sub-zone country_code' declare -Ag SUBZONES for zone in America Australia Asia Atlantic Africa Europe Indian Pacific Arctic Antarctica; do SUBZONES[$zone]="$(awk "/$zone\// {gsub(/$zone\//, \"\"); print \$3 \" \"\$1}" /usr/share/zoneinfo/zone.tab)" done readonly SUBZONES # make it read only if [[ $TERM != 'linux' ]]; then for t in st termite xterm; do hash $t >/dev/null 2>&1 && { readonly TERM_CMD="$t"; break; } done fi # static string of keymap codes and respective language readonly KEYMAPS="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 no Norwegian ro Romanian rs Serbian si Slovenian tj Tajik lk Sinhala tr Turkish uz Uzbek ie Irish pk Urdu mv Dhivehi epo Esperanto np Nepali et Amharic sn Wolof ml Bambara tz Swahili ke Swahili bw Tswana ph Filipino id Indonesian my Malay tm Turkmen bt Dzongkha lv Latvian md Moldavian mao Maori by Belarusian me Montenegrin mk Macedonian kh Khmer az Azerbaijani" declare -Agr BOOT_MNTS=( [UEFI-grub]="/boot/efi" [UEFI-systemd-boot]="/boot" [BIOS-grub]="/boot" [BIOS-syslinux]="/boot" [UEFI-syslinux]="/boot" ) # static list of bootloaders & boot partition mountpoints stored as the system type (BIOS or UEFI) declare -Agr BOOTLOADERS=([BIOS]="grub ${BOOT_MNTS[BIOS-grub]} syslinux ${BOOT_MNTS[BIOS-syslinux]}" [UEFI]="grub ${BOOT_MNTS[UEFI-grub]} systemd-boot ${BOOT_MNTS[UEFI-systemd-boot]} syslinux ${BOOT_MNTS[UEFI-syslinux]}" ) # static mkfs commands for each filesystem offered declare -Agr FS_CMDS=( [ext2]="mkfs.ext2 -q" [ext3]="mkfs.ext3 -q" [ext4]="mkfs.ext4 -q" [f2fs]="mkfs.f2fs" [jfs]="mkfs.jfs -q" [xfs]="mkfs.xfs -f" [nilfs2]="mkfs.nilfs2 -q" [ntfs]="mkfs.ntfs -q" [reiserfs]="mkfs.reiserfs -q" [vfat]="mkfs.vfat -F32" ) # static filesystem mount options declare -Agr FS_OPTS=([vfat]="" [ntfs]="" [ext2]="" [ext3]="" [ext4]="dealloc - off discard - off nofail - off noacl - off relatime - off noatime - off nobarrier - off nodelalloc - off" [jfs]="discard - off errors=continue - off errors=panic - off nointegrity - off" [reiserfs]="acl - off nolog - off notail - off replayonly - off user_xattr - off" [xfs]="discard - off filestreams - off ikeep - off largeio - off noalign - off nobarrier - off norecovery - off noquota - off wsync - off" [nilfs2]="discard - off nobarrier - off errors=continue - off errors=panic - off order=relaxed - off order=strict - off norecovery - off" [f2fs]="data_flush - off disable_roll_forward - off disable_ext_identify - off discard - off fastboot - off flush_merge - off inline_xattr - off inline_data - off inline_dentry - off no_heap - off noacl - off nobarrier - off noextent_cache - off noinline_data - off norecovery - off" ) # } luks_variable_init() { declare -g LUKS=0 declare -g LVM=0 declare -g VOL_GROUP_MB=0 declare -g LUKS_NAME="cryptroot" declare -g LUKS_PART="" declare -g LUKS_PASS="" declare -g LUKS_UUID="" declare -g LUKS_DEV="" declare -g MKINIT_HOOKS="shutdown" declare -g SEPERATE_BOOT=0 } initialize_variables() { # Modified during runtime and are all globally accessible # This is called once when the script is started, and again if/when an error occurs # Some may never be used, depending on the system and choices made declare -g ROOT_PART="" declare -g BOOT_DEVICE="" declare -g BOOT_PART="" declare -g BOOTLOADER="" declare -g EXTRA_MNT="" declare -g SWAP="none" declare -g SWAP_SIZE="${SYS_MEM}M" declare -g KERNEL="linux" declare -g NEWUSER="" declare -g USER_PASS="" declare -g ROOT_PASS="" declare -g LOGIN_WM="" declare -g LOGIN_TYPE="" declare -g INSTALL_WMS="" declare -g WM_PACKAGES="" declare -g EXTRA_PACKAGES="" declare -g REMOVE_PKGS="" declare -g CURRENT_MENU="" declare -g MENU_HIGHLIGHT declare -g EDITOR_CHOICE="" declare -g MIRROR_CMD="reflector --score 100 -l 50 -f 10 --sort rate" # boolean checks declare -g AUTOLOGIN=false declare -g CONFIG_DONE=false # Commands used to install each bootloader. # NOTE: syslinux and grub in particular can/will change during runtime declare -Ag BOOT_CMDS=( [syslinux]="syslinux-install_update -iam" [grub]="grub-install --recheck --force" [systemd-boot]="bootctl --path=${BOOT_MNTS[UEFI-systemd-boot]} install" ) # files able to be reviewed when finishing install # item index [9] can change depending on which bootloader is selected declare -Ag EDIT_FILES=( [2]="/etc/X11/xorg.conf.d/00-keyboard.conf /etc/vconsole.conf /etc/default/keyboard" [3]="/etc/locale.conf /etc/default/locale" [4]="/etc/hostname /etc/hosts" [5]="/etc/sudoers" [6]="/etc/mkinitcpio.conf" [7]="/etc/fstab" [8]="/etc/crypttab" [9]="/etc/default/grub" [10]="/etc/pacman.conf" ) } ###################################################################### ## Utility Functions ## ###################################################################### chroot_cmd() { arch-chroot $MNT /bin/bash -c "$1" } show_devices() { tput civis if [[ $IGNORE_DEV != "" ]]; then lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT | awk "!/$IGNORE_DEV/"' && /disk|part|lvm|crypt|NAME/ {print $0}' > /tmp/.devlist else lsblk -o NAME,MODEL,TYPE,FSTYPE,SIZE,MOUNTPOINT | awk '/disk|part|lvm|crypt|NAME/ {print $0}' > /tmp/.devlist fi dialog --cr-wrap --backtitle "$BT" --title " $_PrepShowDev " --textbox /tmp/.devlist 0 0 } set_debug() { set -x exec 3>| /tmp/debug-log BASH_XTRACEFD=3 if [[ $DISPLAY ]]; then if [[ $TERM_CMD == 'st' ]]; then $TERM_CMD -e tail -f /tmp/debug-log & else $TERM_CMD -e "tail -f /tmp/debug-log" & fi fi } select_language() { tput civis local lang lang=$(dialog --cr-wrap --stdout --backtitle "$DIST Installer - (x86_64) - Version $VER" \ --title " Select Language " --menu \ "\nLanguage - sprache - taal - språk - lingua - idioma - nyelv - língua\n" 0 0 0 \ "1" "English (en_**)" "2" "Español (es_ES)" \ "3" "Português [Brasil] (pt_BR)" "4" "Português (pt_PT)" \ "5" "Français (fr_FR)" "6" "Russkiy (ru_RU)" \ "7" "Italiano (it_IT)" "8" "Nederlands (nl_NL)" \ "9" "Magyar (hu_HU)" "10" "Chinese (zh_CN)") source $TRN/english.trans 2>/dev/null declare -g FONT="ter-i16n" case $lang in 1) LOC="en_US.UTF-8" ;; 2) source $TRN/spanish.trans 2>/dev/null && LOC="es_ES.UTF-8" ;; 3) source $TRN/p_brasil.trans 2>/dev/null && LOC="pt_BR.UTF-8" ;; 4) source $TRN/portuguese.trans 2>/dev/null && LOC="pt_PT.UTF-8" ;; 5) source $TRN/french.trans 2>/dev/null && LOC="fr_FR.UTF-8" ;; 6) source $TRN/russian.trans 2>/dev/null && LOC="ru_RU.UTF-8" FONT="LatKaCyrHeb-14" ;; 7) source $TRN/italian.trans 2>/dev/null && LOC="it_IT.UTF-8" ;; 8) source $TRN/dutch.trans 2>/dev/null && LOC="nl_NL.UTF-8" ;; 9) source $TRN/hungarian.trans 2>/dev/null && LOC="hu_HU.UTF-8" FONT="lat2-16" ;; 10) source $TRN/chinese.trans 2>/dev/null && LOC="zh_CN.UTF-8" ;; *) die 0 esac sed -i "s/#en_US.UTF-8/en_US.UTF-8/" /etc/locale.gen if [[ $LOC != "en_US.UTF-8" ]]; then sed -i "s/#${LOC}/${LOC}/" /etc/locale.gen locale-gen >/dev/null 2>&1 setfont $FONT >/dev/null 2>&1 export LANG="$LOC" fi return 0 } identify_system() { if grep -qi 'apple' /sys/class/dmi/id/sys_vendor; then modprobe -r -q efivars else modprobe -q efivarfs fi if [[ -d /sys/firmware/efi ]]; then SYS="UEFI" [[ $(mount) =~ /sys/firmware/efi/efivars ]] && mount -t efivarfs efivarfs /sys/firmware/efi/efivars >/dev/null 2>&1 else SYS="BIOS" fi readonly BT="$DIST Installer - $SYS (x86_64) - Version $VER" } check_requirements() { local err=0 if [[ $(whoami) != "root" ]]; then infobox "$_ErrTitle" "$_NotRoot\n$_Exit" err=1 elif ! grep -qw 'lm' /proc/cpuinfo; then infobox "$_ErrTitle" "$_Not64Bit\n$_Exit" err=1 elif ! (ping -c 1 archlinux.org || ping -c 1 archlabslinux.com || ping -c 1 google.com || ping -c 1 bitbucket.org || ping -c 1 github.com || ping -c 1 sourceforge.net) >/dev/null 2>&1; then ([[ $(systemctl is-active NetworkManager) == "active" ]] && hash nmtui >/dev/null 2>&1) && { tput civis; nmtui; } if ! (ping -c 1 archlinux.org || ping -c 1 archlabslinux.com || ping -c 1 google.com || ping -c 1 bitbucket.org || ping -c 1 github.com || ping -c 1 sourceforge.net) >/dev/null 2>&1; then infobox "$_ErrTitle" "$_NoNetwork\n$_Exit" err=1 fi fi [[ $err -eq 1 ]] && die 1 || return 0 } check_for_errors() { # return if the last process exited normally (( $? == 0 )) && return 0 local command="$1" local msg="\nThe command exited abnormally: $command" # get error message from logfile and attempt to format slightly better for humans # strip any non-printable characters, escape sequences, and other known messy text local err err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" [[ $err != "" ]] && msgbox "$_ErrTitle" "$msg\n\nWith the following error message:\n\n$err" msg="$([[ $err == "" ]] && echo -n "$msg")\n$_ErrChoice" yesno "$_ErrTitle" "$msg" "Exit & Shutdown" "Ignore & Continue" && die 'shutdown' return 0 } check_install_ready() { if ! [[ $(lsblk -o MOUNTPOINT) =~ $MNT ]]; then msgbox "$_ErrTitle" "$_ErrNoMount"; return 4 elif [[ $CONFIG_DONE != true ]]; then msgbox "$_ErrTitle" "$_ErrNoConfig"; return 5 fi return 0 } getinput() { local answer answer="$(dialog --cr-wrap --max-input 63 --stdout --no-cancel \ --backtitle "$BT" --title " $1 " --inputbox "$2" 0 0 "$3")" [[ $? != 0 || $answer == "" ]] && return 1 echo "$answer" } msgbox() { tput civis dialog --cr-wrap --backtitle "$BT" --title " $1 " --msgbox "$2\n" 0 0 } infobox() { local time="$3" local bt="${BT:-$DIST Installer - (x86_64) - Version $VER}" tput civis dialog --cr-wrap --backtitle "$bt" --title " $1 " --infobox "$2\n" 0 0 sleep ${time:-2} } yesno() { tput civis if [[ $# -eq 5 && $5 == "no" ]]; then # option for default no using custom labels dialog --cr-wrap --backtitle "$BT" --defaultno --title " $1 " \ --yes-label "$3" --no-label "$4" --yesno "$2\n" 0 0 elif [[ $# -eq 4 ]]; then # option for custom labels with standard default yes dialog --cr-wrap --backtitle "$BT" --title " $1 " --yes-label "$3" \ --no-label "$4" --yesno "$2\n" 0 0 else # basic yes no without custom labels and default yes dialog --cr-wrap --backtitle "$BT" --title " $1 " --yesno "$2\n" 0 0 fi } wrap_up() { yesno "$_CloseInst" "$1" "$2" "$3" || return 0 [[ $4 == 'reboot' ]] && die 'reboot' || die 0 } die() { tput cnorm unmount_partitions pgrep -f "$TERM_CMD -e tail" && pkill -f "$TERM_CMD -e tail" [[ $1 =~ [0-9] ]] && exit $1 || systemctl $1 } sigint() { echo -e "\n** CTRL-C caught" die 1 } oneshot() { [[ -e /tmp/.ai_$1 || ! $(type $1) ]] && return 0 $1 || return 1 touch "/tmp/.ai_$1" return 0 } ###################################################################### ## System Settings Functions ## ###################################################################### set_keymap() { tput civis declare -g KEYMAP KEYMAP="$(dialog --cr-wrap --stdout --no-cancel \ --backtitle "$DIST Installer - (x86_64) - Version $VER" \ --title " $_PrepLayout " --menu "$_XMapBody" 20 70 12 $KEYMAPS)" [[ $? != 0 || $KEYMAP == "" ]] && return 1 # when a matching console map is not available open a selection dialog if ! [[ $CONSOLE_MAPS =~ "$KEYMAP -" ]]; then tput civis CONSOLE_MAP="$(dialog --cr-wrap --stdout --no-cancel \ --backtitle "$DIST Installer - (x86_64) - Version $VER" \ --title " $_CMapTitle " --menu "$_CMapBody" 20 70 12 $CONSOLE_MAPS)" [[ $? != 0 || $CONSOLE_MAP == "" ]] && return 1 else CONSOLE_MAP="$KEYMAP" fi if [[ $DISPLAY && $TERM != 'linux' ]]; then (type setxkbmap >/dev/null 2>&1) && setxkbmap $KEYMAP >/dev/null 2>&1 else (type loadkeys >/dev/null 2>&1) && loadkeys $CONSOLE_MAP >/dev/null 2>&1 fi return 0 } set_locale() { tput civis declare -g LOCALE LOCALE="$(dialog --cr-wrap --stdout --no-cancel --backtitle "$BT" \ --title "$_ConfLocale" --menu "$_LocaleBody" 25 70 12 $LOCALES)" [[ $? != 0 || $LOCALE == "" ]] && return 1 return 0 } set_timezone() { tput civis declare -g ZONE ZONE="$(dialog --cr-wrap --stdout --no-cancel --backtitle "$BT" \ --title " $_TimeZTitle " --menu "$_TimeZBody" 20 70 10 America - Australia - \ Asia - Atlantic - Africa - Europe - Indian - Pacific - Arctic - Antarctica -)" declare -g SUBZONE SUBZONE="$(dialog --cr-wrap --stdout --no-cancel --backtitle "$BT" \ --title " $_TimeZTitle " --menu "$_TimeSubZBody" 20 70 12 ${SUBZONES[$ZONE]})" if yesno "$_TimeZTitle" "$_TimeZQ $ZONE/$SUBZONE?\n"; then return 0 else set_timezone fi } set_hwclock() { chroot_cmd "hwclock --systohc --utc" [[ $? != 0 ]] && chroot_cmd "hwclock --systohc --utc --directisa" return 0 } set_hostname() { tput cnorm declare -g HOSTNAME HOSTNAME="$(getinput "$_ConfHost" "$_HostNameBody" "${DIST,,}")" [[ $? != 0 || $HOSTNAME == "" ]] && return 1 return 0 } user_setup() { tput cnorm local values values="$(dialog --stdout --no-cancel --separator '~' --ok-label "Submit" --backtitle "$BT" \ --title " $_UserTitle " --insecure --mixedform "$_UserBody" 27 75 10 \ "$_Username" 1 1 "" 1 $((${#_Username} + 2)) 71 0 0 \ "$_Password" 2 1 "" 2 $((${#_Password} + 2)) 71 0 1 \ "$_Password2" 3 1 "" 3 $((${#_Password2} + 2)) 71 0 1 \ "$_RootBody" 6 1 "" 6 $((${#_RootBody} + 1)) 71 0 2 \ "$_Password" 8 1 "" 8 $((${#_Password} + 2)) 71 0 1 \ "$_Password2" 9 1 "" 9 $((${#_Password2} + 2)) 71 0 1)" [[ $? != 0 || $values == "" ]] && return 1 local user user="$(awk -F'~' '{print $1}' <<< "$values")" local pass pass2 pass="$(awk -F'~' '{print $2}' <<< "$values")" pass2="$(awk -F'~' '{print $3}' <<< "$values")" local rpass rpass2 rpass="$(awk -F'~' '{print $5}' <<< "$values")" rpass2="$(awk -F'~' '{print $6}' <<< "$values")" # both root passwords are empty, so use the user passwords instead [[ $rpass == "" && $rpass2 == "" ]] && { rpass="$pass"; rpass2="$pass2"; } # make sure a username was entered and that the passwords match if [[ ${#user} -eq 0 || $user =~ \ |\' || $user =~ [^a-z0-9\ ] || $pass == "" || "$pass" != "$pass2" || "$rpass" != "$rpass2" ]]; then if [[ $pass == "" || "$pass" != "$pass2" || "$rpass" != "$rpass2" ]]; then # password was left empty or doesn't match if [[ $pass == "" ]]; then msgbox "$_ErrTitle" "\nUser $_Password CANNOT be left empty.\n$_TryAgain" elif [[ "$rpass" != "$rpass2" ]]; then msgbox "$_ErrTitle" "$_RootPassErr\n$_TryAgain" else msgbox "$_ErrTitle" "$_UserPassErr\n$_TryAgain" fi else # bad username msgbox "$_UserErrTitle" "$_UserErrBody" user="" fi # recursively loop back unless the user cancels user_setup || return 1 else NEWUSER="$user" USER_PASS="$pass" ROOT_PASS="$rpass" fi return 0 } window_manager() { LOGIN_WM="" LOGIN_TYPE="" WM_PACKAGES="" INSTALL_WMS="$(dialog --cr-wrap --stdout --backtitle "$BT" --no-cancel \ --title " $_WMChoice " --checklist "$_WMChoiceBody\n" 0 0 0 \ "openbox" "A lightweight, powerful, and highly configurable stacking window manager" off \ "bspwm" "A tiling window manager that represents windows as the leaves of a binary tree" off \ "i3-gaps" "A fork of i3 window manager with more features including gaps" off \ "dwm" "A customized fork of dwm, with patches and modifications" off \ "gnome" "A desktop environment that aims to be simple and easy to use" off \ "cinnamon" "A desktop environment combining a traditional desktop layout with modern graphical effects" off \ "xfce4" "A lightweight and modular desktop environment based on GTK+ 2 and 3" off)" INSTALL_WMS="${INSTALL_WMS:-openbox}" WM_NUM=$(awk '{print NF}' <<< "$INSTALL_WMS") for wm in $INSTALL_WMS; do WM_LOGIN_CHOICES="${WM_LOGIN_CHOICES}$wm - " case $wm in openbox) WM_PACKAGES="$WM_PACKAGES $wm obconf archlabs-ob-autostart archlabs-obkey archlabs-kickshaw" ;; bspwm) WM_PACKAGES="$WM_PACKAGES $wm sxhkd lxappearance" ;; i3-gaps) WM_PACKAGES="$WM_PACKAGES $wm i3status perl-anyevent-i3 lxappearance" ;; gnome) WM_PACKAGES="$WM_PACKAGES $wm gnome-extra" ;; cinnamon) WM_PACKAGES="$WM_PACKAGES $wm" ;; xfce4) WM_PACKAGES="$WM_PACKAGES $wm xfce4-goodies xfce4-pulseaudio-plugin" ;; esac done if yesno "$_WMLogin" "$_LoginTypeBody\n" "xinit" "lightdm"; then LOGIN_TYPE="xinit" else LOGIN_TYPE="lightdm" fi if yesno "$_WMLogin" "$_AutoLoginBody\n"; then AUTOLOGIN=true else AUTOLOGIN=false fi if [[ $WM_NUM -eq 1 ]]; then LOGIN_WM="$INSTALL_WMS" else LOGIN_WM="$(dialog --cr-wrap --stdout --no-cancel --backtitle "$BT" \ --title " $_WMLogin " --menu "$_WMLoginBody" 0 0 0 $WM_LOGIN_CHOICES)" [[ $LOGIN_WM == "" ]] && LOGIN_WM="$(awk '{print $1}' <<< "$INSTALL_WMS")" fi case $LOGIN_WM in i3-gaps) LOGIN_WM='i3' ;; gnome) LOGIN_WM='gnome-session' ;; cinnamon) LOGIN_WM='cinnamon-session' ;; openbox) LOGIN_WM='openbox-session' ;; xfce4) LOGIN_WM='startxfce4' ;; esac return 0 } extra_packages() { EXTRA_PACKAGES="$(dialog --cr-wrap --stdout --no-cancel --backtitle "$BT" \ --title " $_WMChoice " --checklist "$_WMChoiceBody\n" 0 0 20 \ "firefox" "A popular open-source graphical web browser from Mozilla" off \ "chromium" "an open-source graphical web browser based on the Blink rendering engine" off \ "opera" "Fast and secure, free of charge web browser from Opera Software" off \ "epiphany" "A GNOME web browser based on the WebKit rendering engine" off \ "qutebrowser" "A keyboard-focused vim-like web browser based on Python and PyQt5" off \ "atom" "An open-source text editor developed by GitHub that is licensed under the MIT License" off \ "geany" "A fast and lightweight IDE" off \ "emacs" "An extensible, customizable, self-documenting real-time display editor" off \ "neovim" "A fork of Vim aiming to improve user experience, plugins, and GUIs." off \ "mousepad" "A simple text editor" off \ "urxvt" "A unicode enabled rxvt-clone terminal emulator" off \ "tilix" "A tiling terminal emulator for Linux using GTK+ 3" off \ "terminator" "Terminal emulator that supports tabs and grids" off \ "tilda" "A Gtk based drop down terminal for Linux and Unix" off \ "xfce4-terminal" "A terminal emulator primarily for the XFCE desktop" off \ "termite" "A minimal VTE-based terminal emulator" off \ "pcmanfm" "A fast and lightweight file manager" off \ "gnome-disk-utility" "Disk Management Utility" off \ "gnome-system-monitor" "View current processes and monitor system state" off \ "steam steam-native-runtime" "A popular game distribution platform by Valve" off \ "vlc qt4" "a free and open source cross-platform multimedia player" off \ "mpd mpc" "Flexible, powerful, server-side application for playing music" off \ "ncmpcpp" "An mpd client and almost exact clone of ncmpc with some new features" off \ "cmus" "A small, fast and powerful console music player for Unix-like operating systems" off \ "audacious" "A free and advanced audio player based on GTK+" off \ "nicotine+" "A graphical client for Soulseek" off \ "lollypop" "A new music playing application" off \ "rhythmbox" "Music playback and management application" off \ "deadbeef" "A GTK+ audio player for GNU/Linux" off \ "clementine" "A modern music player and library organizer" off \ "thunderbird" "Standalone mail and news reader from mozilla" off \ "geary" "A lightweight email client for the GNOME desktop" off \ "evolution" "Manage your email, contacts and schedule" off \ "mutt" "Small but very powerful text-based mail client" off \ "deluge" "A BitTorrent client written in python" off \ "transmission-gtk" "Free BitTorrent client GTK+ GUI" off \ "qbittorrent" "An advanced BitTorrent client" off \ "hexchat" "A popular and easy to use graphical IRC client" off \ "pidgin" "Multi-protocol instant messaging client" off \ "weechat" "Fast, light and extensible IRC client" off \ "irssi" "Modular text mode IRC client" off \ "libreoffice-fresh" "Full featured office suite" off \ "abiword" "Fully-featured word processor" off \ "calligra" "A set of applications for productivity" off \ "evince" "A document viewer" off \ "zathura zathura-pdf-poppler" "Minimalistic document viewer" off \ "qpdfview" "A tabbed PDF viewer" off \ "mupdf mupdf-tools" "Lightweight PDF and XPS viewer" off \ "gpicview" "Lightweight image viewer" off \ "gimp" "GNU Image Manipulation Program" off \ "inkscape" "Professional vector graphics editor" off \ "krita" "Edit and paint images" off \ "simplescreenrecorder" "A feature-rich screen recorder" off \ "obs-studio" "Free opensource streaming/recording software" off \ "openshot" "An open-source, non-linear video editor for Linux based on MLT framework" off \ "kdenlive" "A non-linear video editor for Linux using the MLT video framework" off \ "audacity" "A program that lets you manipulate digital audio waveforms" off \ "guvcview" "Capture video from camera devices" off \ "gpick" "Advanced color picker using GTK+ toolkit" off \ "gcolor2" "A simple GTK+2 color selector" off \ "plank" "An elegant, simple, and clean dock" off \ "docky" "Full fledged dock that makes opening common applications and managing windows faster and easier" off \ "cairo-dock cairo-dock-plug-ins" "Light eye-candy fully themable animated dock" off \ "qt5-styleplugins qt5ct" "GUI for managing Qt based application themes, icons, and fonts" off \ "ttf-hack" "A hand groomed and optically balanced typeface based on Bitstream Vera Mono" off \ "ttf-anonymous-pro" "A family of four fixed-width fonts designed especially with coding in mind" off \ "ttf-font-awesome" "Iconic font designed for Bootstrap" off \ "ttf-fira-code" "Monospaced font with programming ligatures" off \ "noto-fonts-cjk" "Google Noto CJK fonts (Chinese, Japanese, Korean)" off \ "noto-fonts noto-fonts-emoji" "Google Noto fonts and emoji" off)" [[ $? != 0 ]] && return 1 [[ $EXTRA_PACKAGES =~ kdenlive ]] && EXTRA_PACKAGES="$EXTRA_PACKAGES kdebase-runtime dvdauthor frei0r-plugins breeze breeze-gtk" return 0 } choose_kernel() { if ! grep -qi "hypervisor" <<< "$(dmesg)"; then local msg="\nUse the standard current Linux kernel or the LTS kernel?" if yesno "Choose Kernel" "$msg" "Current" "LTS"; then KERNEL="linux" else KERNEL="linux-lts" fi fi return 0 } mirrorlist_cmd() { MIRROR_CMD="reflector --score 100 -l 50 -f 10 --sort rate" yesno "$_MirrorTitle" "$_MirrorSetup" "Automatic Sort" "Customize Sort" && return 0 infobox "$_MirrorTitle" "\nGathering mirror countries..\n" 0 local countries countries="$(reflector --list-countries | awk 'NF > 1 {print $1 " -"}')" if [[ $countries != "" ]]; then tput civis local country country="$(dialog --cr-wrap --stdout --no-cancel --backtitle "$BT" \ --title "$_MirrorTitle" --menu "$_MirrorCountry" 22 70 10 $countries)" MIRROR_CMD="reflector --country $country --score 80 --latest 40 --fastest 10 --sort rate" fi local ref=" --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." tput cnorm MIRROR_CMD="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ --title " $_MirrorTitle " --inputbox "$_MirrorCmd\n\n$ref\n" 0 0 "$cmd")" return 0 } ###################################################################### ## Partitioning Functions ## ###################################################################### decrease_part_count() { # remove a partition from the dialog list and decrement the number partitions left local p="$1" PARTS="$(sed "s~${p} [0-9]*[G-M]~~; s~${p} [0-9]*\.[0-9]*[G-M]~~" <<< "$PARTS")" (( COUNT > 0 )) && (( COUNT-- )) return 0 } mount_partition() { local part="$1" local mount="${MNT}$2" local fs="$(lsblk -lno FSTYPE $part)" mkdir -p "$mount" if [[ ${FS_OPTS[$fs]} != "" && $part != "$BOOT_PART" ]] && select_mount_opts "$part" "$fs"; then mount -o $MNT_OPTS $part "$mount" 2>$ERR check_for_errors "mount -o $MNT_OPTS $part $mount" else mount $part "$mount" 2>$ERR check_for_errors "mount $part $mount" fi confirm_mount $part "$mount" || return 1 check_cryptlvm "$part" return 0 } unmount_partitions() { swapoff -a for i in $(mount | awk "/${MNT//\//\\/}/"' {print $3}' | sort -r); do umount -r "$i" >/dev/null 2>&1 done } confirm_mount() { local part="$1" local mount="$2" if [[ "$mount" == "$MNT" ]]; then local msg="Partition: $part\nMountpoint: / (root)" else local msg="Partition: $part\nMountpoint: ${mount#$MNT}" fi # partition failed to mount properly if ! grep -q "$mount" <<< "$(mount)"; then infobox "$_MntTitle" "$_MntFail\n$msg\n" 1 return 1 else # mount was successful infobox "$_MntTitle" "$_MntSucc\n$msg\n" 1 decrease_part_count "$part" fi return 0 } find_partitions() { local str="$1" local err='NONE' # 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 COUNT=$(wc -l <<< "$PARTS") # ensure we have enough partitions for the system and action type case $str in 'part|lvm|crypt') [[ $COUNT -eq 0 || ($SYS == 'UEFI' && $COUNT -lt 2) ]] && err="$_PartErrBody" ;; 'part|crypt') (( COUNT == 0 )) && err="$_LvmPartErrBody" ;; 'part|lvm') (( COUNT < 2 )) && err="$_LuksPartErrBody" ;; esac # if an error was found with the partition setup and there aren't enough partitions # show the appropriate error message for the given $str types were using if [[ $err != 'NONE' ]]; then msgbox "$_ErrTitle" "$err" if select_device 'root'; then create_partitions "$DEVICE" else return 1 fi fi return 0 } auto_partition() { local device="$1" # confirm or bail yesno "$_PrepParts" "$_PartBody1 $device $_PartBody2" || return 0 infobox "$_PrepParts" "\nAuto partitioning device: $device\n" 0 swapoff -a # make sure swap is disabled in case the device was used for swap local dev_info="$(parted -s $device print)" # walk the partitions on the device in reverse order and delete them for i in $(awk '/^ [1-9][0-9]?/ {print $1}' <<< "$dev_info" | sort -r); do parted -s $device rm $i 2>$ERR check_for_errors "parted -s $device rm $i" done # make sure we have the correct device table for the system type, gpt or msdos local newtable="gpt" local table="$(awk '/Table:/ {print $3}' <<< "$dev_info")" [[ $SYS == BIOS ]] && newtable="msdos" # if the current device table isn't correct run mklabel if [[ $table != "$newtable" ]]; then parted -s $device mklabel $newtable 2>$ERR check_for_errors "parted -s $device mklabel $newtable" fi # when device contains the string 'nvme' then add 'p' before the part number local nvme="" [[ $device =~ nvme ]] && nvme="p" local part_num=1 BOOT_PART="$device${nvme}$part_num" # set the boot partition label to the first partition if [[ $SYS == "BIOS" ]]; then parted -s $device mkpart primary ext4 1MiB 513MiB 2>$ERR check_for_errors "parted -s $device mkpart primary ext4 1MiB 513MiB" mkfs.ext4 -q $BOOT_PART >/dev/null 2>$ERR check_for_errors "mkfs.ext4 -q $BOOT_PART" parted -s $device set $part_num boot on 2>$ERR check_for_errors "parted -s $device set $part_num boot on" else parted -s $device mkpart ESP fat32 1MiB 513MiB 2>$ERR check_for_errors "parted -s $device mkpart ESP fat32 1MiB 513MiB" mkfs.vfat -F32 $BOOT_PART >/dev/null 2>$ERR check_for_errors "mkfs.vfat -F32 $BOOT_PART" parted -s $device set $part_num esp on 2>$ERR check_for_errors "parted -s $device set $part_num esp on" fi (( part_num++ )) # increment the partition number BOOT_DEVICE="$device" # only grub on BIOS systems uses this ROOT_PART="${device}${nvme}$part_num" # set root partition label to the second partition parted -s $device mkpart primary ext4 514MiB 100% 2>$ERR check_for_errors "parted -s $device mkpart primary ext4 514MiB 100%" mkfs.ext4 -q $ROOT_PART >/dev/null 2>$ERR check_for_errors "mkfs.ext4 -q $ROOT_PART" tput civis; sleep 0.5 echo -e "\nAuto partitioning complete.\n" > /tmp/.devlist lsblk $device -o NAME,MODEL,TYPE,FSTYPE,SIZE >> /tmp/.devlist dialog --cr-wrap --backtitle "$BT" --title " $_PrepParts " --textbox /tmp/.devlist 0 0 return 0 } create_partitions() { local device="$1" tput civis local choice choice="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PartTitle " --menu "$_PartBody" 0 0 0 "$_PartAuto" "-" \ $( ([[ $DISPLAY ]] && hash gparted >/dev/null 2>&1) && echo -n "gparted -") "cfdisk" "-"\ "parted" "-" "$_PartWipe" "-")" [[ $? != 0 || $choice == "" ]] && return 1 clear tput cnorm if [[ $choice != "$_PartWipe" && $choice != "$_PartAuto" ]]; then $choice $device elif [[ $choice == "$_PartWipe" ]]; then if yesno "$_PartWipe" "$_PartBody1 $device $_PartWipeBody2"; then wipe -Ifrev $device fi create_partitions $device else # if auto_partition fails we need to re-initialize the variables, just to be sure auto_partition $device || { initialize_variables; return 1; } fi } swapfile_size() { tput cnorm SWAP_SIZE="$(getinput "$_SelSwpSetup" "$_SelSwpSize" "$SWAP_SIZE")" [[ $? != 0 || $SWAP_SIZE == "" ]] && return 1 # size is incorrect if ! [[ ${SWAP_SIZE:0:1} =~ [1-9] && ${SWAP_SIZE: -1} =~ (M|G) ]]; then msgbox "$_SelSwpSetup Error" "\n$_SelSwpErr $SWAP_SIZE\n" SWAP_SIZE="${SYS_MEM}M" swapfile_size || return 1 else fallocate -l $SWAP_SIZE $MNT/swapfile >/dev/null 2>&1 chmod 600 $MNT/swapfile >/dev/null 2>&1 fi return 0 } enable_swap() { local swap="$1" mkswap $swap >/dev/null 2>$ERR check_for_errors "mkswap $swap" swapon $swap >/dev/null 2>$ERR check_for_error "swapon $swap" return 0 } select_swap() { # Ask user to select partition or create swapfile SWAP="$(dialog --backtitle "$BT" --cr-wrap --stdout --title " $_SelSwpSetup " \ --menu "$_SelSwpBody" 0 0 0 "$_SelSwpNone" "-" "$_SelSwpFile" "-" $PARTS)" [[ $? != 0 || $SWAP == "$_SelSwpNone" ]] && return 0 if [[ $SWAP == "$_SelSwpFile" ]]; then swapfile_size || return 1 enable_swap "$MNT/swapfile" SWAP="/swapfile" else enable_swap "$SWAP" decrease_part_count "$SWAP" fi return 0 } select_device() { local msg [[ $1 == 'boot' ]] && msg="$_DevSelTitle for bootloader\n" if (( DEV_COUNT == 1 )); then DEVICE="$(awk '{print $1}' <<< "$SYS_DEVS")" msg="\nOnly one device available$([[ $1 == 'boot' ]] && echo -n " for grub bootloader"):" infobox "$_DevSelTitle" "$msg $DEVICE\n" 1 elif (( DEV_COUNT > 1 )); then tput civis DEVICE="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_DevSelTitle " \ --menu "${msg}$_DevSelBody" 0 0 0 $SYS_DEVS)" [[ $? != 0 || $DEVICE == "" ]] && return 1 else msg="\nNo available devices for installation to use$([[ $1 == 'boot' ]] && echo -n " for grub bootloader")." msgbox "$_ErrTitle" "$msg\n$_Exit" die 1 fi # if the device selected was for grub bootloader, set the BOOT_DEVICE # this is needed because grub uses the base device for BIOS, not the partition [[ $1 == 'boot' ]] && BOOT_DEVICE="$DEVICE" return 0 } select_mount_opts() { local part="$1" local fs="$2" local title="${fs^} Mount Options" tput civis MNT_OPTS="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $title " \ --checklist "$_MntBody" 0 0 0 ${FS_OPTS[$fs]} | sed 's/ /,/g; $s/,$//')" [[ $? != 0 || $MNT_OPTS == "" ]] && return 1 if ! yesno "$title" "$_MntConfBody $MNT_OPTS\n"; then select_mount_opts "$part" "$fs" || return 1 fi return 0 } select_filesystem() { local part="$1" local cur_fs cur_fs="$(lsblk -lno FSTYPE $part)" tput civis local choice choice="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_FSTitle: $part" \ --menu "\nPartition: ${part}$([[ $cur_fs != "" ]] && echo -n "\nCurrent: ${cur_fs}")\n$_FSBody" 0 0 0 \ $([[ $cur_fs != "" ]] && echo -n "$_Skip -") \ "ext4" "${FS_CMDS[ext4]}" "ext3" "${FS_CMDS[ext3]}" \ "ext2" "${FS_CMDS[ext2]}" "vfat" "${FS_CMDS[vfat]}" \ "ntfs" "${FS_CMDS[ntfs]}" "f2fs" "${FS_CMDS[f2fs]}" \ "jfs" "${FS_CMDS[jfs]}" "nilfs2" "${FS_CMDS[nilfs2]}" \ "reiserfs" "${FS_CMDS[reiserfs]}" "xfs" "${FS_CMDS[xfs]}")" [[ $choice == "$_Skip" ]] && return 0 [[ $choice == "" ]] && return 1 if yesno "$_FSTitle" "\nFormat $part as $choice?\n"; then infobox "$_FSTitle" "\nFormatting: $part\n\nCommand: ${FS_CMDS[$choice]}\n" 0 ${FS_CMDS[$choice]} $part >/dev/null 2>$ERR check_for_errors "${FS_CMDS[$choice]} $part" else select_filesystem "$part" || return 1 fi return 0 } select_boot_setup() { # choose bootloader and by extension mountpoint (if needed) tput civis BOOTLOADER="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PrepMount " --menu "$_MntBootBody" 0 0 0 ${BOOTLOADERS[$SYS]})" [[ $? != 0 || $BOOTLOADER == "" ]] && return 1 if [[ $SYS == 'BIOS' && $BOOTLOADER == 'grub' && $BOOT_DEVICE == "" ]]; then # grub on BIOS needs an install device, NOT partition eg. /dev/sda select_device 'boot' || return 1 fi if [[ $BOOTLOADER == 'systemd-boot' ]]; then EDIT_FILES[9]="/boot/loader/entries/$DIST.conf" elif [[ $BOOTLOADER == 'syslinux' ]]; then if [[ $SYS == 'BIOS' ]]; then BOOT_CMDS[$BOOTLOADER]="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_InstSysTitle " --menu "$_InstSysBody" 0 0 0 \ "syslinux-install_update -iam" "Install to MBR (Master Boot Record)" \ "syslinux-install_update -i" "Install to root partition (/)")" [[ $? != 0 || ${BOOT_CMDS[$BOOTLOADER]} == "" ]] && return 1 fi else EDIT_FILES[9]="/etc/default/grub" fi return 0 } setup_boot_device() { # set BOOT_DEVICE for syslinux on UEFI and grub on BIOS if [[ $BOOT_PART =~ nvme ]]; then BOOT_DEVICE="${BOOT_PART%p[1-9]}" else BOOT_DEVICE="${BOOT_PART%[1-9]}" fi BOOT_PART_NUM="${BOOT_PART: -1}" # setup the needed partition flags for boot on both system types parted -s $BOOT_DEVICE set $BOOT_PART_NUM boot on 2>$ERR check_for_errors "parted -s $BOOT_DEVICE set $BOOT_PART_NUM boot on" if [[ $SYS == 'UEFI' ]]; then parted -s $BOOT_DEVICE set $BOOT_PART_NUM esp on 2>$ERR check_for_errors "parted -s $BOOT_DEVICE set $BOOT_PART_NUM boot on" fi return 0 } select_efi_partition() { format_efi_as_vfat() { infobox "$_FSTitle" "\nFormatting $1 as vfat/fat32.\n" 0 mkfs.vfat -F32 "$1" >/dev/null 2>$ERR check_for_errors "mkfs.vfat -F32 $1" } tput civis if (( COUNT == 1 )); then BOOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_PrepMount" "$_OnlyOne for EFI: $BOOT_PART\n" 1 else BOOT_PART="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PrepMount " --menu "$_SelUefiBody" 0 0 0 $PARTS)" [[ $? != 0 || $BOOT_PART == "" ]] && return 1 fi if grep -q 'fat' <<< "$(fsck -N "$BOOT_PART")"; then local msg="$_FormUefiBody $BOOT_PART $_FormUefiBody2" yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Do Not Format" "no" && format_efi_as_vfat "$BOOT_PART" else format_efi_as_vfat "$BOOT_PART" fi return 0 } select_boot_partition() { format_as_ext4() { infobox "$_FSTitle" "\nFormatting $1 as ext4.\n" 0 mkfs.ext4 -q "$1" >/dev/null 2>$ERR check_for_errors "mkfs.ext4 -q $1" } tput civis BOOT_PART="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title "$_PrepMount" --menu "$_SelBiosBody" 0 0 0 "$_Skip" "-" $PARTS)" if [[ $BOOT_PART == "$_Skip" || $BOOT_PART == "" ]]; then BOOT_PART="" else if grep -q 'ext[34]' <<< "$(fsck -N "$BOOT_PART")"; then local msg="$_FormBiosBody $BOOT_PART $_FormUefiBody2" if yesno "$_PrepMount" "$msg" "Format $BOOT_PART" "Skip Formatting" "no"; then format_as_ext4 "$BOOT_PART" fi else format_as_ext4 "$BOOT_PART" fi fi return 0 } select_root_partition() { # if we used LUKS and no LVM or LUKS+LVM # remove the relevant partition labels from the list if (( LUKS == 1 && LVM == 0 )); then ROOT_PART="/dev/mapper/$LUKS_NAME" decrease_part_count "$LUKS_PART" elif (( LUKS == 1 && LVM == 1 )); then decrease_part_count "$LUKS_PART" for part in $(echo "$GROUP_PARTS"); do decrease_part_count "$part" done ROOT_PART="" elif (( LUKS == 0 && LVM == 1 )); then for part in $(echo "$GROUP_PARTS"); do decrease_part_count "$part" done ROOT_PART="" fi if [[ $COUNT -eq 1 && $ROOT_PART == "" ]]; then ROOT_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_PrepMount" "$_OnlyOne for root (/): $ROOT_PART\n" 1 elif [[ $ROOT_PART == "" || $LVM -eq 1 ]]; then tput civis ROOT_PART="$(dialog --cr-wrap --stdout --backtitle "$BT" --title "$_PrepMount" \ --menu "$_SelRootBody" 0 0 0 $PARTS)" [[ $? != 0 || $ROOT_PART == "" ]] && return 1 else local msg="\nUsing $([[ $LUKS -eq 1 ]] && echo -n "encrypted ")root partition:" infobox "$_PrepMount" "$msg $ROOT_PART\n" 1 fi select_filesystem "$ROOT_PART" || { ROOT_PART=""; return 1; } mount_partition "$ROOT_PART" || { ROOT_PART=""; return 1; } return 0 } select_mountpoint() { tput cnorm EXTRA_MNT="$(getinput "$_PrepMount $part" "$_ExtPartBody1 /home /var\n" "/")" [[ $? != 0 || $EXTRA_MNT == "" ]] && return 1 # bad mountpoint if [[ ${EXTRA_MNT:0:1} != "/" || ${#EXTRA_MNT} -le 1 || $EXTRA_MNT =~ \ |\' ]]; then msgbox "$_ErrTitle" "$_ExtErrBody" select_mountpoint || return 1 fi return 0 } select_extra_partitions() { while (( COUNT > 0 )); do tput civis local part part="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_PrepMount " \ --menu "$_ExtPartBody" 0 0 0 "$_Done" "-" $PARTS)" [[ $? != 0 || $part == "$_Done" || $part == "" ]] && break # choose what filesystem and get mountpoint select_filesystem "$part" || { break; return 1; } select_mountpoint || { break; return 1; } # mount it mount_partition "$part" "$EXTRA_MNT" || { break; return 1; } # if the mountpoint was /usr add 'usr' to MKINIT_HOOKS [[ $EXTRA_MNT == "/usr" && $MKINIT_HOOKS != *usr* ]] && MKINIT_HOOKS="usr $MKINIT_HOOKS" done return 0 } select_partitions() { msgbox "$_PrepMount" "$_WarnMount" lvm_detect # prepare partition list PARTS for dialog unmount_partitions find_partitions 'part|lvm|crypt' || return 1 # remove boot partition from dialog list if we auto partitioned one [[ $BOOT_PART != "" ]] && decrease_part_count "$BOOT_PART" select_root_partition || return 1 if [[ $BOOT_PART == "" ]]; then if [[ $SYS == "UEFI" ]]; then select_efi_partition || { BOOT_PART=""; return 1; } elif (( $COUNT > 0 )); then select_boot_partition || { BOOT_PART=""; return 1; } fi else infobox "$_PrepMount" "\nUsing boot partition: $BOOT_PART\n" 1 fi select_boot_setup || { BOOTLOADER=""; return 1; } if [[ $BOOT_PART != "" ]]; then setup_boot_device mount_partition "$BOOT_PART" "${BOOT_MNTS[$SYS-$BOOTLOADER]}" || return 1 SEPERATE_BOOT=1 fi select_swap || return 1 select_extra_partitions || return 1 return 0 } check_cryptlvm() { local part="$1" local devs="$(lsblk -lno NAME,FSTYPE,TYPE)" # Identify if $part is "crypt" (LUKS on LVM, or LUKS alone) if [[ $(lsblk -lno TYPE "$part") =~ 'crypt' ]]; then LUKS=1 LUKS_NAME="${part#/dev/mapper/}" for dev in $(awk '/lvm/ && /crypto_LUKS/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_DEV="$LUKS_DEV cryptdevice=$dev:$LUKS_NAME" LVM=1 break fi done for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; 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") =~ 'lvm' ]]; then LVM=1 VOLUME_NAME="${part#/dev/mapper/}" for dev in $(awk '/crypt/ && /lvm2_member/ {print "/dev/mapper/"$1}' <<< "$devs" | uniq); do if grep -q "$VOLUME_NAME" <<< "$(lsblk -lno NAME "$dev")"; then LUKS_NAME="$(sed 's~/dev/mapper/~~g' <<< "$dev")" break fi done for dev in $(awk '/part/ && /crypto_LUKS/ {print "/dev/"$1}' <<< "$devs" | uniq); do if grep -q "$LUKS_NAME" <<< "$(lsblk -lno NAME "$dev")"; 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=1 break fi done fi } ###################################################################### ## LUKS Functions ## ###################################################################### luks_open() { LUKS_PART="" modprobe -a dm-mod dm_crypt unmount_partitions find_partitions 'part|crypt|lvm' tput civis if (( COUNT == 1 )); then LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_LuksOpen" "${_OnlyOne}: $LUKS_PART\n" 1 else tput civis LUKS_PART="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_LuksOpen " \ --menu "$_LuksMenuBody" 0 0 0 $PARTS)" [[ $? != 0 || $LUKS_PART == "" ]] && return 1 fi # get password and name for encryption luks_pass "$_LuksOpen" "$LUKS_NAME" || return 1 infobox "$_LuksOpen" "$_LuksWaitBody $LUKS_NAME $_LuksWaitBody2 $LUKS_PART\n" 0 echo "$LUKS_PASS" | cryptsetup open --type luks "$LUKS_PART" "$LUKS_NAME" 2>$ERR check_for_errors "cryptsetup open --type luks $LUKS_PART $LUKS_NAME" LUKS=1 luks_show return 0 } luks_pass() { local title="$1" local name="$2" LUKS_PASS="" LUKS_NAME="" tput cnorm local values values="$(dialog --stdout --separator '~' --ok-label "Submit" --backtitle "$BT" \ --title " $title " --insecure --mixedform "$_LuksOpenBody" 16 75 4 \ "$_Name" 1 1 "$name" 1 $((${#_Name} + 2)) 71 0 0 \ "$_Password" 2 1 "" 2 $((${#_Password} + 2)) 71 0 1 \ "$_Password2" 3 1 "" 3 $((${#_Password2} + 2)) 71 0 1)" [[ $? != 0 || $values == "" ]] && return 1 name="$(awk -F'~' '{print $1}' <<< "$values")" local pass pass2 pass="$(awk -F'~' '{print $2}' <<< "$values")" pass2="$(awk -F'~' '{print $3}' <<< "$values")" if [[ $pass == "" || "$pass" != "$pass2" ]]; then msgbox "$_ErrTitle" "$_PassErr\n$_TryAgain" luks_pass "$title" "$name" || return 1 fi LUKS_PASS="$pass" LUKS_NAME="$name" return 0 } luks_setup() { LUKS_PART="" modprobe -a dm-mod dm_crypt unmount_partitions if [[ $ROOT_PART == "" || $LVM -eq 1 ]]; then find_partitions 'part|lvm' [[ $BOOT_PART != "" ]] && decrease_part_count "$BOOT_PART" if (( COUNT == 1 )); then LUKS_PART="$(awk 'NF > 0 {print $1}' <<< "$PARTS")" infobox "$_LuksEncrypt" "${_OnlyOne}: $LUKS_PART\n" 1 else tput civis LUKS_PART="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title "$_LuksEncrypt" --menu "$_LuksEncryptBody" 0 0 0 $PARTS)" [[ $? != 0 || $LUKS_PART == "" ]] && return 1 fi else infobox "$_PrepMount" "\nUsing root partition created earlier: $ROOT_PART\n" 1 LUKS_PART="$ROOT_PART" fi # get password and name for encrypted device luks_pass "$_LuksEncrypt" "$LUKS_NAME" || return 1 return 0 } luks_default() { luks_setup || return 1 infobox "$_LuksEncrypt" "$_LuksWaitBody $LUKS_NAME $_LuksWaitBody2 $LUKS_PART\n" 0 echo "$LUKS_PASS" | cryptsetup -q luksFormat "$LUKS_PART" 2>$ERR check_for_errors "cryptsetup -q luksFormat $LUKS_PART" echo "$LUKS_PASS" | cryptsetup open "$LUKS_PART" "$LUKS_NAME" 2>$ERR check_for_errors "cryptsetup open $LUKS_PART $LUKS_NAME" LUKS=1 luks_show return 0 } luks_keycmd() { if luks_setup; then tput cnorm local cipher cipher="$(getinput "$_PrepLUKS" "$_LuksCipherKey" "-s 512 -c aes-xts-plain64")" [[ $? != 0 || $cipher == "" ]] && return 1 infobox "$_LuksEncryptAdv" "$_LuksWaitBody $LUKS_NAME $_LuksWaitBody2 $LUKS_PART\n" 0 echo "$LUKS_PASS" | cryptsetup -q $cipher luksFormat $LUKS_PART 2>$ERR check_for_errors "cryptsetup -q $cipher luksFormat $LUKS_PART" echo "$LUKS_PASS" | cryptsetup open $LUKS_PART "$LUKS_NAME" 2>$ERR check_for_errors "cryptsetup open $LUKS_PART $LUKS_NAME" luks_show return 0 fi return 1 } luks_show() { tput civis sleep 0.5 echo -e "$_LuksEncryptSucc" > /tmp/.devlist lsblk -o NAME,TYPE,FSTYPE,SIZE $LUKS_PART >> /tmp/.devlist dialog --cr-wrap --backtitle "$BT" --title " $_LuksEncrypt " --textbox /tmp/.devlist 0 0 } luks_menu() { tput civis local choice choice="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_PrepLUKS " \ --menu "${_LuksMenuBody}${_LuksMenuBody2}${_LuksMenuBody3}" 0 0 0 \ "$_LuksEncrypt" "cryptsetup -q luksFormat" \ "$_LuksOpen" "cryptsetup open --type luks" \ "$_LuksEncryptAdv" "cryptsetup -q -s -c luksFormat" \ "$_Back" "-")" case $choice in "$_LuksEncrypt") luks_default && return 0 ;; "$_LuksOpen") luks_open && return 0 ;; "$_LuksEncryptAdv") luks_keycmd && return 0 ;; *) return 0 esac luks_menu } luks_keyfile() { # Only used when choosing grub as bootloader. # Without a keyfile, during boot the user will be asked # to enter password for decryption twice, this is annoying if [[ ! -e $MNT/crypto_keyfile.bin ]]; then infobox "$_LuksKeyFileTitle" "$_LuksKeyFileCreate" 0 local dev dev="/dev/$(lsblk -lno NAME,UUID,TYPE | awk "/$LUKS_UUID/"' && /part|crypt|lvm/ {print $1}')" local keycmd keycmd="dd bs=512 count=8 if=/dev/urandom of=/crypto_keyfile.bin && chmod 000 /crypto_keyfile.bin" keycmd="$keycmd && echo '$LUKS_PASS' | cryptsetup luksAddKey $dev /crypto_keyfile.bin" chroot_cmd "$keycmd" 2>$ERR check_for_errors "$keycmd" sed -i 's/FILES=()/FILES=(\/crypto_keyfile.bin)/g' $MNT/etc/mkinitcpio.conf 2>$ERR check_for_errors 'sed -i "s/FILES=()/FILES=(/crypto_keyfile.bin)/g"' fi return 0 } ###################################################################### ## LVM Functions ## ###################################################################### lvm_detect() { PHYSICAL_VOLUMES="$(pvs -o pv_name --noheading 2>/dev/null)" VOLUME_GROUP="$(vgs -o vg_name --noheading 2>/dev/null)" VOLUMES="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)" if [[ $VOLUMES && $VOLUME_GROUP && $PHYSICAL_VOLUMES ]]; then infobox "$_PrepLVM" "$_LvmDetBody" 0 modprobe dm-mod 2>$ERR check_for_errors 'modprobe dm-mod' vgscan >/dev/null 2>&1 vgchange -ay >/dev/null 2>&1 fi } lvm_show_vg() { DEL_VG="" VOL_GROUP_LIST="" for i in $(lvs --noheadings | awk '{print $2}' | uniq); do VOL_GROUP_LIST="$VOL_GROUP_LIST $i $(vgdisplay "$i" | awk '/VG Size/ {print $3$4}')" done if [[ $VOL_GROUP_LIST == "" ]]; then msgbox "$_ErrTitle" "$_LvmVGErr" return 1 fi tput civis DEL_VG="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title " $_PrepLVM " --menu "$_LvmSelVGBody" 18 70 10 $VOL_GROUP_LIST)" [[ $? != 0 || $DEL_VG == "" ]] && return 1 } get_lv_size() { tput cnorm local ttl=" $_LvmCreateVG (LV:$VOL_COUNT) " local msg="${VOLUME_GROUP}: ${GROUP_SIZE}$GROUP_SIZE_TYPE (${VOL_GROUP_MB}MB $_LvmLvSizeBody1).$_LvmLvSizeBody2" VOLUME_SIZE="$(getinput "$ttl" "$msg" "")" [[ $? != 0 || $VOLUME_SIZE == "" ]] && return 1 ERR_SIZE=0 # if the size is empty or 0 (( ${#VOLUME_SIZE} == 0 || ${VOLUME_SIZE:0:1} == 0 )) && ERR_SIZE=1 if (( ERR_SIZE == 0 )); then # number of characters in VOLUME_SIZE minus the last, which should be a letter local lv="$((${#VOLUME_SIZE} - 1))" # loop each character (except the last) in VOLUME_SIZE and ensure they are numbers for (( i=0; i= VOL_GROUP_MB )) && ERR_SIZE=1 || VOL_GROUP_MB=$((VOL_GROUP_MB - m)) ;; [Mm]) (( ${VOLUME_SIZE:0:$lv} >= VOL_GROUP_MB )) && ERR_SIZE=1 || VOL_GROUP_MB=$((VOL_GROUP_MB - s)) ;; *) ERR_SIZE=1 esac fi fi fi if (( ERR_SIZE == 1 )); then msgbox "$_ErrTitle" "$_LvmLvSizeErrBody" get_lv_size || return 1 fi return 0 } lvm_volume_name() { local msg="$1" local default="volmain" (( VOL_COUNT > 1 )) && default="volextra" tput cnorm local name name="$(getinput "$_LvmCreateVG (LV:$VOL_COUNT)" "$msg" "$default")" [[ $? != 0 || $name == "" ]] && return 1 # bad volume name answer or name already in use if [[ ${name:0:1} == "/" || ${#name} -eq 0 || $name =~ \ |\' ]] || grep -q "$name" <<< "$(lsblk)"; then msgbox "$_ErrTitle" "$_LvmLvNameErrBody" lvm_volume_name "$msg" || return 1 fi VOLUME_NAME="$name" return 0 } lvm_group_name() { tput cnorm local group group="$(getinput "$_LvmCreateVG" "$_LvmNameVgBody" "VolGroup")" [[ $? != 0 || $group == "" ]] && return 1 # bad answer or group name already taken if [[ ${group:0:1} == "/" || ${#group} -eq 0 || $group =~ \ |\' ]] || grep -q "$group" <<< "$(lsblk)"; then msgbox "$_ErrTitle" "$_LvmNameVgErr" lvm_group_name || return 1 fi VOLUME_GROUP="$group" return 0 } lvm_extra_lvs() { while (( VOL_COUNT > 1 )); do # get the name and size lvm_volume_name "$_LvmLvNameBody1" || { break; return 1; } get_lv_size || { break; return 1; } # create it lvcreate -L "$VOLUME_SIZE" "$VOLUME_GROUP" -n "$VOLUME_NAME" 2>$ERR check_for_errors "lvcreate -L $VOLUME_SIZE $VOLUME_GROUP -n $VOLUME_NAME" msgbox "$_LvmCreateVG (LV:$VOL_COUNT)" "$_Done LV $VOLUME_NAME ($VOLUME_SIZE) $_LvmPvDoneBody2." ((VOL_COUNT--)) # decrement the number of volumes chosen after each loop done return 0 } lvm_volume_count() { VOL_COUNT=$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_LvmCreateVG " \ --radiolist "$_LvmLvNumBody1 $VOLUME_GROUP\n$_LvmLvNumBody2" 0 0 0 \ "1" "-" off "2" "-" off "3" "-" off "4" "-" off "5" "-" off \ "6" "-" off "7" "-" off "8" "-" off "9" "-" off) [[ $? != 0 || $VOL_COUNT == "" ]] && return 1 return 0 } lvm_partitions() { find_partitions 'part|crypt' PARTS="$(awk 'NF > 0 {print $0 " off"}' <<< "$PARTS")" # choose partitions tput civis GROUP_PARTS="$(dialog --cr-wrap --stdout --backtitle "$BT" \ --title "$_LvmCreateVG" --checklist "$_LvmPvSelBody" 0 0 0 $PARTS)" [[ $? != 0 || $GROUP_PARTS == "" ]] && return 1 return 0 } lvm_create_group() { # get volume group name lvm_group_name || return 1 # loop while setup is not confirmed by the user while ! yesno "$_LvmCreateVG" "$_LvmPvConfBody1 $VOLUME_GROUP\n\n$_LvmPvConfBody2 $GROUP_PARTS\n"; do lvm_partitions || { break; return 1; } lvm_group_name || { break; return 1; } done # create it infobox "$_LvmCreateVG" "$_LvmPvActBody1 $VOLUME_GROUP\n" 0 vgcreate -f "$VOLUME_GROUP" "$GROUP_PARTS" >/dev/null 2>$ERR check_for_errors "vgcreate -f $VOLUME_GROUP $GROUP_PARTS" # get volume size size and transform size to MB if size is given in GB GROUP_SIZE=$(vgdisplay "$VOLUME_GROUP" | awk '/VG Size/ {print int($3)}') GROUP_SIZE_TYPE="$(vgdisplay "$VOLUME_GROUP" | awk '/VG Size/ {print substr($NF, 0, 1)}')" [[ $GROUP_SIZE_TYPE == 'G' ]] && VOL_GROUP_MB=$((GROUP_SIZE * 1000)) || VOL_GROUP_MB=$GROUP_SIZE # finished volume group creation local msg="$_LvmPvDoneBody1 $VOLUME_GROUP ($GROUP_SIZE $GROUP_SIZE_TYPE)" msgbox "$_LvmCreateVG" "$msg $_LvmPvDoneBody2\n" return 0 } lvm_create() { VOLUME_GROUP="" GROUP_PARTS="" VOL_GROUP_MB=0 unmount_partitions if [[ $LUKS -eq 1 && $LUKS_NAME != "" ]]; then GROUP_PARTS="/dev/mapper/$LUKS_NAME" infobox "$_LvmCreateVG" "\nUsing encrypted partition created earlier: $GROUP_PARTS\n" 1 else lvm_partitions || return 1 fi lvm_create_group || return 1 # create the volume group we'll be using lvm_volume_count || return 1 # how many logical volumes to create on the group lvm_extra_lvs || return 1 # if we chose more than one logical volume create all but the last # last or only logical volume lvm_volume_name "$_LvmLvNameBody1 $_LvmLvNameBody2 (${VOL_GROUP_MB}MB)" || return 1 lvcreate -l +100%FREE "$VOLUME_GROUP" -n "$VOLUME_NAME" 2>$ERR check_for_errors "lvcreate -l +100%FREE $VOLUME_GROUP -n $VOLUME_NAME" LVM=1 show_devices return 0 } lvm_del_vg() { if lvm_show_vg; then yesno "$_LvmDelVG" "$_LvmDelQ" && vgremove -f "$DEL_VG" >/dev/null 2>&1 fi return 0 } lvm_del_all() { PHYSICAL_VOLUMES="$(pvs -o pv_name --noheading 2>/dev/null)" VOLUME_GROUP="$(vgs -o vg_name --noheading 2>/dev/null)" VOLUMES="$(lvs -o vg_name,lv_name --noheading --separator - 2>/dev/null)" if yesno "$_LvMDelAll" "$_LvmDelQ"; then for i in $VOLUMES; do lvremove -f "/dev/mapper/$i" >/dev/null 2>&1 done for i in $VOLUME_GROUP; do vgremove -f "$i" >/dev/null 2>&1 done for i in $PHYSICAL_VOLUMES; do pvremove -f "$i" >/dev/null 2>&1 done LVM=0 fi return 0 } lvm_menu() { lvm_detect tput civis local choice choice="$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_PrepLVM " \ --menu "$_LvmMenu" 0 0 0 \ "$_LvmCreateVG" "vgcreate -f, lvcreate -L -n" \ "$_LvmDelVG" "vgremove -f" \ "$_LvMDelAll" "lvrmeove, vgremove, pvremove -f" \ "$_Back" "-")" case $choice in "$_LvmCreateVG") lvm_create && return 0 ;; "$_LvmDelVG") lvm_del_vg ;; "$_LvMDelAll") lvm_del_all ;; *) return 0 esac lvm_menu } ###################################################################### ## Install Functions ## ###################################################################### config_install() { # whether to use a custom mirror sorting command later oneshot set_hostname || return 1 oneshot set_locale || return 1 oneshot set_timezone || return 1 oneshot user_setup || return 1 oneshot mirrorlist_cmd || return 1 oneshot window_manager || return 1 oneshot extra_packages || return 1 oneshot choose_kernel CONFIG_DONE=true return 0 } install_main() { # this assumes all needed variables/settings are setup as needed # no additional user confirmation are performed aside from what is needed # unpack the whole filesystem to install directory $MNT oneshot install_base genfstab -U $MNT > $MNT/etc/fstab 2>$ERR check_for_errors "genfstab -U $MNT > $MNT/etc/fstab" if [[ -f $MNT/swapfile ]]; then sed -i "s~${MNT}~~" $MNT/etc/fstab 2>$ERR check_for_errors "sed -i s~${MNT}~~ $MNT/etc/fstab" fi # run package operations before bootloader and mkinitcpio # due to the possibility of choosing lts kernel earlier oneshot update_mirrorlist oneshot update_system oneshot install_packages [[ $LOGIN_TYPE == 'lightdm' ]] && oneshot setup_lightdm run_mkinitcpio || return 1 install_bootloader || return 1 oneshot create_user || return 1 oneshot set_hwclock oneshot edit_configs return 0 } install_base() { # unpack the main system tput cnorm rsync -avh /run/archiso/sfs/airootfs/ $MNT/ # remove archiso init files find $MNT/usr/lib/initcpio -name 'archiso*' -type f -exec rm '{}' \; rm -rf $MNT/etc/{mkinitcpio-archiso.conf,sudoers.d/g_wheel,polkit-1/rules.d/49-nopasswd_global.rules} # cleanup system permissions sed -i 's/volatile/auto/g' $MNT/etc/systemd/journald.conf sed -i "s/# %wheel ALL=(ALL) ALL/%wheel ALL=(ALL) ALL/g" $MNT/etc/sudoers # for virtual machines remove configs for xorg, these cause mouse issues grep -qi "hypervisor" <<< "$(dmesg)" && rm -rf $MNT/etc/X11/xorg.conf.d # if not installing the lts kernel, copy the kernel image [[ $KERNEL != 'linux-lts' ]] && cp -f /run/archiso/bootmnt/arch/boot/x86_64/vmlinuz $MNT/boot/vmlinuz-linux setup_configs return 0 } setup_configs() { # copy network settings cp -rf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/ cp -f /etc/resolv.conf $MNT/etc/ # set the locale and timezone sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${LOCALE}/${LOCALE}/g" $MNT/etc/locale.gen sed -i "s/en_US.UTF-8/${LOCALE}/g" $MNT/etc/locale.conf cp -f $MNT/etc/locale.conf $MNT/etc/default/locale chroot_cmd "locale-gen" 2>$ERR check_for_errors 'locale-gen' chroot_cmd "ln -sf /usr/share/zoneinfo/$ZONE/$SUBZONE /etc/localtime" 2>$ERR check_for_errors "ln -sf /usr/share/zoneinfo/$ZONE/$SUBZONE /etc/localtime" # setup xorg vsync config for intel graphics if [[ $(lspci | grep ' VGA ' | grep 'Intel') != "" ]]; then cat > $MNT/etc/X11/xorg.conf.d/20-intel.conf < $MNT/etc/X11/xorg.conf.d/00-keyboard.conf < $MNT/etc/default/keyboard < $MNT/etc/vconsole.conf < $MNT/etc/hostname cat > $MNT/etc/hosts << EOF 127.0.0.1 localhost 127.0.1.1 $HOSTNAME ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters EOF return 0 } update_system() { tput cnorm local pkgcmd if [[ $KERNEL == 'linux-lts' ]]; then pkgcmd="pacman -Rs linux --noconfirm ; pacman -S iputils --noconfirm ; pacman -S base-devel git linux-lts linux-lts-headers --needed --noconfirm" else pkgcmd="pacman -S iputils --noconfirm ; pacman -S base-devel git --needed --noconfirm" fi pkgcmd="pacman -Rs archlabs-installer --noconfirm ; $pkgcmd" if ! grep -qi "hypervisor" <<< "$(dmesg)"; then pkgcmd="pacman -Rs virtualbox-guest-utils virtualbox-guest-modules-arch --noconfirm ; $pkgcmd" fi if [[ $BOOTLOADER != 'grub' ]]; then pkgcmd="$pkgcmd ; pacman -Rs grub --noconfirm" rm -f $MNT/etc/default/grub find $MNT/boot/ -name 'grub*' -exec rm -rf '{}' \; >/dev/null 2>&1 fi chroot_cmd "pacman -Syyu --noconfirm ; $pkgcmd" 2>/dev/null return 0 } install_packages() { tput civis # packages needed for the selected window manager local pkgs="$WM_PACKAGES" # add lightdm packages if chosen [[ $LOGIN_TYPE == 'lightdm' ]] && pkgs="$pkgs lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice" # add extra packages [[ $EXTRA_PACKAGES != "" ]] && pkgs="$pkgs $EXTRA_PACKAGES" # for gnome and cinnamon we dont need the xfce provided stuff if [[ $INSTALL_WMS == 'gnome' || $INSTALL_WMS == 'cinnamon' ]]; then REMOVE_PKGS="$(pacman -Qssq 'xfce4*' 2>/dev/null)" fi if [[ $INSTALL_WMS =~ dwm && -d $MNT/home/$NEWUSER ]]; then mkdir -p $MNT/home/$NEWUSER/suckless for prog in dwm st dmenu; do echo -e "\nInstalling $prog..\n" git clone https://bitbucket.org/natemaia/$prog $MNT/home/$NEWUSER/suckless/$prog chroot_cmd "cd /home/$NEWUSER/suckless/$prog && make clean install && make clean" echo -e "\n$prog has been installed..\n\nSee /home/$NEWUSER/suckless/$prog to customize and rebuild\n" done fi local pkgcmd="pacman -S $pkgs --needed --noconfirm" # are we removing some packages [[ $REMOVE_PKGS != "" ]] && pkgcmd="pacman -Rs $REMOVE_PKGS --noconfirm ; $pkgcmd" chroot_cmd "$pkgcmd" 2>/dev/null return 0 } setup_lightdm() { # due to the user's information not being entered yet at this point, if they chose # autologin then there is one value in the lightdm config that must be changed later # # autologin-user=$USER chroot_cmd 'systemctl enable lightdm.service && systemctl set-default graphical.target' >/dev/null 2>&1 local cfg="$MNT/etc/lightdm/lightdm-gtk-greeter.conf" sed -i '/#background=/ c background=/usr/share/backgrounds/archlabs/archlabs.jpg' $cfg sed -i '/#theme-name=/ c theme-name=ArchLabs-dARK' $cfg sed -i '/#icon-theme-name=/ c icon-theme-name=ArchLabs-Light' $cfg sed -i '/#position=/ c position=34%,end 66%,end' $cfg sed -i '/#font-name=/ c font-name=DejaVu Sans Mono 11' $cfg sed -i '/\[greeter]/ a default-user-image=/usr/share/icons/ArchLabs-Dark/64x64/places/distributor-logo-archlabs.png' $cfg sed -i '/\[greeter]/ a active-monitor=0' $cfg rm -rf $MNT/etc/systemd/system/getty@tty1.service.d >/dev/null 2>&1 if [[ $AUTOLOGIN == true ]]; then chroot_cmd 'groupadd -r nopasswdlogin' >/dev/null 2>&1 sed -i '/#%PAM-1.0/ a auth sufficient pam_succeed_if.so user ingroup nopasswdlogin' $MNT/etc/pam.d/lightdm sed -i "/#autologin-session=/ c autologin-session=${LOGIN_WM}" $MNT/etc/lightdm/lightdm.conf sed -i "/#autologin-user=/ c autologin-user=${NEWUSER}" $MNT/etc/lightdm/lightdm.conf fi } create_user() { infobox "$_ConfRoot" "\nSetting root password\n" 1 chroot_cmd "echo 'root:$ROOT_PASS' | chpasswd" 2>$ERR check_for_errors "chpasswd root" infobox "$_ConfUser" "$_UserSetBody" 1 swap_livuser chroot_cmd "echo '$NEWUSER:$USER_PASS' | chpasswd" 2>$ERR check_for_errors "chpasswd $NEWUSER" chroot_cmd "chown -Rf $NEWUSER:users /home/$NEWUSER" 2>$ERR check_for_errors "chown -Rf $NEWUSER:users /home/$NEWUSER" setup_user_home return 0 } swap_livuser() { # edit the required files in /etc/ to swap the liveuser account name sed -i "s/${LIVE}/${NEWUSER}/g" $MNT/etc/{group,gshadow,passwd,shadow} # set standard groups for the new user local groups="rfkill,wheel,network,lp,storage,power,video,audio,lp,autologin" if [[ $AUTOLOGIN == true && $LOGIN_TYPE == 'lightdm' ]]; then # add the nopasswdlogin group for lightdm autologin groups="$groups,nopasswdlogin" elif [[ $AUTOLOGIN == true ]]; then # setup autologin on tty1 with systemd + xinit sed -i "s/${LIVE}/${NEWUSER}/g" $MNT/etc/systemd/system/getty@tty1.service.d/autologin.conf fi chroot_cmd "mv -f /home/$LIVE /home/$NEWUSER" 2>$ERR check_for_errors "mv -f /home/$LIVE /home/$NEWUSER" chroot_cmd "usermod -aG $groups $NEWUSER" 2>$ERR check_for_errors "usermod -aG $groups $NEWUSER" return 0 } setup_user_home() { local user_home="$MNT/home/$NEWUSER" sed -i "s/${LIVE}/${NEWUSER}/g" $user_home/.config/gtk-3.0/bookmarks \ $user_home/.mozilla/firefox/{archlabs.default/prefs.js,archlabs.default/sessionstore.js} rm -rf $user_home/.config/awesome if [[ $AUTOLOGIN == true ]]; then if [[ $LOGIN_TYPE == 'lightdm' ]]; then rm -rf $user_home/.{zprofile,xinitrc} else sed -i "s/:-openbox/:-${LOGIN_WM}/g" $user_home/.xinitrc sed -i '/archlabs-installer/d' $user_home/.zprofile echo '[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && exec startx -- vt1 &>/dev/null' >> $user_home/.zprofile fi else sed -i '/archlabs-installer/d' $user_home/.zprofile echo '[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && exec startx -- vt1 &>/dev/null' >> $user_home/.zprofile sed -i "s/:-openbox/:-${LOGIN_WM}/g" $user_home/.xinitrc fi if ! [[ $INSTALL_WMS =~ openbox ]]; then rm -rf $user_home/.config/{openbox,ob-autostart,obmenu-generator} elif ! [[ $INSTALL_WMS =~ bspwm ]]; then rm -rf $user_home/.config/{bspwm,sxhkd} elif ! [[ $INSTALL_WMS =~ i3-gaps ]]; then rm -rf $user_home/.config/i3 fi return 0 } update_mirrorlist() { $MIRROR_CMD --verbose --save $MNT/etc/pacman.d/mirrorlist && return 0 infobox "$_ErrTitle" "\nAn error occurred while updating the mirrorlist.\n\nFalling back to automatic sorting...\n" reflector --score 100 -l 50 -f 10 --sort rate --verbose --save $MNT/etc/pacman.d/mirrorlist return 0 } bootloader_config() { # if not on an LVM we can use the UUID for booting if ! [[ $ROOT_PART =~ /dev/mapper ]]; then ROOT_PART_ID="UUID=$(blkid -s PARTUUID $ROOT_PART | sed 's/.*=//g; s/"//g')" [[ $BOOTLOADER == 'systemd-boot' ]] && ROOT_PART_ID="PART$ROOT_PART_ID" else # for LVM use the partition name eg. /dev/mapper/cryptroot ROOT_PART_ID="$ROOT_PART" fi if [[ $BOOTLOADER == 'grub' ]]; then local cfg="$MNT/etc/default/grub" sed -i "s/GRUB_DISTRIBUTOR=.*/GRUB_DISTRIBUTOR=\"${DIST}\"/g; s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"\"/g" $cfg if (( LUKS == 1 )); then sed -i "s~#GRUB_ENABLE_CRYPTODISK~GRUB_ENABLE_CRYPTODISK~g; s~GRUB_CMDLINE_LINUX=.*~GRUB_CMDLINE_LINUX=\"${LUKS_DEV}\"~g" $cfg fi if [[ $SYS != 'UEFI' && $LVM -eq 1 && $SEPERATE_BOOT -eq 0 ]]; then sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $cfg fi elif [[ $BOOTLOADER == 'syslinux' ]]; then if [[ $SYS == 'BIOS' ]]; then local cfgdir="$MNT/boot/syslinux" local cfgsrcdir="/usr/lib/syslinux/bios/" else local cfgdir="$MNT/boot/EFI/syslinux" local cfgsrcdir="/usr/lib/syslinux/efi64/" fi mkdir -p $cfgdir cp -r $cfgsrcdir $cfgdir/ cat > $cfgdir/syslinux.cfg << EOF UI menu.c32 PROMPT 0 MENU TITLE $DIST Syslinux Boot Menu TIMEOUT 50 DEFAULT $DIST LABEL $DIST MENU LABEL $DIST Linux LINUX ../vmlinuz-$KERNEL APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && echo -n "$LUKS_DEV ")rw INITRD ../initramfs-$KERNEL.img $([[ $(grep 'GenuineIntel' /proc/cpuinfo) && -e $MNT/boot/intel-ucode.img ]] && echo -en "\ninitrd /intel-ucode.img") LABEL ${DIST}fallback MENU LABEL $DIST Linux Fallback LINUX ../vmlinuz-$KERNEL APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && echo -n "$LUKS_DEV ")rw INITRD ../initramfs-$KERNEL-fallback.img $([[ $(grep 'GenuineIntel' /proc/cpuinfo) && -e $MNT/boot/intel-ucode.img ]] && echo -en "\ninitrd /intel-ucode.img") EOF else # systemd-boot requires this before running bootctl systemd-machine-id-setup --root="$MNT" >/dev/null 2>&1 # create the boot entry configs 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}$([[ $(grep 'GenuineIntel' /proc/cpuinfo) && -e $MNT/boot/intel-ucode.img ]] && echo -en "\ninitrd /intel-ucode.img") initrd /initramfs-$KERNEL.img options root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && echo -n "$LUKS_DEV")rw EOF # add pacman hook to update the bootloader when systemd receives an update mkdir -p $MNT/pacman.d/hooks cat > $MNT/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 --path=${BOOT_MNTS[UEFI-systemd-boot]} update EOF fi return 0 } uefi_bootloader_fallback() { # some UEFI firmware is finicky and requires a specific folder in # /boot/efi/EFI/ and named 'boot', 'Boot', or 'BOOT' # copy the bootloaders efi stub to that directory as bootx64.efi local default="BOOT" local esp="${MNT}${BOOT_MNTS[$SYS-$BOOTLOADER]}" for i in $(find "$esp/EFI/" -maxdepth 1 -mindepth 1 -type d 2>/dev/null); do grep -qi "boot" <<< "$(basename $i)" && { default="$(basename $i)"; break; } done mkdir -p $esp/EFI/$default if [[ $1 == 'syslinux' ]]; then cp -f $esp/EFI/$1/* $esp/EFI/$default/ cp -f $esp/EFI/$1/syslinux.efi $esp/EFI/$default/bootx64.efi else cp -f $esp/EFI/$1/grubx64.efi $esp/EFI/$default/bootx64.efi fi return 0 } prep_for_bootloader() { if [[ $SYS == "UEFI" ]]; then local eficmd="mount -o remount,rw -t efivarfs efivarfs /sys/firmware/efi/efivars" $eficmd >/dev/null 2>&1 BOOT_CMDS[$BOOTLOADER]="$eficmd ; ${BOOT_CMDS[$BOOTLOADER]}" local esp="$MNT/boot/efi/EFI/" [[ ! -d $MNT/boot/efi/EFI && -d $MNT/boot/EFI ]] && esp="$MNT/boot/EFI/" find $esp -maxdepth 1 -mindepth 1 \ \( -name '[aA][rR][cC][hH][lL]abs' -o -name '[Bb][oO][oO][tT]' \) \ -type d -exec rm -rf '{}' \; >/dev/null 2>&1 if [[ $BOOTLOADER == "grub" ]]; then BOOT_CMDS[$BOOTLOADER]="${BOOT_CMDS[$BOOTLOADER]} --bootloader-id=$DIST && grub-mkconfig -o /boot/grub/grub.cfg" elif [[ $BOOTLOADER == 'syslinux' ]]; then EDIT_FILES[9]="/boot/EFI/syslinux/syslinux.cfg" BOOT_CMDS[$BOOTLOADER]="efibootmgr -c -d $BOOT_DEVICE -p $BOOT_PART_NUM -l /EFI/syslinux/syslinux.efi -L $DIST" fi else if [[ $BOOTLOADER == "grub" ]]; then BOOT_CMDS[$BOOTLOADER]="${BOOT_CMDS[$BOOTLOADER]} $BOOT_DEVICE && grub-mkconfig -o /boot/grub/grub.cfg" elif [[ $BOOTLOADER == 'syslinux' ]]; then EDIT_FILES[9]="/boot/syslinux/syslinux.cfg" fi fi } install_bootloader() { chroot_cmd "export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/bin/core_perl" local msg="$_InstBootloader $BOOTLOADER\n" [[ $BOOT_PART != "" ]] && msg="$msg\n$_InstBootDev $BOOT_PART\n" infobox "$_InstBootTitle" "$msg\nMountpoint: ${BOOT_MNTS[$SYS-$BOOTLOADER]}\n" 0 prep_for_bootloader # needed for os-prober module to work properly in the chroot mkdir -p $MNT/run/udev && mount --rbind /run/udev $MNT/run/udev >/dev/null 2>&1 # BOOT_CMDS[grub]="mount --rbind /run/udev /run/udev ; ${BOOT_CMDS[grub]}" # create the bootloader config(s) bootloader_config # run the bootloader command chroot_cmd "${BOOT_CMDS[$BOOTLOADER]}" 2>$ERR >/dev/null 2>&1 check_for_errors "${BOOT_CMDS[$BOOTLOADER]}" # copy bootloader efi stub to generic catch all local boot_dir [[ $BOOTLOADER == 'syslinux' ]] && boot_dir="syslinux" || boot_dir="$DIST" [[ $SYS == 'UEFI' && $BOOTLOADER =~ (grub|syslinux) ]] && uefi_bootloader_fallback "$boot_dir" return 0 } run_mkinitcpio() { local conf="$MNT/etc/mkinitcpio.conf" local add # setup a keyfile for LUKS.. Only when choosing grub and system is UEFI if [[ $LUKS -eq 1 && $SYS == 'UEFI' && $BOOTLOADER == 'grub' && $LUKS_PASS && $LUKS_UUID ]]; then luks_keyfile || return 1 fi # new HOOKS needed in /etc/mkinitcpio.conf if we used LUKS and/or LVM (( LVM == 1 )) && add="lvm2" (( LUKS == 1 )) && add="encrypt$([[ $add != "" ]] && echo -n " $add")" sed -i "s/block filesystems/block ${add} filesystems ${MKINIT_HOOKS}/g" $conf 2>$ERR check_for_errors "sed -i 's/block filesystems/block ${add} filesystems ${MKINIT_HOOKS}/g' $conf" tput civis chroot_cmd "mkinitcpio -p $KERNEL" 2>$ERR check_for_errors "mkinitcpio -p $KERNEL" return 0 } edit_configs() { if [[ $CURRENT_MENU != "edit" ]]; then MENU_HIGHLIGHT=1 CURRENT_MENU="edit" elif (( MENU_HIGHLIGHT < 10 )); then ((MENU_HIGHLIGHT++)) fi tput civis MENU_HIGHLIGHT=$(dialog --cr-wrap --stdout --backtitle "$BT" --title " $_EditTitle " \ --default-item $MENU_HIGHLIGHT --menu "${_Final}$_EditBody" 0 0 0 \ "1" "$_Done" "2" "keymaps" "3" "locale" "4" "hostname" "5" "sudoers" \ "6" "mkinitcpio.conf" "7" "fstab" "8" "crypttab" "9" "$BOOTLOADER" "10" "pacman.conf") if [[ $MENU_HIGHLIGHT == "" || $MENU_HIGHLIGHT == 1 ]]; then wrap_up "$_InstFinBody" 'Exit & Reboot' 'Go Back' 'reboot' else local existing_files="" for f in $(echo "${EDIT_FILES[$MENU_HIGHLIGHT]}"); do [[ -e ${MNT}$f ]] && existing_files="$existing_files ${MNT}$f" done if [[ $existing_files != "" ]]; then if [[ $DISPLAY && $TERM != 'linux' ]] && hash geany >/dev/null 2>&1; then geany -i $existing_files else vim -O $existing_files fi else msgbox "$_ErrTitle" "$_NoFileErr" fi fi edit_configs } main() { if [[ $CURRENT_MENU != "main" ]]; then MENU_HIGHLIGHT=1 CURRENT_MENU="main" elif (( MENU_HIGHLIGHT < 8 )); then ((MENU_HIGHLIGHT++)) # increment the highlighted menu item fi tput civis MENU_HIGHLIGHT=$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ --title " $_PrepTitle " --default-item $MENU_HIGHLIGHT --menu "$_PrepBody" 0 0 0 \ "1" "$_PrepShowDev" "2" "$_PrepParts" "3" "$_PrepLUKS" "4" "$_PrepLVM" \ "5" "$_PrepMount" "6" "$_PrepConfig" "7" "$_PrepInstall" "8" "$_Done") # if trying to install the system, make sure the partitions are mounted # and that the needed config variables and user variables have been set up if [[ $MENU_HIGHLIGHT && $MENU_HIGHLIGHT -eq 7 ]]; then check_install_ready local return_val=$? if [[ $return_val -gt 0 ]]; then # when check_install_ready() returns (value > 0) we set the next menu highlight # to (missing step - 1) due to the value being incremented at the beginning # so the missing step is highlighted automatically during the next run MENU_HIGHLIGHT=$return_val return 1 # were inside of a (while true) loop, so returning loops the menu again fi fi case $MENU_HIGHLIGHT in 1) show_devices ;; 2) unmount_partitions && select_device 'root' && create_partitions "$DEVICE" ;; 3) luks_menu ;; 4) lvm_menu ;; 5) select_partitions ;; 6) config_install ;; 7) install_main ;; *) wrap_up "$_CloseInstBody" 'Exit' 'Back' 'exit' esac } # trap ctrl-c and call sigint() to properly exit, without this # exiting via Ctrl-c can leave the terminal in a messed up state trap sigint INT for arg in "$@"; do [[ $arg == "--debug" || $arg == "-d" ]] && set_debug done initialize_variables luks_variable_init select_language set_keymap check_requirements identify_system msgbox "$_WelTitle $DIST Installer" "$_WelBody" while true; do main done