Compare commits

...
6 Commits
10 changed files with 104 additions and 87 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
-e SC2153 -e SC2153
- -
name: Lint Dockerfile name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.3.0 uses: hadolint/hadolint-action@v3.4.0
with: with:
dockerfile: Dockerfile dockerfile: Dockerfile
ignore: DL3008 ignore: DL3008
+1 -1
View File
@@ -8,7 +8,7 @@ ARG TARGETPLATFORM
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
ARG VERSION_CSTRUCT="4.7" ARG VERSION_CSTRUCT="4.7"
ARG VERSION_PASST="2026_07_16" ARG VERSION_PASST="2026_07_28"
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
+1 -1
View File
@@ -2,7 +2,7 @@
set -Eeuo pipefail set -Eeuo pipefail
if enabled "$DEBUG"; then if enabled "$DEBUG"; then
printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}" printf "QEMU arguments:\n\n %s\n\n" "${ARGS// -/$'\n -'}"
fi fi
return 0 return 0
+4 -4
View File
@@ -254,7 +254,7 @@ if ! touch "$SYSTEM"; then
error "Could not create file $SYSTEM for the system disk." && exit 98 error "Could not create file $SYSTEM for the system disk." && exit 98
fi fi
! setOwner "$SYSTEM" && warn "failed to set the owner for \"$SYSTEM\" !" setOwner "$SYSTEM" || warn "failed to set the owner for \"$SYSTEM\" !"
if [[ "${FS,,}" == "btrfs" ]]; then if [[ "${FS,,}" == "btrfs" ]]; then
{ chattr +C "$SYSTEM"; } || : { chattr +C "$SYSTEM"; } || :
@@ -329,7 +329,7 @@ fakeroot -- bash -c "set -Eeu;\
rm -rf "$MOUNT" rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE/dsm.ver" echo "$BASE" > "$STORAGE/dsm.ver"
! setOwner "$STORAGE/dsm.ver" && warn "failed to set the owner for \"$STORAGE/dsm.ver\" !" setOwner "$STORAGE/dsm.ver" || warn "failed to set the owner for \"$STORAGE/dsm.ver\" !"
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$PAT" rm -f "$PAT"
@@ -338,11 +338,11 @@ else
fi fi
if [ -f "$STORAGE/$BASE.pat" ]; then if [ -f "$STORAGE/$BASE.pat" ]; then
! setOwner "$STORAGE/$BASE.pat" && warn "failed to set the owner for \"$STORAGE/$BASE.pat\" !" setOwner "$STORAGE/$BASE.pat" || warn "failed to set the owner for \"$STORAGE/$BASE.pat\" !"
fi fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img" mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
! setOwner "$STORAGE/$BASE.boot.img" && warn "failed to set the owner for \"$STORAGE/$BASE.boot.img\" !" setOwner "$STORAGE/$BASE.boot.img" || warn "failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
rm -rf "$TMP" rm -rf "$TMP"
+72 -53
View File
@@ -405,36 +405,6 @@ natGuestIP() {
return 1 return 1
} }
kernelAtLeast() {
local major="$1"
local minor="${2:-0}"
(( KERNEL > major || (KERNEL == major && MINOR >= minor) ))
}
canBindToDevice() {
local dev="$1"
[ -n "$dev" ] || return 1
kernelAtLeast 5 7 || return 1
[ -d "/sys/class/net/$dev" ] || return 1
command -v python3 > /dev/null 2>&1 || return 0
python3 - "$dev" > /dev/null 2>&1 <<'PY'
import socket
import sys
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.setsockopt(
socket.SOL_SOCKET,
socket.SO_BINDTODEVICE,
sys.argv[1].encode() + b"\0",
)
PY
}
# ###################################### # ######################################
# DNS / port helpers # DNS / port helpers
# ###################################### # ######################################
@@ -522,7 +492,7 @@ configureDNS() {
arguments+=" --log-facility=$log" arguments+=" --log-facility=$log"
arguments=$(echo "$arguments" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') arguments=$(echo "$arguments" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
enabled "$DEBUG" && printf "Dnsmasq arguments:\n\n%s\n\n" "${arguments// -/$'\n-'}" enabled "$DEBUG" && printf "Dnsmasq arguments:\n\n %s\n\n" "${arguments// -/$'\n -'}"
{ $DNSMASQ ${arguments:+ $arguments}; local rc=$?; } || : { $DNSMASQ ${arguments:+ $arguments}; local rc=$?; } || :
@@ -545,11 +515,12 @@ configureDNS() {
return 0 return 0
} }
getHostPorts() { normalizePorts() {
local list="${HOST_PORTS// /}," local list="$1"
local port ports="" local mode="${2:-tcp}"
local mode="${1:-tcp}" local port num
local ports=""
for port in ${list//,/ }; do for port in ${list//,/ }; do
@@ -558,31 +529,78 @@ getHostPorts() {
case "$mode" in case "$mode" in
"tcp" ) "tcp" )
[[ "$port" == *"/udp" ]] && continue [[ "$port" == *"/udp" ]] && continue
local num="${port%/tcp}" num="${port%/tcp}"
[ -n "$num" ] && ports+="$num,"
;; ;;
"all" ) "all" )
if [[ "$port" == *"/udp" ]]; then if [[ "$port" == *"/udp" ]]; then
local num="${port%/udp}" num="${port%/udp}"
[ -n "$num" ] && ports+="$num/udp," [ -n "$num" ] && ports+="$num/udp,"
else else
local num="${port%/tcp}" num="${port%/tcp}"
[ -n "$num" ] && ports+="$num/tcp," [ -n "$num" ] && ports+="$num/tcp,"
fi fi
continue
;; ;;
*) *)
return 1 return 1
;; ;;
esac esac
[ -n "$num" ] && ports+="$num,"
done done
# Remove duplicates # Remove duplicates
ports=$(echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g') echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g'
return 0
}
getReservedPorts() {
local list=""
local mode="${1:-tcp}"
# Reserve the DNS port while the internal dnsmasq resolver is active.
if ! enabled "${DNSMASQ_DISABLE:-}" && ! isNAT; then
list+="53/tcp,53/udp,"
fi
# WEB_PORT and WSD_PORT are intentionally not reserved. In non-DHCP modes,
# closeWeb() releases both before NAT or user-mode forwarding is configured,
# allowing WEB_PORT (normally 5000) to be handed over to DSM.
normalizePorts "$list" "$mode"
return $?
}
getCustomHostPorts() {
local mode="${1:-tcp}"
local reserved user port
local ports=""
reserved=$(getReservedPorts "all")
user=$(normalizePorts "$HOST_PORTS" "all")
for port in ${user//,/ }; do
[[ ",$reserved," == *",$port,"* ]] && continue
ports+="$port,"
done
normalizePorts "$ports" "$mode"
return 0
}
getHostPorts() {
local mode="${1:-tcp}"
local reserved custom
# Merge internal reservations with user-defined host ports without mutating HOST_PORTS.
# User entries already covered by an internal reservation are silently ignored.
reserved=$(getReservedPorts "all")
custom=$(getCustomHostPorts "all")
normalizePorts "$reserved,$custom" "$mode"
echo "$ports"
return 0 return 0
} }
@@ -592,8 +610,9 @@ getUserPorts() {
local list="$defaults,${USER_PORTS// /}," local list="$defaults,${USER_PORTS// /},"
local ports="" local ports=""
local userport hostport exclude local userport hostport exclude reserved
reserved=$(getReservedPorts "all")
exclude=$(getHostPorts "all") exclude=$(getHostPorts "all")
for userport in ${list//,/ }; do for userport in ${list//,/ }; do
@@ -615,7 +634,9 @@ getUserPorts() {
if [[ "$num/$proto" == "$hostport" ]]; then if [[ "$num/$proto" == "$hostport" ]]; then
if [[ "$hostport" != "${WEB_PORT:-}/tcp" ]]; then if [[ ",$reserved," == *",$hostport,"* ]]; then
warn "Could not assign port $hostport to \"USER_PORTS\" because it is reserved by the container!"
elif [[ "$hostport" != "${WEB_PORT:-}/tcp" ]]; then
warn "Could not assign port $hostport to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!" warn "Could not assign port $hostport to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!"
fi fi
@@ -663,7 +684,6 @@ getSlirp() {
getPasst() { getPasst() {
local list port local list port
local bind="$UPLINK"
local tcp="" udp="" args="" local tcp="" udp="" args=""
list=$(getUserPorts) list=$(getUserPorts)
@@ -693,12 +713,8 @@ getPasst() {
tcp="${tcp%,}" tcp="${tcp%,}"
udp="${udp%,}" udp="${udp%,}"
if canBindToDevice "$DEV"; then [ -n "$tcp" ] && args+=" -t $tcp"
bind="%$DEV" [ -n "$udp" ] && args+=" -u $udp"
fi
[ -n "$tcp" ] && args+=" -t $bind/$tcp"
[ -n "$udp" ] && args+=" -u $bind/$udp"
echo "$args" echo "$args"
return 0 return 0
@@ -1905,7 +1921,10 @@ validateHost() {
validateHostPorts() { validateHostPorts() {
if isNAT && [[ "${HOST_PORTS,,}" == *"/udp"* ]]; then local custom
custom=$(getCustomHostPorts "all")
if isNAT && [[ "$custom" == *"/udp"* ]]; then
warn "UDP ports in \"HOST_PORTS\" are not yet implemented for NAT networking." warn "UDP ports in \"HOST_PORTS\" are not yet implemented for NAT networking."
fi fi
+4 -4
View File
@@ -108,8 +108,8 @@ forceKillQemu() {
local reason="$1" local reason="$1"
local pid display local pid display
! readQemuPid pid && return 0 readQemuPid pid || return 0
! isAlive "$pid" && return 0 isAlive "$pid" || return 0
display=$(displayReason "$reason") display=$(displayReason "$reason")
error "Forcefully terminating $(app), reason: $display..." error "Forcefully terminating $(app), reason: $display..."
@@ -305,7 +305,7 @@ waitForShutdown() {
local slp=$! local slp=$!
# Stop waiting if the process has exited # Stop waiting if the process has exited
! isAlive "$pid" && break isAlive "$pid" || break
# Workaround for stale/zombie QEMU pid file # Workaround for stale/zombie QEMU pid file
[ ! -s "$QEMU_START_PID" ] && [ ! -s "$QEMU_PID" ] && break [ ! -s "$QEMU_START_PID" ] && [ ! -s "$QEMU_PID" ] && break
@@ -372,7 +372,7 @@ graceful_shutdown() {
finish "$code" finish "$code"
} }
! enabled "$SHUTDOWN" && return 0 enabled "$SHUTDOWN" || return 0
[ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT" [ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
if interactive; then if interactive; then
+1 -1
View File
@@ -47,7 +47,7 @@ checkSse42() {
if ! hasFlag "sse4_2"; then if ! hasFlag "sse4_2"; then
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!" error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
! enabled "$DEBUG" && exit 88 enabled "$DEBUG" || exit 88
fi fi
return 0 return 0
+3 -3
View File
@@ -209,7 +209,7 @@ normalizeRamSize() {
fi fi
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') 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 numfmt --from=iec "$RAM_SIZE" &>/dev/null || { error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16; }
wanted=$(numfmt --from=iec "$RAM_SIZE") wanted=$(numfmt --from=iec "$RAM_SIZE")
[ "$wanted" -lt "$RAM_MINIMUM" ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16 [ "$wanted" -lt "$RAM_MINIMUM" ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
@@ -254,7 +254,7 @@ checkKvm() {
fi fi
if ! grep -qw "sse4_2" <<< "$flags"; then if ! grep -qw "sse4_2" <<< "$flags"; then
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!" error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
! enabled "$DEBUG" && exit 88 enabled "$DEBUG" || exit 88
fi fi
fi fi
fi fi
@@ -275,7 +275,7 @@ checkKvm() {
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower." error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;; error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
esac esac
! enabled "$DEBUG" && exit 88 enabled "$DEBUG" || exit 88
fi fi
fi fi
+4 -5
View File
@@ -82,7 +82,8 @@ waitForSocket() {
local socket="$1" local socket="$1"
local exit_code="$2" local exit_code="$2"
local pid cnt=0 local timeout=5 pid
local deadline=$((SECONDS + timeout))
while [ ! -S "$socket" ]; do while [ ! -S "$socket" ]; do
@@ -91,14 +92,12 @@ waitForSocket() {
exit "$exit_code" exit "$exit_code"
fi fi
sleep 0.1 if (( SECONDS >= deadline )); then
cnt=$((cnt + 1))
if (( cnt > 50 )); then
error "Failed to create qemu-host socket: $socket" error "Failed to create qemu-host socket: $socket"
exit "$exit_code" exit "$exit_code"
fi fi
sleep 0.1
done done
return 0 return 0
+13 -14
View File
@@ -136,14 +136,13 @@ isAlive() {
waitPid() { waitPid() {
local i=0
local pid="$1" local pid="$1"
local timeout="${2:-10}" local timeout="${2:-10}"
local deadline=$((SECONDS + timeout))
while [ -n "$pid" ] && isAlive "$pid"; do while [ -n "$pid" ] && isAlive "$pid"; do
(( SECONDS >= deadline )) && return 1
sleep 0.2 sleep 0.2
i=$((i + 1))
(( i >= timeout * 5 )) && return 1
done done
return 0 return 0
@@ -151,16 +150,16 @@ waitPid() {
waitPidFile() { waitPidFile() {
local i=0 pid local pid
local file="$1" local file="$1"
local timeout="${2:-10}" local timeout="${2:-10}"
local deadline=$((SECONDS + timeout))
! readPidFile pid "$file" && return 0 readPidFile pid "$file" || return 0
while [ -s "$file" ] && isAlive "$pid"; do while [ -s "$file" ] && isAlive "$pid"; do
(( SECONDS >= deadline )) && return 1
sleep 0.2 sleep 0.2
i=$((i + 1))
(( i >= timeout * 5 )) && return 1
done done
rm -f -- "$file" rm -f -- "$file"
@@ -183,19 +182,19 @@ pKill() {
fWait() { fWait() {
local i=0
local name="$1" local name="$1"
local timeout="${2:-10}" local timeout="${2:-10}"
local deadline=$((SECONDS + timeout))
[ -z "$name" ] && return 0 [ -z "$name" ] && return 0
while pgrep -f -l "$name" >/dev/null; do while pgrep -f -l "$name" >/dev/null; do
sleep 0.2 if (( SECONDS >= deadline )); then
i=$((i + 1))
if (( i >= timeout * 5 )); then
warn "Timed out while waiting for process: $name" warn "Timed out while waiting for process: $name"
break break
fi fi
sleep 0.2
done done
return 0 return 0
@@ -219,7 +218,7 @@ sKill() {
local pid local pid
local file="$1" local file="$1"
! readPidFile pid "$file" && return 0 readPidFile pid "$file" || return 0
if isAlive "$pid"; then if isAlive "$pid"; then
{ kill -15 -- "$pid" || :; } 2>/dev/null { kill -15 -- "$pid" || :; } 2>/dev/null
@@ -257,7 +256,7 @@ setOwner() {
uid=$(stat -c '%u' "$dir") || return 1 uid=$(stat -c '%u' "$dir") || return 1
gid=$(stat -c '%g' "$dir") || return 1 gid=$(stat -c '%g' "$dir") || return 1
! chown "$uid:$gid" "$file" && return 1 chown "$uid:$gid" "$file" || return 1
return 0 return 0
} }
@@ -268,7 +267,7 @@ makeDir() {
local dir uid gid local dir uid gid
[ -d "$path" ] && return 0 [ -d "$path" ] && return 0
! mkdir -p "$path" && return 1 mkdir -p "$path" || return 1
dir=$(dirname -- "$path") dir=$(dirname -- "$path")