Compare commits

..

29 Commits

Author SHA1 Message Date
Kroese
25227944b5
fix: Do not reset loading animation (#1056) 2025-10-12 01:51:17 +02:00
Kroese
06650e916a
build: Run check for all files (#1057)
Removed specific paths from pull request triggers.
2025-10-12 01:48:25 +02:00
Kroese
2e34dffed5
feat: Expose only selected ports with Passt (#1055) 2025-10-12 01:44:24 +02:00
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
Kroese
87a8cf7513
feat: Support 32k sector sizes (#1034) 2025-09-30 10:49:07 +02:00
Kroese
7f31cb6023
fix: Detect host mode networking (#1033) 2025-09-30 10:32:00 +02:00
Kroese
138742c953
feat: Increase default disksize (#1030)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-09-28 18:22:44 +02:00
Kroese
2c6efc45f2
feat: Make webserver port configurable (#1028) 2025-09-27 11:44:13 +02:00
Kroese
24d795fbe3
buid: Add ethtool package (#1027) 2025-09-27 10:45:09 +02:00
Kroese
7fae62d286
feat: Detect host mode networking (#1026) 2025-09-27 10:44:02 +02:00
Kroese
2135df07ea
docs: Additional info for user-mode networking (#1025) 2025-09-27 10:08:11 +02:00
Kroese
521beedf1c
feat: Add note for MAC address availability (#1024) 2025-09-27 09:49:11 +02:00
25 changed files with 787 additions and 400 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

@ -1,11 +1,6 @@
on: on:
workflow_dispatch: workflow_dispatch:
pull_request: pull_request:
paths:
- '**/*.sh'
- 'Dockerfile'
- '.github/workflows/test.yml'
- '.github/workflows/check.yml'
name: "Test" name: "Test"
permissions: {} permissions: {}

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"
@ -28,6 +29,7 @@ RUN set -eu && \
unzip \ unzip \
nginx \ nginx \
procps \ procps \
ethtool \
python3 \ python3 \
python3-pip \ python3-pip \
python3-msgpack \ python3-msgpack \
@ -35,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 && \
@ -57,7 +63,7 @@ RUN set -eu && \
COPY --chmod=755 ./src /run/ COPY --chmod=755 ./src /run/
COPY --chmod=755 ./web /var/www/ COPY --chmod=755 ./web /var/www/
COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/sites-enabled/web.conf COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/default.conf
ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py
VOLUME /storage VOLUME /storage
@ -65,7 +71,7 @@ EXPOSE 22 139 445 5000
ENV RAM_SIZE="2G" ENV RAM_SIZE="2G"
ENV CPU_CORES="2" ENV CPU_CORES="2"
ENV DISK_SIZE="16G" ENV DISK_SIZE="256G"
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh

View File

@ -3,7 +3,7 @@ services:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
environment: environment:
DISK_SIZE: "16G" DISK_SIZE: "256G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun

View File

@ -8,7 +8,7 @@ spec:
- ReadWriteOnce - ReadWriteOnce
resources: resources:
requests: requests:
storage: 16Gi storage: 256Gi
--- ---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
@ -31,7 +31,7 @@ spec:
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
env: env:
- name: DISK_SIZE - name: DISK_SIZE
value: "16G" value: "256G"
ports: ports:
- containerPort: 5000 - containerPort: 5000
name: http name: http

View File

@ -30,7 +30,7 @@ services:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
environment: environment:
DISK_SIZE: "16G" DISK_SIZE: "256G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun
@ -47,7 +47,7 @@ services:
##### Via Docker CLI: ##### Via Docker CLI:
```bash ```bash
docker run -it --rm --name dsm -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm
``` ```
##### Via Kubernetes: ##### Via Kubernetes:
@ -87,35 +87,24 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I change the size of the disk? ### How do I change the size of the disk?
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity: To expand the default size of 256 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
```yaml ```yaml
environment: environment:
DISK_SIZE: "128G" DISK_SIZE: "512G"
``` ```
> [!TIP] > [!TIP]
> This can also be used to resize the existing disk to a larger capacity without any data loss. > This can also be used to resize the existing disk to a larger capacity without any data loss.
### How do I create a growable disk?
By default, the entire capacity of the disk will be reserved in advance.
To create a growable disk that only allocates space that is actually used, add the following environment variable:
```yaml
environment:
DISK_FMT: "qcow2"
```
### How do I add multiple disks? ### How do I add multiple disks?
To create additional disks, modify your compose file like this: To create additional disks, modify your compose file like this:
```yaml ```yaml
environment: environment:
DISK2_SIZE: "32G" DISK2_SIZE: "500G"
DISK3_SIZE: "64G" DISK3_SIZE: "750G"
volumes: volumes:
- ./example2:/storage2 - ./example2:/storage2
- ./example3:/storage3 - ./example3:/storage3
@ -263,14 +252,6 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses. There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
### How do I run Windows in a container?
You can use [dockur/windows](https://github.com/dockur/windows) for that. It shares many of the same features, and even has completely automatic installation.
### How do I run a Linux desktop in a container?
You can use [qemus/qemu](https://github.com/qemus/qemu) in that case.
### Is this project legal? ### Is this project legal?
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project will be considered legal. Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project will be considered legal.

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

@ -507,12 +507,8 @@ addDevice () {
physical="${physical%% *}" physical="${physical%% *}"
if [ -n "$physical" ]; then if [ -n "$physical" ]; then
if [[ "$physical" == "512" || "$physical" == "4096" ]]; then if [[ "$physical" != "512" ]]; then
if [[ "$physical" == "4096" ]]; then sectors=",logical_block_size=$logical,physical_block_size=$physical"
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
else
warn "Unknown physical sector size: $physical for $DISK_DEV"
fi fi
else else
warn "Failed to determine the sector size for $DISK_DEV" warn "Failed to determine the sector size for $DISK_DEV"
@ -523,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"
@ -535,11 +533,7 @@ case "${DISK_TYPE,,}" in
esac esac
if [ -z "$ALLOCATE" ]; then if [ -z "$ALLOCATE" ]; then
if [[ "${DISK_FMT,,}" == "raw" ]]; then ALLOCATE="N"
ALLOCATE="Y"
else
ALLOCATE="N"
fi
fi fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
@ -554,7 +548,7 @@ DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "") DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
DISK1_FILE="$STORAGE/${DISK_NAME}" DISK1_FILE="$STORAGE/${DISK_NAME}"
if [[ ! -f "$DISK1_FILE.img" && -f "$STORAGE/data${DISK_SIZE}.img" ]]; then if [ ! -f "$DISK1_FILE.img" ] && [ -f "$STORAGE/data${DISK_SIZE}.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img" mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi fi

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,12 +71,13 @@ 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
while ! ip link set "$VM_NET_TAP" up; do while ! ip link set "$VM_NET_TAP" up; do
info "Waiting for MAC address $VM_NET_MAC to become available..." info "Waiting for MAC address $VM_NET_MAC to become available..."
info "If you cloned this machine, please delete the 'dsm.mac' file to generate a different MAC address."
sleep 2 sleep 2
done done
@ -104,37 +115,82 @@ 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 fi
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
fi
return 0
}
getHostPorts() {
local list="$1"
list=$(echo "${list// /}" | sed 's/,*$//g')
[ -z "$list" ] && list="$MON_PORT" || list+=",$MON_PORT"
echo "$list"
return 0 return 0
} }
@ -142,25 +198,38 @@ getUserPorts() {
local args="" local args=""
local list=$1 local list=$1
list=$(echo "${list// /}" | sed 's/,*$//g')
local ssh="22" local ssh="22"
local dsm="5000" local dsm="5000"
[ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm" [ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm"
echo "$list"
return 0
}
getSlirp() {
local args=""
local list=""
list=$(getUserPorts)
list="${list//,/ }" list="${list//,/ }"
list="${list## }" list="${list## }"
list="${list%% }" list="${list%% }"
for port in $list; do for port in $list; do
proto="tcp"
num="$port"
if [[ "$port" == */udp ]]; then proto="tcp"
num="${port%/tcp}"
if [[ "$port" == *"/udp" ]]; then
proto="udp"
num="${port%/udp}"
elif [[ "$port" != *"/tcp" ]]; then
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
proto="udp" proto="udp"
num="${port%/udp}" num="${port%/udp}"
elif [[ "$port" == */tcp ]]; then
proto="tcp"
num="${port%/tcp}"
fi fi
args+="hostfwd=$proto::$num-$VM_NET_IP:$num," args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
@ -170,40 +239,119 @@ getUserPorts() {
return 0 return 0
} }
getHostPorts() { configureSlirp() {
local list=$1 [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."
[ -z "$list" ] && echo "" && return 0 local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local base="${ip%.*}."
[ "${ip/$base/}" -lt "4" ] && ip="${ip%.*}.4"
local gateway="${ip%.*}.1"
if [[ "$list" != *","* ]]; then local ipv6=""
echo " ! --dport $list" [ -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=""
forward=$(getUserPorts "${USER_PORTS:-}")
[ -n "$forward" ] && NET_OPTS+=",$forward"
if [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]]; then
echo "$gateway" > /run/shm/qemu.gw
else else
echo " -m multiport ! --dports $list" cp /etc/resolv.conf /etc/resolv.dnsmasq
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi fi
VM_NET_IP="$ip"
return 0 return 0
} }
configureUser() { configurePasst() {
if [ -z "$IP6" ]; then [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode 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"
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 else
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" gateway="${ip%.*}.2"
fi fi
local forward # passt configuration:
forward=$(getUserPorts "$USER_PORTS") [ -z "$IP6" ] && PASST_OPTS+=" -4"
[ -n "$forward" ] && NET_OPTS+=",$forward"
PASST_OPTS+=" -a $ip"
PASST_OPTS+=" -g $gateway"
PASST_OPTS+=" -n $VM_NET_MASK"
[ -n "$PASST_MTU" ] && PASST_OPTS+=" -m $PASST_MTU"
local forward=""
forward=$(getUserPorts "${USER_PORTS:-}")
forward="${forward///tcp}"
forward="${forward///udp}"
if [ -n "$forward" ]; then
forward="%${VM_NET_DEV}/$forward"
PASST_OPTS+=" -t $forward"
PASST_OPTS+=" -u $forward"
fi
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
@ -215,50 +363,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
@ -266,27 +428,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
@ -303,8 +477,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
} }
@ -312,14 +487,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
} }
@ -327,11 +523,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
@ -352,6 +544,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
@ -359,8 +566,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"
@ -392,20 +599,64 @@ 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
BASE_IP="${VM_NET_IP%.*}." 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); rc=$?; } 2>/dev/null || :
if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then if (( rc != 0 )); then
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27 error "Could not determine container IP address!" && exit 26
fi
if [ -z "$MTU" ]; then
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
fi fi
if [ "$MTU" -gt "1500" ]; then IP6=""
info "MTU size is too large: $MTU, ignoring..." && MTU="0" # shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
{ IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up); rc=$?; } 2>/dev/null || :
(( rc != 0 )) && IP6=""
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
fi fi
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 [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus"
error "This container does not support host mode networking!"
exit 29
fi
if [[ "$DHCP" == [Yy1]* ]]; then
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
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 [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0" warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
@ -435,18 +686,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
} }
@ -460,84 +721,80 @@ 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
msg="podman detected, $msg"
fi
warn "$msg"
[ -z "$USER_PORTS" ] && info "Notice: port mapping will not work without \"USER_PORTS\" now."
fi fi ;;
fi esac
if [[ "${NETWORK,,}" == "user"* ]]; then [[ "${NETWORK,,}" == "user"* ]] && NETWORK="passt"
# Configure for user-mode networking (slirp) case "${NETWORK,,}" in
configureUser || exit 24 "nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
"passt" )
fi # 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 ;;
*)
error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
esac
case "${NETWORK,,}" in
"passt" | "slirp" )
if [ -z "$USER_PORTS" ]; then
info "Notice: because user-mode networking is active, if you need to expose ports, add them to the \"USER_PORTS\" variable."
fi ;;
esac
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,86 +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
}
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
if [[ "${WEB:-}" != [Nn]* ]]; then
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen 5000 default_server;/listen [::]:5000 default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
else
sed -i "s/listen [::]:5000 default_server ipv6only=off;/listen 5000 default_server;/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,44 +85,60 @@ 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 extractContent(s) {
var span = document.createElement('span');
span.innerHTML = s;
return span.textContent || span.innerText;
};
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';
} }
var p = "<p class=\"loading\">";
loading = !!loading; loading = !!loading;
if (loading) { if (loading) {
msg = "<p class=\"loading\">" + msg + "</p>"; msg = p + msg + "</p>";
} }
el = document.getElementById("info"); if (msg.includes(p)) {
if (el.innerHTML.includes(p)) {
if (el.innerHTML != msg) { el.getElementsByClassName('loading')[0].textContent = extractContent(msg);
el.innerHTML = msg; return true;
}
} }
el.innerHTML = msg;
return true; return true;
} catch (e) { } catch (e) {
@ -114,6 +148,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 +158,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();