diff --git a/src/archlabs-installer b/src/archlabs-installer index 7bc352f..af1489a 100755 --- a/src/archlabs-installer +++ b/src/archlabs-installer @@ -8,7 +8,7 @@ # Some ideas and code has been taken from other installers # AIF, Cnichi, Calamares, The Arch Wiki.. Credit where credit is due -VER="1.8.7" # version +VER="1.8.8" # version DIST="ArchLabs" # distributor MNT="/mnt" # mountpoint @@ -47,389 +47,416 @@ main() fi case $SELECTED in - 1) - device_tree - ;; - 2) - partition || SELECTED=$((SELECTED - 1)) - ;; - 3) - luks_menu || SELECTED=$((SELECTED - 1)) - ;; - 4) - lvm_menu || SELECTED=$((SELECTED - 1)) - ;; - 5) - mnt_menu || SELECTED=$((SELECTED - 1)) - ;; - 6) - if preinstall_checks; then - user_creation || SELECTED=$((SELECTED - 1)) - fi - ;; - 7) - if preinstall_checks; then - cfg_menu || SELECTED=$((SELECTED - 1)) - fi - ;; - 8) - if preinstall_checks 1; then - select_wm_or_de || SELECTED=$((SELECTED - 1)) - fi - ;; - 9) - if preinstall_checks 1; then - select_packages || SELECTED=$((SELECTED - 1)) - fi - ;; - 10) - preinstall_checks 1 && show_cfg - ;; - 11) - preinstall_checks 1 && install - ;; - *) - yesno "$_CloseInst" "$_CloseInstBody" "Exit" "Back" && die + 1) device_tree ;; + 2) partition || SELECTED=$((SELECTED - 1)) ;; + 3) luks_menu || SELECTED=$((SELECTED - 1)) ;; + 4) lvm_menu || SELECTED=$((SELECTED - 1)) ;; + 5) mnt_menu || SELECTED=$((SELECTED - 1)) ;; + 6) prechecks && { mkuser || SELECTED=$((SELECTED - 1)); } ;; + 7) prechecks && { cfg_menu || SELECTED=$((SELECTED - 1)); } ;; + 8) prechecks 1 && { select_wm_or_de || SELECTED=$((SELECTED - 1)); } ;; + 9) prechecks 1 && { select_packages || SELECTED=$((SELECTED - 1)); } ;; + 10) prechecks 1 && show_cfg ;; + 11) prechecks 1 && install ;; + *) yesno "$_CloseInst" "$_CloseInstBody" "Exit" "Back" && die esac } ############################################################################### -# utility functions +# dialog menus -chrun() +show_cfg() { - arch-chroot $MNT /bin/bash -c "$1" + local cmd="${BCMDS[$BOOTLDR]}" + local mnt="${BMNTS[$SYS-$BOOTLDR]}" + local pkgs="${PACKAGES# }" + msgbox "$_PrepTitle" " + +---------- PARTITION CONFIGURATION ------------ + + Root: ${ROOT_PART:-None} + Boot: ${BOOT_PART:-${BOOT_DEVICE:-None}} + + Swap: ${SWAP_PART:-None} + Size: ${SWAP_SIZE:-None} + + LVM: ${LVM:-None} + LUKS: ${LUKS:-None} + + Extra Mounts: ${EXTRA_MNTS:-${EXTRA_MNT:-None}} + Mkinit Hooks: ${HOOKS:-None} + + +---------- BOOTLOADER CONFIGURATION ----------- + + Bootloader: ${BOOTLDR:-None} + Mountpoint: ${mnt:-None} + Command: ${cmd:-None} + + +------------ SYSTEM CONFIGURATION ------------- + + Locale: ${LOCALE:-None} + Keymap: ${KEYMAP:-None} + Hostname: ${HOSTNAME:-None} + Timezone: ${ZONE:-None}/${SUBZONE:-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}") +" } -json() +cfg_menu() { - # get a value from http://api.ipstack.com in json format using my API key this includes: ip, geolocation, country name - curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])" -} - -src() -{ - # source file ($1), if it fails we die with an error message - if ! . "$1" 2>/dev/null; then - printf "\nFailed to source file %s\n" "$1" - die 1 + tput civis + if ! MYSHELL="$(menubox "$_ShellTitle" "$_ShellBody" '/usr/bin/zsh' '-' '/bin/bash' '-' '/usr/bin/mksh' '-')"; then + return 1 fi + + tput cnorm + if ! HOSTNAME="$(getinput "$_ConfHost" "$_HostNameBody" "${DIST,,}")"; then + return 1 + fi + + tput civis + if ! LOCALE="$(menubox "$_ConfLocale" "$_LocaleBody" $LOCALES)"; then + return 1 + fi + + select_timezone || return 1 + + if ! KERNEL="$(menubox "$_KernelTitle" "$_KernelBody" 'linux' '-' 'linux-lts' '-')"; then + return 1 + fi + + select_mirrorcmd || return 1 + + CONFIG_DONE=true return 0 } -ssd() +mkuser() { - # returns 0 (true) when the device passed ($1) is NOT a rotational device - local i dev=$1 - - # check for LVM and or LUKS for their origin devices - if [[ $LUKS && ! $LVM && $dev =~ $LUKS_NAME ]]; then - dev="${LUKS_PART}" - elif [[ $LVM && ! $LUKS && ${#GROUP_PARTS[@]} -eq 1 && ${GROUP_PARTS[*]} =~ $dev ]]; then - dev="${GROUP_PARTS[*]}" - fi - - dev=${dev#/dev/} - [[ $dev =~ nvme ]] && dev=${dev%p[0-9]*} || dev=${dev%[0-9]*} - - i=$(cat /sys/block/$dev/queue/rotational 2>/dev/null) - - # return value check - (( ${i:-1} == 0 )) -} - -die() -{ - if (( $# >= 1 )); then - local exitcode=$1 - else - local exitcode=0 - fi - - # reset SIGINT - trap - INT - tput cnorm - if [[ -d $MNT ]] && command cd /; then - umount_dir $MNT - if (( exitcode == 127 )); then - umount -l /run/archiso/bootmnt - systemctl -i reboot - fi + local values + if ! values="$(dialog --stdout --no-cancel --separator '~' \ + --ok-label "Submit" --backtitle "$BT" --title " $_UserTitle " \ + --insecure --mixedform "$_UserBody" 0 0 14 \ + "$_Username" 1 1 "" 1 $((${#_Username} + 2)) $COLUMNS 0 0 \ + "$_Password" 2 1 "" 2 $((${#_Password} + 2)) $COLUMNS 0 1 \ + "$_Password2" 3 1 "" 3 $((${#_Password2} + 2)) $COLUMNS 0 1 \ + "$_RootBody" 6 1 "" 6 $((${#_RootBody} + 1)) $COLUMNS 0 2 \ + "$_Password" 8 1 "" 8 $((${#_Password} + 2)) $COLUMNS 0 1 \ + "$_Password2" 9 1 "" 9 $((${#_Password2} + 2)) $COLUMNS 0 1)"; then + return 1 fi - exit $exitcode -} + local user pass pass2 rpass rpass2 + user="$(awk -F'~' '{print $1}' <<< "$values")" + pass="$(awk -F'~' '{print $2}' <<< "$values")" + pass2="$(awk -F'~' '{print $3}' <<< "$values")" + rpass="$(awk -F'~' '{print $5}' <<< "$values")" + rpass2="$(awk -F'~' '{print $6}' <<< "$values")" -sigint() -{ - # used to trap SIGINT and cleanly exit the program - printf "\nCTRL-C caught\nCleaning up...\n" - die 1 -} + # both root passwords are empty, so use the user passwords instead + [[ $rpass == "" && $rpass2 == "" ]] && { rpass="$pass"; rpass2="$pass2"; } -print4() -{ - # takes an arbitrary number of input fields and prints them out in fourths on separate lines - local str="$*" - if [[ ${#str} -gt $((COLUMNS / 2)) ]]; then - str="$(awk -v q="$(awk '{print int(NF / 4)}' <<< "$str")" '{ - pkgs1=pkgs2=pkgs3=pkgs4="" - for (i=1; i/dev/null 2>&1 - rmmod bcma >/dev/null 2>&1 - rmmod b43 >/dev/null 2>&1 - rmmod ssb >/dev/null 2>&1 - modprobe wl >/dev/null 2>&1 - depmod -a >/dev/null 2>&1 - BROADCOM_WL=true -} - -net_connect() -{ - chk_connect() { curl -s --head 'https://www.archlinux.org/mirrorlist/all/' | sed '1q' | grep -qw '200'; } - - if ! chk_connect; then - if [[ $(systemctl is-active NetworkManager) == "active" ]] && hash nmtui >/dev/null 2>&1; then - tput civis - - # fix ugly nmtui colours - printf "\e]P1191919" # #191919 - printf "\e]P4191919" # #191919 - - nmtui-connect - - # restore - printf "\e]P1D15355" # #D15355 - printf "\e]P4255a9b" # #255a9b - chk_connect || return 1 - else + if ! CMAP="$(menubox "$_CMapTitle" "$_CMapBody" $CMAPS)"; then return 1 fi fi - # if we've made it here we likely have a good connection - return 0 -} -system_checks() -{ - if [[ $(whoami) != "root" ]]; then - infobox "$_ErrTitle" "$_NotRoot\n$_Exit" - die 1 - elif ! grep -qw 'lm' /proc/cpuinfo; then - infobox "$_ErrTitle" "$_Not64Bit\n$_Exit" - die 1 - fi - - # setup for specific broadcom chips - grep -q 'BCM4352' <<< "$(lspci -vnn -d 14e4:)" && load_bcm - - # connect or confirm connection, otherwise bail - net_connect || { infobox "$_ErrTitle" "$_NoNetwork\n$_Exit" 3; die 1; } -} - -preinstall_checks() -{ - if ! [[ $(lsblk -o MOUNTPOINT) =~ $MNT ]]; then - msgbox "$_ErrTitle" "$_ErrNoMount" - SELECTED=4 - return 1 - elif [[ $# -eq 1 && $CONFIG_DONE != true ]]; then - msgbox "$_ErrTitle" "$_ErrNoConfig" - SELECTED=5 - return 1 + if [[ $DISPLAY && $TERM != 'linux' ]]; then + setxkbmap $KEYMAP >/dev/null 2>&1 + else + loadkeys $CMAP >/dev/null 2>&1 fi return 0 } -errshow() +select_timezone() { - local last_exit_code=$? - (( last_exit_code == 0 )) && return 0 + # create associative array for SUBZONES[zone] + local f="/usr/share/zoneinfo/zone.tab" + declare -A SUBZONES + for i in America Australia Asia Atlantic Africa Europe Indian Pacific Arctic Antarctica; do + SUBZONES[$i]="$(awk '/'"$i"'\// {gsub(/'"$i"'\//, ""); print $3, $1}' $f | sort)" + done - local err - err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" + tput civis + if ! ZONE="$(menubox "$_TimeZTitle" "$_TimeZBody" \ + 'America' '-' 'Australia' '-' 'Asia' '-' 'Atlantic' '-' 'Africa' '-' \ + 'Europe' '-' 'Indian' '-' 'Pacific' '-' 'Arctic' '-' 'Antarctica' '-')"; then + return 1 + fi - if [[ $err != "" ]]; then - msgbox "$_ErrTitle" "\nERROR: $err" + if ! SUBZONE="$(menubox "$_TimeZTitle" "$_TimeSubZBody" ${SUBZONES[$ZONE]})"; then + return 1 + fi + + yesno "$_TimeZTitle" "$_TimeZQ $ZONE/$SUBZONE?\n" || select_timezone +} + +select_wm_or_de() +{ + tput civis + if ! INSTALL_WMS="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ + --title " $_WMChoice " --checklist "$_WMChoiceBody\n" 0 0 0 \ + "i3-gaps" "A fork of i3wm with more features including gaps" off \ + "dwm" "A fork of dwm, with more layouts and features" off \ + "openbox" "A lightweight, powerful, and highly configurable stacking wm" off \ + "bspwm" "A tiling wm that represents windows as the leaves of a binary tree" off \ + "gnome" "A desktop environment that aims to be simple and easy to use" off \ + "cinnamon" "A desktop environment combining traditional desktop with modern effects" off \ + "plasma" "A kde software project currently comprising a full desktop environment" off \ + "xfce4" "A lightweight and modular desktop environment based on gtk+2/3" off)"; then + return 1 + fi + + WM_NUM=$(awk '{print NF}' <<< "$INSTALL_WMS") + WM_PACKAGES="${INSTALL_WMS/dwm/}" # remove dwm from package list + WM_PACKAGES="${WM_PACKAGES// / }" # remove double spaces + WM_PACKAGES="${WM_PACKAGES# }" # remove leading space + + # packages needed for the selected WMs/DEs + for wm in $INSTALL_WMS; do + LOGIN_CHOICES+="$wm - " + [[ ${WM_EXT[$wm]} ]] && WM_PACKAGES+=" ${WM_EXT[$wm]}" + done + + # choose how to log in + select_login || return 1 + + # choose which WM/DE to start at login, only for xinit + if [[ $LOGIN_TYPE == 'xinit' ]]; then + if [[ $WM_NUM -eq 1 ]]; then + LOGIN_WM="${WM_SESSIONS[$INSTALL_WMS]}" + else + LOGIN_WM="$(menubox "$_WMLogin" "$_WMLoginBody" $LOGIN_CHOICES)" || return 1 + LOGIN_WM="${WM_SESSIONS[$LOGIN_WM]}" + fi + yesno "$_WMLogin" "$_AutoLoginBody\n" && AUTOLOGIN=true || AUTOLOGIN=false else - msgbox "$_ErrTitle" "\nThe command exited abnormally: $1\n\nWith the no error message.\n" + AUTOLOGIN=false + fi + + # add packages to the main package list + PACKAGES+=" ${WM_PACKAGES# }" +} + +select_login() +{ + if ! LOGIN_TYPE="$(menubox "$_WMLogin" "$_LoginTypeBody" \ + "xinit" "Console login without a display manager" \ + "lightdm" "Lightweight display manager with a gtk greeter")"; then + return 1 + fi + + if [[ $LOGIN_TYPE == 'lightdm' ]]; then + WM_PACKAGES+=" lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice" + EDIT_FILES[login]="/etc/lightdm/lightdm.conf /etc/lightdm/lightdm-gtk-greeter.conf" + else + PACKAGES="${PACKAGES// lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice/}" + WM_PACKAGES="${WM_PACKAGES// lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice/}" + EDIT_FILES[login]="/home/$NEWUSER/.xinitrc /home/$NEWUSER/.xprofile" fi } -echeck() +select_packages() { - local last_exit_code=$? - (( last_exit_code == 0 )) && return 0 - - local err - err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" - - if [[ $err != "" ]]; then - msgbox "$_ErrTitle" "\nERROR: $err" - else - msgbox "$_ErrTitle" "\nThe command exited abnormally: $1\n\nWith the no error message.\n" + if [[ $CURRENT_MENU != "packages" ]]; then + SAVED=$SELECTED + SELECTED=1 + CURRENT_MENU="packages" + elif (( SELECTED < 9 )); then + ((SELECTED++)) # increment the highlighted menu item fi - [[ -e $DBG && $TERM == 'linux' ]] && more $DBG + tput civis + SELECTED=$(dialog --cr-wrap --no-cancel --stdout \ + --backtitle "$BT" --title " $_Packages " \ + --default-item $SELECTED --menu "$_PackageMenu" 0 0 14 \ + 1 "Browsers" \ + 2 "Editors" \ + 3 "Terminals" \ + 4 "Multimedia" \ + 5 "Chat/Mail" \ + 6 "Professional" \ + 7 "System" \ + 8 "Miscellaneous" \ + 9 "$_Done") - die 1 -} + if [[ $SELECTED -lt 9 ]]; then + case $SELECTED in + 1) PACKAGES+=" $(select_browsers)" ;; + 2) PACKAGES+=" $(select_editors)" ;; + 3) PACKAGES+=" $(select_terminals)" ;; + 4) PACKAGES+=" $(select_multimedia)" ;; + 5) PACKAGES+=" $(select_mailchat)" ;; + 6) PACKAGES+=" $(select_prof)" ;; + 7) PACKAGES+=" $(select_managment)" ;; + 8) PACKAGES+=" $(select_extra)" ;; + esac + select_packages + fi -debug() -{ - set -x - exec 3>| $DBG - BASH_XTRACEFD=3 - DEBUG=true -} + # add any extras for each package + for pkg in $PACKAGES; do + [[ ${PKG_EXT[$pkg]} ]] && PACKAGES+=" ${PKG_EXT[$pkg]}" + done -umount_dir() -{ - swapoff -a - [[ -d $1 ]] && umount -R $1 >/dev/null 2>&1 + # add mksh to package list if it was chosen as the login shell + [[ $MYSHELL == *mksh ]] && PACKAGES+=" mksh" + + # remove duplicates and leading spaces + PACKAGES="$(uniq <<< "${PACKAGES/^ /}")" return 0 } -msgbox() +select_mirrorcmd() { - tput civis - dialog --cr-wrap --backtitle "$BT" --title " $1 " --msgbox "$2\n" 0 0 -} + local c + local key="5f29642060ab983b31fdf4c2935d8c56" -menubox() -{ - local title="$1" - local body="$2" - shift 2 - local response - if ! response="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ - --title " $title " --menu "$body" 0 0 0 "$@")"; then - return 1 - fi - printf "%s" "$response" -} + if hash reflector >/dev/null 2>&1; then + MIRROR_CMD="reflector --score 100 -l 50 -f 10 --sort rate --verbose" + yesno "$_MirrorTitle" "$_MirrorSetup" "Automatic" "Custom" && return 0 -checkbox() -{ - local title="$1" - local body="$2" - shift 2 - local response - if ! response="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ - --title " $title " --checklist "$body" 0 0 0 "$@")"; then - return 1 - fi - printf "%s" "$response" -} + c="$(json 'country_name' "$(json 'ip' "check&?access_key=${key}&fields=ip")?access_key=${key}&fields=country_name")" + MIRROR_CMD="reflector --country $c --score 80 --latest 40 --fastest 10 --sort rate --verbose" -getinput() -{ - local answer - if [[ $# -eq 4 && $4 == 'nolimit' ]]; then - answer="$(dialog --cr-wrap --stdout --backtitle "$BT" \ - --title " $1 " --inputbox "$2" 0 0 "$3")" + tput cnorm + MIRROR_CMD="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ + --title " $_MirrorTitle " --inputbox "$_MirrorCmd\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")" else - answer="$(dialog --cr-wrap --max-input 63 --stdout --backtitle "$BT" \ - --title " $1 " --inputbox "$2" 0 0 "$3")" + 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" + if [[ $c ]]; then + if [[ $c =~ (CA|US) ]]; then + MIRROR_CMD="curl -s '$w/?country=US&country=CA&protocol=https&use_mirror_status=on'" + else + MIRROR_CMD="curl -s '$w/?country=${c}&protocol=https&use_mirror_status=on'" + fi + else + local countries="country=US&country=CA&country=NZ&country=GB&country=AU" + MIRROR_CMD="curl -s '$w/?$countries&protocol=https&use_mirror_status=on'" + fi fi - - local e=$? - [[ $e -ne 0 || $answer == "" ]] && return 1 - printf "%s" "$answer" + return 0 } -infobox() +edit_configs() { - local sec="$3" - tput civis - dialog --cr-wrap --backtitle "$BT" --title " $1 " --infobox "$2\n" 0 0 - sleep ${sec:-2} -} + [[ $DEBUG == true ]] && str="View log & reboot" || str="Exit & reboot" -yesno() -{ - # usage: yesno <text> [<yes_label> <no_label> [<no>]] tput civis - if [[ $# -eq 5 && $5 == "no" ]]; then - dialog --cr-wrap --backtitle "$BT" --defaultno --title " $1 " \ - --yes-label "$3" --no-label "$4" --yesno "$2\n" 0 0 - elif [[ $# -eq 4 ]]; then - dialog --cr-wrap --backtitle "$BT" --title " $1 " --yes-label "$3" \ - --no-label "$4" --yesno "$2\n" 0 0 + local choice + choice=$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ + --title " $_EditTitle " --menu "$_EditBody" 0 0 14 "$str" "-" \ + "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 [[ ! $choice || $choice == "$str" ]]; then + [[ $DEBUG == true && -r $DBG ]] && vim $DBG + # when die() is passed 127 it will call: systemctl -i reboot + die 127 else - dialog --cr-wrap --backtitle "$BT" --title " $1 " --yesno "$2\n" 0 0 + local exists="" + for f in $(printf "%s" "${EDIT_FILES[$choice]}"); do + [[ -e ${MNT}$f ]] && exists+=" ${MNT}$f" + done + if [[ $exists ]]; then + vim -O $exists + else + msgbox "$_ErrTitle" "$_NoFileErr" + fi fi + edit_configs } ############################################################################### @@ -1151,6 +1178,509 @@ select_extra_partitions() return 0 } +############################################################################### +# installation + +install() +{ + clear + tput cnorm + install_base + genfstab -U $MNT >$MNT/etc/fstab 2>$ERR + echeck "genfstab -U $MNT >$MNT/etc/fstab" + [[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" $MNT/etc/fstab + mirrorlist_sort + package_operations + run_mkinitcpio + install_bootloader + chrun "hwclock --systohc --utc" || chrun "hwclock --systohc --utc --directisa" + create_user + login_manager + chrun "chown -Rf $NEWUSER:users /home/$NEWUSER" + printf "\nThe install section is now finished, press any key to continue.\n" + read -rn1 + edit_configs +} + +install_base() +{ + if [[ -e /run/archiso/sfs/airootfs/etc/skel ]]; then + rsync -ahv /run/archiso/sfs/airootfs/ $MNT/ 2>/dev/null + else + mirrorlist_sort + local packages + pacstrap $MNT base $KERNEL $UCODE $(grep -hv '^#' /usr/share/archlabs/installer/packages.txt) + fi + + printf "\n" + rm -rf $MNT/etc/mkinitcpio-archiso.conf + find $MNT/usr/lib/initcpio -name 'archiso*' -type f -exec rm '{}' \; + 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 + + if [[ $VM ]]; then + rm -rfv $MNT/etc/X11/xorg.conf.d/*?.conf + 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-lts' ]] && cp -vf $RUN/x86_64/vmlinuz $MNT/boot/vmlinuz-linux + [[ $UCODE && ! $VM ]] && cp -vf $RUN/${UCODE/-/_}.img $MNT/boot/${UCODE}.img + fi + + printf "\n" + cp -fv /etc/resolv.conf $MNT/etc/ + if [[ -e /etc/NetworkManager/system-connections ]]; then + cp -rvf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/ + fi + + cat > $MNT/etc/locale.conf << EOF +LANG=$LOCALE +EOF + cat > $MNT/etc/default/locale << EOF +LANG=$LOCALE +EOF + sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${LOCALE}/${LOCALE}/g" $MNT/etc/locale.gen + chrun "echo && locale-gen" 2>/dev/null + printf "\n" + chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZONE /etc/localtime" 2>/dev/null + + if [[ $BROADCOM_WL == true ]]; 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 + cat > $MNT/etc/vconsole.conf <<EOF +KEYMAP=$CMAP +FONT=$FONT +EOF + cat > $MNT/etc/hostname << EOF +$HOSTNAME +EOF + 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 + +} + +create_user() +{ + # set root password and shell if needed + chrun "chpasswd <<< 'root:$ROOT_PASS'" + if [[ $MYSHELL != *zsh ]]; then + chrun "usermod -s $MYSHELL root" + if [[ $MYSHELL == "/usr/bin/mksh" ]]; then + cp -fv $MNT/etc/skel/.mkshrc /root/.mkshrc + fi + fi + + # Create the user, set password, and make sure the ownership of ~/ is correct + local groups='audio,autologin,floppy,log,network,rfkill,scanner,storage,optical,power,wheel' + chrun "groupadd -r autologin" + chrun "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" 2>$ERR + chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" + + # for neovim set up ~/.config/nvim + if [[ $PACKAGES =~ 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 + + [[ $INSTALL_WMS =~ dwm ]] && suckless_install + + if [[ $INSTALL_WMS == 'plasma' || $LOGIN_WM == 'startkde' ]]; then + # plasma has their own superkey daemon that conflicts with ksuperkey + sed -i '/super/d' $HOME/.xprofile + sed -i '/super/d' /root/.xprofile + fi + + return 0 +} + +setup_xinit() +{ + if [[ -e $MNT/home/$NEWUSER/.xinitrc ]]; then + sed -i "s/openbox-session/${LOGIN_WM}/g" $MNT/home/$NEWUSER/.xinitrc + else + printf "%s\n" "exec $LOGIN_WM" > $MNT/home/$NEWUSER/.xinitrc + fi + + # automatic startx for login shells + local loginrc + case $MYSHELL in + "/bin/bash") + loginrc=".bash_profile" + ;; + "/usr/bin/mksh") + loginrc=".profile" + 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=vim +export MANWIDTH=100 + +# source shell configs +for f in "\$HOME/.mksh/"*?.sh; do + . "\$f" +done + +al-info +EOF + ;; + *) + loginrc=".zprofile" + esac + + if ! [[ ${EDIT_FILES[login]} =~ $loginrc ]]; then + # add the shell login file to the edit list after install + EDIT_FILES[login]+=" /home/$NEWUSER/$loginrc" + fi + + if [[ $AUTOLOGIN == true ]]; then + sed -i "s/root/${NEWUSER}/g" $SERVICE/autologin.conf + cat > $MNT/home/$NEWUSER/$loginrc << EOF +# ~/$loginrc +# sourced by $(basename $MYSHELL) when used as a login shell + +# automatically run startx when logging in on tty1 +[[ ! \$DISPLAY && \$XDG_VTNR -eq 1 ]] && exec startx -- vt1 + +EOF + else + rm -rf $SERVICE + rm -rf $MNT/home/$NEWUSER/.{profile,zprofile,bash_profile} + fi +} + +setup_lightdm() +{ + rm -rf $SERVICE + rm -rf $MNT/home/$NEWUSER/.{xinitrc,profile,zprofile,bash_profile} + chrun '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 +} + +login_manager() +{ + SERVICE="$MNT/etc/systemd/system/getty@tty1.service.d" + + # remove welcome message + sed -i '/printf/d' $MNT/root/.zshrc + + case $MYSHELL in + "/bin/bash") rm -rf $MNT/home/$NEWUSER/.{zsh,mksh}* ;; + "/usr/bin/mksh") rm -rf $MNT/home/$NEWUSER/.{zsh,bash}* $MNT/home/$NEWUSER/.inputrc ;; + "/usr/bin/zsh") rm -rf $MNT/home/$NEWUSER/.{bash,mksh}* $MNT/home/$NEWUSER/.inputrc ;; + esac + + if [[ $LOGIN_TYPE == 'lightdm' ]]; then + setup_lightdm + else + setup_xinit + fi +} + +run_mkinitcpio() +{ + local add="" + [[ $LUKS && $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 + echeck "mkinitcpio -p $KERNEL" +} + +mirrorlist_sort() +{ + printf "\n%s\n\n" "Sorting the 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 -n 10 - > $MNT/etc/pacman.d/mirrorlist + fi +} + +package_operations() +{ + local inpkg="$PACKAGES" # add the packages chosen during setup + local rmpkg="archlabs-installer" # always remove the installer + + if [[ $KERNEL == 'linux-lts' ]]; then + rmpkg+=" linux" + inpkg+=" linux-lts" + fi + if [[ $INSTALL_WMS == 'plasma' || $INSTALL_WMS == 'gnome' || $INSTALL_WMS == 'cinnamon' ]]; then + chrun "pacman -Rnsc archlabs-ksuperkey xfce4 --noconfirm" + fi + if [[ $BOOTLDR != 'grub' ]]; then + chrun "pacman -Rns grub --noconfirm" + rm -f $MNT/etc/default/grub 2>dev/null + find $MNT/boot/ -name 'grub*' -exec rm -rf '{}' \; >/dev/null 2>&1 + fi + if [[ $BOOTLDR != 'syslinux' ]]; then + find $MNT/boot/ -name 'syslinux*' -exec rm -rf '{}' \; >/dev/null 2>&1 + fi + + chrun "pacman -Syyu --noconfirm" + chrun "pacman -S iputils --noconfirm" + chrun "pacman -S base-devel git --needed --noconfirm" + chrun "pacman -S $inpkg --needed --noconfirm" + chrun "pacman -Rns $rmpkg --noconfirm" + return 0 +} + +suckless_install() +{ + # install and setup dwm + printf "\n%s\n\n" "Installing and setting up dwm." + mkdir -pv $MNT/home/$NEWUSER/suckless + + for i in dwm dmenu st; do + p="/home/$NEWUSER/suckless/$i" + chrun "git clone https://bitbucket.org/natemaia/$i $p" + e=$? + if (( e == 0 )); then + chrun "cd $p; rm -f config.h; make clean install; make clean" + else + printf "\n\nFailed to clone $i repo\n\n" + fi + done + + if [[ -d /home/$NEWUSER/suckless/dwm ]]; then + printf "\n\n%s" "To configure dwm edit /home/$NEWUSER/suckless/dwm/config.h" + printf "\n%s\n\n" "You can then recompile it with 'sudo make clean install'" + sleep 2 + fi +} + +############################################################################### +# bootloader setup + +setup_boot() +{ + tput civis + if ! BOOTLDR="$(menubox "$_PrepMount" "$_MntBootBody" ${BOOTLDRS[$SYS]})"; then + return 1 + fi + + if [[ $BOOT_PART != "" ]]; then + mount_partition "$BOOT_PART" "${BMNTS[$SYS-$BOOTLDR]}" && SEP_BOOT=true || return 1 + setup_boot_device + fi + + setup_${BOOTLDR} || return 1 +} + +setup_grub() +{ + # grub has by far the worst setup of the three however + # the configuration is shorter due to grub-mkconfig + EDIT_FILES[bootloader]="/etc/default/grub" + if [[ $SYS == 'BIOS' ]]; then + [[ $BOOT_DEVICE ]] || { select_device 'boot' || return 1; } + BCMDS[grub]="${BCMDS[grub]} --target=i386-pc $BOOT_DEVICE" + else + if [[ $ROOT_PART == */dev/mapper/* && ! $LVM && ! $LUKS_PASS ]]; then + luks_pass "$_LuksOpen" "" || return 1 + fi + BCMDS[grub]="${BCMDS[grub]} --removable" + fi + + BCMDS[grub]="mkdir -p /run/udev && + mkdir -p /run/lvm && + mount --bind /hostrun/udev /run/udev && + mount --bind /hostrun/lvm /run/lvm && + ${BCMDS[grub]} && + grub-mkconfig -o /boot/grub/grub.cfg && + umount /run/udev && + umount /run/lvm" + return 0 +} + +setup_syslinux() +{ + EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg" +} + +setup_systemd-boot() +{ + EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf" +} + +prerun_grub() +{ + 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_DEV ]]; 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 == 'BIOS' && $LVM && $SEP_BOOT == false ]]; then + sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $cfg + fi + + # needed for grub-probe module to work properly in the chroot + # once the grub install is done these will be umounted and removed + mkdir -p /run/lvm + mkdir -p /run/udev + mkdir -p $MNT/hostrun/lvm + mkdir -p $MNT/hostrun/udev + mount --bind /run/lvm $MNT/hostrun/lvm + mount --bind /run/udev $MNT/hostrun/udev + + return 0 +} + +prerun_systemd-boot() +{ + # no LVM then systemd-boot uses PARTUUID + [[ $ROOT_PART =~ /dev/mapper ]] || ROOT_PART_ID="PART$ROOT_PART_ID" + + # create the boot entry configs + mkdir -p ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/entries + cat > ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/loader.conf << EOF +default $DIST +timeout 5 +editor no +EOF + cat > ${MNT}${BMNTS[$SYS-systemd-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 + # add pacman hook to update the bootloader when systemd receives an update + 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-boot requires this before running bootctl + systemd-machine-id-setup --root="$MNT" + return 0 +} + +prerun_syslinux() +{ + mkdir -pv $MNT${BMNTS[$SYS-syslinux]}/syslinux + cp -rfv /usr/lib/syslinux/bios/* $MNT${BMNTS[$SYS-syslinux]}/syslinux/ + + cat > $cfgdir/syslinux.cfg << EOF +UI menu.c32 +PROMPT 0 + +MENU TITLE $DIST Boot Menu +TIMEOUT 50 +DEFAULT $DIST + +LABEL $DIST +MENU LABEL $DIST Linux +LINUX ../vmlinuz-$KERNEL +APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw +INITRD ../initramfs-$KERNEL.img$([[ $UCODE ]] && printf "\nINITRD %s" "../${UCODE}.img") + +LABEL ${DIST}fallback +MENU LABEL $DIST Linux Fallback +LINUX ../vmlinuz-$KERNEL +APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw +INITRD ../initramfs-$KERNEL-fallback.img$([[ $UCODE ]] && printf "\nINITRD %s" "../${UCODE}.img") +EOF + return 0 +} + +install_bootloader() +{ + if ! [[ $ROOT_PART =~ /dev/mapper ]]; then + ROOT_PART_ID="UUID=$(blkid -s PARTUUID -o value $ROOT_PART)" + else + # for LVM we just use the partition label + ROOT_PART_ID="$ROOT_PART" + fi + + # remove old UEFI boot entries + if [[ $SYS == 'UEFI' ]]; then + find ${MNT}${BMNTS[UEFI-$BOOTLDR]}/EFI/ \ + -maxdepth 1 -mindepth 1 -name '[aA][rR][cC][hH][lL]abs' -type d -exec rm -rf '{}' \; >/dev/null 2>&1 + find ${MNT}${BMNTS[UEFI-$BOOTLDR]}/EFI/ \ + -maxdepth 1 -mindepth 1 -name '[Bb][oO][oO][tT]' -type d -exec rm -rf '{}' \; >/dev/null 2>&1 + fi + + prerun_$BOOTLDR + printf "\nInstalling and setting up $BOOTLDR in ${BMNTS[$SYS-$BOOTLDR]}\n\n" + chrun "${BCMDS[$BOOTLDR]}" + echeck "${BCMDS[$BOOTLDR]}" + + if [[ -d $MNT/hostrun ]]; then + # remove mounted directories + 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 + + return 0 +} + ############################################################################### # lvm functions @@ -1638,616 +2168,7 @@ luks_keyfile() } ############################################################################### -# installation - -install() -{ - clear - tput cnorm - - # unpack the file system - install_base - - # generate /etc/fstab and touch it up if we used a swapfile - genfstab -U $MNT >$MNT/etc/fstab 2>$ERR - echeck "genfstab -U $MNT >$MNT/etc/fstab" - [[ -f $MNT/swapfile ]] && sed -i "s~${MNT}~~" $MNT/etc/fstab - - # update the mirrorlist.. MUST be done before updating or it may be slow - mirrorlist_sort - - # MUST be before bootloader and running mkinitcpio - package_operations - - # mkinitcpio and bootloader install should only be done after installing the packages - # and updating the mirrorlist, otherwise the chosen kernel may not be fully set up - run_mkinitcpio - install_bootloader - - # hwclock setup, falls back to setting --directisa if the default fails - chrun "hwclock --systohc --utc" || chrun "hwclock --systohc --utc --directisa" - - # create the user - create_user - - # set up user login.. MUST be done after package operation and user creation - login_manager - - # fix any messed up file permissions from editing during install - chrun "chown -Rf $NEWUSER:users /home/$NEWUSER" - - printf "\nThe install section is now finished, press any key to continue.\n" - read -rn1 - - # drop off the user at the config editing menu - edit_configs -} - -install_base() -{ - if [[ -e /run/archiso/sfs/airootfs/etc/skel ]]; then - rsync -ahv /run/archiso/sfs/airootfs/ $MNT/ 2>/dev/null - else - mirrorlist_sort - local packages - pacstrap $MNT base $KERNEL $UCODE $(grep -hv '^#' /usr/share/archlabs/installer/packages.txt) - fi - - printf "\n" - rm -rf $MNT/etc/mkinitcpio-archiso.conf - find $MNT/usr/lib/initcpio -name 'archiso*' -type f -exec rm '{}' \; - 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 - - if [[ $VM ]]; then - rm -rfv $MNT/etc/X11/xorg.conf.d/*?.conf - 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-lts' ]] && cp -vf $RUN/x86_64/vmlinuz $MNT/boot/vmlinuz-linux - [[ $UCODE && ! $VM ]] && cp -vf $RUN/${UCODE/-/_}.img $MNT/boot/${UCODE}.img - fi - - printf "\n" - cp -fv /etc/resolv.conf $MNT/etc/ - if [[ -e /etc/NetworkManager/system-connections ]]; then - cp -rvf /etc/NetworkManager/system-connections $MNT/etc/NetworkManager/ - fi - - cat > $MNT/etc/locale.conf << EOF -LANG=$LOCALE -EOF - cat > $MNT/etc/default/locale << EOF -LANG=$LOCALE -EOF - sed -i "s/#en_US.UTF-8/en_US.UTF-8/g; s/#${LOCALE}/${LOCALE}/g" $MNT/etc/locale.gen - chrun "echo && locale-gen" 2>/dev/null - printf "\n" - chrun "ln -svf /usr/share/zoneinfo/$ZONE/$SUBZONE /etc/localtime" 2>/dev/null - - if [[ $BROADCOM_WL == true ]]; 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 - cat > $MNT/etc/vconsole.conf <<EOF -KEYMAP=$CMAP -FONT=$FONT -EOF - cat > $MNT/etc/hostname << EOF -$HOSTNAME -EOF - 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 - -} - -create_user() -{ - # set root password and shell if needed - chrun "chpasswd <<< 'root:$ROOT_PASS'" - if [[ $MYSHELL != *zsh ]]; then - chrun "usermod -s $MYSHELL root" - if [[ $MYSHELL == "/usr/bin/mksh" ]]; then - cp -fv $MNT/etc/skel/.mkshrc /root/.mkshrc - fi - fi - - # Create the user, set password, and make sure the ownership of ~/ is correct - local groups='audio,autologin,floppy,log,network,rfkill,scanner,storage,optical,power,wheel' - chrun "groupadd -r autologin" - chrun "useradd -m -u 1000 -g users -G $groups -s $MYSHELL $NEWUSER" 2>$ERR - chrun "chpasswd <<< '$NEWUSER:$USER_PASS'" - - # for neovim set up ~/.config/nvim - if [[ $PACKAGES =~ 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 - - [[ $INSTALL_WMS =~ dwm ]] && suckless_install - - if [[ $INSTALL_WMS == 'plasma' || $LOGIN_WM == 'startkde' ]]; then - # plasma has their own superkey daemon that conflicts with ksuperkey - sed -i '/super/d' $HOME/.xprofile - sed -i '/super/d' /root/.xprofile - fi - - return 0 -} - -setup_xinit() -{ - if [[ -e $MNT/home/$NEWUSER/.xinitrc ]]; then - sed -i "s/openbox-session/${LOGIN_WM}/g" $MNT/home/$NEWUSER/.xinitrc - else - printf "%s\n" "exec $LOGIN_WM" > $MNT/home/$NEWUSER/.xinitrc - fi - - # automatic startx for login shells - local loginrc - case $MYSHELL in - "/bin/bash") - loginrc=".bash_profile" - ;; - "/usr/bin/mksh") - loginrc=".profile" - 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=vim -export MANWIDTH=100 - -# source shell configs -for f in "\$HOME/.mksh/"*?.sh; do - . "\$f" -done - -al-info -EOF - ;; - *) - loginrc=".zprofile" - esac - - if ! [[ ${EDIT_FILES[login]} =~ $loginrc ]]; then - # add the shell login file to the edit list after install - EDIT_FILES[login]+=" /home/$NEWUSER/$loginrc" - fi - - if [[ $AUTOLOGIN == true ]]; then - sed -i "s/root/${NEWUSER}/g" $SERVICE/autologin.conf - cat > $MNT/home/$NEWUSER/$loginrc << EOF -# ~/$loginrc -# sourced by $(basename $MYSHELL) when used as a login shell - -# automatically run startx when logging in on tty1 -[[ ! \$DISPLAY && \$XDG_VTNR -eq 1 ]] && exec startx -- vt1 - -EOF - else - rm -rf $SERVICE - rm -rf $MNT/home/$NEWUSER/.{profile,zprofile,bash_profile} - fi -} - -setup_lightdm() -{ - rm -rf $SERVICE - rm -rf $MNT/home/$NEWUSER/.{xinitrc,profile,zprofile,bash_profile} - chrun '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 -} - -login_manager() -{ - SERVICE="$MNT/etc/systemd/system/getty@tty1.service.d" - - # remove welcome message - sed -i '/printf/d' $MNT/root/.zshrc - - case $MYSHELL in - "/bin/bash") rm -rf $MNT/home/$NEWUSER/.{zsh,mksh}* ;; - "/usr/bin/mksh") rm -rf $MNT/home/$NEWUSER/.{zsh,bash}* $MNT/home/$NEWUSER/.inputrc ;; - "/usr/bin/zsh") rm -rf $MNT/home/$NEWUSER/.{bash,mksh}* $MNT/home/$NEWUSER/.inputrc ;; - esac - - if [[ $LOGIN_TYPE == 'lightdm' ]]; then - setup_lightdm - else - setup_xinit - fi -} - -run_mkinitcpio() -{ - local add="" - [[ $LUKS && $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 - echeck "mkinitcpio -p $KERNEL" -} - -mirrorlist_sort() -{ - printf "\n%s\n\n" "Sorting the 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 -n 10 - > $MNT/etc/pacman.d/mirrorlist - fi -} - -package_operations() -{ - local inpkg="$PACKAGES" # add the packages chosen during setup - local rmpkg="archlabs-installer" # always remove the installer - - if [[ $KERNEL == 'linux-lts' ]]; then - rmpkg+=" linux" - inpkg+=" linux-lts" - fi - if [[ $INSTALL_WMS == 'plasma' || $INSTALL_WMS == 'gnome' || $INSTALL_WMS == 'cinnamon' ]]; then - chrun "pacman -Rnsc archlabs-ksuperkey xfce4 --noconfirm" - fi - if [[ $BOOTLDR != 'grub' ]]; then - chrun "pacman -Rns grub --noconfirm" - rm -f $MNT/etc/default/grub 2>dev/null - find $MNT/boot/ -name 'grub*' -exec rm -rf '{}' \; >/dev/null 2>&1 - fi - if [[ $BOOTLDR != 'syslinux' ]]; then - find $MNT/boot/ -name 'syslinux*' -exec rm -rf '{}' \; >/dev/null 2>&1 - fi - - chrun "pacman -Syyu --noconfirm" - chrun "pacman -S iputils --noconfirm" - chrun "pacman -S base-devel git --needed --noconfirm" - chrun "pacman -S $inpkg --needed --noconfirm" - chrun "pacman -Rns $rmpkg --noconfirm" - return 0 -} - -suckless_install() -{ - # install and setup dwm - printf "\n%s\n\n" "Installing and setting up dwm." - mkdir -pv $MNT/home/$NEWUSER/suckless - - for i in dwm dmenu st; do - p="/home/$NEWUSER/suckless/$i" - chrun "git clone https://bitbucket.org/natemaia/$i $p" - e=$? - if (( e == 0 )); then - chrun "cd $p; rm -f config.h; make clean install; make clean" - else - printf "\n\nFailed to clone $i repo\n\n" - fi - done - - if [[ -d /home/$NEWUSER/suckless/dwm ]]; then - printf "\n\n%s" "To configure dwm edit /home/$NEWUSER/suckless/dwm/config.h" - printf "\n%s\n\n" "You can then recompile it with 'sudo make clean install'" - sleep 2 - fi -} - -############################################################################### -# bootloader setup - -setup_boot() -{ - tput civis - if ! BOOTLDR="$(menubox "$_PrepMount" "$_MntBootBody" ${BOOTLDRS[$SYS]})"; then - return 1 - fi - - if [[ $BOOT_PART != "" ]]; then - mount_partition "$BOOT_PART" "${BMNTS[$SYS-$BOOTLDR]}" && SEP_BOOT=true || return 1 - setup_boot_device - fi - - setup_${BOOTLDR} || return 1 -} - -setup_grub() -{ - # grub has by far the worst setup of the three however - # the configuration is shorter due to grub-mkconfig - EDIT_FILES[bootloader]="/etc/default/grub" - if [[ $SYS == 'BIOS' ]]; then - [[ $BOOT_DEVICE ]] || { select_device 'boot' || return 1; } - BCMDS[grub]="${BCMDS[grub]} --target=i386-pc $BOOT_DEVICE" - else - if [[ $ROOT_PART == */dev/mapper/* && ! $LVM && ! $LUKS_PASS ]]; then - luks_pass "$_LuksOpen" "" || return 1 - fi - BCMDS[grub]="${BCMDS[grub]} --removable" - fi - - BCMDS[grub]="mkdir -p /run/udev && - mkdir -p /run/lvm && - mount --bind /hostrun/udev /run/udev && - mount --bind /hostrun/lvm /run/lvm && - ${BCMDS[grub]} && - grub-mkconfig -o /boot/grub/grub.cfg && - umount /run/udev && - umount /run/lvm" - return 0 -} - -setup_syslinux() -{ - EDIT_FILES[bootloader]="/boot/syslinux/syslinux.cfg" -} - -setup_systemd-boot() -{ - EDIT_FILES[bootloader]="/boot/loader/entries/$DIST.conf" -} - -prerun_grub() -{ - 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_DEV ]]; 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 == 'BIOS' && $LVM && $SEP_BOOT == false ]]; then - sed -i "s/GRUB_PRELOAD_MODULES=.*/GRUB_PRELOAD_MODULES=\"lvm\"/g" $cfg - fi - - # needed for grub-probe module to work properly in the chroot - # once the grub install is done these will be umounted and removed - mkdir -p /run/lvm - mkdir -p /run/udev - mkdir -p $MNT/hostrun/lvm - mkdir -p $MNT/hostrun/udev - mount --bind /run/lvm $MNT/hostrun/lvm - mount --bind /run/udev $MNT/hostrun/udev - - return 0 -} - -prerun_systemd-boot() -{ - # no LVM then systemd-boot uses PARTUUID - [[ $ROOT_PART =~ /dev/mapper ]] || ROOT_PART_ID="PART$ROOT_PART_ID" - - # create the boot entry configs - mkdir -p ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/entries - cat > ${MNT}${BMNTS[$SYS-systemd-boot]}/loader/loader.conf << EOF -default $DIST -timeout 5 -editor no -EOF - cat > ${MNT}${BMNTS[$SYS-systemd-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 - # add pacman hook to update the bootloader when systemd receives an update - 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-boot requires this before running bootctl - systemd-machine-id-setup --root="$MNT" - return 0 -} - -prerun_syslinux() -{ - mkdir -pv $MNT${BMNTS[$SYS-syslinux]}/syslinux - cp -rfv /usr/lib/syslinux/bios/* $MNT${BMNTS[$SYS-syslinux]}/syslinux/ - - cat > $cfgdir/syslinux.cfg << EOF -UI menu.c32 -PROMPT 0 - -MENU TITLE $DIST Boot Menu -TIMEOUT 50 -DEFAULT $DIST - -LABEL $DIST -MENU LABEL $DIST Linux -LINUX ../vmlinuz-$KERNEL -APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw -INITRD ../initramfs-$KERNEL.img$([[ $UCODE ]] && printf "\nINITRD %s" "../${UCODE}.img") - -LABEL ${DIST}fallback -MENU LABEL $DIST Linux Fallback -LINUX ../vmlinuz-$KERNEL -APPEND root=$ROOT_PART_ID $([[ $LUKS_DEV ]] && printf "%s " "$LUKS_DEV")rw -INITRD ../initramfs-$KERNEL-fallback.img$([[ $UCODE ]] && printf "\nINITRD %s" "../${UCODE}.img") -EOF - return 0 -} - -install_bootloader() -{ - if ! [[ $ROOT_PART =~ /dev/mapper ]]; then - ROOT_PART_ID="UUID=$(blkid -s PARTUUID -o value $ROOT_PART)" - else - # for LVM we just use the partition label - ROOT_PART_ID="$ROOT_PART" - fi - - # remove old UEFI boot entries - if [[ $SYS == 'UEFI' ]]; then - find ${MNT}${BMNTS[UEFI-$BOOTLDR]}/EFI/ \ - -maxdepth 1 -mindepth 1 -name '[aA][rR][cC][hH][lL]abs' -type d -exec rm -rf '{}' \; >/dev/null 2>&1 - find ${MNT}${BMNTS[UEFI-$BOOTLDR]}/EFI/ \ - -maxdepth 1 -mindepth 1 -name '[Bb][oO][oO][tT]' -type d -exec rm -rf '{}' \; >/dev/null 2>&1 - fi - - prerun_$BOOTLDR - printf "\nInstalling and setting up $BOOTLDR in ${BMNTS[$SYS-$BOOTLDR]}\n\n" - chrun "${BCMDS[$BOOTLDR]}" - echeck "${BCMDS[$BOOTLDR]}" - - if [[ -d $MNT/hostrun ]]; then - # remove mounted directories - 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 - - return 0 -} - -############################################################################### -# dialog menus - -show_cfg() -{ - local cmd="${BCMDS[$BOOTLDR]}" - local mnt="${BMNTS[$SYS-$BOOTLDR]}" - local pkgs="${PACKAGES# }" - msgbox "$_PrepTitle" " - ----------- PARTITION CONFIGURATION ------------ - - Root: ${ROOT_PART:-None} - Boot: ${BOOT_PART:-${BOOT_DEVICE:-None}} - - Swap: ${SWAP_PART:-None} - Size: ${SWAP_SIZE:-None} - - LVM: ${LVM:-None} - LUKS: ${LUKS:-None} - - Extra Mounts: ${EXTRA_MNTS:-${EXTRA_MNT:-None}} - Mkinit Hooks: ${HOOKS:-None} - - ----------- BOOTLOADER CONFIGURATION ----------- - - Bootloader: ${BOOTLDR:-None} - Mountpoint: ${mnt:-None} - Command: ${cmd:-None} - - ------------- SYSTEM CONFIGURATION ------------- - - Locale: ${LOCALE:-None} - Keymap: ${KEYMAP:-None} - Hostname: ${HOSTNAME:-None} - Timezone: ${ZONE:-None}/${SUBZONE:-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}") -" -} - -cfg_menu() -{ - tput civis - if ! MYSHELL="$(menubox "$_ShellTitle" "$_ShellBody" '/usr/bin/zsh' '-' '/bin/bash' '-' '/usr/bin/mksh' '-')"; then - return 1 - fi - - tput cnorm - if ! HOSTNAME="$(getinput "$_ConfHost" "$_HostNameBody" "${DIST,,}")"; then - return 1 - fi - - tput civis - if ! LOCALE="$(menubox "$_ConfLocale" "$_LocaleBody" $LOCALES)"; then - return 1 - fi - - select_timezone || return 1 - - if ! KERNEL="$(menubox "$_KernelTitle" "$_KernelBody" 'linux' '-' 'linux-lts' '-')"; then - return 1 - fi - - select_mirrorcmd || return 1 - - CONFIG_DONE=true - return 0 -} +# helper functions select_language() { @@ -2292,320 +2213,343 @@ select_language() return 0 } -user_creation() +chrun() { - tput cnorm - local values - if ! values="$(dialog --stdout --no-cancel --separator '~' \ - --ok-label "Submit" --backtitle "$BT" --title " $_UserTitle " \ - --insecure --mixedform "$_UserBody" 0 0 14 \ - "$_Username" 1 1 "" 1 $((${#_Username} + 2)) $COLUMNS 0 0 \ - "$_Password" 2 1 "" 2 $((${#_Password} + 2)) $COLUMNS 0 1 \ - "$_Password2" 3 1 "" 3 $((${#_Password2} + 2)) $COLUMNS 0 1 \ - "$_RootBody" 6 1 "" 6 $((${#_RootBody} + 1)) $COLUMNS 0 2 \ - "$_Password" 8 1 "" 8 $((${#_Password} + 2)) $COLUMNS 0 1 \ - "$_Password2" 9 1 "" 9 $((${#_Password2} + 2)) $COLUMNS 0 1)"; then - return 1 + arch-chroot $MNT /bin/bash -c "$1" +} + +json() +{ + # get a value from http://api.ipstack.com in json format using my API key this includes: ip, geolocation, country name + curl -s "http://api.ipstack.com/$2" | python3 -c "import sys, json; print(json.load(sys.stdin)['$1'])" +} + +src() +{ + # source file ($1), if it fails we die with an error message + if ! . "$1" 2>/dev/null; then + printf "\nFailed to source file %s\n" "$1" + die 1 fi - - local user pass pass2 rpass rpass2 - user="$(awk -F'~' '{print $1}' <<< "$values")" - pass="$(awk -F'~' '{print $2}' <<< "$values")" - pass2="$(awk -F'~' '{print $3}' <<< "$values")" - 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] ]]; then - msgbox "$_UserErrTitle" "$_UserErrBody" - user_creation || return 1 - elif [[ $pass == "" ]]; then - msgbox "$_ErrTitle" "$_UserPassEmpty\n$_TryAgain" - user_creation || return 1 - elif [[ "$rpass" != "$rpass2" ]]; then - msgbox "$_ErrTitle" "$_RootPassErr\n$_TryAgain" - user_creation || return 1 - elif [[ "$pass" != "$pass2" ]]; then - msgbox "$_ErrTitle" "$_UserPassErr\n$_TryAgain" - user_creation || return 1 - fi - - NEWUSER="$user" - USER_PASS="$pass" - ROOT_PASS="$rpass" return 0 } -select_keymap() +ssd() { - tput civis - if ! KEYMAP="$(menubox "$_PrepLayout" "$_XMapBody" \ - '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')"; then - return 1 + # returns 0 (true) when the device passed ($1) is NOT a rotational device + local i dev=$1 + + # check for LVM and or LUKS for their origin devices + if [[ $LUKS && ! $LVM && $dev =~ $LUKS_NAME ]]; then + dev="${LUKS_PART}" + elif [[ $LVM && ! $LUKS && ${#GROUP_PARTS[@]} -eq 1 && ${GROUP_PARTS[*]} =~ $dev ]]; then + dev="${GROUP_PARTS[*]}" fi - # when a matching console map is not available open a selection dialog - if [[ $CMAPS == *"$KEYMAP"* ]]; then - CMAP="$KEYMAP" + dev=${dev#/dev/} + [[ $dev =~ nvme ]] && dev=${dev%p[0-9]*} || dev=${dev%[0-9]*} + + i=$(cat /sys/block/$dev/queue/rotational 2>/dev/null) + + # return value check + (( ${i:-1} == 0 )) +} + +die() +{ + if (( $# >= 1 )); then + local exitcode=$1 else - if ! CMAP="$(menubox "$_CMapTitle" "$_CMapBody" $CMAPS)"; then + local exitcode=0 + fi + + # reset SIGINT + trap - INT + + tput cnorm + if [[ -d $MNT ]] && command cd /; then + umount_dir $MNT + if (( exitcode == 127 )); then + umount -l /run/archiso/bootmnt + systemctl -i reboot + fi + fi + + exit $exitcode +} + +sigint() +{ + # used to trap SIGINT and cleanly exit the program + printf "\nCTRL-C caught\nCleaning up...\n" + die 1 +} + +print4() +{ + # takes an arbitrary number of input fields and prints them out in fourths on separate lines + local str="$*" + if [[ ${#str} -gt $((COLUMNS / 2)) ]]; then + str="$(awk -v q="$(awk '{print int(NF / 4)}' <<< "$str")" '{ + pkgs1=pkgs2=pkgs3=pkgs4="" + for (i=1; i<q; i++) { + if (i == 1) { + pkgs1=$i + } else { + pkgs1=pkgs1" "$i + } + } + for (i=q; i<q * 2; i++) { + pkgs2=pkgs2" "$i + } + for (i=q * 2; i<q * 3; i++) { + pkgs3=pkgs3" "$i + } + for (i=q * 3; i<NF; i++) { + pkgs4=pkgs4" "$i + } + printf "%s\n %s\n %s\n %s", pkgs1, pkgs2, pkgs3, pkgs4 + }' <<< "$str")" + fi + printf "%s\n" "$str" +} + +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 + + DEV_COUNT="$(wc -l <<< "$SYS_DEVS")" +} + +system_identify() +{ + local efidir="/sys/firmware/efi" + + # for virtual machine remove the ucode + 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 + + if [[ -d $efidir ]]; then + SYS="UEFI" + grep -q $efidir/efivars <<< "$(mount)" || + mount -t efivarfs efivarfs $efidir/efivars + else + SYS="BIOS" + fi + + BT="$DIST Installer - $SYS - v$VER" +} + +load_bcm() +{ + infobox "Broadcom Wireless Setup" "\nLoading wifi kernel modules please wait...\n" 1 + rmmod wl >/dev/null 2>&1 + rmmod bcma >/dev/null 2>&1 + rmmod b43 >/dev/null 2>&1 + rmmod ssb >/dev/null 2>&1 + modprobe wl >/dev/null 2>&1 + depmod -a >/dev/null 2>&1 + BROADCOM_WL=true +} + +net_connect() +{ + chk_connect() { curl -s --head 'https://www.archlinux.org/mirrorlist/all/' | sed '1q' | grep -qw '200'; } + + if ! chk_connect; then + if [[ $(systemctl is-active NetworkManager) == "active" ]] && hash nmtui >/dev/null 2>&1; then + tput civis + + # fix ugly nmtui colours + printf "\e]P1191919" # #191919 + printf "\e]P4191919" # #191919 + + nmtui-connect + + # restore + printf "\e]P1D15355" # #D15355 + printf "\e]P4255a9b" # #255a9b + chk_connect || return 1 + else return 1 fi fi + # if we've made it here we likely have a good connection + return 0 +} - if [[ $DISPLAY && $TERM != 'linux' ]]; then - setxkbmap $KEYMAP >/dev/null 2>&1 - else - loadkeys $CMAP >/dev/null 2>&1 +system_checks() +{ + if [[ $(whoami) != "root" ]]; then + infobox "$_ErrTitle" "$_NotRoot\n$_Exit" + die 1 + elif ! grep -qw 'lm' /proc/cpuinfo; then + infobox "$_ErrTitle" "$_Not64Bit\n$_Exit" + die 1 + fi + + # setup for specific broadcom chips + grep -q 'BCM4352' <<< "$(lspci -vnn -d 14e4:)" && load_bcm + + # connect or confirm connection, otherwise bail + net_connect || { infobox "$_ErrTitle" "$_NoNetwork\n$_Exit" 3; die 1; } +} + +prechecks() +{ + if ! [[ $(lsblk -o MOUNTPOINT) =~ $MNT ]]; then + msgbox "$_ErrTitle" "$_ErrNoMount" + SELECTED=4 + return 1 + elif [[ $# -eq 1 && $CONFIG_DONE != true && ! $NEWUSER ]]; then + msgbox "$_ErrTitle" "\nYou need to create a user first.\n" + SELECTED=5 + return 1 + elif [[ $# -eq 1 && $CONFIG_DONE != true ]]; then + msgbox "$_ErrTitle" "$_ErrNoConfig" + SELECTED=6 + return 1 fi return 0 } -select_timezone() +errshow() { - # create associative array for SUBZONES[zone] - local f="/usr/share/zoneinfo/zone.tab" - declare -A SUBZONES - for i in America Australia Asia Atlantic Africa Europe Indian Pacific Arctic Antarctica; do - SUBZONES[$i]="$(awk '/'"$i"'\// {gsub(/'"$i"'\//, ""); print $3, $1}' $f | sort)" - done + local last_exit_code=$? + (( last_exit_code == 0 )) && return 0 - tput civis - if ! ZONE="$(menubox "$_TimeZTitle" "$_TimeZBody" \ - 'America' '-' 'Australia' '-' 'Asia' '-' 'Atlantic' '-' 'Africa' '-' \ - 'Europe' '-' 'Indian' '-' 'Pacific' '-' 'Arctic' '-' 'Antarctica' '-')"; then - return 1 - fi + local err + err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" - if ! SUBZONE="$(menubox "$_TimeZTitle" "$_TimeSubZBody" ${SUBZONES[$ZONE]})"; then - return 1 - fi - - yesno "$_TimeZTitle" "$_TimeZQ $ZONE/$SUBZONE?\n" || select_timezone -} - -select_wm_or_de() -{ - tput civis - if ! INSTALL_WMS="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ - --title " $_WMChoice " --checklist "$_WMChoiceBody\n" 0 0 14 \ - "i3-gaps" "A fork of i3wm with more features including gaps" off \ - "dwm" "A fork of dwm, with more layouts and features" off \ - "openbox" "A lightweight, powerful, and highly configurable stacking wm" off \ - "bspwm" "A tiling wm that represents windows as the leaves of a binary tree" off \ - "gnome" "A desktop environment that aims to be simple and easy to use" off \ - "cinnamon" "A desktop environment combining traditional desktop with modern effects" off \ - "plasma" "A kde software project currently comprising a full desktop environment" off \ - "xfce4" "A lightweight and modular desktop environment based on gtk+2/3" off)"; then - return 1 - fi - - WM_NUM=$(awk '{print NF}' <<< "$INSTALL_WMS") - WM_PACKAGES="${INSTALL_WMS/dwm/}" # remove dwm from package list - WM_PACKAGES="${WM_PACKAGES// / }" # remove double spaces - WM_PACKAGES="${WM_PACKAGES# }" # remove leading space - - # packages needed for the selected WMs/DEs - for wm in $INSTALL_WMS; do - LOGIN_CHOICES+="$wm - " - if [[ ${WM_EXT[$wm]} ]]; then - WM_PACKAGES+=" ${WM_EXT[$wm]}" - fi - done - - # choose how to log in - select_login || return 1 - - # choose which WM/DE to start at login, only for xinit - if [[ $LOGIN_TYPE == 'xinit' ]]; then - if [[ $WM_NUM -eq 1 ]]; then - LOGIN_WM="${WM_SESSIONS[$INSTALL_WMS]}" - else - if ! LOGIN_WM="$(menubox "$_WMLogin" "$_WMLoginBody" $LOGIN_CHOICES)"; then - return 1 - fi - LOGIN_WM="${WM_SESSIONS[$LOGIN_WM]}" - fi - yesno "$_WMLogin" "$_AutoLoginBody\n" && AUTOLOGIN=true || AUTOLOGIN=false + if [[ $err != "" ]]; then + msgbox "$_ErrTitle" "\nERROR: $err" else - AUTOLOGIN=false + msgbox "$_ErrTitle" "\nThe command exited abnormally: $1\n\nWith the no error message.\n" fi - - # add packages to the main package list - PACKAGES+=" ${WM_PACKAGES# }" } -select_login() +echeck() { - if ! LOGIN_TYPE="$(menubox "$_WMLogin" "$_LoginTypeBody" \ - "xinit" "Console login without a display manager" \ - "lightdm" "Lightweight display manager with a gtk greeter")"; then - return 1 - fi + local last_exit_code=$? + (( last_exit_code == 0 )) && return 0 - if [[ $LOGIN_TYPE == 'lightdm' ]]; then - WM_PACKAGES+=" lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice" - EDIT_FILES[login]="/etc/lightdm/lightdm.conf /etc/lightdm/lightdm-gtk-greeter.conf" + local err + err="$(sed 's/[^[:print:]]//g; s/\[[0-9\;:]*\?m//g; s/==> //g; s/] ERROR:/]\nERROR:/g' "$ERR")" + + if [[ $err != "" ]]; then + msgbox "$_ErrTitle" "\nERROR: $err" else - PACKAGES="${PACKAGES// lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice/}" - WM_PACKAGES="${WM_PACKAGES// lightdm lightdm-gtk-greeter lightdm-gtk-greeter-settings accountsservice/}" - EDIT_FILES[login]="/home/$NEWUSER/.xinitrc /home/$NEWUSER/.xprofile" + msgbox "$_ErrTitle" "\nThe command exited abnormally: $1\n\nWith the no error message.\n" fi + + [[ -e $DBG && $TERM == 'linux' ]] && more $DBG + + die 1 } -select_packages() +debug() { - if [[ $CURRENT_MENU != "packages" ]]; then - SAVED=$SELECTED - SELECTED=1 - CURRENT_MENU="packages" - elif (( SELECTED < 9 )); then - ((SELECTED++)) # increment the highlighted menu item - fi + set -x + exec 3>| $DBG + BASH_XTRACEFD=3 + DEBUG=true +} - tput civis - SELECTED=$(dialog --cr-wrap --no-cancel --stdout \ - --backtitle "$BT" --title " $_Packages " \ - --default-item $SELECTED --menu "$_PackageMenu" 0 0 14 \ - 1 "Browsers" \ - 2 "Editors" \ - 3 "Terminals" \ - 4 "Multimedia" \ - 5 "Chat/Mail" \ - 6 "Professional" \ - 7 "System" \ - 8 "Miscellaneous" \ - 9 "$_Done") - - if [[ $SELECTED -lt 9 ]]; then - case $SELECTED in - 1) PACKAGES+=" $(select_browsers)" ;; - 2) PACKAGES+=" $(select_editors)" ;; - 3) PACKAGES+=" $(select_terminals)" ;; - 4) PACKAGES+=" $(select_multimedia)" ;; - 5) PACKAGES+=" $(select_mailchat)" ;; - 6) PACKAGES+=" $(select_prof)" ;; - 7) PACKAGES+=" $(select_managment)" ;; - 8) PACKAGES+=" $(select_extra)" ;; - esac - select_packages - fi - - # add any extras for each package - for pkg in $PACKAGES; do - [[ ${PKG_EXT[$pkg]} ]] && PACKAGES+=" ${PKG_EXT[$pkg]}" - done - - # add mksh to package list if it was chosen as the login shell - if [[ $MYSHELL == *mksh ]]; then - PACKAGES+=" mksh" - fi - - # remove duplicates and leading spaces - PACKAGES="$(uniq <<< "${PACKAGES/^ /}")" +umount_dir() +{ + swapoff -a + [[ -d $1 ]] && umount -R $1 >/dev/null 2>&1 return 0 } -select_mirrorcmd() +msgbox() { - local c - local key="5f29642060ab983b31fdf4c2935d8c56" - - if hash reflector >/dev/null 2>&1; then - MIRROR_CMD="reflector --score 100 -l 50 -f 10 --sort rate --verbose" - yesno "$_MirrorTitle" "$_MirrorSetup" "Automatic" "Custom" && 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 --score 80 --latest 40 --fastest 10 --sort rate --verbose" - - tput cnorm - MIRROR_CMD="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ - --title " $_MirrorTitle " --inputbox "$_MirrorCmd\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")" - else - 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" - if [[ $c ]]; then - if [[ $c =~ (CA|US) ]]; then - MIRROR_CMD="curl -s '$w/?country=US&country=CA&protocol=https&use_mirror_status=on'" - else - MIRROR_CMD="curl -s '$w/?country=${c}&protocol=https&use_mirror_status=on'" - fi - else - local countries="country=US&country=CA&country=NZ&country=GB&country=AU" - MIRROR_CMD="curl -s '$w/?$countries&protocol=https&use_mirror_status=on'" - fi - fi - return 0 + tput civis + dialog --cr-wrap --backtitle "$BT" --title " $1 " --msgbox "$2\n" 0 0 } -edit_configs() +menubox() { - [[ $DEBUG == true ]] && str="View log & reboot" || str="Exit & reboot" - - tput civis - local choice - choice=$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ - --title " $_EditTitle " --menu "$_EditBody" 0 0 14 "$str" "-" \ - "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 [[ ! $choice || $choice == "$str" ]]; then - [[ $DEBUG == true && -r $DBG ]] && vim $DBG - # when die() is passed 127 it will call: systemctl -i reboot - die 127 - else - local exists="" - for f in $(printf "%s" "${EDIT_FILES[$choice]}"); do - [[ -e ${MNT}$f ]] && exists+=" ${MNT}$f" - done - if [[ $exists ]]; then - vim -O $exists - else - msgbox "$_ErrTitle" "$_NoFileErr" - fi + local title="$1" + local body="$2" + shift 2 + local response + if ! response="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ + --title " $title " --menu "$body" 0 0 0 "$@")"; then + return 1 + fi + printf "%s" "$response" +} + +checkbox() +{ + local title="$1" + local body="$2" + shift 2 + local response + if ! response="$(dialog --cr-wrap --no-cancel --stdout --backtitle "$BT" \ + --title " $title " --checklist "$body" 0 0 0 "$@")"; then + return 1 + fi + printf "%s" "$response" +} + +getinput() +{ + local answer + if [[ $# -eq 4 && $4 == 'nolimit' ]]; then + answer="$(dialog --cr-wrap --stdout --backtitle "$BT" \ + --title " $1 " --inputbox "$2" 0 0 "$3")" + else + answer="$(dialog --cr-wrap --max-input 63 --stdout --backtitle "$BT" \ + --title " $1 " --inputbox "$2" 0 0 "$3")" + fi + + local e=$? + [[ $e -ne 0 || $answer == "" ]] && return 1 + printf "%s" "$answer" +} + +infobox() +{ + local sec="$3" + tput civis + dialog --cr-wrap --backtitle "$BT" --title " $1 " --infobox "$2\n" 0 0 + sleep ${sec:-2} +} + +yesno() +{ + tput civis + if [[ $# -eq 5 && $5 == "no" ]]; then + dialog --cr-wrap --backtitle "$BT" --defaultno --title " $1 " \ + --yes-label "$3" --no-label "$4" --yesno "$2\n" 0 0 + elif [[ $# -eq 4 ]]; then + dialog --cr-wrap --backtitle "$BT" --title " $1 " --yes-label "$3" \ + --no-label "$4" --yesno "$2\n" 0 0 + else + dialog --cr-wrap --backtitle "$BT" --title " $1 " --yesno "$2\n" 0 0 fi - edit_configs } ############################################################################### @@ -2681,6 +2625,8 @@ CMAPS="$(find /usr/share/kbd/keymaps -name '*.map.gz' | awk '{ print $1 " -" }' | sort)" +# make sure these are defined for some dialog size calculation +: "${LINES=$(tput lines)}" : "${COLUMNS=$(tput cols)}" # various associative arrays