thiblizzfix: minor typosdcd8b8b3 years agoHistory
453 lines · 10.9 KB · Shell
RawDownload
# shellcheck source=./scripts/protection.shsource "$GENTOO_INSTALL_REPO_DIR/scripts/protection.sh" || exit 1 function elog() {	echo "[+] $*"} function einfo() {	echo "[+] $*"} function ewarn() {	echo "[!] $*" >&2} function eerror() {	echo "error: $*" >&2} function die() {	eerror "$*"	[[ -v GENTOO_INSTALL_REPO_SCRIPT_PID && $$ -ne $GENTOO_INSTALL_REPO_SCRIPT_PID ]] \		&& kill "$GENTOO_INSTALL_REPO_SCRIPT_PID"	exit 1} # 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.function die_trace() {	local idx="${1:-0}"	shift	echo "${BASH_SOURCE[$((idx + 1))]}:${BASH_LINENO[$idx]}: error: ${FUNCNAME[$idx]}: $*" >&2	exit 1} function for_line_in() {	while IFS="" read -r line || [[ -n $line ]]; do		"$2" "$line"	done <"$1"} function flush_stdin() {	local empty_stdin	# Unused variable is intentional.	# shellcheck disable=SC2034	while read -r -t 0.01 empty_stdin; do true; done} function ask() {	local response	while true; do		flush_stdin		read -r -p "$* (Y/n) " response \			|| die "Error in read"		case "${response,,}" in			'') return 0 ;;			y|yes) return 0 ;;			n|no) return 1 ;;			*) continue ;;		esac	done} function try() {	local response	local cmd_status	local prompt_parens="(Shell/retry/abort/continue/print)" 	# Outer loop, allows us to retry the command	while true; do		# Try command		"$@"		cmd_status="$?" 		if [[ $cmd_status != 0 ]]; then			echo " * Command failed: \$ $*"			echo "Last command failed with exit code $cmd_status" 			# Prompt until input is valid			while true; do				echo -n "Specify next action $prompt_parens "				flush_stdin				read -r response \					|| die "Error in read"				case "${response,,}" in					''|s|shell)						echo "You will be prompted for action again after exiting this shell."						/bin/bash --init-file <(echo "init_bash")						;;					r|retry) continue 2 ;;					a|abort) die "Installation aborted" ;;					c|continue) return 0 ;;					p|print) echo "\$ $*" ;;					*) ;;				esac			done		fi 		return	done} function countdown() {	echo -n "$1" >&2 	local i="$2"	while [[ $i -gt 0 ]]; do		echo -n "$i " >&2		i=$((i - 1))		sleep 1	done	echo >&2} function download_stdout() {	wget --quiet --https-only --secure-protocol=PFS -O - -- "$1"} function download() {	wget --quiet --https-only --secure-protocol=PFS --show-progress -O "$2" -- "$1"} function get_blkid_field_by_device() {	local blkid_field="$1"	local device="$2"	blkid -g -c /dev/null \		|| die "Error while executing blkid"	partprobe &>/dev/null	local val	val="$(blkid -c /dev/null -o export "$device")" \		|| die "Error while executing blkid '$device'"	val="$(grep -- "^$blkid_field=" <<< "$val")" \		|| die "Could not find $blkid_field=... in blkid output"	val="${val#"$blkid_field="}"	echo -n "$val"} 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"	echo -n "$uuid"} function get_device_by_blkid_field() {	local blkid_field="$1"	local field_value="$2"	blkid -g -c /dev/null \		|| die "Error while executing blkid"	type partprobe &>/dev/null && partprobe &>/dev/null	local dev	dev="$(blkid -c /dev/null -o export -t "$blkid_field=$field_value")" \		|| die "Error while executing blkid to find $blkid_field=$field_value"	dev="$(grep DEVNAME <<< "$dev")" \		|| die "Could not find DEVNAME=... in blkid output"	dev="${dev#"DEVNAME="}"	echo -n "$dev"} function get_device_by_partuuid() {	if [[ -e "/dev/disk/by-partuuid/$1" ]]; then		echo -n "/dev/disk/by-partuuid/$1"	else		get_device_by_blkid_field 'PARTUUID' "$1"	fi} function get_device_by_uuid() {	if [[ -e "/dev/disk/by-uuid/$1" ]]; then		echo -n "/dev/disk/by-uuid/$1"	else		get_device_by_blkid_field 'UUID' "$1"	fi} function cache_lsblk_output() {	CACHED_LSBLK_OUTPUT="$(lsblk --all --path --pairs --output NAME,PTUUID,PARTUUID)" \		|| die "Error while executing lsblk to cache output"} function get_device_by_ptuuid() {	local ptuuid="${1,,}"	local dev	if [[ -v CACHED_LSBLK_OUTPUT && -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	dev="$(grep "ptuuid=\"$ptuuid\" partuuid=\"\"" <<< "${dev,,}")" \		|| die "Could not find PTUUID=... in lsblk output"	dev="${dev%'" ptuuid='*}"	dev="${dev#'name="'}"	echo -n "$dev"} function uuid_to_mduuid() {	local mduuid="${1,,}"	mduuid="${mduuid//-/}"	mduuid="${mduuid:0:8}:${mduuid:8:8}:${mduuid:16:8}:${mduuid:24:8}"	echo -n "$mduuid"} function get_device_by_mdadm_uuid() {	local mduuid	mduuid="$(uuid_to_mduuid "$1")" \		|| die "Could not resolve mduuid from uuid=$1"	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"} function get_device_by_luks_name() {	echo -n "/dev/mapper/$1"} function create_resolve_entry() {	local id="$1"	local type="$2"	local arg="${3,,}" 	DISK_ID_TO_RESOLVABLE[$id]="$type:$arg"} function create_resolve_entry_device() {	local id="$1"	local dev="$2" 	DISK_ID_TO_RESOLVABLE[$id]="device:$dev"} # Returns the basename of the device, if its path starts with /dev/disk/by-id/function shorten_device() {	echo -n "${1#/dev/disk/by-id/}"} # Return matching device from /dev/disk/by-id/ if possible,# otherwise return the parameter unchanged.function canonicalize_device() {	given_dev="$(realpath "$1")"	for dev in /dev/disk/by-id/*; do		if [[ "$(realpath "$dev")" == "$given_dev" ]]; then			echo -n "$dev"			return 0		fi	done 	echo -n "$1"} function resolve_device_by_id() {	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]#*:}" 	local dev	case "$type" in		'partuuid') dev=$(get_device_by_partuuid   "$arg") ;;		'ptuuid')   dev=$(get_device_by_ptuuid     "$arg") ;;		'uuid')     dev=$(get_device_by_uuid       "$arg") ;;		'mdadm')    dev=$(get_device_by_mdadm_uuid "$arg") ;;		'luks')     dev=$(get_device_by_luks_name  "$arg") ;;		'device')   dev="$arg" ;;		*) die "Cannot resolve '$type:$arg' to device (unknown type)"	esac 	canonicalize_device "$dev"} function load_or_generate_uuid() {	local uuid	local uuid_file="$UUID_STORAGE_DIR/$1" 	if [[ -e $uuid_file ]]; then		uuid="$(cat "$uuid_file")"	else		uuid="$(uuidgen -r)"		mkdir -p "$UUID_STORAGE_DIR"		echo -n "$uuid" > "$uuid_file"	fi 	echo -n "$uuid"} # Parses named arguments and stores them in the associative array `arguments`.# 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.function parse_arguments() {	local key	local value	local a	for a in "$@"; do		key="${a%%=*}"		value="${a#*=}" 		if [[ $key == "$a" ]]; then			extra_arguments+=("$a")			continue		fi 		arguments[$key]="$value"	done 	declare -A allowed_keys	if [[ -v known_arguments ]]; then		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					# shellcheck disable=SC2086					for m_opt in ${m//|/ }; do						allowed_keys[$m_opt]=true						if [[ -v arguments[$m_opt] ]]; then							has_opt=true						fi					done 					[[ $has_opt == "true" ]] \						|| die_trace 2 "Missing mandatory argument $m=..."					;; 				'?')					allowed_keys[${m:1}]=true					;; 				*) die_trace 2 "Invalid start character in known_arguments, in argument '$m'" ;;			esac		done 		for a in "${!arguments[@]}"; do			[[ -v allowed_keys[$a] ]] \				|| die_trace 2 "Unknown argument '$a'"		done	fi} # $1: program# $2: checkfilefunction has_program() {	local program="$1"	local checkfile="$2"	if [[ -z "$checkfile" ]]; then		type "$program" &>/dev/null \			|| return 1	elif [[ "${checkfile:0:1}" == "/" ]]; then		[[ -e "$checkfile" ]] \			|| return 1	else		type "$checkfile" &>/dev/null \			|| return 1	fi	return 0} function check_wanted_programs() {	local missing_required=()	local missing_wanted=()	local tuple	local program	local checkfile	for tuple in "$@"; do		program="${tuple%%=*}"		checkfile=""		[[ "$tuple" == *=* ]] \			&& checkfile="${tuple##*=}"		if ! has_program "${program#"?"}" "$checkfile"; then			if [[ "$program" == "?"* ]]; then				missing_wanted+=("${program#"?"}")			else				missing_required+=("$program")			fi		fi	done 	[[ "${#missing_required[@]}" -eq 0 && "${#missing_wanted[@]}" -eq 0 ]] \		&& return 	if [[ "${#missing_required[@]}" -gt 0 ]]; then		elog "The following programs are required for the installer to work, but are currently missing on your system:" >&2		elog "  ${missing_required[*]}" >&2	fi	if [[ "${#missing_wanted[@]}" -gt 0 ]]; then		elog "Missing optional programs:" >&2		elog "  ${missing_wanted[*]}" >&2	fi 	if type pacman &>/dev/null; then		declare -A pacman_packages		pacman_packages=(			[ntpd]=ntp			[zfs]=""		)		elog "Detected pacman package manager."		if ask "Do you want to install all missing programs automatically?"; then			local packages			local need_zfs=false 			for program in "${missing_required[@]}" "${missing_wanted[@]}"; do				[[ "$program" == "zfs" ]] \					&& need_zfs=true 				if [[ -v "pacman_packages[$program]" ]]; then					# Assignments to the empty string are explicitly ignored,					# as for example, zfs needs to be handled separately.					[[ -n "${pacman_packages[$program]}" ]] \						&& packages+=("${pacman_packages[$program]}")				else					packages+=("$program")				fi			done			pacman -Sy "${packages[@]}" 			if [[ "$need_zfs" == true ]]; then				elog "On an Arch live-stick you need the archzfs repository and some tools and modifications to use zfs."				elog "There is an automated installer available at https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init."				if ask "Do you want to automatically download and execute this zfs installation script?"; then					curl -s "https://raw.githubusercontent.com/eoli3n/archiso-zfs/master/init" | bash				fi			fi 			return		fi	fi 	if [[ "${#missing_required[@]}" -gt 0 ]]; then		die "Aborted installer because of missing required programs."	else		ask "Continue without recommended programs?"	fi} # exec function if defined# $@ function name and argumentsfunction maybe_exec() {	type "$1" &>/dev/null && "$@"}