Compare commits

...

18 Commits

Author SHA1 Message Date
Kroese
3db91f077f
fix: Relay last status message (#1054) 2025-10-11 17:56:44 +02:00
Kroese
fae14f4dd9
feat: Set listening interface for Passt (#1052) 2025-10-09 11:48:08 +02:00
Kroese
d190b3ba87
feat: Use websocket for status messages (#1051) 2025-10-08 21:28:26 +02:00
Kroese
554f3295cb
fix: Terminate tail on exit (#1049) 2025-10-08 08:13:27 +02:00
Kroese
cdc4689d6a
feat: Use the engine variable (#1048) 2025-10-07 12:41:42 +02:00
Kroese
21d18fd439
feat: Improve healthcheck (#1047) 2025-10-07 05:53:06 +02:00
Kroese
fd5ec52226
fix: Check if logfile exists (#1046) 2025-10-07 02:21:49 +02:00
Kroese
fc7ab22741
fix: Use gateway MAC for passt (#1045) 2025-10-07 02:15:26 +02:00
Kroese
59cf59faa4
feat: Improved networking implementation (#1044) 2025-10-07 00:58:34 +02:00
Kroese
705c6ce7f1
fix: Exclude 'tap' from bus check (#1043) 2025-10-05 17:06:57 +02:00
Kroese
f2937ab507
feat: Allow large MTU sizes (#1042) 2025-10-04 10:35:20 +02:00
Kroese
399829cf3c
feat: Make monitor port configurable (#1041) 2025-10-04 09:39:20 +02:00
xrh0905
b694d6faf8
feat: Support 8k sector sizes (#1040) 2025-10-03 12:57:08 +02:00
Kroese
7acd1f6cdb
feat: Enhanced Dnsmasq logging (#1039) 2025-10-03 01:54:44 +02:00
Kroese
09ca3bf118
feat: Add debug trace option (#1038) 2025-10-02 17:18:53 +02:00
renovate[bot]
6cac45c397
chore(deps): update peter-evans/dockerhub-description action to v5 (#1037)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 23:39:30 +02:00
Kroese
f18663d840
feat: Network mode detection (#1036) 2025-10-01 13:05:13 +02:00
Kroese
fefe1af9e6
fix: CPU detection (#1035)
Refactor CPU string processing to remove 'th Gen' suffix.
2025-09-30 17:18:43 +02:00
21 changed files with 740 additions and 356 deletions

View File

@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- -
name: Docker Hub Description name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4 uses: peter-evans/dockerhub-description@v5
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@ -11,6 +11,7 @@ FROM qemux/qemu-host:2.05 AS builder
FROM debian:trixie-slim FROM debian:trixie-slim
ARG TARGETARCH
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
@ -36,16 +37,20 @@ RUN set -eu && \
xz-utils \ xz-utils \
iptables \ iptables \
iproute2 \ iproute2 \
apt-utils \
dnsmasq \ dnsmasq \
fakeroot \ fakeroot \
apt-utils \
net-tools \ net-tools \
e2fsprogs \ e2fsprogs \
qemu-utils \ qemu-utils \
websocketd \
iputils-ping \ iputils-ping \
inotify-tools \
ca-certificates \ ca-certificates \
netcat-openbsd \ netcat-openbsd \
qemu-system-x86 && \ qemu-system-x86 && \
wget "https://github.com/qemus/passt/releases/download/v2025_09_19/passt_2025_09_19_${TARGETARCH}.deb" -O /tmp/passt.deb -q && \
dpkg -i /tmp/passt.deb && \
apt-get clean && \ apt-get clean && \
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \ pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
mkdir -p /etc/qemu && \ mkdir -p /etc/qemu && \

View File

@ -1,6 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${DHCP:="N"}"
: "${NETWORK:="Y"}" : "${NETWORK:="Y"}"
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1 [ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
@ -9,6 +10,7 @@ set -Eeuo pipefail
file="/run/shm/dsm.url" file="/run/shm/dsm.url"
address="/run/shm/qemu.ip" address="/run/shm/qemu.ip"
gateway="/run/shm/qemu.gw"
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1 [ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
@ -16,13 +18,13 @@ location=$(<"$file")
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
if [[ "$location" == "20.20"* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
ip="20.20.20.1" ip=$(<"$address")
echo "Failed to reach DSM at http://$location"
else
ip=$(<"$gateway")
port="${location##*:}" port="${location##*:}"
echo "Failed to reach DSM at port $port" echo "Failed to reach DSM at port $port"
else
echo "Failed to reach DSM at http://$location"
ip=$(<"$address")
fi fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1 echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1

View File

@ -37,7 +37,7 @@ if [[ "$RAM_CHECK" != [Nn]* ]]; then
fi fi
if [[ "$DEBUG" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}" printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi fi
return 0 return 0

View File

@ -509,9 +509,6 @@ addDevice () {
if [ -n "$physical" ]; then if [ -n "$physical" ]; then
if [[ "$physical" != "512" ]]; then if [[ "$physical" != "512" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical" sectors=",logical_block_size=$logical,physical_block_size=$physical"
if [[ "$physical" != "4096" && "$physical" != "32768" ]]; then
warn "Unknown physical sector size: $physical for $DISK_DEV"
fi
fi fi
else else
warn "Failed to determine the sector size for $DISK_DEV" warn "Failed to determine the sector size for $DISK_DEV"
@ -522,7 +519,9 @@ addDevice () {
return 0 return 0
} }
html "Initializing disks..." msg="Initializing disks..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS="" [ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi" [ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"

View File

@ -18,6 +18,10 @@ if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64"
fi fi
msg="Configuring display drivers..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE" DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
DISPLAY_OPTS+=" -vga $VGA" DISPLAY_OPTS+=" -vga $VGA"

View File

@ -1,13 +1,16 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${PLATFORM:="x64"}"
: "${APP:="Virtual DSM"}" : "${APP:="Virtual DSM"}"
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}" : "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
cd /run cd /run
. start.sh # Startup hook
. utils.sh # Load functions . utils.sh # Load functions
. reset.sh # Initialize system . reset.sh # Initialize system
. server.sh # Start webserver
. install.sh # Run installation . install.sh # Run installation
. disk.sh # Initialize disks . disk.sh # Initialize disks
. display.sh # Initialize graphics . display.sh # Initialize graphics
@ -30,7 +33,7 @@ fi
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15 (( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
terminal terminal
tail -fn +0 "$QEMU_LOG" 2>/dev/null & tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || : cat "$QEMU_TERM" 2>/dev/null & wait $! || :
sleep 1 & wait $! sleep 1 & wait $!

View File

@ -31,8 +31,9 @@ if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
BASE=$(basename "$URL" .pat) BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}" BASE="${BASE//+/ }"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g') printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65 [ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
@ -65,8 +66,9 @@ fi
if [ ! -s "$FILE" ]; then if [ ! -s "$FILE" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}" BASE="${BASE//+/ }"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g') printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
@ -299,7 +301,4 @@ fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img" mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
rm -rf "$TMP" rm -rf "$TMP"
html "Booting DSM instance..."
sleep 1.2
return 0 return 0

View File

@ -7,17 +7,25 @@ set -Eeuo pipefail
: "${MTU:=""}" : "${MTU:=""}"
: "${DHCP:="N"}" : "${DHCP:="N"}"
: "${NETWORK:="Y"}" : "${NETWORK:="Y"}"
: "${USER_PORTS:=""}"
: "${HOST_PORTS:=""}" : "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}"
: "${ADAPTER:="virtio-net-pci"}" : "${ADAPTER:="virtio-net-pci"}"
: "${VM_NET_IP:=""}"
: "${VM_NET_DEV:=""}" : "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="dsm"}" : "${VM_NET_TAP:="dsm"}"
: "${VM_NET_MAC:="$MAC"}" : "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_IP:="20.20.20.21"}" : "${VM_NET_BRIDGE:="docker"}"
: "${VM_NET_HOST:="VirtualDSM"}" : "${VM_NET_HOST:="VirtualDSM"}"
: "${VM_NET_MASK:="255.255.255.0"}"
: "${PASST:="passt"}"
: "${PASST_MTU:=""}"
: "${PASST_OPTS:=""}"
: "${PASST_DEBUG:=""}"
: "${DNSMASQ_OPTS:=""}" : "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ_DEBUG:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}" : "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}" : "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
@ -29,6 +37,8 @@ ADD_ERR="Please add the following setting to your container:"
configureDHCP() { configureDHCP() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
# Create the necessary file structure for /dev/vhost-net # Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then if mknod /dev/vhost-net c 10 238; then
@ -61,7 +71,7 @@ configureDHCP() {
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size.." warn "Failed to set MTU size to $MTU."
fi fi
fi fi
@ -105,35 +115,69 @@ configureDHCP() {
configureDNS() { configureDNS() {
# Create lease file for faster resolve local if="$1"
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases local ip="$2"
chmod 644 /var/lib/misc/dnsmasq.leases local mac="$3"
local host="$4"
local mask="$5"
local gateway="$6"
# dnsmasq configuration: echo "$gateway" > /run/shm/qemu.gw
DNSMASQ_OPTS+=" --dhcp-authoritative"
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
# Set DHCP range and host local log="/var/log/dnsmasq.log"
DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP" rm -f "$log"
DNSMASQ_OPTS+=" --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite"
# Set DNS server and gateway case "${NETWORK,,}" in
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,255.255.255.0" "nat" | "tap" | "tun" | "tuntap" | "y" )
DNSMASQ_OPTS+=" --dhcp-option=option:router,${VM_NET_IP%.*}.1"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1" # Create lease file for faster resolve
echo "0 $mac $ip $host 01:$mac" > /var/lib/misc/dnsmasq.leases
chmod 644 /var/lib/misc/dnsmasq.leases
# dnsmasq configuration:
DNSMASQ_OPTS+=" --dhcp-authoritative"
# Set DHCP range and host
DNSMASQ_OPTS+=" --dhcp-range=$ip,$ip"
DNSMASQ_OPTS+=" --dhcp-host=$mac,,$ip,$host,infinite"
# Set DNS server and gateway
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,$mask"
DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway"
esac
# Set interfaces
DNSMASQ_OPTS+=" --interface=$if"
DNSMASQ_OPTS+=" --bind-interfaces"
# Add DNS entry for container # Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1" DNSMASQ_OPTS+=" --address=/host.lan/$gateway"
# Set local dns resolver to dnsmasq when needed
[ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"
# Enable logging to file
DNSMASQ_OPTS+=" --log-facility=$log"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}"
if [[ "${DEBUG_DNS:-}" == [Yy1]* ]]; then
DNSMASQ_OPTS+=" -d"
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
return 0
fi
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && return 1
local msg="Failed to start Dnsmasq, reason: $?"
[ -f "$log" ] && cat "$log"
error "$msg"
return 1
fi
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
fi fi
return 0 return 0
@ -174,37 +218,139 @@ getUserPorts() {
getHostPorts() { getHostPorts() {
local list="$1" local list="$1"
list=$(echo "${list// /}" | sed 's/,*$//g')
[ -z "$list" ] && echo "" && return 0 [ -z "$list" ] && list="$MON_PORT" || list+=",$MON_PORT"
if [[ "${NETWORK,,}" == "passt" ]]; then
local DNS_PORT="53"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
[ -z "$list" ] && list="$DNS_PORT" || list+=",$DNS_PORT"
fi
[ -z "$list" ] && list="$COM_PORT" || list+=",$COM_PORT"
[ -z "$list" ] && list="$CHR_PORT" || list+=",$CHR_PORT"
[ -z "$list" ] && list="$WSD_PORT" || list+=",$WSD_PORT"
if [[ "$list" != *","* ]]; then
echo " ! --dport $list"
else
echo " -m multiport ! --dports $list"
fi fi
echo "$list"
return 0 return 0
} }
configureUser() { configureSlirp() {
if [ -z "$IP6" ]; then [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."
NET_OPTS="-netdev user,id=hostnet0,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,hostname=$VM_NET_HOST"
else local ip="$IP"
NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,ipv6=on,hostname=$VM_NET_HOST" [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
fi local base="${ip%.*}."
[ "${ip/$base/}" -lt "4" ] && ip="${ip%.*}.4"
local gateway="${ip%.*}.1"
local ipv6=""
[ -n "$IP6" ] && ipv6="ipv6=on,"
NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=$gateway,net=${gateway%.*}.0/24,dhcpstart=$ip,${ipv6}hostname=$VM_NET_HOST"
local forward local forward
forward=$(getUserPorts "$USER_PORTS") forward=$(getUserPorts "${USER_PORTS:-}")
[ -n "$forward" ] && NET_OPTS+=",$forward" [ -n "$forward" ] && NET_OPTS+=",$forward"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
cp /etc/resolv.conf /etc/resolv.dnsmasq
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0
}
configurePasst() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
local log="/var/log/passt.log"
rm -f "$log"
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else
gateway="${ip%.*}.2"
fi
# passt configuration:
[ -z "$IP6" ] && PASST_OPTS+=" -4"
PASST_OPTS+=" -a $ip"
PASST_OPTS+=" -g $gateway"
PASST_OPTS+=" -n $VM_NET_MASK"
[ -n "$PASST_MTU" ] && PASST_OPTS+=" -m $PASST_MTU"
exclude=$(getHostPorts "$HOST_PORTS")
if [ -z "$exclude" ]; then
exclude="%${VM_NET_DEV}/all"
else
exclude="%${VM_NET_DEV}/~${exclude//,/,~}"
fi
PASST_OPTS+=" -t $exclude"
PASST_OPTS+=" -u $exclude"
PASST_OPTS+=" -H $VM_NET_HOST"
PASST_OPTS+=" -M $GATEWAY_MAC"
PASST_OPTS+=" -P /var/run/passt.pid"
PASST_OPTS+=" -l $log"
PASST_OPTS+=" -q"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
cp /etc/resolv.conf /etc/resolv.dnsmasq
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi
PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then
local msg="Failed to start passt, reason: $?"
[ -f "$log" ] && cat "$log"
error "$msg"
return 1
fi
if [[ "$PASST_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
else
if [[ "$DEBUG" == [Yy1]* ]]; then
[ -f "$log" ] && cat "$log" && echo ""
fi
fi
NET_OPTS="-netdev stream,id=hostnet0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket"
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0 return 0
} }
configureNAT() { configureNAT() {
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun" local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat" local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring NAT networking..."
# Create the necessary file structure for /dev/net/tun # Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
@ -216,50 +362,64 @@ configureNAT() {
fi fi
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
error "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
# Check port forwarding flag # Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || : { sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
[[ "$PODMAN" == [Yy1]* ]] && return 1 warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
return 1 return 1
fi fi
fi fi
local ip base
base=$(echo "$IP" | sed -r 's/([^.]*.){2}//')
if [[ "$IP" != "172.30."* ]]; then
ip="172.30.$base"
else
ip="172.31.$base"
fi
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else
gateway="${ip%.*}.2"
fi
# Create a bridge with a static IP for the VM guest # Create a bridge with a static IP for the VM guest
{ ip link add dev dockerbridge type bridge ; rc=$?; } || : { ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1 warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi fi
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then
error "Failed to add IP address pool!" && return 1 warn "failed to add IP address pool!" && return 1
fi fi
while ! ip link set dockerbridge up; do while ! ip link set "$VM_NET_BRIDGE" up; do
info "Waiting for IP address to become available..." info "Waiting for IP address to become available..."
sleep 2 sleep 2
done done
# QEMU Works with taps, set tap to the bridge created # QEMU Works with taps, set tap to the bridge created
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
error "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size.." warn "failed to set MTU size to $MTU."
fi fi
fi fi
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
warn "Failed to set gateway MAC address.." warn "failed to set gateway MAC address.."
fi fi
while ! ip link set "$VM_NET_TAP" up promisc on; do while ! ip link set "$VM_NET_TAP" up promisc on; do
@ -267,27 +427,39 @@ configureNAT() {
sleep 2 sleep 2
done done
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
error "Failed to set IP link!" && return 1 warn "failed to set master bridge!" && return 1
fi fi
# Add internet connection to the VM if grep -wq "nf_tables" /proc/modules; then
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null update-alternatives --set iptables /usr/sbin/iptables-nft > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null update-alternatives --set ip6tables /usr/sbin/ip6tables-nft > /dev/null
else
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
fi
exclude=$(getHostPorts "$HOST_PORTS") exclude=$(getHostPorts "$HOST_PORTS")
if [ -n "$exclude" ]; then
if [[ "$exclude" != *","* ]]; then
exclude=" ! --dport $exclude"
else
exclude=" -m multiport ! --dports $exclude"
fi
fi
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
error "$tables" && return 1 warn "$tables" && return 1
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$ip"; then
error "Failed to configure IP tables!" && return 1 warn "failed to configure IP tables!" && return 1
fi fi
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$ip"; then
error "Failed to configure IP tables!" && return 1 warn "failed to configure IP tables!" && return 1
fi fi
if (( KERNEL > 4 )); then if (( KERNEL > 4 )); then
@ -304,8 +476,9 @@ configureNAT() {
NET_OPTS+=",script=no,downscript=no" NET_OPTS+=",script=no,downscript=no"
configureDNS || return 1 configureDNS "$VM_NET_BRIDGE" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0 return 0
} }
@ -313,14 +486,35 @@ closeBridge() {
local pid="/var/run/dnsmasq.pid" local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")" [ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
[[ "${NETWORK,,}" == "user"* ]] && return 0 pid="/var/run/passt.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
case "${NETWORK,,}" in
"user"* | "passt" | "slirp" ) return 0 ;;
esac
ip link set "$VM_NET_TAP" down promisc off &> null || true ip link set "$VM_NET_TAP" down promisc off &> null || true
ip link delete "$VM_NET_TAP" &> null || true ip link delete "$VM_NET_TAP" &> null || true
ip link set dockerbridge down &> null || true ip link set "$VM_NET_BRIDGE" down &> null || true
ip link delete dockerbridge &> null || true ip link delete "$VM_NET_BRIDGE" &> null || true
return 0
}
closeWeb() {
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
# Shutdown websocket
local pid="/var/run/websocketd.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
return 0 return 0
} }
@ -328,11 +522,7 @@ closeBridge() {
closeNetwork() { closeNetwork() {
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then
closeWeb
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
fi fi
[[ "$NETWORK" == [Nn]* ]] && return 0 [[ "$NETWORK" == [Nn]* ]] && return 0
@ -353,6 +543,21 @@ closeNetwork() {
return 0 return 0
} }
cleanUp() {
# Clean up old files
rm -f /etc/resolv.dnsmasq
rm -f /var/run/passt.pid
rm -f /var/run/dnsmasq.pid
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
info "Lingering interface will be removed..."
ip link delete "$VM_NET_TAP" || true
fi
return 0
}
checkOS() { checkOS() {
local kernel local kernel
@ -360,8 +565,8 @@ checkOS() {
local if="macvlan" local if="macvlan"
kernel=$(uname -a) kernel=$(uname -a)
[[ "${kernel,,}" == *"darwin"* ]] && os="Docker Desktop for macOS" [[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
[[ "${kernel,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows" [[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
if="macvtap" if="macvtap"
@ -393,25 +598,62 @@ getInfo() {
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26 error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
fi fi
NIC=$(ethtool -i "$VM_NET_DEV" | grep -m 1 -i 'driver:' | awk '{print $(2)}') GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
IP6=""
if [[ "${NIC,,}" != "veth" && "${NIC,,}" != "macvlan" ]]; then # shellcheck disable=SC2143
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $NIC" if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
error "This container does not support host mode networking!" && exit 29 IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
fi fi
BASE_IP="${VM_NET_IP%.*}." local result nic bus
result=$(ethtool -i "$VM_NET_DEV")
nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $(2)}')
bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $(2)}')
if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27 [[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus"
error "This container does not support host mode networking!"
exit 29
fi fi
if [ -z "$MTU" ]; then if [[ "$DHCP" == [Yy1]* ]]; then
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
checkOS
if [[ "${nic,,}" == "ipvlan" ]]; then
error "This container does not support IPVLAN networking when DHCP=Y."
exit 29
fi
if [[ "${nic,,}" != "macvlan" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $nic"
error "The container needs to be in a MACVLAN network when DHCP=Y."
exit 29
fi
else
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
checkOS
fi
fi fi
local mtu=""
if [ -f "/sys/class/net/$VM_NET_DEV/mtu" ]; then
mtu=$(< "/sys/class/net/$VM_NET_DEV/mtu")
fi
[ -z "$MTU" ] && MTU="$mtu"
[ -z "$MTU" ] && MTU="0"
if [ "$MTU" -gt "1500" ]; then if [ "$MTU" -gt "1500" ]; then
info "MTU size is too large: $MTU, ignoring..." && MTU="0" [[ "$DEBUG" == [Yy1]* ]] && echo "MTU size is too large: $MTU, ignoring..."
MTU="0"
fi fi
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
@ -443,18 +685,28 @@ getInfo() {
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28 error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi fi
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1) GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
IP6="" if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then
# shellcheck disable=SC2143 if [ -z "$NETWORK" ] || [[ "${NETWORK^^}" == "Y" ]]; then
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then # By default Podman has no permissions for NAT networking
IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up) NETWORK="user"
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1) fi
fi
if [[ "$DEBUG" == [Yy1]* ]]; then
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi fi
[ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
echo "$IP" > /run/shm/qemu.ip echo "$IP" > /run/shm/qemu.ip
echo "$nic" > /run/shm/qemu.nic
return 0 return 0
} }
@ -468,84 +720,75 @@ if [[ "$NETWORK" == [Nn]* ]]; then
return 0 return 0
fi fi
getInfo msg="Initializing network..."
html "Initializing network..." html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
if [[ "$DEBUG" == [Yy1]* ]]; then getInfo
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu") cleanUp
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi
if [[ "$IP" == "172.17."* ]]; then if [[ "$IP" == "172.17."* ]]; then
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!" warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
fi fi
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then MSG="Booting DSM instance..."
info "Lingering interface will be removed..." html "$MSG"
ip link delete "$VM_NET_TAP" || true
fi
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
checkOS
if [[ "$IP" == "172."* ]]; then
warn "container IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
fi
# Configure for macvtap interface # Configure for macvtap interface
configureDHCP || exit 20 configureDHCP || exit 20
MSG="Booting DSM instance..."
html "$MSG"
else else
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
checkOS
fi
if [[ "${WEB:-}" != [Nn]* ]]; then if [[ "${WEB:-}" != [Nn]* ]]; then
sleep 1.2
# Shutdown nginx closeWeb
nginx -s stop 2> /dev/null
fWait "nginx"
fi fi
if [[ "${NETWORK,,}" != "user"* ]]; then case "${NETWORK,,}" in
"user"* | "passt" | "slirp" ) ;;
"nat" | "tap" | "tun" | "tuntap" | "y" )
# Configure for tap interface # Configure tap interface
if ! configureNAT; then if ! configureNAT; then
closeBridge closeBridge
NETWORK="user" NETWORK="user"
msg="falling back to user-mode networking!" msg="falling back to user-mode networking!"
if [[ "$PODMAN" != [Yy1]* ]]; then msg="failed to setup NAT networking, $msg"
msg="an error occured, $msg"
else fi ;;
msg="podman detected, $msg"
esac
[[ "${NETWORK,,}" == "user"* ]] && NETWORK="passt"
case "${NETWORK,,}" in
"nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
"passt" )
# Configure for user-mode networking (passt)
if ! configurePasst; then
error "Failed to configure user-mode networking!"
exit 24
fi ;;
"slirp" )
# Configure for user-mode networking (slirp)
if ! configureSlirp; then
error "Failed to configure user-mode networking!"
exit 24
fi fi
warn "$msg"
[ -z "$USER_PORTS" ] && info "Notice: when you want to expose ports in this mode, map them using this variable: \"USER_PORTS=5000,5001\"."
fi if [ -z "$USER_PORTS" ]; then
info "Notice: slirp networking is active, so when you want to expose ports, you will need to map them using this variable: \"USER_PORTS=5000,5001\"."
fi ;;
fi *)
error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
if [[ "${NETWORK,,}" == "user"* ]]; then esac
# Configure for user-mode networking (slirp)
configureUser || exit 24
fi
fi fi

View File

@ -1,15 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${API_TIMEOUT:="50"}" # API Call timeout
: "${QEMU_TIMEOUT:="50"}" # QEMU Termination timeout
# Configure QEMU for graceful shutdown # Configure QEMU for graceful shutdown
API_CMD=6 API_CMD=6
API_HOST="127.0.0.1:2210" API_HOST="127.0.0.1:$COM_PORT"
: "${API_TIMEOUT:="50"}" # API Call timeout
QEMU_TERM="" QEMU_TERM=""
QEMU_PORT=7100
: "${QEMU_TIMEOUT:="50"}" # QEMU Termination timeout
QEMU_DIR="/run/shm" QEMU_DIR="/run/shm"
QEMU_PID="$QEMU_DIR/qemu.pid" QEMU_PID="$QEMU_DIR/qemu.pid"
QEMU_LOG="$QEMU_DIR/qemu.log" QEMU_LOG="$QEMU_DIR/qemu.log"
@ -83,7 +83,7 @@ terminal() {
fi fi
if [ ! -c "$dev" ]; then if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000') dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$MON_PORT" | tr -d '\000')
dev="${dev#*serial0}" dev="${dev#*serial0}"
dev="${dev#*pty:}" dev="${dev#*pty:}"
dev="${dev%%$'\n'*}" dev="${dev%%$'\n'*}"
@ -127,7 +127,7 @@ _graceful_shutdown() {
fi fi
# Don't send the powerdown signal because vDSM ignores ACPI signals # Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null # echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null
# Send shutdown command to guest agent via serial port # Send shutdown command to guest agent via serial port
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT" url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
@ -172,7 +172,7 @@ _graceful_shutdown() {
MON_OPTS="\ MON_OPTS="\
-pidfile $QEMU_PID \ -pidfile $QEMU_PID \
-name $PROCESS,process=$PROCESS,debug-threads=on \ -name $PROCESS,process=$PROCESS,debug-threads=on \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay" -monitor telnet:localhost:$MON_PORT,server,nowait,nodelay"
if [[ "$CONSOLE" != [Yy]* ]]; then if [[ "$CONSOLE" != [Yy]* ]]; then

View File

@ -11,6 +11,7 @@ error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
file="/run/shm/dsm.url" file="/run/shm/dsm.url"
info="/run/shm/msg.html" info="/run/shm/msg.html"
driver="/run/shm/qemu.nic"
page="/run/shm/index.html" page="/run/shm/index.html"
address="/run/shm/qemu.ip" address="/run/shm/qemu.ip"
shutdown="/run/shm/qemu.end" shutdown="/run/shm/qemu.end"
@ -56,11 +57,7 @@ do
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue [[ "$ip" == "null" ]] && error "$resp_err $json" && continue
if [ -z "$ip" ]; then [ -z "$ip" ] && continue
[[ "$DHCP" == [Yy1]* ]] && continue
ip="20.20.20.21"
fi
echo "$ip:$port" > $file echo "$ip:$port" > $file
done done
@ -69,7 +66,7 @@ done
location=$(<"$file") location=$(<"$file")
if [[ "$location" != "20.20"* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
msg="http://$location" msg="http://$location"
title="<title>Virtual DSM</title>" title="<title>Virtual DSM</title>"
@ -88,10 +85,11 @@ if [[ "$location" != "20.20"* ]]; then
else else
nic=$(<"$driver")
ip=$(<"$address") ip=$(<"$address")
port="${location##*:}" port="${location##*:}"
if [[ "$ip" == "172."* ]]; then if [[ "${nic,,}" != "macvlan" ]]; then
msg="port $port" msg="port $port"
else else
msg="http://$ip:$port" msg="http://$ip:$port"

View File

@ -3,7 +3,6 @@ set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: "${KVM:="Y"}"
: "${HOST_CPU:=""}" : "${HOST_CPU:=""}"
: "${CPU_FLAGS:=""}" : "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}" : "${CPU_MODEL:=""}"
@ -26,52 +25,7 @@ else
esac esac
fi fi
if [[ "$KVM" == [Nn]* ]]; then flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
else
if [[ "${ARCH,,}" != "amd64" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, so the machine will run about 10 times slower."
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(/dev/kvm is unwriteable)"
else
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
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)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
if [[ "$KVM" != [Nn]* ]]; then if [[ "$KVM" != [Nn]* ]]; then

View File

@ -25,7 +25,7 @@ do
if [ -s "$file" ]; then if [ -s "$file" ]; then
bytes=$(du -sb "$file" | cut -f1) bytes=$(du -sb "$file" | cut -f1)
if (( bytes > 1000 )); then if (( bytes > 1000 )); then
if [ -z "$total" ] || [[ "$total" == "0" ]]; then if [ -z "$total" ] || [[ "$total" == "0" ]] || [ "$bytes" -gt "$total" ]; then
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/') size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
else else
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')" size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"

View File

@ -2,16 +2,15 @@
set -Eeuo pipefail set -Eeuo pipefail
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11 [ ! -f "/run/entry.sh" ] && error "Script must be run inside the container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12 [ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
echo " Starting $APP for Docker v$(</run/version)..."
echo " For support visit $SUPPORT"
# Docker environment variables # Docker environment variables
: "${TZ:=""}" # System local timezone : "${TZ:=""}" # System timezone
: "${KVM:="Y"}" # KVM acceleration
: "${DEBUG:="N"}" # Disable debugging mode : "${DEBUG:="N"}" # Disable debugging mode
: "${COUNTRY:=""}" # Country code for mirror : "${COUNTRY:=""}" # Country code for mirror
: "${CONSOLE:="N"}" # Disable console mode : "${CONSOLE:="N"}" # Disable console mode
@ -25,13 +24,23 @@ echo " For support visit $SUPPORT"
# Helper variables # Helper variables
PODMAN="N"
ENGINE="Docker"
PROCESS="${APP,,}" PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}" PROCESS="${PROCESS// /-}"
if [ -f "/run/.containerenv" ]; then
PODMAN="Y"
ENGINE="Podman"
fi
echo " Starting $APP for $ENGINE v$(</run/version)..."
echo " For support visit $SUPPORT"
INFO="/run/shm/msg.html" INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html" PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html" TEMPLATE="/var/www/index.html"
FOOTER1="$APP for Docker v$(</run/version)" FOOTER1="$APP for $ENGINE v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>" FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
CPU=$(cpu) CPU=$(cpu)
@ -133,6 +142,63 @@ if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); th
info "$msg" info "$msg"
fi fi
# Check KVM support
if [[ "${PLATFORM,,}" == "x64" ]]; then
TARGET="amd64"
else
TARGET="arm64"
fi
if [[ "$KVM" == [Nn]* ]]; then
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
else
if [[ "${ARCH,,}" != "$TARGET" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ${PLATFORM^^} instructions, so the machine will run about 10 times slower."
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(/dev/kvm is unwriteable)"
else
if [[ "${PLATFORM,,}" == "x64" ]]; then
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
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)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
# Cleanup files # Cleanup files
rm -f /run/shm/qemu.* rm -f /run/shm/qemu.*
rm -f /run/shm/dsm.url rm -f /run/shm/dsm.url
@ -141,89 +207,4 @@ rm -f /run/shm/dsm.url
rm -rf /tmp/dsm rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp" rm -rf "$STORAGE/tmp"
getCountry() {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.ipquery.io/?format=json" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0
}
addPackage() {
local pkg=$1
local desc=$2
if apt-mark showinstall | grep -qx "$pkg"; then
return 0
fi
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
[ -z "$COUNTRY" ] && setCountry
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
: "${WEB_PORT:="5000"}" # Webserver port
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
if [[ "${WEB:-}" != [Nn]* ]]; then
mkdir -p /etc/nginx/sites-enabled
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
nginx -e stderr
fi
return 0 return 0

View File

@ -45,7 +45,7 @@ fi
cnt=0 cnt=0
sleep 0.2 sleep 0.2
while ! nc -z -w2 127.0.0.1 2210 > /dev/null 2>&1; do while ! nc -z -w2 127.0.0.1 "$COM_PORT" > /dev/null 2>&1; do
sleep 0.1 sleep 0.1
cnt=$((cnt + 1)) cnt=$((cnt + 1))
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58 (( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
@ -53,7 +53,7 @@ done
cnt=0 cnt=0
while ! nc -z -w2 127.0.0.1 12345 > /dev/null 2>&1; do while ! nc -z -w2 127.0.0.1 "$CHR_PORT" > /dev/null 2>&1; do
sleep 0.1 sleep 0.1
cnt=$((cnt + 1)) cnt=$((cnt + 1))
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59 (( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
@ -69,7 +69,7 @@ fi
SERIAL_OPTS+=" \ SERIAL_OPTS+=" \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \ -device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \ -chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel" -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
return 0 return 0

39
src/server.sh Normal file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${COM_PORT:="2210"}" # Comm port
: "${MON_PORT:="7100"}" # Monitor port
: "${WEB_PORT:="5000"}" # Webserver port
: "${CHR_PORT:="12345"}" # Character port
: "${WSD_PORT:="8004"}" # Websockets port
cp -r /var/www/* /run/shm
rm -f /var/run/websocketd.pid
html "Starting $APP for $ENGINE..."
if [[ "${WEB:-}" != [Nn]* ]]; then
mkdir -p /etc/nginx/sites-enabled
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
nginx -e stderr
# Start websocket server
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
echo "$!" > /var/run/websocketd.pid
fi
return 0

16
src/socket.sh Normal file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -Eeuo pipefail
path="/run/shm/msg.html"
if [ -f "$path" ] && [ -s "$path" ]; then
echo -n "s: " && cat "$path"
fi
inotifywait -m "$path" |
while read -r fp event fn; do
case "${event,,}" in
"modify"* ) echo -n "s: " && cat "$path" ;;
"delete_self" ) echo "c: vnc" ;;
esac
done

6
src/start.sh Normal file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# You can override this hook to execute a script before startup!
return 0

View File

@ -126,8 +126,8 @@ cpu() {
cpu="${cpu// [0-9] Core}" cpu="${cpu// [0-9] Core}"
cpu="${cpu// [0-9][0-9] Core}" cpu="${cpu// [0-9][0-9] Core}"
cpu="${cpu// [0-9][0-9][0-9] Core}" cpu="${cpu// [0-9][0-9][0-9] Core}"
cpu="${cpu// [0-9]th Gen}" cpu="${cpu//[0-9]th Gen }"
cpu="${cpu// [0-9][0-9]th Gen}" cpu="${cpu//[0-9][0-9]th Gen }"
cpu="${cpu// Processor/}" cpu="${cpu// Processor/}"
cpu="${cpu// Quad core/}" cpu="${cpu// Quad core/}"
cpu="${cpu// Dual core/}" cpu="${cpu// Dual core/}"
@ -159,4 +159,65 @@ hasDisk() {
return 1 return 1
} }
getCountry() {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.ipquery.io/?format=json" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0
}
addPackage() {
local pkg=$1
local desc=$2
if apt-mark showinstall | grep -qx "$pkg"; then
return 0
fi
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
[ -z "$COUNTRY" ] && setCountry
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
return 0 return 0

View File

@ -27,4 +27,18 @@ server {
index index.html; index index.html;
} }
location /status {
proxy_http_version 1.1;
proxy_set_header Connection 'upgrade';
proxy_set_header Upgrade $http_upgrade;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_pass http://127.0.0.1:8004/;
}
} }

View File

@ -1,4 +1,5 @@
var request; var request;
var booting = false;
var interval = 1000; var interval = 1000;
function getInfo() { function getInfo() {
@ -6,7 +7,6 @@ function getInfo() {
var url = "msg.html"; var url = "msg.html";
try { try {
if (window.XMLHttpRequest) { if (window.XMLHttpRequest) {
request = new XMLHttpRequest(); request = new XMLHttpRequest();
} else { } else {
@ -18,22 +18,49 @@ function getInfo() {
request.send(); request.send();
} catch (e) { } catch (e) {
var err = "Error: " + e.message; setError("Error: " + e.message);
console.log(err);
setError(err);
} }
} }
function getURL() {
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, '');
return protocol + "//" + window.location.host + path;
}
function processMsg(msg) {
if (msg.toLowerCase().indexOf("href=") !== -1) {
var div = document.createElement("div");
div.innerHTML = msg;
var url = div.querySelector("a").href;
setTimeout(() => {
window.location.assign(url);
}, 3000);
}
setInfo(msg);
return true;
}
function processInfo() { function processInfo() {
try { try {
if (request.readyState != 4) { if (request.readyState != 4) {
return true; return true;
} }
var msg = request.responseText; var msg = request.responseText;
if (msg == null || msg.length == 0) { if (msg == null || msg.length == 0) {
setInfo("Booting DSM instance", true);
schedule(); if (booting) {
schedule();
return true;
}
window.location.reload();
return false; return false;
} }
@ -43,20 +70,11 @@ function processInfo() {
if (msg.toLowerCase().indexOf("<html>") !== -1) { if (msg.toLowerCase().indexOf("<html>") !== -1) {
notFound = true; notFound = true;
} else { } else {
if (msg.toLowerCase().indexOf("href=") !== -1) { processMsg(msg);
var div = document.createElement("div"); if (msg.toLowerCase().indexOf("href=") == -1) {
div.innerHTML = msg;
var url = div.querySelector("a").href;
setTimeout(() => {
window.location.assign(url);
}, 3000);
setInfo(msg);
return true;
} else {
setInfo(msg);
schedule(); schedule();
return true;
} }
return true;
} }
} }
@ -67,31 +85,38 @@ function processInfo() {
} }
setError("Error: Received statuscode " + request.status); setError("Error: Received statuscode " + request.status);
schedule();
return false; return false;
} catch (e) { } catch (e) {
var err = "Error: " + e.message; setError("Error: " + e.message);
console.log(err);
setError(err);
return false; return false;
} }
} }
function setInfo(msg, loading, error) { function setInfo(msg, loading, error) {
try { try {
if (msg == null || msg.length == 0) { if (msg == null || msg.length == 0) {
return false; return false;
} }
var el = document.getElementById("spinner"); if (msg.includes("Booting ")) {
booting = true;
}
var el = document.getElementById("info");
if (el.innerText == msg || el.innerHTML == msg) {
return true;
}
var spin = document.getElementById("spinner");
error = !!error; error = !!error;
if (!error) { if (!error) {
el.style.visibility = 'visible'; spin.style.visibility = 'visible';
} else { } else {
el.style.visibility = 'hidden'; spin.style.visibility = 'hidden';
} }
loading = !!loading; loading = !!loading;
@ -99,12 +124,7 @@ function setInfo(msg, loading, error) {
msg = "<p class=\"loading\">" + msg + "</p>"; msg = "<p class=\"loading\">" + msg + "</p>";
} }
el = document.getElementById("info"); el.innerHTML = msg;
if (el.innerHTML != msg) {
el.innerHTML = msg;
}
return true; return true;
} catch (e) { } catch (e) {
@ -114,6 +134,7 @@ function setInfo(msg, loading, error) {
} }
function setError(text) { function setError(text) {
console.warn(text);
return setInfo(text, false, true); return setInfo(text, false, true);
} }
@ -123,8 +144,47 @@ function schedule() {
function reload() { function reload() {
setTimeout(() => { setTimeout(() => {
document.location.reload(); window.location.reload();
}, 3000); }, 3000);
} }
function connect() {
var wsUrl = getURL() + "/status";
var ws = new WebSocket(wsUrl);
ws.onmessage = function(e) {
var pos = e.data.indexOf(":");
var cmd = e.data.substring(0, pos);
var msg = e.data.substring(pos + 2);
switch (cmd) {
case "s":
processMsg(msg);
break;
case "e":
setError(msg);
break;
default:
console.warn("Unknown event: " + cmd);
break;
}
};
ws.onclose = function(e) {
setTimeout(function() {
connect();
}, interval);
};
ws.onerror = function(e) {
ws.close();
if (!booting) {
window.location.reload();
}
};
}
schedule(); schedule();
connect();