2021-04-28 14:06:52 +00:00
|
|
|
|
# shellcheck source=./scripts/protection.sh
|
2020-01-08 16:21:01 -01:00
|
|
|
|
source "$GENTOO_INSTALL_REPO_DIR/scripts/protection.sh" || exit 1
|
2019-12-31 14:28:42 -01:00
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function elog() {
|
2020-04-23 20:31:18 +00:00
|
|
|
|
echo "[[1m+[m] $*"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function einfo() {
|
2021-04-19 22:51:48 +00:00
|
|
|
|
echo "[[1;32m+[m] [1;33m$*[m"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function ewarn() {
|
2020-04-23 20:31:18 +00:00
|
|
|
|
echo "[[1;33m+[m] $*" >&2
|
2019-12-31 14:28:42 -01:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function eerror() {
|
2021-04-19 22:51:48 +00:00
|
|
|
|
echo "[1;31merror:[m $*" >&2
|
2019-12-31 14:28:42 -01:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function die() {
|
2019-12-31 14:28:42 -01:00
|
|
|
|
eerror "$*"
|
2021-04-28 14:06:52 +00:00
|
|
|
|
[[ $$ == "$GENTOO_INSTALL_REPO_SCRIPT_PID" ]] \
|
2021-04-19 22:51:48 +00:00
|
|
|
|
|| kill "$GENTOO_INSTALL_REPO_SCRIPT_PID"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 19:34:36 +00:00
|
|
|
|
# Prints an error with file:line info of the nth "stack frame".
|
|
|
|
|
# 0 is this function, 1 the calling function, 2 its parent, and so on.
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function die_trace() {
|
2020-04-19 19:34:36 +00:00
|
|
|
|
local idx="${1:-0}"
|
|
|
|
|
shift
|
|
|
|
|
echo "[1m${BASH_SOURCE[$((idx + 1))]}:${BASH_LINENO[$idx]}: [1;31merror:[m ${FUNCNAME[$idx]}: $*" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function for_line_in() {
|
2020-04-20 22:40:54 +00:00
|
|
|
|
while IFS="" read -r line || [[ -n $line ]]; do
|
2020-01-02 22:29:17 -01:00
|
|
|
|
"$2" "$line"
|
|
|
|
|
done <"$1"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function flush_stdin() {
|
2020-01-02 22:29:17 -01:00
|
|
|
|
local empty_stdin
|
2021-04-28 14:06:52 +00:00
|
|
|
|
# Unused variable is intentional.
|
|
|
|
|
# shellcheck disable=SC2034
|
2020-01-02 22:29:17 -01:00
|
|
|
|
while read -r -t 0.01 empty_stdin; do true; done
|
2020-01-04 10:55:31 -01:00
|
|
|
|
}
|
2020-01-02 22:29:17 -01:00
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function ask() {
|
2020-01-04 10:55:31 -01:00
|
|
|
|
local response
|
2019-12-31 14:28:42 -01:00
|
|
|
|
while true; do
|
2020-01-04 10:55:31 -01:00
|
|
|
|
flush_stdin
|
2020-01-04 11:09:29 -01:00
|
|
|
|
read -r -p "$* (Y/n) " response \
|
|
|
|
|
|| die "Error in read"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
case "${response,,}" in
|
|
|
|
|
'') return 0 ;;
|
|
|
|
|
y|yes) return 0 ;;
|
|
|
|
|
n|no) return 1 ;;
|
|
|
|
|
*) continue ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function try() {
|
2020-01-04 10:55:31 -01:00
|
|
|
|
local response
|
|
|
|
|
local cmd_status
|
2020-01-04 11:09:29 -01:00
|
|
|
|
local prompt_parens="([1mS[mhell/[1mr[metry/[1ma[mbort/[1mc[montinue/[1mp[mrint)"
|
2020-01-04 10:55:31 -01:00
|
|
|
|
|
|
|
|
|
# Outer loop, allows us to retry the command
|
|
|
|
|
while true; do
|
|
|
|
|
# Try command
|
|
|
|
|
"$@"
|
|
|
|
|
cmd_status="$?"
|
|
|
|
|
|
2020-04-20 22:40:54 +00:00
|
|
|
|
if [[ $cmd_status != 0 ]]; then
|
2020-01-04 12:10:40 -01:00
|
|
|
|
echo "[1;31m * Command failed: [1;33m\$[m $*"
|
|
|
|
|
echo "Last command failed with exit code $cmd_status"
|
2020-01-04 10:55:31 -01:00
|
|
|
|
|
|
|
|
|
# Prompt until input is valid
|
|
|
|
|
while true; do
|
2020-01-04 12:10:40 -01:00
|
|
|
|
echo -n "Specify next action $prompt_parens "
|
2020-01-04 10:55:31 -01:00
|
|
|
|
flush_stdin
|
2020-01-04 11:09:29 -01:00
|
|
|
|
read -r response \
|
|
|
|
|
|| die "Error in read"
|
2020-01-04 10:55:31 -01:00
|
|
|
|
case "${response,,}" in
|
|
|
|
|
''|s|shell)
|
2020-01-04 12:10:40 -01:00
|
|
|
|
echo "You will be prompted for action again after exiting this shell."
|
|
|
|
|
/bin/bash --init-file <(echo "init_bash")
|
2020-01-04 10:55:31 -01:00
|
|
|
|
;;
|
|
|
|
|
r|retry) continue 2 ;;
|
2020-01-04 11:09:29 -01:00
|
|
|
|
a|abort) die "Installation aborted" ;;
|
|
|
|
|
c|continue) return 0 ;;
|
2020-01-04 12:10:40 -01:00
|
|
|
|
p|print) echo "[1;33m\$[m $*" ;;
|
2020-01-04 11:31:39 -01:00
|
|
|
|
*) ;;
|
2020-01-04 10:55:31 -01:00
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
fi
|
2020-01-04 11:27:12 -01:00
|
|
|
|
|
|
|
|
|
return
|
2020-01-04 10:55:31 -01:00
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function countdown() {
|
2020-01-04 12:19:21 -01:00
|
|
|
|
echo -n "$1" >&2
|
2019-12-31 14:28:42 -01:00
|
|
|
|
|
|
|
|
|
local i="$2"
|
|
|
|
|
while [[ $i -gt 0 ]]; do
|
2020-01-04 12:19:21 -01:00
|
|
|
|
echo -n "[1;31m$i[m " >&2
|
2019-12-31 14:28:42 -01:00
|
|
|
|
i=$((i - 1))
|
|
|
|
|
sleep 1
|
|
|
|
|
done
|
2020-01-04 12:19:21 -01:00
|
|
|
|
echo >&2
|
2019-12-31 14:28:42 -01:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function download_stdout() {
|
2019-12-31 14:28:42 -01:00
|
|
|
|
wget --quiet --https-only --secure-protocol=PFS -O - -- "$1"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function download() {
|
2019-12-31 14:28:42 -01:00
|
|
|
|
wget --quiet --https-only --secure-protocol=PFS --show-progress -O "$2" -- "$1"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_blkid_field_by_device() {
|
2020-04-25 13:12:35 +00:00
|
|
|
|
local blkid_field="$1"
|
|
|
|
|
local device="$2"
|
|
|
|
|
blkid -g \
|
|
|
|
|
|| die "Error while executing blkid -g"
|
2020-11-06 16:58:15 -01:00
|
|
|
|
partprobe
|
2020-04-25 13:12:35 +00:00
|
|
|
|
local val
|
|
|
|
|
val="$(blkid -o export "$device")" \
|
|
|
|
|
|| die "Error while executing blkid '$device'"
|
2020-04-25 18:12:00 +00:00
|
|
|
|
val="$(grep -- "^$blkid_field=" <<< "$val")" \
|
2020-04-25 13:12:35 +00:00
|
|
|
|
|| die "Could not find $blkid_field=... in blkid output"
|
|
|
|
|
val="${val#"$blkid_field="}"
|
|
|
|
|
echo -n "$val"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_blkid_uuid_for_id() {
|
|
|
|
|
local dev
|
|
|
|
|
dev="$(resolve_device_by_id "$1")" \
|
|
|
|
|
|| die "Could not resolve device with id=$dev"
|
|
|
|
|
local uuid
|
|
|
|
|
uuid="$(get_blkid_field_by_device 'UUID' "$dev")" \
|
|
|
|
|
|| die "Could not get UUID from blkid for device=$dev"
|
2020-04-25 16:06:38 +00:00
|
|
|
|
echo -n "$uuid"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_device_by_blkid_field() {
|
2020-04-21 18:58:21 +00:00
|
|
|
|
local blkid_field="$1"
|
|
|
|
|
local field_value="$2"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
blkid -g \
|
2020-04-25 13:12:35 +00:00
|
|
|
|
|| die "Error while executing blkid -g"
|
2020-11-06 16:58:15 -01:00
|
|
|
|
partprobe
|
2019-12-31 14:28:42 -01:00
|
|
|
|
local dev
|
2020-04-21 18:58:21 +00:00
|
|
|
|
dev="$(blkid -o export -t "$blkid_field=$field_value")" \
|
|
|
|
|
|| die "Error while executing blkid to find $blkid_field=$field_value"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
dev="$(grep DEVNAME <<< "$dev")" \
|
|
|
|
|
|| die "Could not find DEVNAME=... in blkid output"
|
2020-04-25 13:12:35 +00:00
|
|
|
|
dev="${dev#"DEVNAME="}"
|
2019-12-31 14:28:42 -01:00
|
|
|
|
echo -n "$dev"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_device_by_partuuid() {
|
2020-04-21 18:58:21 +00:00
|
|
|
|
get_device_by_blkid_field 'PARTUUID' "$1"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_device_by_uuid() {
|
2020-04-21 21:41:27 +00:00
|
|
|
|
get_device_by_blkid_field 'UUID' "$1"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function cache_lsblk_output() {
|
2020-10-08 20:16:26 +00:00
|
|
|
|
CACHED_LSBLK_OUTPUT="$(lsblk --all --path --pairs --output NAME,PTUUID,PARTUUID)" \
|
|
|
|
|
|| die "Error while executing lsblk to cache output"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_device_by_ptuuid() {
|
2020-04-23 21:15:41 +00:00
|
|
|
|
local ptuuid="${1,,}"
|
|
|
|
|
local dev
|
2020-10-08 20:16:26 +00:00
|
|
|
|
if [[ -n $CACHED_LSBLK_OUTPUT ]]; then
|
|
|
|
|
dev="$CACHED_LSBLK_OUTPUT"
|
|
|
|
|
else
|
|
|
|
|
dev="$(lsblk --all --path --pairs --output NAME,PTUUID,PARTUUID)" \
|
|
|
|
|
|| die "Error while executing lsblk to find PTUUID=$ptuuid"
|
|
|
|
|
fi
|
2020-04-23 21:15:41 +00:00
|
|
|
|
dev="$(grep "ptuuid=\"$ptuuid\" partuuid=\"\"" <<< "${dev,,}")" \
|
|
|
|
|
|| die "Could not find PTUUID=... in lsblk output"
|
2020-04-23 21:19:00 +00:00
|
|
|
|
dev="${dev%'" ptuuid='*}"
|
2020-04-23 21:15:41 +00:00
|
|
|
|
dev="${dev#'name="'}"
|
|
|
|
|
echo -n "$dev"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function uuid_to_mduuid() {
|
2020-04-23 21:15:41 +00:00
|
|
|
|
local mduuid="${1,,}"
|
|
|
|
|
mduuid="${mduuid//-/}"
|
|
|
|
|
mduuid="${mduuid:0:8}:${mduuid:8:8}:${mduuid:16:8}:${mduuid:24:8}"
|
2020-04-25 13:12:35 +00:00
|
|
|
|
echo -n "$mduuid"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_device_by_mdadm_uuid() {
|
|
|
|
|
local mduuid
|
|
|
|
|
mduuid="$(uuid_to_mduuid "$1")" \
|
|
|
|
|
|| die "Could not resolve mduuid from uuid=$1"
|
2020-04-23 21:15:41 +00:00
|
|
|
|
local dev
|
|
|
|
|
dev="$(mdadm --examine --scan)" \
|
|
|
|
|
|| die "Error while executing mdadm to find array with UUID=$mduuid"
|
|
|
|
|
dev="$(grep "uuid=$mduuid" <<< "${dev,,}")" \
|
|
|
|
|
|| die "Could not find UUID=... in mdadm output"
|
|
|
|
|
dev="${dev%'metadata='*}"
|
|
|
|
|
dev="${dev#'array'}"
|
|
|
|
|
dev="${dev#"${dev%%[![:space:]]*}"}"
|
|
|
|
|
dev="${dev%"${dev##*[![:space:]]}"}"
|
|
|
|
|
echo -n "$dev"
|
2020-04-21 12:33:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function get_device_by_luks_name() {
|
2020-04-24 21:02:00 +00:00
|
|
|
|
echo -n "/dev/mapper/$1"
|
2020-04-23 20:31:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function create_resolve_entry() {
|
2020-04-23 15:32:36 +00:00
|
|
|
|
local id="$1"
|
|
|
|
|
local type="$2"
|
|
|
|
|
local arg="${3,,}"
|
|
|
|
|
|
|
|
|
|
DISK_ID_TO_RESOLVABLE[$id]="$type:$arg"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function create_resolve_entry_device() {
|
2020-10-03 14:24:48 +00:00
|
|
|
|
local id="$1"
|
|
|
|
|
local dev="$2"
|
|
|
|
|
|
|
|
|
|
DISK_ID_TO_RESOLVABLE[$id]="device:$dev"
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function resolve_device_by_id() {
|
2020-04-23 15:32:36 +00:00
|
|
|
|
local id="$1"
|
|
|
|
|
[[ -v DISK_ID_TO_RESOLVABLE[$id] ]] \
|
|
|
|
|
|| die "Cannot resolve id='$id' to a block device (no table entry)"
|
|
|
|
|
|
|
|
|
|
local type="${DISK_ID_TO_RESOLVABLE[$id]%%:*}"
|
|
|
|
|
local arg="${DISK_ID_TO_RESOLVABLE[$id]#*:}"
|
|
|
|
|
|
|
|
|
|
case "$type" in
|
2020-04-23 21:15:41 +00:00
|
|
|
|
'partuuid') get_device_by_partuuid "$arg" ;;
|
|
|
|
|
'ptuuid') get_device_by_ptuuid "$arg" ;;
|
|
|
|
|
'uuid') get_device_by_uuid "$arg" ;;
|
|
|
|
|
'mdadm') get_device_by_mdadm_uuid "$arg" ;;
|
2020-04-24 21:02:00 +00:00
|
|
|
|
'luks') get_device_by_luks_name "$arg" ;;
|
2020-10-03 14:24:48 +00:00
|
|
|
|
'device') echo -n "$arg" ;;
|
2020-04-23 15:32:36 +00:00
|
|
|
|
*) die "Cannot resolve '$type:$arg' to device (unknown type)"
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function load_or_generate_uuid() {
|
2019-12-31 14:28:42 -01:00
|
|
|
|
local uuid
|
|
|
|
|
local uuid_file="$UUID_STORAGE_DIR/$1"
|
|
|
|
|
|
2020-04-20 22:40:54 +00:00
|
|
|
|
if [[ -e $uuid_file ]]; then
|
2019-12-31 14:28:42 -01:00
|
|
|
|
uuid="$(cat "$uuid_file")"
|
|
|
|
|
else
|
|
|
|
|
uuid="$(uuidgen -r)"
|
|
|
|
|
mkdir -p "$UUID_STORAGE_DIR"
|
|
|
|
|
echo -n "$uuid" > "$uuid_file"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo -n "$uuid"
|
|
|
|
|
}
|
2020-04-19 19:34:36 +00:00
|
|
|
|
|
|
|
|
|
# Parses named arguments and stores them in the associative array `arguments`.
|
2020-04-20 22:40:54 +00:00
|
|
|
|
# If given, the associative array `known_arguments` must contain a list of arguments
|
|
|
|
|
# prefixed with + (mandatory) or ? (optional). "at least one of" can be expressed by +a|b|c.
|
2021-04-28 14:06:52 +00:00
|
|
|
|
function parse_arguments() {
|
2020-04-19 19:34:36 +00:00
|
|
|
|
local key
|
|
|
|
|
local value
|
|
|
|
|
local a
|
|
|
|
|
for a in "$@"; do
|
|
|
|
|
key="${a%%=*}"
|
|
|
|
|
value="${a#*=}"
|
2020-04-21 19:52:46 +00:00
|
|
|
|
|
2020-04-21 20:04:39 +00:00
|
|
|
|
if [[ $key == "$a" ]]; then
|
2020-04-21 19:52:46 +00:00
|
|
|
|
extra_arguments+=("$a")
|
|
|
|
|
continue
|
|
|
|
|
fi
|
|
|
|
|
|
2020-04-20 22:40:54 +00:00
|
|
|
|
arguments[$key]="$value"
|
2020-04-19 19:34:36 +00:00
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
declare -A allowed_keys
|
2020-04-20 22:40:54 +00:00
|
|
|
|
if [[ -v known_arguments ]]; then
|
2020-04-19 19:34:36 +00:00
|
|
|
|
local m
|
|
|
|
|
for m in "${known_arguments[@]}"; do
|
|
|
|
|
case "${m:0:1}" in
|
|
|
|
|
'+')
|
|
|
|
|
m="${m:1}"
|
|
|
|
|
local has_opt=false
|
|
|
|
|
local m_opt
|
|
|
|
|
# Splitting is intentional here
|
2020-04-20 22:40:54 +00:00
|
|
|
|
# shellcheck disable=SC2086
|
|
|
|
|
for m_opt in ${m//|/ }; do
|
|
|
|
|
allowed_keys[$m_opt]=true
|
|
|
|
|
if [[ -v arguments[$m_opt] ]]; then
|
2020-04-19 19:34:36 +00:00
|
|
|
|
has_opt=true
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
2020-04-23 21:56:53 +00:00
|
|
|
|
[[ $has_opt == "true" ]] \
|
2020-04-19 19:34:36 +00:00
|
|
|
|
|| die_trace 2 "Missing mandatory argument $m=..."
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
'?')
|
2020-04-20 22:40:54 +00:00
|
|
|
|
allowed_keys[${m:1}]=true
|
2020-04-19 19:34:36 +00:00
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
*) die_trace 2 "Invalid start character in known_arguments, in argument '$m'" ;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
2020-04-20 22:40:54 +00:00
|
|
|
|
for a in "${!arguments[@]}"; do
|
|
|
|
|
[[ -v allowed_keys[$a] ]] \
|
2020-04-23 15:32:36 +00:00
|
|
|
|
|| die_trace 2 "Unknown argument '$a'"
|
2020-04-20 22:40:54 +00:00
|
|
|
|
done
|
|
|
|
|
fi
|
2020-04-19 19:34:36 +00:00
|
|
|
|
}
|
2021-05-29 20:14:46 +00:00
|
|
|
|
|
|
|
|
|
function check_has_programs() {
|
|
|
|
|
local failed=()
|
|
|
|
|
local program
|
|
|
|
|
for program in "$@"; do
|
|
|
|
|
type "$1" &>/dev/null \
|
|
|
|
|
|| failed+=("$program")
|
|
|
|
|
done
|
|
|
|
|
|
2021-05-29 20:16:05 +00:00
|
|
|
|
[[ ${#failed[@]} -eq 0 ]] \
|
|
|
|
|
&& return
|
|
|
|
|
|
2021-05-29 20:14:46 +00:00
|
|
|
|
echo "The following programs are required for the installer to work, but are currently missing on your system:" >&2
|
|
|
|
|
echo " ${failed[@]}" >&2
|
|
|
|
|
|
|
|
|
|
if type pacman &>/dev/null; then
|
|
|
|
|
echo "We have detected that pacman is available."
|
|
|
|
|
if ask "Do you want to install the missing programs automatically?"; then
|
|
|
|
|
pacman -Sy "${failed[@]}"
|
|
|
|
|
return
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
die "Aborted installer because of missing required programs."
|
|
|
|
|
}
|