GEE / src /install.sh
Fred808's picture
Upload 29 files
601c581 verified
#!/usr/bin/env bash
set -Eeuo pipefail
getBase() {
local base="${1%%\?*}"
base=$(basename "$base")
base="${base//+/ }"
printf -v base '%b' "${base//%/\\x}"
base="${base//[!A-Za-z0-9._-]/_}"
echo "$base"
return 0
}
getFolder() {
local base=""
local result="$1"
if [[ "$result" != *"."* ]]; then
result="${result,,}"
else
base=$(getBase "$result")
result="${base%.*}"
case "${base,,}" in
*".gz" | *".gzip" | *".xz" | *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
[[ "$result" == *"."* ]] && result="${result%.*}" ;;
esac
fi
[ -z "$result" ] && result="unknown"
echo "$result"
return 0
}
moveFile() {
local file="$1"
local ext="${file##*.}"
local dest="$STORAGE/boot.$ext"
if [[ "$file" == "$dest" ]]; then
BOOT="$file"
return 0
fi
if [[ "${file,,}" == "/boot.${ext,,}" || "${file,,}" == "/custom.${ext,,}" ]]; then
BOOT="$file"
return 0
fi
if ! mv -f "$file" "$dest"; then
error "Failed to move $file to $dest !"
return 1
fi
BOOT="$dest"
return 0
}
detectType() {
local file="$1"
local result=""
[ ! -f "$file" ] && return 1
[ ! -s "$file" ] && return 1
case "${file,,}" in
*".iso" | *".img" | *".raw" | *".qcow2" ) ;;
* ) return 1 ;;
esac
if [ -n "$BOOT_MODE" ] || [[ "${file,,}" == *".qcow2" ]]; then
moveFile "$file" && return 0
return 1
fi
if [[ "${file,,}" == *".iso" ]]; then
result=$(head -c 512 "$file" | tail -c 2 | xxd -p)
if [[ "$result" != "0000" ]]; then
[ -z "${HYBRID:-}" ] && HYBRID="Y"
fi
if [[ "${HYBRID:-}" != [Yy]* ]]; then
result=$(isoinfo -f -i "$file" 2>/dev/null)
if [ -z "$result" ]; then
error "Failed to read ISO file, invalid format!"
return 1
fi
result=$(echo "${result^^}" | grep "^/EFI")
[ -z "$result" ] && BOOT_MODE="legacy"
moveFile "$file" && return 0
return 1
fi
fi
result=$(fdisk -l "$file" 2>/dev/null)
[[ "${result^^}" != *"EFI "* ]] && BOOT_MODE="legacy"
moveFile "$file" && return 0
return 1
}
delay() {
local i
local delay="$1"
local msg="Retrying failed download in X seconds..."
info "${msg/X/$delay}"
for i in $(seq "$delay" -1 1); do
html "${msg/X/$i}"
sleep 1
done
return 0
}
downloadFile() {
local url="$1"
local base="$2"
local name="$3"
local msg rc total size progress
local dest="$STORAGE/$base"
# Check if running with interactive TTY or redirected to docker log
if [ -t 1 ]; then
progress="--progress=bar:noscroll"
else
progress="--progress=dot:giga"
fi
if [ -z "$name" ]; then
msg="Downloading image"
info "Downloading $base..."
else
msg="Downloading $name"
info "Downloading $name..."
fi
html "$msg..."
/run/progress.sh "$dest" "0" "$msg ([P])..." &
{ wget "$url" -O "$dest" --continue -q --timeout=30 --no-http-keep-alive --show-progress "$progress"; rc=$?; } || :
fKill "progress.sh"
if (( rc == 0 )) && [ -f "$dest" ]; then
total=$(stat -c%s "$dest")
size=$(formatBytes "$total")
if [ "$total" -lt 100000 ]; then
error "Invalid image file: is only $size ?" && return 1
fi
html "Download finished successfully..."
return 0
fi
msg="Failed to download $url"
(( rc == 3 )) && error "$msg , cannot write file (disk full?)" && return 1
(( rc == 4 )) && error "$msg , network failure!" && return 1
(( rc == 8 )) && error "$msg , server issued an error response!" && return 1
error "$msg , reason: $rc"
return 1
}
convertImage() {
local source_file=$1
local source_fmt=$2
local dst_file=$3
local dst_fmt=$4
local dir base fs fa space space_gb
local cur_size cur_gb src_size disk_param
[ -f "$dst_file" ] && error "Conversion failed, destination file $dst_file already exists?" && return 1
[ ! -f "$source_file" ] && error "Conversion failed, source file $source_file does not exists?" && return 1
if [[ "${source_fmt,,}" == "${dst_fmt,,}" ]]; then
mv -f "$source_file" "$dst_file"
return 0
fi
local tmp_file="$dst_file.tmp"
dir=$(dirname "$tmp_file")
rm -f "$tmp_file"
if [ -n "$ALLOCATE" ] && [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace
src_size=$(qemu-img info "$source_file" -f "$source_fmt" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/')
space=$(df --output=avail -B 1 "$dir" | tail -n 1)
if (( src_size > space )); then
space_gb=$(formatBytes "$space")
error "Not enough free space to convert image in $dir, it has only $space_gb available..." && return 1
fi
fi
base=$(basename "$source_file")
info "Converting $base..."
html "Converting image..."
local conv_flags="-p"
if [ -z "$ALLOCATE" ] || [[ "$ALLOCATE" == [Nn]* ]]; then
disk_param="preallocation=off"
else
disk_param="preallocation=falloc"
fi
fs=$(stat -f -c %T "$dir")
[[ "${fs,,}" == "btrfs" ]] && disk_param+=",nocow=on"
if [[ "$dst_fmt" != "raw" ]]; then
if [ -z "$ALLOCATE" ] || [[ "$ALLOCATE" == [Nn]* ]]; then
conv_flags+=" -c"
fi
[ -n "${DISK_FLAGS:-}" ] && disk_param+=",$DISK_FLAGS"
fi
# shellcheck disable=SC2086
if ! qemu-img convert -f "$source_fmt" $conv_flags -o "$disk_param" -O "$dst_fmt" -- "$source_file" "$tmp_file"; then
rm -f "$tmp_file"
error "Failed to convert image in $dir, is there enough space available?" && return 1
fi
if [[ "$dst_fmt" == "raw" ]]; then
if [ -n "$ALLOCATE" ] && [[ "$ALLOCATE" != [Nn]* ]]; then
# Work around qemu-img bug
cur_size=$(stat -c%s "$tmp_file")
cur_gb=$(formatBytes "$cur_size")
if ! fallocate -l "$cur_size" "$tmp_file" &>/dev/null; then
if ! fallocate -l -x "$cur_size" "$tmp_file"; then
error "Failed to allocate $cur_gb for image!"
fi
fi
fi
fi
rm -f "$source_file"
mv "$tmp_file" "$dst_file"
if [[ "${fs,,}" == "btrfs" ]]; then
fa=$(lsattr "$dst_file")
if [[ "$fa" != *"C"* ]]; then
error "Failed to disable COW for image on ${fs^^} filesystem!"
fi
fi
html "Conversion completed..."
return 0
}
findFile() {
local dir file
local base="$1"
local ext="$2"
local fname="${base}.${ext}"
dir=$(find / -maxdepth 1 -type d -iname "$fname" -print -quit)
[ ! -d "$dir" ] && dir=$(find "$STORAGE" -maxdepth 1 -type d -iname "$fname" -print -quit)
if [ -d "$dir" ]; then
if hasDisk; then
BOOT="none"
return 0
fi
error "The bind $dir maps to a file that does not exist!" && exit 37
fi
file=$(find / -maxdepth 1 -type f -iname "$fname" -print -quit)
[ ! -s "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname "$fname" -print -quit)
detectType "$file" && return 0
return 1
}
findFile "boot" "img" && return 0
findFile "boot" "raw" && return 0
findFile "boot" "iso" && return 0
findFile "boot" "qcow2" && return 0
findFile "custom" "iso" && return 0
if hasDisk; then
BOOT="none"
return 0
fi
if [[ "${BOOT}" == \"*\" || "${BOOT}" == \'*\' ]]; then
VERSION="${BOOT:1:-1}"
fi
BOOT=$(expr "$BOOT" : "^\ *\(.*[^ ]\)\ *$")
if [ -z "$BOOT" ] || [[ "$BOOT" == *"example.com/"* ]]; then
BOOT="alpine"
warn "no value specified for the BOOT variable, defaulting to \"${BOOT}\"."
fi
folder=$(getFolder "$BOOT")
STORAGE="$STORAGE/$folder"
if [ -d "$STORAGE" ]; then
findFile "boot" "img" && return 0
findFile "boot" "raw" && return 0
findFile "boot" "iso" && return 0
findFile "boot" "qcow2" && return 0
findFile "custom" "iso" && return 0
if hasDisk; then
BOOT="none"
return 0
fi
fi
name=$(getURL "$BOOT" "name") || exit 34
if [ -n "$name" ]; then
msg="Retrieving latest $name version..."
info "$msg" && html "$msg..."
url=$(getURL "$BOOT" "url") || exit 34
[ -n "$url" ] && BOOT="$url"
fi
if [[ "$BOOT" != *"."* ]]; then
if [ -z "$BOOT" ]; then
error "No BOOT value specified!"
else
error "Invalid BOOT value specified, option \"$BOOT\" is not recognized!"
fi
exit 64
fi
if [[ "${BOOT,,}" != "http"* ]]; then
error "Invalid BOOT value specified, \"$BOOT\" is not a valid URL!" && exit 64
fi
mkdir -p "$STORAGE"
find "$STORAGE" -maxdepth 1 -type f \( -iname '*.rom' -or -iname '*.vars' \) -delete
find "$STORAGE" -maxdepth 1 -type f \( -iname 'data.*' -or -iname 'qemu.*' \) -delete
base=$(getBase "$BOOT")
rm -f "$STORAGE/$base"
if ! downloadFile "$BOOT" "$base" "$name"; then
delay 5
if ! downloadFile "$BOOT" "$base" "$name"; then
delay 10
if ! downloadFile "$BOOT" "$base" "$name"; then
rm -f "$STORAGE/$base" && exit 60
fi
fi
fi
case "${base,,}" in
*".gz" | *".gzip" | *".xz" | *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
info "Extracting $base..."
html "Extracting image..." ;;
esac
case "${base,,}" in
*".gz" | *".gzip" )
gzip -dc "$STORAGE/$base" > "$STORAGE/${base%.*}"
rm -f "$STORAGE/$base"
base="${base%.*}"
;;
*".xz" )
xz -dc "$STORAGE/$base" > "$STORAGE/${base%.*}"
rm -f "$STORAGE/$base"
base="${base%.*}"
;;
*".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
tmp="$STORAGE/extract"
rm -rf "$tmp"
mkdir -p "$tmp"
7z x "$STORAGE/$base" -o"$tmp" > /dev/null
rm -f "$STORAGE/$base"
base="${base%.*}"
if [ ! -s "$tmp/$base" ]; then
rm -rf "$tmp"
error "Cannot find file \"${base}\" in .${BOOT/*./} archive!" && exit 32
fi
mv "$tmp/$base" "$STORAGE/$base"
rm -rf "$tmp"
;;
esac
case "${base,,}" in
*".iso" | *".img" | *".raw" | *".qcow2" )
detectType "$STORAGE/$base" && return 0
error "Cannot read file \"${base}\"" && exit 63 ;;
esac
target_ext="img"
target_fmt="${DISK_FMT:-}"
[ -z "$target_fmt" ] && target_fmt="raw"
[[ "$target_fmt" != "raw" ]] && target_ext="qcow2"
case "${base,,}" in
*".vdi" ) source_fmt="vdi" ;;
*".vhd" ) source_fmt="vpc" ;;
*".vhdx" ) source_fmt="vpc" ;;
*".vmdk" ) source_fmt="vmdk" ;;
* ) error "Unknown file extension, type \".${base/*./}\" is not recognized!" && exit 33 ;;
esac
dst="$STORAGE/${base%.*}.$target_ext"
! convertImage "$STORAGE/$base" "$source_fmt" "$dst" "$target_fmt" && exit 35
base=$(basename "$dst")
detectType "$STORAGE/$base" && return 0
error "Cannot convert file \"${base}\"" && exit 36