From 438e992653a7837cef291c3e7aae862a2da8f112 Mon Sep 17 00:00:00 2001 From: Kroese Date: Fri, 7 Aug 2026 02:52:21 +0200 Subject: [PATCH] feat: Check available memory before installation starts (#1353) --- Dockerfile | 18 ++---- src/config.sh | 2 + src/entry.sh | 4 +- src/finish.sh | 3 + src/{reset.sh => init.sh} | 49 +------------- src/memory.sh | 130 ++++++++++++++++++++++++++++++++++---- src/network.sh | 13 ++++ src/power.sh | 24 ++++--- src/server.sh | 19 +++++- src/utils.sh | 12 ++-- 10 files changed, 181 insertions(+), 93 deletions(-) rename src/{reset.sh => init.sh} (85%) diff --git a/Dockerfile b/Dockerfile index 7453cf2..2c4200a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,10 +29,6 @@ RUN < /etc/qemu/bridge.conf - - # Configure nginx - unlink /etc/nginx/sites-enabled/default - sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf - # Set version file echo "$VERSION_ARG" > /etc/version diff --git a/src/config.sh b/src/config.sh index f4bfe3f..4c147c5 100644 --- a/src/config.sh +++ b/src/config.sh @@ -61,6 +61,8 @@ buildArguments() { return 0 } +finalizeMemory + configureMemory configureMonitor configureMachine diff --git a/src/entry.sh b/src/entry.sh index 4b76284..39cd0dd 100755 --- a/src/entry.sh +++ b/src/entry.sh @@ -9,7 +9,8 @@ cd /run . start.sh # Startup hook . utils.sh # Load functions -. reset.sh # Initialize system +. init.sh # Initialize system +. memory.sh # Check memory . server.sh # Start webserver . install.sh # Run installation . disk.sh # Initialize disks @@ -18,7 +19,6 @@ cd /run . proc.sh # Initialize processor . serial.sh # Initialize serialport . power.sh # Configure shutdown -. memory.sh # Check available memory . config.sh # Configure arguments . finish.sh # Finish initialization diff --git a/src/finish.sh b/src/finish.sh index 0f83776..3d96fd2 100644 --- a/src/finish.sh +++ b/src/finish.sh @@ -5,4 +5,7 @@ if enabled "$DEBUG"; then printf "QEMU arguments:\n\n %s\n\n" "${ARGS// -/$'\n -'}" fi +# Must always remain the very last command +enableTrap + return 0 diff --git a/src/reset.sh b/src/init.sh similarity index 85% rename from src/reset.sh rename to src/init.sh index fcfd5df..eef5466 100644 --- a/src/reset.sh +++ b/src/init.sh @@ -158,53 +158,6 @@ checkHost() { return 0 } -checkRam() { - - # Read host and container memory limits. - getMemoryInfo - - RAM_SPARE=500000000 - RAM_MINIMUM="${RAM_MINIMUM:-1073741824}" - - RAM_MINIMUM=$(strip "$RAM_MINIMUM") - RAM_MINIMUM="${RAM_MINIMUM// /}" - RAM_MINIMUM=$(echo "${RAM_MINIMUM^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') - numfmt --from=iec "$RAM_MINIMUM" &>/dev/null || { - error "Invalid RAM_MINIMUM: $RAM_MINIMUM" - exit 16 - } - RAM_MINIMUM=$(numfmt --from=iec "$RAM_MINIMUM") - - RAM_SIZE=$(strip "$RAM_SIZE") - RAM_SIZE="${RAM_SIZE// /}" - [ -z "$RAM_SIZE" ] && RAM_SIZE="2G" - - if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then - - # Bare values below 130 are interpreted as GiB for convenience; larger bare - # values are treated as MiB to preserve historical configurations. - if [ -z "${RAM_SIZE//[0-9. ]}" ]; then - [ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M" - fi - - RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') - numfmt --from=iec "$RAM_SIZE" &>/dev/null || { - error "Invalid RAM_SIZE: $RAM_SIZE" - exit 16 - } - - wanted=$(numfmt --from=iec "$RAM_SIZE") - - if [ "$wanted" -lt "$RAM_MINIMUM" ]; then - error "$(app) requires at least $(formatBytes "$RAM_MINIMUM") of RAM, but RAM_SIZE is set to $(formatBytes "$wanted")." - exit 16 - fi - - fi - - return 0 -} - checkKvm() { # Check KVM support @@ -311,7 +264,7 @@ IFS=. read -r KERNEL MINOR _ <<< "$SYS" checkSockets checkCores checkStorage -checkRam +getMemoryInfo # Print system info SYS="${SYS/-generic/}" diff --git a/src/memory.sh b/src/memory.sh index 0cb53ab..58f09a3 100644 --- a/src/memory.sh +++ b/src/memory.sh @@ -1,14 +1,61 @@ #!/usr/bin/env bash set -Eeuo pipefail -msg="Checking memory..." -enabled "$DEBUG" && echo "$msg" +normalizeMemory() { -RAM_WARNING="" -RAM_ALLOCATION="" + local wanted + + RAM_SPARE=500000000 + RAM_MINIMUM="${RAM_MINIMUM:-1073741824}" + + RAM_MINIMUM=$(strip "$RAM_MINIMUM") + RAM_MINIMUM="${RAM_MINIMUM// /}" + RAM_MINIMUM=$(echo "${RAM_MINIMUM^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') + numfmt --from=iec "$RAM_MINIMUM" &>/dev/null || { + error "Invalid RAM_MINIMUM: $RAM_MINIMUM" + exit 16 + } + RAM_MINIMUM=$(numfmt --from=iec "$RAM_MINIMUM") + + RAM_SIZE=$(strip "$RAM_SIZE") + RAM_SIZE="${RAM_SIZE// /}" + [ -z "$RAM_SIZE" ] && RAM_SIZE="2G" + + if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then + + # Bare values below 130 are interpreted as GiB for convenience; larger bare + # values are treated as MiB to preserve historical configurations. + if [ -z "${RAM_SIZE//[0-9. ]}" ]; then + [ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M" + fi + + RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') + numfmt --from=iec "$RAM_SIZE" &>/dev/null || { + error "Invalid RAM_SIZE: $RAM_SIZE" + exit 16 + } + + wanted=$(numfmt --from=iec "$RAM_SIZE") + + if [ "$wanted" -lt "$RAM_MINIMUM" ]; then + error "$(app) requires at least $(formatBytes "$RAM_MINIMUM") of RAM, but RAM_SIZE is set to $(formatBytes "$wanted")." + exit 16 + fi + + # QEMU requires a whole-number memory value, so convert decimal sizes to MiB. + if [[ "$RAM_SIZE" == *.* ]]; then + RAM_SIZE="$(( wanted / 1048576 ))M" + fi + + fi + + return 0 +} checkConfiguredMemory() { + local final="$1" + if disabled "$RAM_CHECK" || [[ "${RAM_SIZE,,}" == "max" || "${RAM_SIZE,,}" == "half" ]]; then return 0 fi @@ -25,7 +72,7 @@ checkConfiguredMemory() { # heuristic remains informational instead of rewriting RAM_SIZE. if [[ "${FS,,}" == "zfs" ]]; then - info "$msg but since ZFS is active this will be ignored." + enabled "$final" && info "$msg but since ZFS is active this will be ignored." else @@ -41,9 +88,9 @@ checkConfiguredMemory() { local msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $avail_mem of free memory available," if [[ "${FS,,}" == "zfs" ]]; then - info "$msg but since ZFS is active this will be ignored." + enabled "$final" && info "$msg but since ZFS is active this will be ignored." else - warn "$msg please consider a lower amount." + enabled "$final" && warn "$msg please consider a lower amount." fi fi @@ -107,27 +154,82 @@ configureMaxMemory() { return 0 } +showMemoryLimitHint() { + + local kernel + kernel=$(uname -r) + + if [[ "${kernel,,}" == *-wsl2* ]]; then + echo + info "Docker Desktop (WSL2) is detected, follow these instructions:" + info "" + info "Increase the memory limit in \"%UserProfile%\\.wslconfig\" by setting \"memory=\" under \"[wsl2]\"." + info "Then run \"wsl --shutdown\" in PowerShell and restart Docker Desktop for the new limit to take effect." + echo + fi + + return 0 +} + checkMinimumMemory() { local wanted wanted=$(numfmt --from=iec "$RAM_SIZE") if [ "$wanted" -lt "$RAM_MINIMUM" ]; then + error "$(app) requires at least $(formatBytes "$RAM_MINIMUM") of RAM, but only $(formatBytes "$wanted") can be allocated." + + showMemoryLimitHint + exit 16 fi return 0 } -getMemoryInfo +checkMemoryAllocation() { -checkConfiguredMemory -configureHalfMemory -configureMaxMemory -checkMinimumMemory + local final="${1:-N}" + local configured -[ -n "$RAM_WARNING" ] && warn "$RAM_WARNING" -[ -n "$RAM_ALLOCATION" ] && info "Allocated $(formatBytes "$RAM_ALLOCATION") of RAM for $(app)." + normalizeMemory + configured="$RAM_SIZE" + + RAM_WARNING="" + RAM_ALLOCATION="" + + getMemoryInfo + checkConfiguredMemory "$final" + + configureHalfMemory + configureMaxMemory + checkMinimumMemory + + if enabled "$final"; then + [ -n "$RAM_WARNING" ] && warn "$RAM_WARNING" + [ -n "$RAM_ALLOCATION" ] && info "Allocated $(formatBytes "$RAM_ALLOCATION") of RAM for $(app)." + else + RAM_SIZE="$configured" + fi + + return 0 +} + +checkMemoryRequirement() { + + checkMemoryAllocation "N" + + return 0 +} + +finalizeMemory() { + + checkMemoryAllocation "Y" + + return 0 +} + +checkMemoryRequirement return 0 diff --git a/src/network.sh b/src/network.sh index f2ed468..0ead3ad 100644 --- a/src/network.sh +++ b/src/network.sh @@ -968,6 +968,17 @@ configurePasst() { return 0 } +configureBridge() { + + local file="/etc/qemu/bridge.conf" + + [ -e "$file" ] && return 0 + mkdir -p "${file%/*}" || return 0 + echo "allow br0" > "$file" || return 0 + + return 0 +} + createBridge() { local gateway="$1" @@ -2137,6 +2148,7 @@ showHostInfo() { echo "❯ DNS: $nameservers" fi + enabled "$DEBUG" && echo return 0 } @@ -2191,6 +2203,7 @@ initializeNetwork() { configureMTU configureMAC + configureBridge showHostInfo diff --git a/src/power.sh b/src/power.sh index 69810e9..6008d76 100644 --- a/src/power.sh +++ b/src/power.sh @@ -335,7 +335,7 @@ waitForShutdown() { return 0 } -graceful_shutdown() { +gracefulShutdown() { local sig="$1" local pid code @@ -387,13 +387,21 @@ graceful_shutdown() { finish "$code" } -enabled "$SHUTDOWN" || return 0 +enableTrap() { + + enabled "$SHUTDOWN" || return 0 + + # Keep Ctrl-C available to interactive users without installing an unnecessary + # SIGINT handler for background/container execution. + if interactive; then + _trap gracefulShutdown SIGINT + fi + + _trap gracefulShutdown SIGTERM SIGHUP SIGABRT SIGQUIT + + return 0 +} + [ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT" -if interactive; then - _trap graceful_shutdown SIGINT -fi - -_trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT - return 0 diff --git a/src/server.sh b/src/server.sh index 7b930a4..88b8a1d 100644 --- a/src/server.sh +++ b/src/server.sh @@ -51,11 +51,26 @@ configureIpv6Listen() { return 0 } -configureWebServer() { +configureNginx() { + + mkdir -p /etc/nginx/sites-enabled || return 1 + rm -f /etc/nginx/sites-enabled/default || return 1 + + if ! sed -i \ + -e 's/^worker_processes.*/worker_processes 1;/' \ + /etc/nginx/nginx.conf; then + error "Failed to configure nginx!" + return 1 + fi - mkdir -p /etc/nginx/sites-enabled cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf + return 0 +} + +configureWebServer() { + + configureNginx || return 1 configureWebPorts || return 1 configureIpv6Listen || return 1 diff --git a/src/utils.sh b/src/utils.sh index 25f0ea4..e5e02f0 100644 --- a/src/utils.sh +++ b/src/utils.sh @@ -7,6 +7,12 @@ info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "${1:-}" "\E[0m\n"; } error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: ${1:-}" "\E[0m\n" >&2; } warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; } +app() { + + echo "Virtual DSM" + return 0 +} + readPidFile() { local -n _pid="$1" @@ -307,12 +313,6 @@ makeDir() { return 0 } -app() { - - echo "Virtual DSM" - return 0 -} - finiteMemoryLimit() { local limit="$1"