Compare commits

...
38 Commits
Author SHA1 Message Date
KroeseandGitHub 489e3eda6f feat: Support simpler QMP configuration (#1403) 2026-08-28 04:09:16 +02:00
KroeseandGitHub b00f1d5d78 fix: Update createDevice parameters for base image (#1402) 2026-08-27 14:31:52 +02:00
renovate[bot]andGitHub 8fea46853e chore(deps): update hadolint/hadolint-action action to v3.5.0 (#1401) 2026-08-25 16:11:10 +02:00
KroeseandGitHub a41692add1 docs: Environment variables (#1400) 2026-08-25 15:32:26 +02:00
KroeseandGitHub b012806b86 build: Update Dockerfile (#1399) 2026-08-24 04:30:37 +02:00
KroeseandGitHub b9b464d2cc docs: GPU acceleration (#1398) 2026-08-23 22:03:58 +02:00
renovate[bot]andGitHub 20fa7018bc build: Update QEMU base image to v7.49 (#1397) 2026-08-23 21:01:47 +02:00
KroeseandGitHub 401d990381 docs: Environment variables (#1396) 2026-08-23 20:56:33 +02:00
KroeseandGitHub 47af1d07f4 build: Update QEMU base image to v7.48 (#1394) 2026-08-19 20:11:14 +02:00
KroeseandGitHub 4660512f12 docs: GPU acceleration (#1393) 2026-08-18 05:15:43 +02:00
KroeseandGitHub 6d37a0426b build: Update QEMU base image to v7.47 (#1392) 2026-08-18 05:14:00 +02:00
KroeseandGitHub 604cad0207 feat: Support GPU acceleration for AMD GPU's (#1391) 2026-08-18 03:23:38 +02:00
KroeseandGitHub 3ad7e78787 fix: Sanitize config variables (#1390) 2026-08-18 03:02:33 +02:00
KroeseandGitHub 5f908488ef docs: GPU acceleration (#1389) 2026-08-18 02:30:58 +02:00
KroeseandGitHub dcdc796e59 docs: GPU acceleration (#1388) 2026-08-18 02:16:21 +02:00
KroeseandGitHub 087d876eaf docs: Add AMD GPU support (#1387) 2026-08-18 02:04:17 +02:00
KroeseandGitHub 25a7d55cdd fix: Improve argument ordering (#1386) 2026-08-17 15:01:24 +02:00
KroeseandGitHub 9683b195f3 docs: Environment variables (#1385) 2026-08-16 16:12:54 +02:00
KroeseandGitHub f810576651 docs: Environment variables (#1384) 2026-08-16 15:54:51 +02:00
KroeseandGitHub 5fd954bea4 fix: Set machine name (#1383) 2026-08-16 08:45:26 +02:00
KroeseandGitHub 768b8fa51a feat: Don't remove casing from machine name (#1382) 2026-08-16 07:27:05 +02:00
KroeseandGitHub b909555ef3 fix: Make RNG device configurable (#1381) 2026-08-16 06:18:04 +02:00
KroeseandGitHub a77026eafb build: Update QEMU base image to v7.46 (#1380) 2026-08-16 03:00:51 +02:00
KroeseandGitHub e8ef357ed2 fix: Restore HTTP portal redirect fallback (#1379) 2026-08-15 11:50:16 +02:00
KroeseandGitHub a16c27ecce fix: Use replacement navigation for web transitions (#1378) 2026-08-14 19:40:47 +02:00
KroeseandGitHub 1917eae0ae fix: Do not set no-store on index page (#1377) 2026-08-14 14:02:07 +02:00
KroeseandGitHub d5bb90f012 fix: Disable COW for Virtual DSM boot image on Btrfs (#1376) 2026-08-13 16:03:19 +02:00
KroeseandGitHub d854ae811c fix: Prevent caching of web root (#1375) 2026-08-13 07:00:48 +02:00
KroeseandGitHub 03993aa773 fix: Prevent estimated progress from reaching 100% (#1374) 2026-08-11 20:08:08 +02:00
KroeseandGitHub b39ff267f2 fix: Update entrypoint (#1373) 2026-08-11 17:48:21 +02:00
KroeseandGitHub f7870d1693 fix: Only disable no-store for msg.html (#1372) 2026-08-11 15:47:14 +02:00
KroeseandGitHub c648338a18 feat: Use Intel Mesa package from base image (#1371) 2026-08-11 15:42:50 +02:00
KroeseandGitHub 9fe52c874e fix: Prevent caching of web status (#1370) 2026-08-10 23:47:43 +02:00
KroeseandGitHub c5eae88dfc fix: Improve error reporting (#1369) 2026-08-10 14:33:35 +02:00
KroeseandGitHub 1d0ac30ae1 fix: Add comment (#1368) 2026-08-10 07:25:00 +02:00
KroeseandGitHub c355cbe5dc fix: Separate stopped container status lines (#1367) 2026-08-10 07:23:35 +02:00
KroeseandGitHub 6f69eb5c68 fix: Stop console after QEMU exits (#1366) 2026-08-10 03:36:49 +02:00
KroeseandGitHub 07595976b2 feat: Use stopAllServers from base image (#1365) 2026-08-10 03:01:45 +02:00
13 changed files with 157 additions and 136 deletions
+1 -1
View File
@@ -27,7 +27,7 @@ jobs:
-e SC2153
-
name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.4.0
uses: hadolint/hadolint-action@v3.5.0
with:
dockerfile: Dockerfile
ignore: DL3008,DL3067
+2 -3
View File
@@ -1,9 +1,8 @@
# syntax=docker/dockerfile:1
FROM qemux/qemu-host:2.06 AS host
FROM scratch
COPY --from=qemux/qemu:7.45 / /
COPY --from=qemux/qemu:7.49 / /
ARG VERSION_ARG="0.0"
ARG VERSION_CSTRUCT="4.7"
@@ -34,8 +33,8 @@ EOF
COPY --chmod=755 ./src /run/
COPY --chmod=755 ./web /var/www/
COPY --chmod=755 --from=host /qemu-host.bin /run/host.bin
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/default.conf
COPY --chmod=755 --from=qemux/qemu-host:2.06 /qemu-host.bin /run/host.bin
ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py
VOLUME /storage
+6 -5
View File
@@ -25,6 +25,7 @@ An empty default means the variable is unset and its value is determined automat
| `HOST_CPU` | | CPU name reported to DSM. Selected automatically when unset. |
| `KVM` | `Y` | Enables KVM hardware acceleration. |
| `RAM_SIZE` | `2G` | Amount of RAM assigned to DSM, such as `2G`, `4G`, `half`, or `max`. |
| `RAM_BACKEND` | | Guest RAM backing mechanism, set `memfd` for memfd-backed memory. |
| `RAM_CHECK` | `Y` | Checks whether enough host memory is available before starting DSM. |
## 💾 Storage
@@ -33,7 +34,7 @@ An empty default means the variable is unset and its value is determined automat
|---|---|---|
| `DISK_SIZE` | `256G` | Size of the main data disk. |
| `DISK_FMT` | `raw` | Disk image format: `raw` or `qcow2`. |
| `DISK_TYPE` | `scsi` | Disk device type, such as `sata`, `scsi`, `nvme`, or `blk`. |
| `DISK_TYPE` | `scsi` | Disk device type, such as `ide`, `scsi`, or `blk`. |
| `DISK_CACHE` | `none` | Disk cache mode, such as `none` or `writeback`. |
| `DISK_IO` | `native` | Disk I/O mode, such as `native`, `threads`, or `io_uring`. |
| `DISK_DISCARD` | `unmap` | Discard/TRIM mode for the primary disk. |
@@ -71,9 +72,9 @@ An empty default means the variable is unset and its value is determined automat
| Variable | Default | Description |
|---|---|---|
| `DISPLAY` | `none` | Display backend, such as `vnc`, `disabled`, or `none`. |
| `LOSSY` | `N` | Enables lossy VNC compression to reduce bandwidth usage. |
| `VGA` | `none` | QEMU video adapter model. |
| `GPU` | `N` | Enables Intel iGPU acceleration. |
| `GPU` | `N` | Enables GPU acceleration. |
| `VRAM_SIZE` | `4G` | Virtual GPU memory budget. |
| `RENDERNODE` | `/dev/dri/renderD128` | Render node used for GPU acceleration. |
## 🎈 Memory Ballooning
@@ -109,6 +110,6 @@ Also see [Dynamic memory allocation](https://github.com/qemus/qemu/blob/master/d
| `DEBUG` | `N` | Enables verbose debug output. |
| `TRACE` | `N` | Enables shell command tracing. |
| `HOST_DEBUG` | `N` | Enables debug output for the DSM host helper. |
| `MONITOR` | | QEMU monitor configuration. |
| `QMP` | | QEMU Machine Protocol configuration. |
| `MONITOR` | | QEMU monitor, such as `/storage/monitor.sock` or `4444`. |
| `QMP` | | QEMU Machine Protocol, such as `/storage/qmp.sock` or `4444`. |
| `ARGUMENTS` | | Additional raw arguments appended to the QEMU command line. |
+20 -4
View File
@@ -201,9 +201,9 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- 'c *:* rwm'
```
### How do I pass through the GPU?
### How do I enable GPU acceleration?
To pass through your Intel GPU, add the following lines to your compose file:
To enable GPU acceleration using an Intel or AMD GPU, add the following lines to your compose file:
```yaml
environment:
@@ -212,8 +212,24 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- /dev/dri
```
> [!NOTE]
> This can be used to enable the facial recognition function in Synology Photos, but does not provide hardware transcoding for video.
For NVIDIA GPUs, the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) must be installed on the host and the GPU must be exposed to the container:
```yaml
environment:
GPU: "Y"
NVIDIA_DRIVER_CAPABILITIES: "all"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities:
- gpu
```
GPU acceleration can be used to enable the facial recognition function in Synology Photos, but currently does not provide hardware transcoding for video yet.
### How do I enable dynamic memory allocation?
+69 -17
View File
@@ -1,12 +1,18 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${RNG:=""}"
: "${QMP:=""}"
: "${UUID:=""}"
: "${MONITOR:=""}"
DEF_OPTS="-nodefaults -boot strict=on"
DEV_OPTS=""
msg="Configuring QEMU..."
enabled "$DEBUG" && echo "$msg"
# Sanitize variables
QMP=$(strip "$QMP")
UUID=$(strip "$UUID")
MONITOR=$(strip "$MONITOR")
configureProcessor() {
@@ -25,16 +31,54 @@ configureMemory() {
return 0
}
normalizeSocket() {
local value="$1"
local backend="${value%%,*}"
if [[ "$backend" == *.sock && "$backend" != *:* ]]; then
value="unix:$value"
[[ ",$value," == *,server=* ]] || value+=",server=on"
if [[ ",$value," != *,wait=* ]] && [[ ",$value," != *,server=off,* ]]; then
value+=",wait=off"
fi
fi
echo "$value"
}
normalizePort() {
local value="$1"
local protocol="$2"
if [[ "$value" =~ ^[0-9]+$ ]]; then
value="$protocol:0.0.0.0:$value,server=on,wait=off"
fi
echo "$value"
}
configureMonitor() {
MON_OPTS=""
[ -n "$QMP" ] && MON_OPTS+=" -qmp $QMP"
[ -n "$MONITOR" ] && MON_OPTS+=" -monitor $MONITOR"
MON_OPTS+=" -name $PROCESS,process=$PROCESS,debug-threads=on"
MON_OPTS+=" -pidfile $QEMU_PID"
if [ -n "$MONITOR" ]; then
MONITOR=$(normalizePort "$MONITOR" "telnet")
MONITOR=$(normalizeSocket "$MONITOR")
MON_OPTS+=" -monitor $MONITOR"
fi
if [ -n "$QMP" ]; then
QMP=$(normalizePort "$QMP" "tcp")
QMP=$(normalizeSocket "$QMP")
MON_OPTS+=" -qmp $QMP"
fi
ID_OPTS="-name ${APP// /-},process=$PROCESS"
PID_OPTS="-pidfile $QEMU_PID"
MON_OPTS="${MON_OPTS# }"
return 0
}
@@ -48,36 +92,44 @@ configureMachine() {
MAC_OPTS="-machine type=$MACHINE,smm=$smm,usb=off"
MAC_OPTS+=",vmport=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
UUID=$(strip "$UUID")
[ -n "$UUID" ] && MAC_OPTS+=" -uuid $UUID"
[ -n "${SM_BIOS:-}" ] && MAC_OPTS+=" $SM_BIOS"
[ -n "$UUID" ] && ID_OPTS+=" -uuid $UUID"
[ -n "${SM_BIOS:-}" ] && ID_OPTS+=" $SM_BIOS"
return 0
}
configureVirtioDevices() {
configureDevices() {
local bus
bus=$(getPciBus)
DEV_OPTS=""
if ! disabled "$RNG"; then
DEV_OPTS+=" -object rng-random,id=objrng0,filename=/dev/urandom"
DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=$bus,addr=0x1c"
fi
# Keep the existing balloon device by default. When dynamic ballooning is
# enabled, expose guest statistics and a dedicated QMP control socket.
if ! enabled "${BALLOONING:-}"; then
DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=$bus,addr=0x4"
DEV_OPTS+=" -device virtio-balloon-pci,id=balloon0,bus=$bus,addr=0x4"
else
MON_OPTS+=" -qmp unix:${BALLOONING_SOCKET},server=on,wait=off"
DEV_OPTS="-device virtio-balloon-pci,free-page-reporting=on,guest-stats-polling-interval=1,id=balloon0,bus=$bus,addr=0x4"
DEV_OPTS+=" -device virtio-balloon-pci,free-page-reporting=on,guest-stats-polling-interval=1,id=balloon0,bus=$bus,addr=0x4"
fi
DEV_OPTS+=" -object rng-random,id=objrng0,filename=/dev/urandom"
DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=$bus,addr=0x1c"
DEV_OPTS="${DEV_OPTS# }"
return 0
}
buildArguments() {
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $BOOT_OPTS $DEV_OPTS $ARGUMENTS"
local default="-nodefaults"
local boot="-boot strict=on"
ARGS="$default $MAC_OPTS $CPU_OPTS $RAM_OPTS $ID_OPTS $PID_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $boot $BOOT_OPTS $DEV_OPTS $ARGUMENTS"
# Collapse whitespace after optional argument groups are assembled so empty
# features cannot leave malformed spacing in the final QEMU command.
@@ -92,7 +144,7 @@ configureMemory
configureMonitor
configureMachine
configureProcessor
configureVirtioDevices
configureDevices
buildArguments
-91
View File
@@ -1,91 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: "${GPU:="N"}" # GPU passthrough
: "${VGA:="virtio"}" # VGA adaptor
: "${DISPLAY:="none"}" # Display type
: "${LOSSY:="N"}" # Lossy VNC compression
: "${RENDERNODE:="/dev/dri/renderD128"}" # Render node
# Sanitize variables
VGA=$(strip "$VGA")
LOSSY=$(strip "$LOSSY")
DISPLAY=$(strip "$DISPLAY")
RENDERNODE=$(strip "$RENDERNODE")
CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}')
# The accelerated Intel render-node path is restricted to x86 Intel hosts;
# other platforms retain the normal QEMU display backend.
if ! enabled "$GPU" || isAmdCpu || [[ "$ARCH" != "amd64" ]]; then
# A disabled frontend also removes the emulated VGA device to keep the guest
# hardware layout headless.
[[ "${DISPLAY,,}" == "none" ]] && VGA="none"
if enabled "$LOSSY" && [[ "${DISPLAY,,}" == vnc=* ]]; then
DISPLAY+=",lossy=on"
fi
DISPLAY_OPTS="-display ${DISPLAY} -vga ${VGA}"
return 0
fi
msg="Configuring display drivers..."
html "$msg"
enabled "$DEBUG" && echo "$msg"
DISPLAY_OPTS="-display egl-headless,rendernode=${RENDERNODE}"
DISPLAY_OPTS+=" -vga $VGA"
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
# Extract the card number from the render node
# Linux renderD128 corresponds to card0; derive both device minors because
# container device bindings may expose only the render node.
CARD_NUMBER=$(echo "$RENDERNODE" | grep -oP '(?<=renderD)\d+')
CARD_DEVICE="/dev/dri/card$((CARD_NUMBER - 128))"
if [ ! -c "$CARD_DEVICE" ]; then
if mknod "$CARD_DEVICE" c 226 $((CARD_NUMBER - 128)); then
chmod 666 "$CARD_DEVICE"
fi
fi
if [ ! -c "$RENDERNODE" ]; then
if mknod "$RENDERNODE" c 226 "$CARD_NUMBER"; then
chmod 666 "$RENDERNODE"
fi
fi
if [ ! -c "$RENDERNODE" ] || [ ! -r "$RENDERNODE" ] || [ ! -w "$RENDERNODE" ]; then
warn "render device '${RENDERNODE}' is unavailable or inaccessible."
fi
addDsmPackage() {
local pkg=$1
local desc=$2
if ! apt-mark showinstall | grep -qx "$pkg"; then
[ -z "$COUNTRY" ] && setCountry
# Use a mainland mirror only for on-demand package installation, avoiding
# slow or inaccessible Debian endpoints in that region.
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
fi
addPackage "$pkg" "$desc"
}
# Install acceleration packages lazily so non-GPU deployments keep the base
# image small and do not require OpenGL modules.
addDsmPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addDsmPackage "qemu-system-modules-opengl" "OpenGL module"
return 0
+1
View File
@@ -23,6 +23,7 @@ cd /run
. init.sh # Initialize system
. memory.sh # Check memory
. server.sh # Start webserver
. download.sh # Load functions
. install.sh # Run installation
. disk.sh # Initialize disks
. display.sh # Initialize graphics
+2
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env bash
set -Eeuo pipefail
fKill "progress.sh"
if [ -s "$QEMU_DIR/qemu.host" ] && [[ "$(<"$QEMU_DIR/qemu.host")" == "172.17."* ]]; then
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
fi
+29 -2
View File
@@ -178,8 +178,35 @@ createSystemImage() {
return 67
fi
7z x -y -o"$tmp" "$DSM_BOOT" >/dev/null || return $?
DSM_BOOT="${DSM_BOOT%.zip}"
local bootTmp="$tmp/boot" bootBase
bootBase="$(basename "${DSM_BOOT%.zip}")"
rm -rf "$bootTmp" || return $?
if ! makeDir "$bootTmp"; then
error "Failed to create directory \"$bootTmp\" !"
return 93
fi
if [[ "${fs,,}" == "btrfs" ]]; then
{ chattr +C "$bootTmp"; } || :
fi
7z x -y -o"$bootTmp" "$DSM_BOOT" >/dev/null || return $?
DSM_BOOT="$bootTmp/$bootBase"
if [ ! -s "$DSM_BOOT" ]; then
error "The PAT boot image could not be extracted."
return 67
fi
if [[ "${fs,,}" == "btrfs" ]]; then
local attrs
attrs=$(lsattr "$DSM_BOOT") || return $?
if [[ "$attrs" != *"C"* ]]; then
error "Failed to disable COW for boot image $DSM_BOOT on ${fs^^} filesystem."
fi
fi
SYSTEM="$STORAGE/$BASE.system.img"
rm -f "$SYSTEM" || return $?
+3 -1
View File
@@ -21,6 +21,7 @@ finish() {
local reason=$1 failed=0
# A nonzero exit is unexpected only when QEMU_END is missing.
if [ ! -f "$QEMU_END" ] && (( reason != 0 )); then
failed=1
fi
@@ -29,14 +30,15 @@ finish() {
forceKillQemu "$reason"
cleanupHelpers "$HOST_PID"
fKill "print.sh"
rm -f -- "$HOST_API_SOCKET" "$HOST_AGENT_SOCKET"
if ! waitQemuExit 10; then
warn "Timed out while waiting for $(app) to exit!"
fi
stopConsole
echo
if (( failed == 0 )); then
+4 -5
View File
@@ -12,8 +12,8 @@ addSystemDisks() {
setOwner "$boot" || warn "failed to set the owner for \"$boot\" !"
setOwner "$system" || warn "failed to set the owner for \"$system\" !"
DISK_OPTS+=$(createDevice "$boot" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "" "synoboot")
DISK_OPTS+=$(createDevice "$system" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "" "synosys")
DISK_OPTS+=$(createDevice "$boot" "$DISK_TYPE" "1" "0xa" "raw" "" "" "synoboot")
DISK_OPTS+=$(createDevice "$system" "$DISK_TYPE" "2" "0xb" "raw" "" "" "synosys")
return 0
}
@@ -30,10 +30,9 @@ closeWebserver() {
fi
writeAtomic "$WSD_COMMAND" "portal" || return $?
sleep 1.2
local pids=( "${WEB_PID:-}" "${WSD_PID:-}" )
mKill "${pids[@]}"
sleep 1.2
stopAllServers
WEB="N"
+7 -1
View File
@@ -23,7 +23,13 @@ server {
gzip_disable "msie6";
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
add_header Cache-Control "no-cache";
set $cache_control "no-cache";
if ($uri = /msg.html) {
set $cache_control "no-store";
}
add_header Cache-Control $cache_control always;
location / {
+13 -6
View File
@@ -7,7 +7,6 @@ var portal = false;
var portalUrl = "";
var interval = 1000;
var lastStatus = "";
var stopped = "The container has stopped. Check the container logs for details.";
function abortRequest() {
@@ -160,6 +159,12 @@ function processMsg(msg) {
rememberStatus(msg);
setInfo(msg);
if (msg.toLowerCase().indexOf("href=") !== -1) {
var div = document.createElement("div");
div.innerHTML = msg;
return beginPortal(div.querySelector("a").href);
}
return true;
}
@@ -297,14 +302,14 @@ function estimateProgress(bytes) {
}
if (previousBoundary === 0) {
return Math.min(bytes / boundary * 100, 100);
return Math.min(bytes / boundary * 100, 99.9);
}
var rangeProgress =
(bytes - previousBoundary) /
(boundary - previousBoundary);
return 30 + Math.pow(rangeProgress, 2) * 70;
return Math.min(30 + Math.pow(rangeProgress, 2) * 70, 99.9);
}
function parseProgress(msg) {
@@ -512,9 +517,11 @@ function setError(text) {
function setStopped() {
var msg = stopped;
var msg = "<span style=\"display:block\">The container has stopped.</span>";
msg += "<span style=\"display:block; margin-top:1em; font-size:0.85em; font-weight:normal\">Check the container log for more details.</span>";
if (lastStatus.length > 0) {
msg += "<br>(Last status: " + escapeContent(lastStatus) + ")";
msg += "<span style=\"display:block; margin-top:1em; font-size:0.75em; font-weight:normal; color:rgba(255,255,255,0.65)\">[ Last status: " + escapeContent(lastStatus) + " ]</span>";
}
return setError(msg);
@@ -536,7 +543,7 @@ function reload() {
navigationTimer = setTimeout(function() {
navigationTimer = null;
window.location.reload();
window.location.replace(window.location.href);
}, 3000);
return true;