diff --git a/misc/alpine-tools.func b/misc/alpine-tools.func index be71e61c3..f5ba60368 100644 --- a/misc/alpine-tools.func +++ b/misc/alpine-tools.func @@ -20,7 +20,7 @@ need_tool() { msg_info "Installing tools: $*" apk add --no-cache "$@" >/dev/null 2>&1 || { msg_error "apk add failed for: $*" - return 1 + return 100 } msg_ok "Tools ready: $*" fi @@ -52,17 +52,17 @@ ensure_usr_local_bin_persist() { download_with_progress() { # $1 url, $2 dest local url="$1" out="$2" cl - need_tool curl pv || return 1 + need_tool curl pv || return 127 cl=$(curl -fsSLI "$url" 2>/dev/null | awk 'tolower($0) ~ /^content-length:/ {print $2}' | tr -d '\r') if [ -n "$cl" ]; then curl -fsSL "$url" | pv -s "$cl" >"$out" || { msg_error "Download failed: $url" - return 1 + return 250 } else curl -fL# -o "$out" "$url" || { msg_error "Download failed: $url" - return 1 + return 250 } fi } @@ -82,14 +82,14 @@ check_for_gh_release() { net_resolves api.github.com || { msg_error "DNS/network error: api.github.com" - return 1 + return 6 } - need_tool curl jq || return 1 + need_tool curl jq || return 127 tag=$(curl -fsSL "https://api.github.com/repos/${source}/releases/latest" | jq -r '.tag_name // empty') [ -z "$tag" ] && { msg_error "Unable to fetch latest tag for $app" - return 1 + return 22 } release="${tag#v}" @@ -133,12 +133,12 @@ fetch_and_deploy_gh() { net_resolves api.github.com || { msg_error "DNS/network error" - return 1 + return 6 } - need_tool curl jq tar || return 1 + need_tool curl jq tar || return 127 [ "$mode" = "prebuild" ] || [ "$mode" = "singlefile" ] && need_tool unzip >/dev/null 2>&1 || true - tmpd="$(mktemp -d)" || return 1 + tmpd="$(mktemp -d)" || return 252 mkdir -p "$target" # Release JSON @@ -146,13 +146,13 @@ fetch_and_deploy_gh() { json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/latest")" || { msg_error "GitHub API failed" rm -rf "$tmpd" - return 1 + return 22 } else json="$(curl -fsSL "https://api.github.com/repos/$repo/releases/tags/$version")" || { msg_error "GitHub API failed" rm -rf "$tmpd" - return 1 + return 22 } fi @@ -163,7 +163,7 @@ fetch_and_deploy_gh() { [ -z "$version" ] && { msg_error "No tag in release json" rm -rf "$tmpd" - return 1 + return 65 } case "$mode" in @@ -173,26 +173,26 @@ fetch_and_deploy_gh() { filename="${app_lc}-${version}.tar.gz" download_with_progress "$url" "$tmpd/$filename" || { rm -rf "$tmpd" - return 1 + return 250 } tar -xzf "$tmpd/$filename" -C "$tmpd" || { msg_error "tar extract failed" rm -rf "$tmpd" - return 1 + return 251 } unpack="$(find "$tmpd" -mindepth 1 -maxdepth 1 -type d | head -n1)" # copy content of unpack to target (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" - return 1 + return 252 } ;; prebuild) [ -n "$pattern" ] || { msg_error "prebuild requires asset pattern" rm -rf "$tmpd" - return 1 + return 65 } url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' BEGIN{IGNORECASE=1} @@ -201,19 +201,19 @@ fetch_and_deploy_gh() { [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" - return 1 + return 250 } filename="${url##*/}" download_with_progress "$url" "$tmpd/$filename" || { rm -rf "$tmpd" - return 1 + return 250 } # unpack archive (Zip or tarball) case "$filename" in *.zip) need_tool unzip || { rm -rf "$tmpd" - return 1 + return 127 } mkdir -p "$tmpd/unp" unzip -q "$tmpd/$filename" -d "$tmpd/unp" @@ -225,7 +225,7 @@ fetch_and_deploy_gh() { *) msg_error "unsupported archive: $filename" rm -rf "$tmpd" - return 1 + return 251 ;; esac # top-level folder strippen @@ -234,13 +234,13 @@ fetch_and_deploy_gh() { (cd "$unpack" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" - return 1 + return 252 } else (cd "$tmpd/unp" && tar -cf - .) | (cd "$target" && tar -xf -) || { msg_error "copy failed" rm -rf "$tmpd" - return 1 + return 252 } fi ;; @@ -248,7 +248,7 @@ fetch_and_deploy_gh() { [ -n "$pattern" ] || { msg_error "singlefile requires asset pattern" rm -rf "$tmpd" - return 1 + return 65 } url="$(printf '%s' "$json" | jq -r '.assets[].browser_download_url' | awk -v p="$pattern" ' BEGIN{IGNORECASE=1} @@ -257,19 +257,19 @@ fetch_and_deploy_gh() { [ -z "$url" ] && { msg_error "asset not found for pattern: $pattern" rm -rf "$tmpd" - return 1 + return 250 } filename="${url##*/}" download_with_progress "$url" "$target/$app" || { rm -rf "$tmpd" - return 1 + return 250 } chmod +x "$target/$app" ;; *) msg_error "Unknown mode: $mode" rm -rf "$tmpd" - return 1 + return 65 ;; esac @@ -291,19 +291,19 @@ setup_yq() { return 0 fi - need_tool curl || return 1 + need_tool curl || return 127 local arch bin url tmp case "$(uname -m)" in x86_64) arch="amd64" ;; aarch64) arch="arm64" ;; *) msg_error "Unsupported arch for yq: $(uname -m)" - return 1 + return 238 ;; esac url="https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${arch}" tmp="$(mktemp)" - download_with_progress "$url" "$tmp" || return 1 + download_with_progress "$url" "$tmp" || return 250 /usr/bin/install -m 0755 "$tmp" /usr/local/bin/yq rm -f "$tmp" msg_ok "Setup yq ($(yq --version 2>/dev/null))" @@ -313,13 +313,13 @@ setup_yq() { # Adminer – Alpine # ------------------------------ setup_adminer() { - need_tool curl || return 1 + need_tool curl || return 127 msg_info "Setup Adminer (Alpine)" mkdir -p /var/www/localhost/htdocs/adminer curl -fsSL https://github.com/vrana/adminer/releases/latest/download/adminer.php \ -o /var/www/localhost/htdocs/adminer/index.php || { msg_error "Adminer download failed" - return 1 + return 250 } msg_ok "Adminer at /adminer (served by your webserver)" } @@ -329,7 +329,7 @@ setup_adminer() { # optional: PYTHON_VERSION="3.12" # ------------------------------ setup_uv() { - need_tool curl tar || return 1 + need_tool curl tar || return 127 local UV_BIN="/usr/local/bin/uv" local arch tarball url tmpd ver installed @@ -338,7 +338,7 @@ setup_uv() { aarch64) arch="aarch64-unknown-linux-musl" ;; *) msg_error "Unsupported arch for uv: $(uname -m)" - return 1 + return 238 ;; esac @@ -346,7 +346,7 @@ setup_uv() { ver="${ver#v}" [ -z "$ver" ] && { msg_error "uv: cannot determine latest version" - return 1 + return 250 } if has "$UV_BIN"; then @@ -360,18 +360,18 @@ setup_uv() { msg_info "Setup uv $ver" fi - tmpd="$(mktemp -d)" || return 1 + tmpd="$(mktemp -d)" || return 252 tarball="uv-${arch}.tar.gz" url="https://github.com/astral-sh/uv/releases/download/v${ver}/${tarball}" download_with_progress "$url" "$tmpd/uv.tar.gz" || { rm -rf "$tmpd" - return 1 + return 250 } tar -xzf "$tmpd/uv.tar.gz" -C "$tmpd" || { msg_error "uv: extract failed" rm -rf "$tmpd" - return 1 + return 251 } # tar contains ./uv @@ -382,7 +382,7 @@ setup_uv() { /usr/bin/install -m 0755 "$tmpd"/*/uv "$UV_BIN" 2>/dev/null || { msg_error "uv binary not found in tar" rm -rf "$tmpd" - return 1 + return 252 } fi rm -rf "$tmpd" @@ -395,13 +395,13 @@ setup_uv() { $0 ~ "^cpython-"maj"\\." { print $0 }' | awk -F- '{print $2}' | sort -V | tail -n1)" [ -z "$match" ] && { msg_error "No matching Python for $PYTHON_VERSION" - return 1 + return 250 } if ! uv python list | grep -q "cpython-${match}-linux"; then msg_info "Installing Python $match via uv" uv python install "$match" || { msg_error "uv python install failed" - return 1 + return 150 } msg_ok "Python $match installed (uv)" fi @@ -421,7 +421,7 @@ setup_java() { msg_info "Setup Java (OpenJDK $JAVA_VERSION)" apk add --no-cache "$pkg" >/dev/null 2>&1 || { msg_error "apk add $pkg failed" - return 1 + return 100 } # set JAVA_HOME local prof="/etc/profile.d/20-java.sh" @@ -441,32 +441,32 @@ setup_go() { msg_info "Setup Go (apk)" apk add --no-cache go >/dev/null 2>&1 || { msg_error "apk add go failed" - return 1 + return 100 } msg_ok "Go ready: $(go version 2>/dev/null)" return 0 fi - need_tool curl tar || return 1 + need_tool curl tar || return 127 local ARCH TARBALL URL TMP case "$(uname -m)" in x86_64) ARCH="amd64" ;; aarch64) ARCH="arm64" ;; *) msg_error "Unsupported arch for Go: $(uname -m)" - return 1 + return 238 ;; esac TARBALL="go${GO_VERSION}.linux-${ARCH}.tar.gz" URL="https://go.dev/dl/${TARBALL}" msg_info "Setup Go $GO_VERSION (tarball)" TMP="$(mktemp)" - download_with_progress "$URL" "$TMP" || return 1 + download_with_progress "$URL" "$TMP" || return 250 rm -rf /usr/local/go tar -C /usr/local -xzf "$TMP" || { msg_error "extract go failed" rm -f "$TMP" - return 1 + return 251 } rm -f "$TMP" ln -sf /usr/local/go/bin/go /usr/local/bin/go @@ -488,7 +488,7 @@ setup_composer() { # Fallback to generic php if 83 not available apk add --no-cache php-cli php-openssl php-phar php-iconv >/dev/null 2>&1 || { msg_error "Failed to install php-cli for composer" - return 1 + return 100 } } msg_ok "PHP CLI ready: $(php -v | head -n1)" @@ -500,14 +500,14 @@ setup_composer() { msg_info "Setup Composer" fi - need_tool curl || return 1 + need_tool curl || return 127 curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php || { msg_error "composer installer download failed" - return 1 + return 250 } php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer >/dev/null 2>&1 || { msg_error "composer install failed" - return 1 + return 150 } rm -f /tmp/composer-setup.php ensure_usr_local_bin_persist diff --git a/misc/build.func b/misc/build.func index abb6f5bb0..ca147f42b 100644 --- a/misc/build.func +++ b/misc/build.func @@ -267,12 +267,12 @@ install_ssh_keys_into_ct() { msg_info "Installing selected SSH keys into CT ${CTID}" pct exec "$CTID" -- sh -c 'mkdir -p /root/.ssh && chmod 700 /root/.ssh' || { msg_error "prepare /root/.ssh failed" - return 1 + return 252 } pct push "$CTID" "$SSH_KEYS_FILE" /root/.ssh/authorized_keys >/dev/null 2>&1 || pct exec "$CTID" -- sh -c "cat > /root/.ssh/authorized_keys" <"$SSH_KEYS_FILE" || { msg_error "write authorized_keys failed" - return 1 + return 252 } pct exec "$CTID" -- sh -c 'chmod 600 /root/.ssh/authorized_keys' || true msg_ok "Installed SSH keys into CT ${CTID}" @@ -839,7 +839,7 @@ choose_and_set_storage_for_file() { template) key="var_template_storage" ;; *) msg_error "Unknown storage class: $class" - return 1 + return 65 ;; esac @@ -862,7 +862,7 @@ choose_and_set_storage_for_file() { fi else # If the current value is preselectable, we could show it, but per your requirement we always offer selection - select_storage "$class" || return 1 + select_storage "$class" || return 150 fi _write_storage_to_vars "$vf" "$key" "$STORAGE_RESULT" @@ -1264,7 +1264,7 @@ default_var_settings() { return 0 } done - return 1 + return 252 } # Allow override of storages via env (for non-interactive use cases) [ -n "${var_template_storage:-}" ] && TEMPLATE_STORAGE="$var_template_storage" @@ -1357,7 +1357,7 @@ EOF local dv dv="$(_find_default_vars)" || { msg_error "default.vars not found after ensure step" - return 1 + return 252 } load_vars_file "$dv" @@ -4690,7 +4690,7 @@ EOF' destroy_lxc() { if [[ -z "$CT_ID" ]]; then msg_error "No CT_ID found. Nothing to remove." - return 1 + return 65 fi # Abort on Ctrl-C / Ctrl-D / ESC @@ -4729,12 +4729,12 @@ resolve_storage_preselect() { case "$class" in template) required_content="vztmpl" ;; container) required_content="rootdir" ;; - *) return 1 ;; + *) return 65 ;; esac [[ -z "$preselect" ]] && return 1 if ! pvesm status -content "$required_content" | awk 'NR>1{print $1}' | grep -qx -- "$preselect"; then msg_warn "Preselected storage '${preselect}' does not support content '${required_content}' (or not found)" - return 1 + return 238 fi local line total used free @@ -4858,7 +4858,7 @@ select_storage() { ;; *) msg_error "Invalid storage class '$CLASS'" - return 1 + return 65 ;; esac @@ -4940,7 +4940,7 @@ validate_storage_space() { # Check if storage exists and is active if [[ -z "$storage_line" ]]; then [[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' not found!\n\nThe storage may be unavailable or disabled." 10 60 - return 2 + return 236 fi # Check storage status (column 3) @@ -4948,7 +4948,7 @@ validate_storage_space() { status=$(awk '{print $3}' <<<"$storage_line") if [[ "$status" == "disabled" ]]; then [[ "$show_dialog" == "yes" ]] && whiptail --msgbox "⚠️ Warning: Storage '$storage' is disabled!\n\nPlease enable the storage first." 10 60 - return 2 + return 236 fi # Get storage type and free space (column 6) @@ -4971,7 +4971,7 @@ validate_storage_space() { if [[ "$show_dialog" == "yes" ]]; then whiptail --msgbox "⚠️ Warning: Storage '$storage' may not have enough space!\n\nStorage Type: ${storage_type}\nRequired: ${required_gb}GB\nAvailable: ${free_gb_fmt}\n\nYou can continue, but creation might fail." 14 70 fi - return 1 + return 236 fi return 0 diff --git a/misc/cloud-init.func b/misc/cloud-init.func index 0c8597f9b..9e97e4d9f 100644 --- a/misc/cloud-init.func +++ b/misc/cloud-init.func @@ -319,11 +319,11 @@ function setup_cloud_init() { if [ "$network_mode" = "static" ]; then if [ -n "$static_ip" ] && ! validate_ip_cidr "$static_ip"; then _ci_msg_error "Invalid static IP format: $static_ip (expected: x.x.x.x/xx)" - return 1 + return 65 fi if [ -n "$gateway" ] && ! validate_ip "$gateway"; then _ci_msg_error "Invalid gateway IP format: $gateway" - return 1 + return 65 fi fi @@ -433,7 +433,7 @@ function configure_cloud_init_interactive() { if ! command -v whiptail >/dev/null 2>&1; then echo "Warning: whiptail not available, skipping interactive configuration" export CLOUDINIT_ENABLE="no" - return 1 + return 127 fi # Ask if user wants to enable Cloud-Init @@ -603,7 +603,7 @@ function get_vm_ip() { elapsed=$((elapsed + 2)) done - return 1 + return 7 } # ------------------------------------------------------------------------------ @@ -621,7 +621,7 @@ function wait_for_cloud_init() { if [ -z "$vm_ip" ]; then _ci_msg_warn "Unable to determine VM IP address" - return 1 + return 7 fi _ci_msg_info "Waiting for Cloud-Init to complete on ${vm_ip}" @@ -638,7 +638,7 @@ function wait_for_cloud_init() { done _ci_msg_warn "Cloud-Init did not complete within ${timeout}s" - return 1 + return 150 } # ============================================================================== diff --git a/misc/core.func b/misc/core.func index e3b9d2cff..95d93c83d 100644 --- a/misc/core.func +++ b/misc/core.func @@ -858,7 +858,7 @@ get_header() { if [ ! -s "$local_header_path" ]; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then msg_warn "Failed to download header: $header_url" - return 1 + return 250 fi fi @@ -1358,7 +1358,7 @@ prompt_select() { if [[ $num_options -eq 0 ]]; then msg_warn "prompt_select called with no options" echo "" >&2 - return 1 + return 65 fi # Validate default @@ -1600,7 +1600,7 @@ check_or_create_swap() { swap_size_mb=$(prompt_input "Enter swap size in MB (e.g., 2048 for 2GB):" "2048" 60) if ! [[ "$swap_size_mb" =~ ^[0-9]+$ ]]; then msg_error "Invalid swap size: '${swap_size_mb}' (must be a number in MB)" - return 1 + return 65 fi local swap_file="/swapfile" @@ -1608,19 +1608,19 @@ check_or_create_swap() { msg_info "Creating ${swap_size_mb}MB swap file at $swap_file" if ! dd if=/dev/zero of="$swap_file" bs=1M count="$swap_size_mb" status=progress; then msg_error "Failed to allocate swap file (dd failed)" - return 1 + return 150 fi if ! chmod 600 "$swap_file"; then msg_error "Failed to set permissions on $swap_file" - return 1 + return 150 fi if ! mkswap "$swap_file"; then msg_error "Failed to format swap file (mkswap failed)" - return 1 + return 150 fi if ! swapon "$swap_file"; then msg_error "Failed to activate swap (swapon failed)" - return 1 + return 150 fi msg_ok "Swap file created and activated successfully" } @@ -1699,13 +1699,13 @@ function get_lxc_ip() { fi done - return 1 + return 6 } LOCAL_IP="$(get_current_ip || true)" if [[ -z "$LOCAL_IP" ]]; then msg_error "Could not determine LOCAL_IP (checked: eth0, hostname -I, ip route, IPv6 targets)" - return 1 + return 6 fi fi diff --git a/misc/vm-core.func b/misc/vm-core.func index 5f5c494f8..f0d44cfa2 100644 --- a/misc/vm-core.func +++ b/misc/vm-core.func @@ -42,7 +42,7 @@ get_header() { if [ ! -s "$local_header_path" ]; then if ! curl -fsSL "$header_url" -o "$local_header_path"; then - return 1 + return 250 fi fi