mirror of
https://github.com/vdsm/virtual-dsm.git
synced 2026-08-29 12:17:07 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c9d2b1fd5 | ||
|
|
b68d243b6a | ||
|
|
79e85f9fd1 | ||
|
|
476048a4ee | ||
|
|
f983dc3e46 | ||
|
|
38f9c49200 | ||
|
|
7157717a85 | ||
|
|
031674424d | ||
|
|
28e6f8da78 | ||
|
|
d5802607b5 | ||
|
|
7168f2f843 | ||
|
|
819dd29025 | ||
|
|
48ef47b85b | ||
|
|
10c902bc9b | ||
|
|
d6de10efe1 | ||
|
|
6d1d9d92b4 | ||
|
|
799649e78c | ||
|
|
12060c72b2 | ||
|
|
6eadd1b953 | ||
|
|
342ca4da03 | ||
|
|
b0b6190560 | ||
|
|
075410e4cf | ||
|
|
2a388b434b | ||
|
|
280c4037f3 | ||
|
|
007bdc735d | ||
|
|
41fc0a6a10 | ||
|
|
7753e4ac74 | ||
|
|
f74bf35202 | ||
|
|
aa472c96c6 | ||
|
|
0dcb880800 | ||
|
|
8daa6973b7 | ||
|
|
3884befa56 | ||
|
|
85b83f8eb1 | ||
|
|
f395dfaa66 | ||
|
|
b3124075ea | ||
|
|
aed909bdee | ||
|
|
d4cf5778e2 | ||
|
|
48de7dd00b | ||
|
|
686e64da3f | ||
|
|
7517dc8b3e | ||
|
|
660fe715e4 | ||
|
|
04ea29ec77 | ||
|
|
02c891cb16 | ||
|
|
0b5d21357e | ||
|
|
e0545b37d7 | ||
|
|
4161c21082 | ||
|
|
48d9a1771d | ||
|
|
471cdbb338 | ||
|
|
e77bca202b | ||
|
|
2e6c01e934 | ||
|
|
302c991c0c | ||
|
|
a89007ee03 | ||
|
|
8a89149d58 | ||
|
|
5e8bbc2868 | ||
|
|
4e48920309 | ||
|
|
8b145924b9 | ||
|
|
a0328e1e9c | ||
|
|
b7f5214a7b | ||
|
|
b0e4c4ac5f | ||
|
|
bbb67aac93 | ||
|
|
433c83b393 | ||
|
|
5577178eeb | ||
|
|
221b0242fa | ||
|
|
c05623f8af | ||
|
|
eb9884cc96 | ||
|
|
8e2490e6bc | ||
|
|
399243886e | ||
|
|
b0dbfcb805 | ||
|
|
dec2dc9230 | ||
|
|
2c44669d91 | ||
|
|
175d90aad7 | ||
|
|
e1e5f8f91d | ||
|
|
dff8abbe1e | ||
|
|
0f97091e61 | ||
|
|
ea0d7ee6cd | ||
|
|
75daa31d36 | ||
|
|
8c44319c45 | ||
|
|
dac574d933 | ||
|
|
fd4c5001e8 | ||
|
|
dfe0b23995 | ||
|
|
6158372eaa | ||
|
|
b1a778d7b2 | ||
|
|
bc0defd813 | ||
|
|
48e7a9fff0 | ||
|
|
c2fa58ef27 | ||
|
|
ea49cb144b | ||
|
|
b8e778a79d | ||
|
|
c70e12f0a2 | ||
|
|
6281205912 | ||
|
|
1ffc5c55b2 | ||
|
|
c3302e1720 | ||
|
|
25227944b5 | ||
|
|
06650e916a | ||
|
|
2e34dffed5 | ||
|
|
3db91f077f | ||
|
|
fae14f4dd9 | ||
|
|
d190b3ba87 | ||
|
|
554f3295cb | ||
|
|
cdc4689d6a | ||
|
|
21d18fd439 | ||
|
|
fd5ec52226 | ||
|
|
fc7ab22741 | ||
|
|
59cf59faa4 | ||
|
|
705c6ce7f1 | ||
|
|
f2937ab507 | ||
|
|
399829cf3c | ||
|
|
b694d6faf8 | ||
|
|
7acd1f6cdb | ||
|
|
09ca3bf118 | ||
|
|
6cac45c397 | ||
|
|
f18663d840 | ||
|
|
fefe1af9e6 | ||
|
|
87a8cf7513 | ||
|
|
7f31cb6023 |
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "dsm",
|
||||
"service": "dsm",
|
||||
"forwardPorts": [5000],
|
||||
"dockerComposeFile": "compose.yml"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
dsm:
|
||||
container_name: dsm
|
||||
image: ghcr.io/vdsm/virtual-dsm
|
||||
environment:
|
||||
RAM_SIZE: "half"
|
||||
DISK_SIZE: "max"
|
||||
CPU_CORES: "max"
|
||||
devices:
|
||||
- /dev/kvm
|
||||
- /dev/net/tun
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
ports:
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- ./dsm:/storage
|
||||
restart: on-failure
|
||||
stop_grace_period: 2m
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "Virtual DSM",
|
||||
"service": "dsm",
|
||||
"forwardPorts": [5000],
|
||||
"portsAttributes": {
|
||||
"5000": {
|
||||
"label": "Web",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
},
|
||||
"otherPortsAttributes": {
|
||||
"onAutoForward": "ignore"
|
||||
},
|
||||
"dockerComposeFile": "codespaces.yml"
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
.dockerignore
|
||||
.devcontainer
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
|
||||
@@ -22,13 +22,13 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
context: git
|
||||
images: |
|
||||
@@ -43,23 +43,23 @@ jobs:
|
||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
-
|
||||
name: Login into Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Build Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
@@ -22,3 +22,8 @@ jobs:
|
||||
dockerfile: Dockerfile
|
||||
ignore: DL3008,DL3003,DL3006,DL3013
|
||||
failure-threshold: warning
|
||||
-
|
||||
name: Validate JSON and YML files
|
||||
uses: GrantBirki/json-yaml-validate@v5.0.0
|
||||
with:
|
||||
yaml_exclude_regex: ".*\\kubernetes\\.yml$"
|
||||
|
||||
+12
-10
@@ -12,13 +12,15 @@ jobs:
|
||||
dockerHubDescription:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
-
|
||||
name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
repository: ${{ secrets.DOCKERHUB_REPO }}
|
||||
short-description: ${{ github.event.repository.description }}
|
||||
readme-filepath: ./readme.md
|
||||
-
|
||||
name: Checkout repo
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v5
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
repository: ${{ secrets.DOCKERHUB_REPO }}
|
||||
short-description: ${{ github.event.repository.description }}
|
||||
readme-filepath: ./readme.md
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
name: "Review"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
checks: write
|
||||
|
||||
jobs:
|
||||
review:
|
||||
name: review
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
-
|
||||
name: Spelling
|
||||
uses: reviewdog/action-misspell@v1
|
||||
with:
|
||||
locale: "US"
|
||||
level: warning
|
||||
pattern: |
|
||||
*.md
|
||||
*.sh
|
||||
reporter: github-pr-review
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Hadolint
|
||||
uses: reviewdog/action-hadolint@v1
|
||||
with:
|
||||
level: warning
|
||||
fail_level: error
|
||||
reporter: github-pr-review
|
||||
hadolint_ignore: DL3008 DL3003 DL3006 DL3013
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: YamlLint
|
||||
uses: reviewdog/action-yamllint@v1
|
||||
with:
|
||||
level: warning
|
||||
reporter: github-pr-review
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: ActionLint
|
||||
uses: reviewdog/action-actionlint@v1
|
||||
with:
|
||||
level: warning
|
||||
reporter: github-pr-review
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Shellformat
|
||||
uses: reviewdog/action-shfmt@v1
|
||||
if: false
|
||||
with:
|
||||
level: warning
|
||||
fail_on_error: "true"
|
||||
shfmt_flags: "-i 2 -ci -bn"
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
-
|
||||
name: Shellcheck
|
||||
uses: reviewdog/action-shellcheck@v1
|
||||
with:
|
||||
level: warning
|
||||
fail_level: error
|
||||
reporter: github-pr-review
|
||||
shellcheck_flags: -x -e SC1091 -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,11 +1,6 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**/*.sh'
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/test.yml'
|
||||
- '.github/workflows/check.yml'
|
||||
|
||||
name: "Test"
|
||||
permissions: {}
|
||||
|
||||
+9
-2
@@ -11,8 +11,11 @@ FROM qemux/qemu-host:2.05 AS builder
|
||||
|
||||
FROM debian:trixie-slim
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG TARGETPLATFORM
|
||||
ARG VERSION_ARG="0.0"
|
||||
ARG VERSION_PASST="2026_05_26"
|
||||
|
||||
ARG DEBCONF_NOWARNINGS="yes"
|
||||
ARG DEBIAN_FRONTEND="noninteractive"
|
||||
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
||||
@@ -36,23 +39,27 @@ RUN set -eu && \
|
||||
xz-utils \
|
||||
iptables \
|
||||
iproute2 \
|
||||
apt-utils \
|
||||
dnsmasq \
|
||||
fakeroot \
|
||||
apt-utils \
|
||||
net-tools \
|
||||
e2fsprogs \
|
||||
qemu-utils \
|
||||
websocketd \
|
||||
iputils-ping \
|
||||
inotify-tools \
|
||||
ca-certificates \
|
||||
netcat-openbsd \
|
||||
qemu-system-x86 && \
|
||||
wget "https://github.com/qemus/passt/releases/download/v${VERSION_PASST}/passt_${VERSION_PASST}_${TARGETARCH}.deb" -O /tmp/passt.deb -q --timeout=10 && \
|
||||
dpkg -i /tmp/passt.deb && \
|
||||
apt-get clean && \
|
||||
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
|
||||
mkdir -p /etc/qemu && \
|
||||
echo "allow br0" > /etc/qemu/bridge.conf && \
|
||||
unlink /etc/nginx/sites-enabled/default && \
|
||||
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \
|
||||
echo "$VERSION_ARG" > /run/version && \
|
||||
echo "$VERSION_ARG" > /etc/version && \
|
||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY --chmod=755 ./src /run/
|
||||
|
||||
@@ -47,7 +47,7 @@ services:
|
||||
##### Via Docker CLI:
|
||||
|
||||
```bash
|
||||
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
|
||||
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 docker.io/vdsm/virtual-dsm
|
||||
```
|
||||
|
||||
##### Via Kubernetes:
|
||||
@@ -124,7 +124,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
|
||||
|
||||
### How do I change the amount of CPU or RAM?
|
||||
|
||||
By default, the container will be allowed to use a maximum of 2 CPU cores and 2 GB of RAM.
|
||||
By default, Virtual DSM will be allowed to use 2 CPU cores and 2 GB of RAM.
|
||||
|
||||
If you want to adjust this, you can specify the desired amount using the following environment variables:
|
||||
|
||||
|
||||
+11
-9
@@ -1,28 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: "${DHCP:="N"}"
|
||||
: "${NETWORK:="Y"}"
|
||||
|
||||
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
|
||||
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
|
||||
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled.." && exit 0
|
||||
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down..." && exit 1
|
||||
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet..." && exit 0
|
||||
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled." && exit 0
|
||||
|
||||
file="/run/shm/dsm.url"
|
||||
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
|
||||
|
||||
location=$(<"$file")
|
||||
|
||||
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
||||
|
||||
if [[ "$location" == "20.20"* ]]; then
|
||||
ip="20.20.20.1"
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
ip=$(<"$address")
|
||||
echo "Failed to reach DSM at http://$location"
|
||||
else
|
||||
ip=$(<"$gateway")
|
||||
port="${location##*:}"
|
||||
echo "Failed to reach DSM at port $port"
|
||||
else
|
||||
echo "Failed to reach DSM at http://$location"
|
||||
ip=$(<"$address")
|
||||
fi
|
||||
|
||||
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1
|
||||
|
||||
+1
-28
@@ -3,6 +3,7 @@ set -Eeuo pipefail
|
||||
|
||||
DEF_OPTS="-nodefaults -boot strict=on"
|
||||
RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||
MON_OPTS="-name $PROCESS,process=$PROCESS,debug-threads=on -pidfile $QEMU_PID"
|
||||
CPU_OPTS="-cpu $CPU_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
|
||||
MAC_OPTS="-machine type=q35,smm=off,usb=off,vmport=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
|
||||
DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
|
||||
@@ -12,32 +13,4 @@ DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
|
||||
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $DEV_OPTS $ARGUMENTS"
|
||||
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
|
||||
|
||||
# Check available memory as the very last step
|
||||
|
||||
if [[ "$RAM_CHECK" != [Nn]* ]]; then
|
||||
|
||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
||||
|
||||
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
|
||||
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
|
||||
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
|
||||
info "$msg"
|
||||
else
|
||||
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
|
||||
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available, please consider a lower value."
|
||||
if [[ "${FS,,}" != "zfs" ]]; then
|
||||
warn "$msg"
|
||||
else
|
||||
info "$msg"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
|
||||
+177
-106
@@ -17,8 +17,16 @@ SYSTEM="$STORAGE/$BASE.system.img"
|
||||
[ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81
|
||||
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
|
||||
|
||||
if ! setOwner "$BOOT"; then
|
||||
error "Failed to set the owner for \"$BOOT\" !"
|
||||
fi
|
||||
|
||||
if ! setOwner "$SYSTEM"; then
|
||||
error "Failed to set the owner for \"$SYSTEM\" !"
|
||||
fi
|
||||
|
||||
fmt2ext() {
|
||||
local DISK_FMT=$1
|
||||
local DISK_FMT="$1"
|
||||
|
||||
case "${DISK_FMT,,}" in
|
||||
qcow2)
|
||||
@@ -34,7 +42,7 @@ fmt2ext() {
|
||||
}
|
||||
|
||||
ext2fmt() {
|
||||
local DISK_EXT=$1
|
||||
local DISK_EXT="$1"
|
||||
|
||||
case "${DISK_EXT,,}" in
|
||||
qcow2)
|
||||
@@ -50,7 +58,7 @@ ext2fmt() {
|
||||
}
|
||||
|
||||
getSize() {
|
||||
local DISK_FILE=$1
|
||||
local DISK_FILE="$1"
|
||||
local DISK_EXT DISK_FMT
|
||||
|
||||
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
||||
@@ -70,7 +78,7 @@ getSize() {
|
||||
}
|
||||
|
||||
isCow() {
|
||||
local FS=$1
|
||||
local FS="$1"
|
||||
|
||||
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||
return 0
|
||||
@@ -80,7 +88,7 @@ isCow() {
|
||||
}
|
||||
|
||||
supportsDirect() {
|
||||
local FS=$1
|
||||
local FS="$1"
|
||||
|
||||
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
||||
return 1
|
||||
@@ -91,17 +99,17 @@ supportsDirect() {
|
||||
|
||||
createDisk() {
|
||||
|
||||
local DISK_FILE=$1
|
||||
local DISK_SPACE=$2
|
||||
local DISK_DESC=$3
|
||||
local DISK_FMT=$4
|
||||
local FS=$5
|
||||
local DISK_FILE="$1"
|
||||
local DISK_SPACE="$2"
|
||||
local DISK_DESC="$3"
|
||||
local DISK_FMT="$4"
|
||||
local FS="$5"
|
||||
local DATA_SIZE DIR SPACE GB FA
|
||||
|
||||
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||
|
||||
rm -f "$DISK_FILE"
|
||||
|
||||
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||
|
||||
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||
|
||||
# Check free diskspace
|
||||
@@ -113,6 +121,7 @@ createDisk() {
|
||||
error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..."
|
||||
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
html "Creating a $DISK_DESC image..."
|
||||
@@ -177,16 +186,16 @@ createDisk() {
|
||||
|
||||
resizeDisk() {
|
||||
|
||||
local DISK_FILE=$1
|
||||
local DISK_SPACE=$2
|
||||
local DISK_DESC=$3
|
||||
local DISK_FMT=$4
|
||||
local FS=$5
|
||||
local DISK_FILE="$1"
|
||||
local DISK_SPACE="$2"
|
||||
local DISK_DESC="$3"
|
||||
local DISK_FMT="$4"
|
||||
local FS="$5"
|
||||
local CUR_SIZE DATA_SIZE DIR SPACE GB
|
||||
|
||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||
local REQ=$((DATA_SIZE-CUR_SIZE))
|
||||
local REQ=$(( DATA_SIZE - CUR_SIZE ))
|
||||
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
|
||||
|
||||
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||
@@ -200,6 +209,7 @@ resizeDisk() {
|
||||
error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.."
|
||||
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
GB=$(formatBytes "$CUR_SIZE")
|
||||
@@ -245,26 +255,28 @@ resizeDisk() {
|
||||
|
||||
convertDisk() {
|
||||
|
||||
local SOURCE_FILE=$1
|
||||
local SOURCE_FMT=$2
|
||||
local DST_FILE=$3
|
||||
local DST_FMT=$4
|
||||
local DISK_BASE=$5
|
||||
local DISK_DESC=$6
|
||||
local FS=$7
|
||||
local SOURCE_FILE="$1"
|
||||
local SOURCE_FMT="$2"
|
||||
local DST_FILE="$3"
|
||||
local DST_FMT="$4"
|
||||
local DISK_BASE="$5"
|
||||
local DISK_DESC="$6"
|
||||
local FS="$7"
|
||||
|
||||
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
|
||||
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
|
||||
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exist?" && exit 79
|
||||
|
||||
local TMP_FILE="$DISK_BASE.tmp"
|
||||
rm -f "$TMP_FILE"
|
||||
|
||||
local DIR FA
|
||||
DIR=$(dirname "$TMP_FILE")
|
||||
|
||||
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||
|
||||
local DIR CUR_SIZE SPACE GB
|
||||
local CUR_SIZE SPACE GB
|
||||
|
||||
# Check free diskspace
|
||||
DIR=$(dirname "$TMP_FILE")
|
||||
CUR_SIZE=$(getSize "$SOURCE_FILE")
|
||||
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||
|
||||
@@ -273,6 +285,7 @@ convertDisk() {
|
||||
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..."
|
||||
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
local msg="Converting $DISK_DESC to $DST_FMT"
|
||||
@@ -327,31 +340,31 @@ convertDisk() {
|
||||
|
||||
checkFS () {
|
||||
|
||||
local FS=$1
|
||||
local DISK_FILE=$2
|
||||
local DISK_DESC=$3
|
||||
local FS="$1"
|
||||
local DISK_FILE="$2"
|
||||
local DISK_DESC="$3"
|
||||
local DIR FA
|
||||
|
||||
DIR=$(dirname "$DISK_FILE")
|
||||
[ ! -d "$DIR" ] && return 0
|
||||
|
||||
if [[ "${FS,,}" == "overlay"* ]]; then
|
||||
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
||||
if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
|
||||
warn "the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" == "fuse"* ]]; then
|
||||
info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
|
||||
warn "the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
|
||||
fi
|
||||
|
||||
if ! supportsDirect "$FS"; then
|
||||
info "Warning: the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
|
||||
warn "the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
|
||||
fi
|
||||
|
||||
if isCow "$FS"; then
|
||||
if [ -f "$DISK_FILE" ]; then
|
||||
FA=$(lsattr "$DISK_FILE")
|
||||
if [[ "$FA" != *"C"* ]]; then
|
||||
info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
|
||||
warn "COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -361,15 +374,15 @@ checkFS () {
|
||||
|
||||
createDevice () {
|
||||
|
||||
local DISK_FILE=$1
|
||||
local DISK_TYPE=$2
|
||||
local DISK_INDEX=$3
|
||||
local DISK_ADDRESS=$4
|
||||
local DISK_FMT=$5
|
||||
local DISK_IO=$6
|
||||
local DISK_CACHE=$7
|
||||
local DISK_SERIAL=$8
|
||||
local DISK_SECTORS=$9
|
||||
local DISK_FILE="$1"
|
||||
local DISK_TYPE="$2"
|
||||
local DISK_INDEX="$3"
|
||||
local DISK_ADDRESS="$4"
|
||||
local DISK_FMT="$5"
|
||||
local DISK_IO="$6"
|
||||
local DISK_CACHE="$7"
|
||||
local DISK_SERIAL="$8"
|
||||
local DISK_SECTORS="$9"
|
||||
local DISK_ID="data$DISK_INDEX"
|
||||
|
||||
local index=""
|
||||
@@ -413,18 +426,32 @@ createDevice () {
|
||||
return 0
|
||||
}
|
||||
|
||||
finishDisks () {
|
||||
|
||||
case "${DISK_TYPE,,}" in
|
||||
"blk" | "scsi" | "virtio-blk" | "virtio-scsi" )
|
||||
DISK_OPTS+=" -object iothread,id=io2" ;;
|
||||
esac
|
||||
|
||||
if [[ "$DISK_DISABLE" != [Yy1]* ]]; then
|
||||
html "Initialized disks successfully..."
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
addDisk () {
|
||||
|
||||
local DISK_BASE=$1
|
||||
local DISK_TYPE=$2
|
||||
local DISK_DESC=$3
|
||||
local DISK_SPACE=$4
|
||||
local DISK_INDEX=$5
|
||||
local DISK_ADDRESS=$6
|
||||
local DISK_FMT=$7
|
||||
local DISK_IO=$8
|
||||
local DISK_CACHE=$9
|
||||
local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
|
||||
local DISK_BASE="$1"
|
||||
local DISK_TYPE="$2"
|
||||
local DISK_DESC="$3"
|
||||
local DISK_SPACE="$4"
|
||||
local DISK_INDEX="$5"
|
||||
local DISK_ADDRESS="$6"
|
||||
local DISK_FMT="$7"
|
||||
local DISK_IO="$8"
|
||||
local DISK_CACHE="$9"
|
||||
local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE LEFT FREE USED
|
||||
|
||||
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
||||
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
||||
@@ -432,8 +459,25 @@ addDisk () {
|
||||
DIR=$(dirname "$DISK_FILE")
|
||||
[ ! -d "$DIR" ] && return 0
|
||||
|
||||
if [[ "${DISK_SPACE,,}" == "max" || "${DISK_SPACE,,}" == "half" ]]; then
|
||||
|
||||
local SPARE=1073741824
|
||||
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||
|
||||
if [[ "${DISK_SPACE,,}" == "max" ]]; then
|
||||
FREE=$(( FREE - SPARE ))
|
||||
else
|
||||
FREE=$(( FREE / 2 ))
|
||||
fi
|
||||
|
||||
(( FREE < SPARE )) && FREE="$SPARE"
|
||||
GB=$(( FREE / 1073741825 ))
|
||||
DISK_SPACE="${GB}G"
|
||||
|
||||
fi
|
||||
|
||||
SPACE="${DISK_SPACE// /}"
|
||||
[ -z "$SPACE" ] && SPACE="16G"
|
||||
[ -z "$SPACE" ] && SPACE="256G"
|
||||
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
||||
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||
|
||||
@@ -444,7 +488,7 @@ addDisk () {
|
||||
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
||||
|
||||
if (( DATA_SIZE < 6442450944 )); then
|
||||
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
|
||||
error "Please increase the ${DISK_DESC^^}_SIZE variable to at least 6 GB." && exit 73
|
||||
fi
|
||||
|
||||
FS=$(stat -f -c %T "$DIR")
|
||||
@@ -455,7 +499,7 @@ addDisk () {
|
||||
DISK_CACHE="writeback"
|
||||
fi
|
||||
|
||||
if ! [ -s "$DISK_FILE" ] ; then
|
||||
if [ ! -s "$DISK_FILE" ] ; then
|
||||
|
||||
if [[ "${DISK_FMT,,}" != "raw" ]]; then
|
||||
PREV_FMT="raw"
|
||||
@@ -468,6 +512,7 @@ addDisk () {
|
||||
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
|
||||
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ -s "$DISK_FILE" ]; then
|
||||
@@ -475,7 +520,18 @@ addDisk () {
|
||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||
|
||||
if (( DATA_SIZE > CUR_SIZE )); then
|
||||
|
||||
resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
||||
|
||||
else
|
||||
|
||||
if (( DATA_SIZE < CUR_SIZE )); then
|
||||
|
||||
if [[ "${DISK_SPACE,,}" != "max" && "${DISK_SPACE,,}" != "half" ]]; then
|
||||
info "You decreased the ${DISK_DESC^^}_SIZE variable to ${DISK_SPACE/G/ GB} but shrinking disks is not supported, will be ignored..."
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
@@ -484,6 +540,38 @@ addDisk () {
|
||||
|
||||
fi
|
||||
|
||||
if [ -f "$DISK_FILE" ] && [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||
|
||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||
USED=$(du -sB 1 "$DISK_FILE" | cut -f1)
|
||||
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||
LEFT=$(( CUR_SIZE - USED - FREE ))
|
||||
|
||||
if (( LEFT > 0 )); then
|
||||
|
||||
GB=$(formatBytes "$FREE")
|
||||
LEFT=$(formatBytes "$LEFT")
|
||||
CUR_SIZE=$(formatBytes "$CUR_SIZE")
|
||||
msg="The virtual size of the ${DISK_DESC,,} is $CUR_SIZE"
|
||||
|
||||
if [ -n "$USED" ] && [[ "$USED" != "0" ]]; then
|
||||
USED=$(formatBytes "$USED")
|
||||
msg+=" (of which $USED is used)"
|
||||
fi
|
||||
|
||||
info "$msg, but there is only $GB of free space remaining in $DIR now."
|
||||
info "Please consider making at least $LEFT more space available in $DIR for future expansions."
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ -f "$DISK_FILE" ]; then
|
||||
if ! setOwner "$DISK_FILE"; then
|
||||
error "Failed to set the owner for \"$DISK_FILE\" !"
|
||||
fi
|
||||
fi
|
||||
|
||||
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "")
|
||||
|
||||
return 0
|
||||
@@ -491,31 +579,34 @@ addDisk () {
|
||||
|
||||
addDevice () {
|
||||
|
||||
local DISK_DEV=$1
|
||||
local DISK_TYPE=$2
|
||||
local DISK_INDEX=$3
|
||||
local DISK_ADDRESS=$4
|
||||
local DISK_DEV="$1"
|
||||
local DISK_TYPE="$2"
|
||||
local DISK_INDEX="$3"
|
||||
local DISK_ADDRESS="$4"
|
||||
|
||||
[ -z "$DISK_DEV" ] && return 0
|
||||
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
|
||||
|
||||
local sectors=""
|
||||
local result logical physical
|
||||
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
|
||||
logical="${result%% *}"
|
||||
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
|
||||
physical="${physical%% *}"
|
||||
local dev_type=""
|
||||
dev_type=$(lsblk -no TYPE "$DISK_DEV" 2>/dev/null | head -n1)
|
||||
|
||||
if [ -n "$physical" ]; then
|
||||
if [[ "$physical" == "512" || "$physical" == "4096" ]]; then
|
||||
if [[ "$physical" == "4096" ]]; then
|
||||
# Only detect and apply sector sizes for partitions, not whole disks
|
||||
# Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk
|
||||
if [[ "$dev_type" == "part" ]]; then
|
||||
local result logical physical
|
||||
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
|
||||
logical="${result%% *}"
|
||||
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
|
||||
physical="${physical%% *}"
|
||||
|
||||
if [ -n "$physical" ]; then
|
||||
if [[ "$physical" != "512" ]]; then
|
||||
sectors=",logical_block_size=$logical,physical_block_size=$physical"
|
||||
fi
|
||||
else
|
||||
warn "Unknown physical sector size: $physical for $DISK_DEV"
|
||||
warn "Failed to determine the sector size for $DISK_DEV"
|
||||
fi
|
||||
else
|
||||
warn "Failed to determine the sector size for $DISK_DEV"
|
||||
fi
|
||||
|
||||
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
|
||||
@@ -523,11 +614,16 @@ addDevice () {
|
||||
return 0
|
||||
}
|
||||
|
||||
html "Initializing disks..."
|
||||
|
||||
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
|
||||
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
|
||||
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
|
||||
[ -z "${DISK_DISABLE:-}" ] && DISK_DISABLE=""
|
||||
|
||||
if [[ "$DISK_DISABLE" != [Yy1]* ]]; then
|
||||
msg="Initializing disks..."
|
||||
html "$msg"
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
||||
fi
|
||||
|
||||
case "${DISK_TYPE,,}" in
|
||||
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
||||
@@ -549,38 +645,14 @@ fi
|
||||
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
|
||||
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
|
||||
|
||||
if [[ "$DISK_DISABLE" == [Yy1]* ]]; then
|
||||
finishDisks
|
||||
return 0
|
||||
fi
|
||||
|
||||
DISK1_FILE="$STORAGE/${DISK_NAME}"
|
||||
if [ ! -f "$DISK1_FILE.img" ] && [ -f "$STORAGE/data${DISK_SIZE}.img" ]; then
|
||||
# Fallback for legacy installs
|
||||
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
|
||||
fi
|
||||
|
||||
DISK2_FILE="/storage2/${DISK_NAME}2"
|
||||
if [ ! -f "$DISK2_FILE.img" ]; then
|
||||
# Fallback for legacy installs
|
||||
FALLBACK="/storage2/data.img"
|
||||
if [[ -f "$DISK1_FILE.img" && -f "$FALLBACK" ]]; then
|
||||
SIZE1=$(stat -c%s "$FALLBACK")
|
||||
SIZE2=$(stat -c%s "$DISK1_FILE.img")
|
||||
if [[ SIZE1 -ne SIZE2 ]]; then
|
||||
mv "$FALLBACK" "$DISK2_FILE.img"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
DISK3_FILE="/storage3/${DISK_NAME}3"
|
||||
if [ ! -f "$DISK3_FILE.img" ]; then
|
||||
# Fallback for legacy installs
|
||||
FALLBACK="/storage3/data.img"
|
||||
if [[ -f "$DISK1_FILE.img" && -f "$FALLBACK" ]]; then
|
||||
SIZE1=$(stat -c%s "$FALLBACK")
|
||||
SIZE2=$(stat -c%s "$DISK1_FILE.img")
|
||||
if [[ SIZE1 -ne SIZE2 ]]; then
|
||||
mv "$FALLBACK" "$DISK3_FILE.img"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
DISK4_FILE="/storage4/${DISK_NAME}4"
|
||||
|
||||
: "${DISK2_SIZE:=""}"
|
||||
@@ -627,7 +699,6 @@ else
|
||||
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||
fi
|
||||
|
||||
DISK_OPTS+=" -object iothread,id=io2"
|
||||
finishDisks
|
||||
|
||||
html "Initialized disks successfully..."
|
||||
return 0
|
||||
|
||||
@@ -18,6 +18,10 @@ if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64"
|
||||
|
||||
fi
|
||||
|
||||
msg="Configuring display drivers..."
|
||||
html "$msg"
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
||||
|
||||
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
|
||||
DISPLAY_OPTS+=" -vga $VGA"
|
||||
|
||||
|
||||
+18
-9
@@ -1,13 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: "${PLATFORM:="x64"}"
|
||||
: "${APP:="Virtual DSM"}"
|
||||
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
|
||||
|
||||
cd /run
|
||||
|
||||
. start.sh # Startup hook
|
||||
. utils.sh # Load functions
|
||||
. reset.sh # Initialize system
|
||||
. server.sh # Start webserver
|
||||
. install.sh # Run installation
|
||||
. disk.sh # Initialize disks
|
||||
. display.sh # Initialize graphics
|
||||
@@ -15,23 +18,29 @@ cd /run
|
||||
. proc.sh # Initialize processor
|
||||
. serial.sh # Initialize serialport
|
||||
. power.sh # Configure shutdown
|
||||
. memory.sh # Check available memory
|
||||
. config.sh # Configure arguments
|
||||
. finish.sh # Finish initialization
|
||||
|
||||
trap - ERR
|
||||
|
||||
version=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }')
|
||||
cmd=(qemu-system-x86_64)
|
||||
version=$("${cmd[@]}" --version | awk 'NR==1 { print $4 }')
|
||||
info "Booting $APP using QEMU v$version..."
|
||||
|
||||
if [[ "$CONSOLE" == [Yy]* ]]; then
|
||||
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
|
||||
if [[ "$SHUTDOWN" != [Yy1]* ]]; then
|
||||
exec "${cmd[@]}" ${ARGS:+ $ARGS}
|
||||
fi
|
||||
|
||||
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || :
|
||||
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
|
||||
if [ ! -t 1 ] || [ ! -c /dev/tty ]; then
|
||||
"${cmd[@]}" ${ARGS:+ $ARGS} &
|
||||
else
|
||||
"${cmd[@]}" ${ARGS:+ $ARGS} </dev/tty >/dev/tty &
|
||||
fi
|
||||
|
||||
terminal
|
||||
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
||||
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
|
||||
rc=0
|
||||
wait $! || rc=$?
|
||||
[ -f "$QEMU_END" ] && exit "$rc"
|
||||
|
||||
sleep 1 & wait $!
|
||||
[ ! -f "$QEMU_END" ] && finish 0
|
||||
finish "$rc"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
+27
-14
@@ -31,8 +31,8 @@ if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
|
||||
BASE=$(basename "$URL" .pat)
|
||||
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
|
||||
BASE=$(basename "${URL%%\?*}" .pat)
|
||||
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
|
||||
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
||||
printf -v BASE '%b' "${BASE//%/\\x}"
|
||||
BASE="${BASE//[!A-Za-z0-9._-]/_}"
|
||||
fi
|
||||
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then
|
||||
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
|
||||
@@ -65,8 +65,8 @@ fi
|
||||
|
||||
if [ ! -s "$FILE" ]; then
|
||||
BASE=$(basename "${URL%%\?*}" .pat)
|
||||
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
|
||||
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
||||
printf -v BASE '%b' "${BASE//%/\\x}"
|
||||
BASE="${BASE//[!A-Za-z0-9._-]/_}"
|
||||
fi
|
||||
|
||||
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
|
||||
@@ -80,16 +80,16 @@ rm -f "$STORAGE/$BASE.system.img"
|
||||
# Check filesystem
|
||||
FS=$(stat -f -c %T "$STORAGE")
|
||||
|
||||
if [[ "${FS,,}" == "overlay"* ]]; then
|
||||
info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
|
||||
if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
|
||||
warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" == "fuse"* ]]; then
|
||||
info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
|
||||
warn "the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
||||
info "Warning: the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..."
|
||||
warn "the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..."
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then
|
||||
@@ -98,6 +98,10 @@ fi
|
||||
|
||||
if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then
|
||||
TMP="$STORAGE/tmp"
|
||||
rm -rf "$TMP"
|
||||
if ! makeDir "$TMP"; then
|
||||
error "Failed to create directory \"$TMP\" !" && exit 93
|
||||
fi
|
||||
else
|
||||
TMP="/tmp/dsm"
|
||||
TMP_SPACE=2147483648
|
||||
@@ -106,10 +110,9 @@ else
|
||||
if (( TMP_SPACE > SPACE )); then
|
||||
error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93
|
||||
fi
|
||||
rm -rf "$TMP" && mkdir -p "$TMP"
|
||||
fi
|
||||
|
||||
rm -rf "$TMP" && mkdir -p "$TMP"
|
||||
|
||||
# Check free diskspace
|
||||
ROOT_SPACE=536870912
|
||||
SPACE=$(df --output=avail -B 1 / | tail -n 1)
|
||||
@@ -222,6 +225,8 @@ if ! touch "$SYSTEM"; then
|
||||
error "Could not create file $SYSTEM for the system disk." && exit 98
|
||||
fi
|
||||
|
||||
! setOwner "$SYSTEM" && error "Failed to set the owner for \"$SYSTEM\" !"
|
||||
|
||||
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||
{ chattr +C "$SYSTEM"; } || :
|
||||
FA=$(lsattr "$SYSTEM")
|
||||
@@ -254,7 +259,11 @@ PART="$TMP/partition.fdisk"
|
||||
sfdisk -q "$SYSTEM" < "$PART"
|
||||
|
||||
MOUNT="$TMP/system"
|
||||
rm -rf "$MOUNT" && mkdir -p "$MOUNT"
|
||||
rm -rf "$MOUNT"
|
||||
|
||||
if ! makeDir "$MOUNT"; then
|
||||
error "Failed to create directory \"$MOUNT\" !" && exit 93
|
||||
fi
|
||||
|
||||
MSG="Extracting system partition..."
|
||||
info "Install: $MSG" && html "$MSG"
|
||||
@@ -289,6 +298,7 @@ fakeroot -- bash -c "set -Eeu;\
|
||||
|
||||
rm -rf "$MOUNT"
|
||||
echo "$BASE" > "$STORAGE/dsm.ver"
|
||||
! setOwner "$STORAGE/dsm.ver" && error "Failed to set the owner for \"$STORAGE/dsm.ver\" !"
|
||||
|
||||
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
||||
rm -f "$PAT"
|
||||
@@ -296,10 +306,13 @@ else
|
||||
mv -f "$PAT" "$STORAGE/$BASE.pat"
|
||||
fi
|
||||
|
||||
if [ -f "$STORAGE/$BASE.pat" ]; then
|
||||
! setOwner "$STORAGE/$BASE.pat" && error "Failed to set the owner for \"$STORAGE/$BASE.pat\" !"
|
||||
fi
|
||||
|
||||
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
|
||||
! setOwner "$STORAGE/$BASE.boot.img" && error "Failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
|
||||
|
||||
rm -rf "$TMP"
|
||||
|
||||
html "Booting DSM instance..."
|
||||
sleep 1.2
|
||||
|
||||
return 0
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
msg="Checking memory..."
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
||||
|
||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
||||
|
||||
if [[ "$RAM_CHECK" != [Nn]* && "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
|
||||
|
||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||
|
||||
if (( (wanted + RAM_SPARE) > RAM_AVAIL )); then
|
||||
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of free memory available,"
|
||||
if [[ "${FS,,}" == "zfs" ]]; then
|
||||
info "$msg but since ZFS is active this will be ignored."
|
||||
else
|
||||
RAM_SIZE="max"
|
||||
warn "$msg it will automatically be adjusted to a lower amount."
|
||||
fi
|
||||
else
|
||||
if (( (wanted + (RAM_SPARE * 3)) > RAM_AVAIL )); then
|
||||
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of free memory available,"
|
||||
if [[ "${FS,,}" == "zfs" ]]; then
|
||||
info "$msg but since ZFS is active this will be ignored."
|
||||
else
|
||||
warn "$msg please consider a lower amount."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ "${RAM_SIZE,,}" == "half" ]]; then
|
||||
|
||||
if (( (RAM_AVAIL / 2) > RAM_SPARE )); then
|
||||
wanted=$(( (RAM_AVAIL / 2) / 1048577 ))
|
||||
RAM_SIZE="${wanted}M"
|
||||
info "Allocated $wanted MB of RAM for the virtual machine."
|
||||
else
|
||||
RAM_SIZE="max"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ "${RAM_SIZE,,}" == "max" ]]; then
|
||||
|
||||
if (( RAM_AVAIL < (RAM_SPARE * 2) )); then
|
||||
|
||||
wanted=$(( RAM_AVAIL / 2 ))
|
||||
|
||||
else
|
||||
|
||||
wanted=$(( RAM_AVAIL - (RAM_SPARE * 3) ))
|
||||
|
||||
if (( wanted < (RAM_SPARE * 6) )); then
|
||||
wanted=$(( RAM_AVAIL - RAM_SPARE ))
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
wanted=$(( wanted / 1048577 ))
|
||||
RAM_SIZE="${wanted}M"
|
||||
|
||||
info "Allocated $wanted MB of RAM for the virtual machine."
|
||||
|
||||
fi
|
||||
|
||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||
|
||||
if [ "$wanted" -lt "$RAM_MINIMUM" ]; then
|
||||
wanted=$(( wanted / 1048577 ))
|
||||
error "Not enough memory available, there is only $wanted MB left!"
|
||||
exit 16
|
||||
fi
|
||||
|
||||
return 0
|
||||
+532
-197
@@ -7,18 +7,28 @@ set -Eeuo pipefail
|
||||
: "${MTU:=""}"
|
||||
: "${DHCP:="N"}"
|
||||
: "${NETWORK:="Y"}"
|
||||
: "${USER_PORTS:=""}"
|
||||
: "${HOST_PORTS:=""}"
|
||||
: "${USER_PORTS:=""}"
|
||||
: "${ADAPTER:="virtio-net-pci"}"
|
||||
|
||||
: "${VM_NET_IP:=""}"
|
||||
: "${VM_NET_DEV:=""}"
|
||||
: "${VM_NET_TAP:="dsm"}"
|
||||
: "${VM_NET_MAC:="$MAC"}"
|
||||
: "${VM_NET_IP:="20.20.20.21"}"
|
||||
: "${VM_NET_BRIDGE:="docker"}"
|
||||
: "${VM_NET_HOST:="VirtualDSM"}"
|
||||
: "${VM_NET_MASK:="255.255.255.0"}"
|
||||
|
||||
: "${PASST:="/run/passt"}"
|
||||
: "${PASST_MTU:=""}"
|
||||
: "${PASST_OPTS:=""}"
|
||||
: "${PASST_DEBUG:=""}"
|
||||
: "${PASST_PID:="/var/run/passt.pid"}"
|
||||
|
||||
: "${DNSMASQ_OPTS:=""}"
|
||||
: "${DNSMASQ_DEBUG:=""}"
|
||||
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
|
||||
: "${DNSMASQ_PID:="/var/run/dnsmasq.pid"}"
|
||||
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
|
||||
|
||||
ADD_ERR="Please add the following setting to your container:"
|
||||
@@ -29,6 +39,8 @@ ADD_ERR="Please add the following setting to your container:"
|
||||
|
||||
configureDHCP() {
|
||||
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
|
||||
|
||||
# Create the necessary file structure for /dev/vhost-net
|
||||
if [ ! -c /dev/vhost-net ]; then
|
||||
if mknod /dev/vhost-net c 10 238; then
|
||||
@@ -61,7 +73,7 @@ configureDHCP() {
|
||||
|
||||
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; 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
|
||||
|
||||
@@ -105,98 +117,319 @@ configureDHCP() {
|
||||
|
||||
configureDNS() {
|
||||
|
||||
# Create lease file for faster resolve
|
||||
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
|
||||
chmod 644 /var/lib/misc/dnsmasq.leases
|
||||
local fa="$1"
|
||||
local ip="$2"
|
||||
local mac="$3"
|
||||
local host="$4"
|
||||
local mask="$5"
|
||||
local gateway="$6"
|
||||
local arguments="$DNSMASQ_OPTS"
|
||||
|
||||
# dnsmasq configuration:
|
||||
DNSMASQ_OPTS+=" --dhcp-authoritative"
|
||||
echo "$gateway" > /run/shm/qemu.gw
|
||||
|
||||
# Set DHCP range and host
|
||||
DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP"
|
||||
DNSMASQ_OPTS+=" --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite"
|
||||
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
|
||||
|
||||
# Set DNS server and gateway
|
||||
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,255.255.255.0"
|
||||
DNSMASQ_OPTS+=" --dhcp-option=option:router,${VM_NET_IP%.*}.1"
|
||||
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1"
|
||||
[ -s "$DNSMASQ_PID" ] && pKill "$(<"$DNSMASQ_PID")"
|
||||
rm -f "$DNSMASQ_PID"
|
||||
|
||||
case "${NETWORK,,}" in
|
||||
"tap" | "tun" | "tuntap" | "y" )
|
||||
|
||||
# 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:
|
||||
arguments+=" --dhcp-authoritative"
|
||||
|
||||
# Set DHCP range and host
|
||||
arguments+=" --dhcp-range=$ip,$ip"
|
||||
arguments+=" --dhcp-host=$mac,,$ip,$host,infinite"
|
||||
|
||||
# Set DNS server and gateway
|
||||
arguments+=" --dhcp-option=option:netmask,$mask"
|
||||
arguments+=" --dhcp-option=option:router,$gateway"
|
||||
arguments+=" --dhcp-option=option:dns-server,$gateway"
|
||||
|
||||
esac
|
||||
|
||||
# Set interfaces
|
||||
arguments+=" --interface=$fa"
|
||||
arguments+=" --bind-interfaces"
|
||||
|
||||
# Workaround NET_RAW capability
|
||||
arguments+=" --no-ping"
|
||||
|
||||
# Add DNS entry for container
|
||||
DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1"
|
||||
arguments+=" --address=/host.lan/$gateway"
|
||||
|
||||
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
||||
# Set local dns resolver to dnsmasq when needed
|
||||
[ -f /etc/resolv.dnsmasq ] && arguments+=" --resolv-file=/etc/resolv.dnsmasq"
|
||||
|
||||
if [[ "${DEBUG_DNS:-}" == [Yy1]* ]]; then
|
||||
DNSMASQ_OPTS+=" -d"
|
||||
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
|
||||
return 0
|
||||
fi
|
||||
# Enable logging to file
|
||||
local log="/var/log/dnsmasq.log"
|
||||
rm -f "$log"
|
||||
arguments+=" --log-facility=$log"
|
||||
|
||||
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
|
||||
error "Failed to start dnsmasq, reason: $?" && return 1
|
||||
fi
|
||||
arguments=$(echo "$arguments" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
||||
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${arguments// -/$'\n-'}"
|
||||
|
||||
return 0
|
||||
}
|
||||
{ $DNSMASQ ${arguments:+ $arguments}; rc=$?; } || :
|
||||
|
||||
getUserPorts() {
|
||||
if (( rc != 0 )); then
|
||||
|
||||
local args=""
|
||||
local list=$1
|
||||
local ssh="22"
|
||||
local dsm="5000"
|
||||
local msg="Failed to start Dnsmasq, reason: $rc"
|
||||
|
||||
[ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm"
|
||||
|
||||
list="${list//,/ }"
|
||||
list="${list## }"
|
||||
list="${list%% }"
|
||||
|
||||
for port in $list; do
|
||||
proto="tcp"
|
||||
num="$port"
|
||||
|
||||
if [[ "$port" == */udp ]]; then
|
||||
proto="udp"
|
||||
num="${port%/udp}"
|
||||
elif [[ "$port" == */tcp ]]; then
|
||||
proto="tcp"
|
||||
num="${port%/tcp}"
|
||||
if [[ "${NETWORK,,}" == "slirp" || "${NETWORK,,}" == "passt" || "$ROOTLESS" != [Yy1]* || "$DEBUG" == [Yy1]* ]]; then
|
||||
[ -f "$log" ] && [ -s "$log" ] && cat "$log"
|
||||
error "$msg"
|
||||
fi
|
||||
|
||||
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
|
||||
tail -fn +0 "$log" --pid=$$ &
|
||||
fi
|
||||
|
||||
echo "${args%?}"
|
||||
return 0
|
||||
}
|
||||
|
||||
getHostPorts() {
|
||||
|
||||
local list="$1"
|
||||
local list="${HOST_PORTS// /},"
|
||||
|
||||
[ -z "$list" ] && echo "" && return 0
|
||||
|
||||
if [[ "$list" != *","* ]]; then
|
||||
echo " ! --dport $list"
|
||||
else
|
||||
echo " -m multiport ! --dports $list"
|
||||
fi
|
||||
# Remove duplicates
|
||||
list=$(echo "${list//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
|
||||
|
||||
echo "$list"
|
||||
return 0
|
||||
}
|
||||
|
||||
configureUser() {
|
||||
getUserPorts() {
|
||||
|
||||
if [ -z "$IP6" ]; then
|
||||
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 ssh="22"
|
||||
local dsm="5000,5001"
|
||||
|
||||
local list="$ssh,$dsm,"
|
||||
list+="${USER_PORTS// /},"
|
||||
|
||||
local exclude
|
||||
exclude=$(getHostPorts)
|
||||
|
||||
local ports=""
|
||||
local userport=""
|
||||
local hostport=""
|
||||
|
||||
for userport in ${list//,/ }; do
|
||||
|
||||
local num="${userport///tcp}"
|
||||
num="${num///udp}"
|
||||
|
||||
for hostport in ${exclude//,/ }; do
|
||||
|
||||
local port="${hostport///tcp}"
|
||||
port="${port///udp}"
|
||||
|
||||
if [[ "$num" == "$port" ]]; then
|
||||
num=""
|
||||
if [[ "$port" != "$WEB_PORT" ]]; then
|
||||
warn "Could not assign port $port to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!"
|
||||
fi
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
[ -n "$num" ] && ports+="$userport,"
|
||||
|
||||
done
|
||||
|
||||
# Remove duplicates
|
||||
echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g'
|
||||
return 0
|
||||
}
|
||||
|
||||
getSlirp() {
|
||||
|
||||
local ip="$1"
|
||||
local args=""
|
||||
local list=""
|
||||
|
||||
list=$(getUserPorts)
|
||||
|
||||
for port in ${list//,/ }; do
|
||||
|
||||
local proto="tcp"
|
||||
local num="${port%/tcp}"
|
||||
[ -z "$num" ] && continue
|
||||
|
||||
if [[ "$port" == *"/udp" ]]; then
|
||||
proto="udp"
|
||||
num="${port%/udp}"
|
||||
elif [[ "$port" != *"/tcp" ]]; then
|
||||
args+="hostfwd=$proto::$num-$ip:$num,"
|
||||
proto="udp"
|
||||
num="${port%/udp}"
|
||||
fi
|
||||
|
||||
args+="hostfwd=$proto::$num-$ip:$num,"
|
||||
done
|
||||
|
||||
echo "$args" | sed 's/,*$//g'
|
||||
return 0
|
||||
}
|
||||
|
||||
configureSlirp() {
|
||||
|
||||
NETWORK="slirp"
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."
|
||||
|
||||
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"
|
||||
|
||||
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=""
|
||||
forward=$(getSlirp "$ip")
|
||||
[ -n "$forward" ] && NET_OPTS+=",$forward"
|
||||
|
||||
if [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]]; then
|
||||
echo "$gateway" > /run/shm/qemu.gw
|
||||
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"
|
||||
[ ! -f /etc/resolv.dnsmasq ] && 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
|
||||
|
||||
local forward
|
||||
forward=$(getUserPorts "$USER_PORTS")
|
||||
[ -n "$forward" ] && NET_OPTS+=",$forward"
|
||||
VM_NET_IP="$ip"
|
||||
return 0
|
||||
}
|
||||
|
||||
configurePasst() {
|
||||
|
||||
NETWORK="passt"
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
|
||||
|
||||
local log="/tmp/passt.log"
|
||||
rm -f "$log"
|
||||
|
||||
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"
|
||||
|
||||
local forward=""
|
||||
forward=$(getUserPorts)
|
||||
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 $PASST_PID"
|
||||
PASST_OPTS+=" -l $log"
|
||||
PASST_OPTS+=" -q"
|
||||
|
||||
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
|
||||
[ ! -f /etc/resolv.dnsmasq ] && 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/^ *//')
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* || "$PASST_DEBUG" == [Yy1]* ]]; then
|
||||
printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
|
||||
fi
|
||||
|
||||
[ ! -f "$PASST" ] && cp /usr/bin/passt* /run
|
||||
|
||||
if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then
|
||||
|
||||
rm -f "$log"
|
||||
PASST_OPTS="${PASST_OPTS/ -q/}"
|
||||
{ $PASST ${PASST_OPTS:+ $PASST_OPTS}; rc=$?; } || :
|
||||
|
||||
if (( rc != 0 )); then
|
||||
[ -f "$log" ] && [ -s "$log" ] && cat "$log"
|
||||
warn "failed to start passt ($rc), falling back to slirp networking!"
|
||||
configureSlirp && return 0 || return 1
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ "$PASST_DEBUG" == [Yy1]* ]]; then
|
||||
tail -fn +0 "$log" --pid=$$ &
|
||||
else
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
[ -f "$log" ] && [ -s "$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
|
||||
}
|
||||
|
||||
clearTables() {
|
||||
local table="" line rules
|
||||
|
||||
# Choose between iptables or nftables
|
||||
if command -v iptables-nft >/dev/null 2>&1 && iptables-nft -V >/dev/null 2>&1; then
|
||||
update-alternatives --set iptables /usr/sbin/iptables-nft > /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
|
||||
|
||||
# Store the current iptables ruleset
|
||||
! rules=$(iptables-save 2> /dev/null) && return 0
|
||||
[ -z "$rules" ] && return 0
|
||||
|
||||
# Delete every rule tagged with our unique identifier, leaving all other rules intact.
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
\*nat) table="nat" ;;
|
||||
\*filter) table="filter" ;;
|
||||
\*mangle) table="mangle" ;;
|
||||
\*raw) table="raw" ;;
|
||||
esac
|
||||
if [[ "$line" == -A* ]]; then
|
||||
local re="--comment[[:space:]]+\"?remove\"?([[:space:]]|\$)"
|
||||
if [[ "$line" =~ $re ]]; then
|
||||
read -ra args <<< "${line/-A /-D }"
|
||||
iptables -t "$table" "${args[@]}" &> /dev/null || :
|
||||
fi
|
||||
fi
|
||||
done <<< "$rules"
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -204,11 +437,12 @@ configureUser() {
|
||||
configureNAT() {
|
||||
|
||||
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
|
||||
if [ ! -c /dev/net/tun ]; then
|
||||
[[ "$PODMAN" == [Yy1]* ]] && return 1
|
||||
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
|
||||
if mknod /dev/net/tun c 10 200; then
|
||||
chmod 666 /dev/net/tun
|
||||
@@ -216,50 +450,71 @@ configureNAT() {
|
||||
fi
|
||||
|
||||
if [ ! -c /dev/net/tun ]; then
|
||||
error "$tuntap" && return 1
|
||||
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||
warn "$tuntap" && return 1
|
||||
fi
|
||||
|
||||
# Check port forwarding flag
|
||||
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
|
||||
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||
[[ "$PODMAN" == [Yy1]* ]] && return 1
|
||||
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
|
||||
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||
warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
local ip base gateway exclude
|
||||
base=$(cut -d. -f3,4 <<< "$IP")
|
||||
|
||||
if [[ "$IP" != "172.30."* ]]; then
|
||||
ip="172.30.$base"
|
||||
else
|
||||
ip="172.31.$base"
|
||||
fi
|
||||
|
||||
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
|
||||
|
||||
if [[ "$ip" != *".1" ]]; then
|
||||
gateway="${ip%.*}.1"
|
||||
else
|
||||
gateway="${ip%.*}.2"
|
||||
fi
|
||||
|
||||
local subnet="${ip%.*}.0/24"
|
||||
local broadcast="${ip%.*}.255"
|
||||
|
||||
# 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
|
||||
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
|
||||
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
|
||||
fi
|
||||
|
||||
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then
|
||||
error "Failed to add IP address pool!" && return 1
|
||||
if ! ip address add "$gateway/24" broadcast "$broadcast" dev "$VM_NET_BRIDGE"; then
|
||||
warn "failed to add IP address pool!" && return 1
|
||||
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..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# QEMU Works with taps, set tap to the bridge created
|
||||
# Set tap to the bridge created
|
||||
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
|
||||
error "$tuntap" && return 1
|
||||
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||
warn "$tuntap" && return 1
|
||||
fi
|
||||
|
||||
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; 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
|
||||
|
||||
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
|
||||
warn "Failed to set gateway MAC address.."
|
||||
warn "failed to set gateway MAC address."
|
||||
fi
|
||||
|
||||
while ! ip link set "$VM_NET_TAP" up promisc on; do
|
||||
@@ -267,32 +522,53 @@ configureNAT() {
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
|
||||
error "Failed to set IP link!" && return 1
|
||||
if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
|
||||
warn "failed to set master bridge!" && return 1
|
||||
fi
|
||||
|
||||
# Add internet connection to the VM
|
||||
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
|
||||
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
|
||||
# Flush existing tables
|
||||
clearTables
|
||||
|
||||
exclude=$(getHostPorts "$HOST_PORTS")
|
||||
exclude=$(getHostPorts)
|
||||
|
||||
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
|
||||
error "$tables" && return 1
|
||||
if [ -n "$exclude" ]; then
|
||||
if [[ "$exclude" != *","* ]]; then
|
||||
exclude=" ! --dport $exclude"
|
||||
else
|
||||
exclude=" -m multiport ! --dports $exclude"
|
||||
fi
|
||||
fi
|
||||
|
||||
# NAT traffic from bridge subnet to Docker uplink
|
||||
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -s "$subnet" ! -d "$subnet" -m comment --comment "remove" -j MASQUERADE > /dev/null 2>&1; then
|
||||
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -s "$subnet" ! -d "$subnet" -m comment --comment "remove" -j MASQUERADE; then
|
||||
warn "$tables" && return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 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
|
||||
error "Failed to configure IP tables!" && return 1
|
||||
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -m comment --comment "remove" -j DNAT --to "$ip"; then
|
||||
warn "failed to configure IP tables!" && return 1
|
||||
fi
|
||||
|
||||
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then
|
||||
error "Failed to configure IP tables!" && return 1
|
||||
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -m comment --comment "remove" -j DNAT --to "$ip"; then
|
||||
warn "failed to configure IP tables!" && return 1
|
||||
fi
|
||||
|
||||
if (( KERNEL > 4 )); then
|
||||
# Hack for guest VMs complaining about "bad udp checksums in 5 packets"
|
||||
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
|
||||
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -m comment --comment "remove" -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Allow forwarding from bridge -> dev
|
||||
if ! iptables -A FORWARD -i "$VM_NET_BRIDGE" -o "$VM_NET_DEV" -m comment --comment "remove" -j ACCEPT; then
|
||||
warn "failed to configure IP tables!" && return 1
|
||||
fi
|
||||
|
||||
# Allow return traffic
|
||||
if ! iptables -A FORWARD -i "$VM_NET_DEV" -o "$VM_NET_BRIDGE" -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "remove" -j ACCEPT; then
|
||||
warn "failed to configure IP tables!" && return 1
|
||||
fi
|
||||
|
||||
NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP"
|
||||
@@ -304,23 +580,31 @@ configureNAT() {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
closeBridge() {
|
||||
|
||||
local pid="/var/run/dnsmasq.pid"
|
||||
[ -s "$pid" ] && pKill "$(<"$pid")"
|
||||
local pids=( "$PASST_PID" "$DNSMASQ_PID" )
|
||||
mKill "${pids[@]}"
|
||||
|
||||
[[ "${NETWORK,,}" == "user"* ]] && return 0
|
||||
ip link set "$VM_NET_TAP" down promisc off &> /dev/null || :
|
||||
ip link delete "$VM_NET_TAP" &> /dev/null || :
|
||||
|
||||
ip link set "$VM_NET_TAP" down promisc off &> null || true
|
||||
ip link delete "$VM_NET_TAP" &> null || true
|
||||
ip link set "$VM_NET_BRIDGE" down &> /dev/null || :
|
||||
ip link delete "$VM_NET_BRIDGE" &> /dev/null || :
|
||||
|
||||
ip link set dockerbridge down &> null || true
|
||||
ip link delete dockerbridge &> null || true
|
||||
clearTables
|
||||
return 0
|
||||
}
|
||||
|
||||
closeWeb() {
|
||||
|
||||
local pids=( "$WEB_PID" "$WSD_PID" )
|
||||
mKill "${pids[@]}"
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -328,11 +612,7 @@ closeBridge() {
|
||||
closeNetwork() {
|
||||
|
||||
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then
|
||||
|
||||
# Shutdown nginx
|
||||
nginx -s stop 2> /dev/null
|
||||
fWait "nginx"
|
||||
|
||||
closeWeb
|
||||
fi
|
||||
|
||||
[[ "$NETWORK" == [Nn]* ]] && return 0
|
||||
@@ -340,36 +620,41 @@ closeNetwork() {
|
||||
exec 30<&- || true
|
||||
exec 40<&- || true
|
||||
|
||||
if [[ "$DHCP" != [Yy1]* ]]; then
|
||||
closeBridge
|
||||
|
||||
closeBridge
|
||||
return 0
|
||||
return 0
|
||||
}
|
||||
|
||||
fi
|
||||
cleanUp() {
|
||||
|
||||
ip link set "$VM_NET_TAP" down || true
|
||||
ip link delete "$VM_NET_TAP" || true
|
||||
closeBridge
|
||||
|
||||
# Clean up old files
|
||||
rm -f "$PASST_PID"
|
||||
rm -f "$DNSMASQ_PID"
|
||||
rm -f /etc/resolv.dnsmasq
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
checkOS() {
|
||||
|
||||
local kernel
|
||||
local os=""
|
||||
local if="macvlan"
|
||||
local kernel=""
|
||||
local iface="macvlan"
|
||||
|
||||
kernel=$(uname -a)
|
||||
|
||||
[[ "${kernel,,}" == *"darwin"* ]] && os="Docker Desktop for macOS"
|
||||
[[ "${kernel,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows"
|
||||
[[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
|
||||
[[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
|
||||
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
if="macvtap"
|
||||
iface="macvtap"
|
||||
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
|
||||
fi
|
||||
|
||||
if [ -n "$os" ]; then
|
||||
warn "you are using $os which does not support $if, please revert to bridge networking!"
|
||||
warn "you are using $os which does not support $iface, please revert to bridge networking!"
|
||||
fi
|
||||
|
||||
return 0
|
||||
@@ -383,8 +668,8 @@ getInfo() {
|
||||
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
|
||||
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
|
||||
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
|
||||
# Automaticly detect the default network interface
|
||||
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
|
||||
# Automatically detect the default network interface
|
||||
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1; exit }' /proc/net/route)
|
||||
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
|
||||
fi
|
||||
|
||||
@@ -393,29 +678,77 @@ getInfo() {
|
||||
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
|
||||
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); } 2>/dev/null || :
|
||||
[ -z "$IP" ] && [[ "$DHCP" != [Yy1]* ]] && error "Could not determine container IPv4 address!" && exit 26
|
||||
|
||||
if [[ "${NIC,,}" != "veth" ]]; then
|
||||
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $NIC"
|
||||
error "This container does not support host mode networking!" && exit 29
|
||||
IP6=""
|
||||
# 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
|
||||
|
||||
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
|
||||
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27
|
||||
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
|
||||
|
||||
if uname -a | grep -Eqi 'unraid|truenas'; then
|
||||
|
||||
# Check if host exposes the bridge-nf sysctl
|
||||
# (only visible if br_netfilter is loaded and /proc/sys is accessible)
|
||||
|
||||
if [ -z "$MTU" ]; then
|
||||
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
|
||||
BNF="/proc/sys/net/bridge/bridge-nf-call-iptables"
|
||||
|
||||
if [[ -r "$BNF" ]] && [[ "$(cat "$BNF")" != "0" ]]; then
|
||||
warn "external LAN clients may not be able to reach this container, because net.bridge.bridge-nf-call-iptables=1."
|
||||
warn "you can fix this issue by running 'sysctl -w net.bridge.bridge-nf-call-iptables=0' on the host system."
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
|
||||
checkOS
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [ "$MTU" -gt "1500" ]; then
|
||||
info "MTU size is too large: $MTU, ignoring..." && MTU="0"
|
||||
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 [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
||||
if [[ "$MTU" != "0" ]] && [ "$MTU" -lt "1500" ]; then
|
||||
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
|
||||
fi
|
||||
fi
|
||||
@@ -428,6 +761,7 @@ getInfo() {
|
||||
# Generate MAC address based on Docker container ID in hostname
|
||||
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
|
||||
echo "${VM_NET_MAC^^}" > "$file"
|
||||
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -443,18 +777,21 @@ getInfo() {
|
||||
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
|
||||
fi
|
||||
|
||||
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)
|
||||
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
||||
|
||||
IP6=""
|
||||
# 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)
|
||||
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
|
||||
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 | sed 's/^nameserver //' | paste -sd ',' | sed 's/,/, /g')
|
||||
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
|
||||
[ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
|
||||
echo "$IP" > /run/shm/qemu.ip
|
||||
echo "$IP" > "$QEMU_DIR"/qemu.ip
|
||||
echo "$nic" > "$QEMU_DIR"//qemu.nic
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -468,84 +805,82 @@ if [[ "$NETWORK" == [Nn]* ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
getInfo
|
||||
html "Initializing network..."
|
||||
msg="Initializing network..."
|
||||
html "$msg"
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
|
||||
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
|
||||
getInfo
|
||||
cleanUp
|
||||
|
||||
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!"
|
||||
fi
|
||||
|
||||
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
|
||||
info "Lingering interface will be removed..."
|
||||
ip link delete "$VM_NET_TAP" || true
|
||||
fi
|
||||
MSG="Booting DSM instance..."
|
||||
html "$MSG"
|
||||
|
||||
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
|
||||
configureDHCP || exit 20
|
||||
|
||||
MSG="Booting DSM instance..."
|
||||
html "$MSG"
|
||||
|
||||
else
|
||||
|
||||
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
|
||||
checkOS
|
||||
fi
|
||||
|
||||
if [[ "${WEB:-}" != [Nn]* ]]; then
|
||||
|
||||
# Shutdown nginx
|
||||
nginx -s stop 2> /dev/null
|
||||
fWait "nginx"
|
||||
|
||||
sleep 1.2
|
||||
closeWeb
|
||||
fi
|
||||
|
||||
if [[ "${NETWORK,,}" != "user"* ]]; then
|
||||
case "${NETWORK,,}" in
|
||||
"passt" | "slirp" | "user"* ) ;;
|
||||
"tap" | "tun" | "tuntap" | "y" | "" )
|
||||
|
||||
# Configure for tap interface
|
||||
if ! configureNAT; then
|
||||
# Configure tap interface
|
||||
if ! configureNAT; then
|
||||
|
||||
closeBridge
|
||||
NETWORK="user"
|
||||
msg="falling back to user-mode networking!"
|
||||
if [[ "$PODMAN" != [Yy1]* ]]; then
|
||||
msg="an error occured, $msg"
|
||||
else
|
||||
msg="podman detected, $msg"
|
||||
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\"."
|
||||
closeBridge
|
||||
NETWORK="user"
|
||||
|
||||
fi
|
||||
if [[ "$ROOTLESS" != [Yy1]* || "$DEBUG" == [Yy1]* ]]; then
|
||||
msg="falling back to user-mode networking!"
|
||||
msg="failed to setup NAT networking, $msg"
|
||||
warn "$msg"
|
||||
fi
|
||||
|
||||
fi
|
||||
fi ;;
|
||||
|
||||
if [[ "${NETWORK,,}" == "user"* ]]; then
|
||||
esac
|
||||
|
||||
# Configure for user-mode networking (slirp)
|
||||
configureUser || exit 24
|
||||
case "${NETWORK,,}" in
|
||||
"tap" | "tun" | "tuntap" | "y" | "" ) ;;
|
||||
"passt" | "user"* )
|
||||
|
||||
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, when you need to forward custom ports to DSM, add them to the \"USER_PORTS\" variable."
|
||||
fi ;;
|
||||
|
||||
esac
|
||||
|
||||
fi
|
||||
|
||||
|
||||
+97
-110
@@ -1,133 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: "${SHUTDOWN:="Y"}" # Graceful ACPI shutdown
|
||||
: "${TIMEOUT:="115"}" # QEMU termination timeout
|
||||
: "${API_TIMEOUT:="90"}" # External API call timeout
|
||||
|
||||
# Configure QEMU for graceful shutdown
|
||||
|
||||
API_CMD=6
|
||||
API_HOST="127.0.0.1:2210"
|
||||
: "${API_TIMEOUT:="50"}" # API Call timeout
|
||||
API_HOST="127.0.0.1:$COM_PORT"
|
||||
|
||||
# Configure QEMU for graceful shutdown
|
||||
|
||||
QEMU_TERM=""
|
||||
QEMU_PORT=7100
|
||||
: "${QEMU_TIMEOUT:="50"}" # QEMU Termination timeout
|
||||
QEMU_DIR="/run/shm"
|
||||
QEMU_PID="$QEMU_DIR/qemu.pid"
|
||||
QEMU_LOG="$QEMU_DIR/qemu.log"
|
||||
QEMU_OUT="$QEMU_DIR/qemu.out"
|
||||
QEMU_END="$QEMU_DIR/qemu.end"
|
||||
|
||||
if [[ "$KVM" == [Nn]* ]]; then
|
||||
API_TIMEOUT=$(( API_TIMEOUT*2 ))
|
||||
QEMU_TIMEOUT=$(( QEMU_TIMEOUT*2 ))
|
||||
fi
|
||||
|
||||
touch "$QEMU_LOG"
|
||||
|
||||
_trap() {
|
||||
func="$1" ; shift
|
||||
for sig ; do
|
||||
local func="$1"; shift
|
||||
local sig
|
||||
TRAP_PID=$BASHPID
|
||||
|
||||
for sig; do
|
||||
trap "$func $sig" "$sig"
|
||||
done
|
||||
}
|
||||
|
||||
app() {
|
||||
echo "$APP" && return 0
|
||||
}
|
||||
|
||||
finish() {
|
||||
|
||||
local pid
|
||||
local i=0
|
||||
local pid=""
|
||||
local reason=$1
|
||||
local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \
|
||||
"${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" )
|
||||
|
||||
touch "$QEMU_END"
|
||||
|
||||
if [ -s "$QEMU_PID" ]; then
|
||||
|
||||
pid=$(<"$QEMU_PID")
|
||||
echo && error "Forcefully terminating QEMU process, reason: $reason..."
|
||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||
|
||||
while isAlive "$pid"; do
|
||||
sleep 1
|
||||
# Workaround for zombie pid
|
||||
[ ! -s "$QEMU_PID" ] && break
|
||||
done
|
||||
fi
|
||||
|
||||
fKill "print.sh"
|
||||
fKill "host.bin"
|
||||
|
||||
closeNetwork
|
||||
|
||||
sleep 1
|
||||
echo && echo "❯ Shutdown completed!"
|
||||
|
||||
exit "$reason"
|
||||
}
|
||||
|
||||
terminal() {
|
||||
|
||||
local dev=""
|
||||
|
||||
if [ -s "$QEMU_OUT" ]; then
|
||||
|
||||
local msg
|
||||
msg=$(<"$QEMU_OUT")
|
||||
|
||||
if [ -n "$msg" ]; then
|
||||
|
||||
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
|
||||
echo "$msg"
|
||||
if read -r pid <"$QEMU_PID"; then
|
||||
if [ -n "$pid" ] && isAlive "$pid"; then
|
||||
local display="$reason"
|
||||
case "$reason" in
|
||||
129 ) display="SIGHUP" ;;
|
||||
130 ) display="SIGINT" ;;
|
||||
131 ) display="SIGQUIT" ;;
|
||||
134 ) display="SIGABRT" ;;
|
||||
143 ) display="SIGTERM" ;;
|
||||
esac
|
||||
error "Forcefully terminating $(app), reason: $display..."
|
||||
{ disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null
|
||||
fi
|
||||
|
||||
dev="${msg#*/dev/p}"
|
||||
dev="/dev/p${dev%% *}"
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -c "$dev" ]; then
|
||||
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
|
||||
dev="${dev#*serial0}"
|
||||
dev="${dev#*pty:}"
|
||||
dev="${dev%%$'\n'*}"
|
||||
dev="${dev%%$'\r'*}"
|
||||
mKill "${pids[@]}"
|
||||
fKill "print.sh"
|
||||
|
||||
closeNetwork
|
||||
|
||||
if ! waitPidFile "$QEMU_PID" 10; then
|
||||
warn "Timed out while waiting for $(app) to exit!"
|
||||
fi
|
||||
|
||||
if [ ! -c "$dev" ]; then
|
||||
error "Device '$dev' not found!"
|
||||
finish 34 && return 34
|
||||
fi
|
||||
|
||||
QEMU_TERM="$dev"
|
||||
return 0
|
||||
(( reason != 1 )) && echo && echo "❯ Shutdown completed!"
|
||||
exit "$reason"
|
||||
}
|
||||
|
||||
_graceful_shutdown() {
|
||||
graceful_shutdown() {
|
||||
|
||||
local code=$?
|
||||
local pid url response
|
||||
local sig="$1"
|
||||
local pid=""
|
||||
local code=0
|
||||
local start url response elapsed
|
||||
|
||||
set +e
|
||||
[[ $BASHPID != "$TRAP_PID" ]] && return
|
||||
|
||||
case "$sig" in
|
||||
SIGHUP) code=129 ;;
|
||||
SIGINT) code=130 ;;
|
||||
SIGQUIT) code=131 ;;
|
||||
SIGABRT) code=134 ;;
|
||||
SIGTERM) code=143 ;;
|
||||
esac
|
||||
|
||||
if [ -f "$QEMU_END" ]; then
|
||||
echo && info "Received $1 signal while already shutting down..."
|
||||
return
|
||||
fi
|
||||
|
||||
set +e
|
||||
start=$SECONDS
|
||||
touch "$QEMU_END"
|
||||
echo && info "Received $1 signal, sending shutdown command..."
|
||||
|
||||
if [ ! -s "$QEMU_PID" ]; then
|
||||
echo && error "QEMU PID file does not exist?"
|
||||
finish "$code" && return "$code"
|
||||
if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then
|
||||
warn "QEMU PID file ($QEMU_PID) does not exist?"
|
||||
finish "$code"
|
||||
fi
|
||||
|
||||
pid=$(<"$QEMU_PID")
|
||||
|
||||
if ! isAlive "$pid"; then
|
||||
echo && error "QEMU process does not exist?"
|
||||
finish "$code" && return "$code"
|
||||
if [ -z "$pid" ] || ! isAlive "$pid"; then
|
||||
warn "QEMU process with PID $pid does not exist?"
|
||||
finish "$code"
|
||||
fi
|
||||
|
||||
# Don't send the powerdown signal because vDSM ignores ACPI signals
|
||||
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
|
||||
# nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || :
|
||||
|
||||
# Send shutdown command to guest agent via serial port
|
||||
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
|
||||
@@ -142,44 +121,52 @@ _graceful_shutdown() {
|
||||
response="${response#*message\"\: \"}"
|
||||
[ -z "$response" ] && response="second signal"
|
||||
echo && error "Forcefully terminating because of: ${response%%\"*}"
|
||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||
{ kill -15 -- "$pid" || :; } 2>/dev/null
|
||||
|
||||
fi
|
||||
|
||||
local cnt=0
|
||||
local cnt=0 abort=0 factor=3 offset=3 min max name
|
||||
|
||||
while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
|
||||
[[ "$TIMEOUT" =~ ^[0-9]+$ ]] || TIMEOUT=115
|
||||
[ "$TIMEOUT" -ge 15 ] && factor=4 && offset=4
|
||||
[ "$TIMEOUT" -ge 30 ] && factor=5 && offset=5
|
||||
min=$(( factor + offset + 1 ))
|
||||
[ "$TIMEOUT" -lt "$min" ] && TIMEOUT="$min"
|
||||
elapsed=$(( SECONDS - start ))
|
||||
max=$(( TIMEOUT - offset - elapsed ))
|
||||
[ "$max" -lt "$factor" ] && max=$(( factor + 1 ))
|
||||
abort=$(( max - factor ))
|
||||
name="$(app)"
|
||||
|
||||
while [ "$cnt" -le "$max" ]; do
|
||||
|
||||
sleep 1 &
|
||||
local slp=$!
|
||||
|
||||
! isAlive "$pid" && break
|
||||
|
||||
sleep 1
|
||||
cnt=$((cnt+1))
|
||||
|
||||
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
|
||||
|
||||
# Workaround for zombie pid
|
||||
[ ! -s "$QEMU_PID" ] && break
|
||||
|
||||
if [ "$cnt" -ne "$abort" ]; then
|
||||
if [ "$cnt" -gt 0 ] && [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
info "Waiting for $name to shut down... ($cnt/$max)"
|
||||
fi
|
||||
else
|
||||
info "${name^} is still running, sending SIGTERM... ($cnt/$max)"
|
||||
{ kill -15 -- "$pid" || :; } 2>/dev/null
|
||||
fi
|
||||
|
||||
wait $slp
|
||||
(( cnt++ ))
|
||||
|
||||
done
|
||||
|
||||
if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
|
||||
echo && error "Shutdown timeout reached, aborting..."
|
||||
fi
|
||||
|
||||
finish "$code" && return "$code"
|
||||
finish "$code"
|
||||
}
|
||||
|
||||
MON_OPTS="\
|
||||
-pidfile $QEMU_PID \
|
||||
-name $PROCESS,process=$PROCESS,debug-threads=on \
|
||||
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
|
||||
[[ "$SHUTDOWN" != [Yy1]* ]] && return 0
|
||||
[ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
|
||||
|
||||
if [[ "$CONSOLE" != [Yy]* ]]; then
|
||||
|
||||
MON_OPTS+=" -daemonize -D $QEMU_LOG"
|
||||
|
||||
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
|
||||
|
||||
fi
|
||||
_trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT
|
||||
|
||||
return 0
|
||||
|
||||
+5
-7
@@ -11,6 +11,7 @@ error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: $1" "\E[0m\n" >&2; }
|
||||
|
||||
file="/run/shm/dsm.url"
|
||||
info="/run/shm/msg.html"
|
||||
driver="/run/shm/qemu.nic"
|
||||
page="/run/shm/index.html"
|
||||
address="/run/shm/qemu.ip"
|
||||
shutdown="/run/shm/qemu.end"
|
||||
@@ -56,11 +57,7 @@ do
|
||||
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
||||
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue
|
||||
|
||||
if [ -z "$ip" ]; then
|
||||
[[ "$DHCP" == [Yy1]* ]] && continue
|
||||
ip="20.20.20.21"
|
||||
fi
|
||||
|
||||
[ -z "$ip" ] && continue
|
||||
echo "$ip:$port" > $file
|
||||
|
||||
done
|
||||
@@ -69,7 +66,7 @@ done
|
||||
|
||||
location=$(<"$file")
|
||||
|
||||
if [[ "$location" != "20.20"* ]]; then
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
|
||||
msg="http://$location"
|
||||
title="<title>Virtual DSM</title>"
|
||||
@@ -88,10 +85,11 @@ if [[ "$location" != "20.20"* ]]; then
|
||||
|
||||
else
|
||||
|
||||
nic=$(<"$driver")
|
||||
ip=$(<"$address")
|
||||
port="${location##*:}"
|
||||
|
||||
if [[ "$ip" == "172."* ]]; then
|
||||
if [[ "${nic,,}" != "macvlan" ]]; then
|
||||
msg="port $port"
|
||||
else
|
||||
msg="http://$ip:$port"
|
||||
|
||||
+4
-51
@@ -3,7 +3,6 @@ set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: "${KVM:="Y"}"
|
||||
: "${HOST_CPU:=""}"
|
||||
: "${CPU_FLAGS:=""}"
|
||||
: "${CPU_MODEL:=""}"
|
||||
@@ -13,7 +12,7 @@ CLOCKSOURCE="tsc"
|
||||
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
||||
|
||||
if [ ! -f "$CLOCK" ]; then
|
||||
warn "file \"$CLOCK\" cannot not found?"
|
||||
warn "file \"$CLOCK\" cannot be found?"
|
||||
else
|
||||
result=$(<"$CLOCK")
|
||||
result="${result//[![:print:]]/}"
|
||||
@@ -26,52 +25,7 @@ else
|
||||
esac
|
||||
fi
|
||||
|
||||
if [[ "$KVM" == [Nn]* ]]; then
|
||||
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
|
||||
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
||||
|
||||
if [[ "$KVM" != [Nn]* ]]; then
|
||||
|
||||
@@ -79,9 +33,8 @@ if [[ "$KVM" != [Nn]* ]]; then
|
||||
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
|
||||
|
||||
if ! grep -qw "sse4_2" <<< "$flags"; then
|
||||
info "Your CPU does not have the SSE4 instruction set that Virtual DSM requires, it will be emulated..."
|
||||
[ -z "$CPU_MODEL" ] && CPU_MODEL="qemu64"
|
||||
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
|
||||
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
||||
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
||||
fi
|
||||
|
||||
if [ -z "$CPU_MODEL" ]; then
|
||||
|
||||
+21
-14
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
info="/run/shm/msg.html"
|
||||
|
||||
escape () {
|
||||
local s
|
||||
s=${1//&/\&}
|
||||
@@ -11,28 +13,33 @@ escape () {
|
||||
return 0
|
||||
}
|
||||
|
||||
file="$1"
|
||||
path="$1"
|
||||
total="$2"
|
||||
body=$(escape "$3")
|
||||
info="/run/shm/msg.html"
|
||||
|
||||
if [[ "$body" == *"..." ]]; then
|
||||
body="<p class=\"loading\">${body/.../}</p>"
|
||||
body="<p class=\"loading\">${body::-3}</p>"
|
||||
fi
|
||||
|
||||
while true
|
||||
do
|
||||
if [ -s "$file" ]; then
|
||||
bytes=$(du -sb "$file" | cut -f1)
|
||||
if (( bytes > 1000 )); then
|
||||
if [ -z "$total" ] || [[ "$total" == "0" ]]; then
|
||||
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
|
||||
else
|
||||
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
|
||||
size="$size%"
|
||||
fi
|
||||
echo "${body//(\[P\])/($size)}"> "$info"
|
||||
fi
|
||||
|
||||
if [ ! -s "$path" ] && [ ! -d "$path" ]; then
|
||||
bytes="0"
|
||||
else
|
||||
bytes=$(du -sb "$path" | cut -f1)
|
||||
fi
|
||||
|
||||
if (( bytes > 4096 )); then
|
||||
if [ -z "$total" ] || [[ "$total" == "0" ]] || [ "$bytes" -gt "$total" ]; then
|
||||
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
|
||||
else
|
||||
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
|
||||
size="$size%"
|
||||
fi
|
||||
[[ "$size" != "0.0%" ]] && echo "${body//(\[P\])/($size)}"> "$info"
|
||||
fi
|
||||
|
||||
sleep 1 & wait $!
|
||||
|
||||
done
|
||||
|
||||
+130
-125
@@ -2,19 +2,17 @@
|
||||
set -Eeuo pipefail
|
||||
|
||||
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
|
||||
|
||||
echo "❯ Starting $APP for Docker v$(</run/version)..."
|
||||
echo "❯ For support visit $SUPPORT"
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: "${TZ:=""}" # System local timezone
|
||||
: "${TZ:=""}" # System timezone
|
||||
: "${KVM:="Y"}" # KVM acceleration
|
||||
: "${DEBUG:="N"}" # Disable debugging mode
|
||||
: "${COUNTRY:=""}" # Country code for mirror
|
||||
: "${CONSOLE:="N"}" # Disable console mode
|
||||
: "${ALLOCATE:=""}" # Preallocate diskspace
|
||||
: "${ARGUMENTS:=""}" # Extra QEMU parameters
|
||||
: "${CPU_CORES:="2"}" # Amount of CPU cores
|
||||
@@ -25,15 +23,47 @@ echo "❯ For support visit $SUPPORT"
|
||||
|
||||
# Helper variables
|
||||
|
||||
ROOTLESS="N"
|
||||
PRIVILEGED="N"
|
||||
ENGINE="Docker"
|
||||
PROCESS="${APP,,}"
|
||||
PROCESS="${PROCESS// /-}"
|
||||
|
||||
if [ -f "/run/.containerenv" ]; then
|
||||
ENGINE="${container:-}"
|
||||
if [[ "${ENGINE,,}" == *"podman"* ]]; then
|
||||
ROOTLESS="Y"
|
||||
ENGINE="Podman"
|
||||
else
|
||||
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "❯ Starting $APP for $ENGINE v$(</etc/version)..."
|
||||
echo "❯ For support visit $SUPPORT"
|
||||
|
||||
# Get the capability bounding set
|
||||
CAP_BND=$(grep '^CapBnd:' /proc/$$/status | awk '{print $2}')
|
||||
CAP_BND=$(printf "%d" "0x${CAP_BND}")
|
||||
|
||||
# Get the last capability number
|
||||
LAST_CAP=$(cat /proc/sys/kernel/cap_last_cap)
|
||||
|
||||
# Calculate the maximum capability value
|
||||
MAX_CAP=$(((1 << (LAST_CAP + 1)) - 1))
|
||||
|
||||
if [ "${CAP_BND}" -eq "${MAX_CAP}" ]; then
|
||||
ROOTLESS="N"
|
||||
PRIVILEGED="Y"
|
||||
fi
|
||||
|
||||
INFO="/run/shm/msg.html"
|
||||
PAGE="/run/shm/index.html"
|
||||
TEMPLATE="/var/www/index.html"
|
||||
FOOTER1="$APP for Docker v$(</run/version)"
|
||||
FOOTER1="$APP for $ENGINE v$(</etc/version)"
|
||||
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
||||
|
||||
SOCKETS=1
|
||||
CPU=$(cpu)
|
||||
SYS=$(uname -r)
|
||||
HOST=$(hostname -s)
|
||||
@@ -42,33 +72,39 @@ MINOR=$(echo "$SYS" | cut -d '.' -f2)
|
||||
ARCH=$(dpkg --print-architecture)
|
||||
CORES=$(grep -c '^processor' /proc/cpuinfo)
|
||||
|
||||
if ! grep -qi "socket(s)" <<< "$(lscpu)"; then
|
||||
SOCKETS=1
|
||||
else
|
||||
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
|
||||
if grep -qi "socket(s)" <<< "$(lscpu)"; then
|
||||
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $2}')
|
||||
[ -z "${SOCKETS##*[!0-9]*}" ] && SOCKETS=1
|
||||
[ "$SOCKETS" -lt "1" ] && SOCKETS=1
|
||||
fi
|
||||
|
||||
CPU_CORES="${CPU_CORES// /}"
|
||||
[ -z "$CPU_CORES" ] && CPU_CORES=2
|
||||
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
|
||||
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
|
||||
[[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 ))
|
||||
[ -z "${CPU_CORES##*[!0-9]*}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
|
||||
|
||||
[ "$CPU_CORES" -lt "1" ] && CPU_CORES=1
|
||||
if [ "$CPU_CORES" -gt "$CORES" ]; then
|
||||
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of physical cores, so will be limited to ${CORES}."
|
||||
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available (${CORES}) and will be limited."
|
||||
CPU_CORES="$CORES"
|
||||
fi
|
||||
|
||||
# Check system
|
||||
|
||||
QEMU_DIR="/run/shm"
|
||||
|
||||
if [ ! -d "/dev/shm" ]; then
|
||||
error "Directory /dev/shm not found!" && exit 14
|
||||
else
|
||||
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
|
||||
[ ! -d "$QEMU_DIR" ] && ln -s /dev/shm "$QEMU_DIR"
|
||||
fi
|
||||
|
||||
QEMU_PID="$QEMU_DIR/qemu.pid"
|
||||
|
||||
# Check folder
|
||||
|
||||
if [[ "${COMMIT:-}" == [Yy1]* ]]; then
|
||||
STORAGE="/local"
|
||||
if [[ "${STORAGE,,}" != "/storage" ]]; then
|
||||
mkdir -p "$STORAGE"
|
||||
fi
|
||||
|
||||
@@ -77,7 +113,9 @@ if [ ! -d "$STORAGE" ]; then
|
||||
fi
|
||||
|
||||
if [ ! -w "$STORAGE" ]; then
|
||||
error "Storage folder ($STORAGE) is not writeable!" && exit 13
|
||||
msg="Storage folder ($STORAGE) is not writeable!"
|
||||
msg+=" If SELinux is active, you need to add the \":Z\" flag to the bind mount."
|
||||
error "$msg" && exit 13
|
||||
fi
|
||||
|
||||
# Check filesystem
|
||||
@@ -89,28 +127,27 @@ if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
||||
fi
|
||||
|
||||
# Read memory
|
||||
RAM_SPARE=500000000
|
||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
||||
|
||||
RAM_SPARE=500000000
|
||||
RAM_MINIMUM=136314880
|
||||
RAM_SIZE="${RAM_SIZE// /}"
|
||||
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
|
||||
[ -z "$RAM_SIZE" ] && RAM_SIZE="2G"
|
||||
|
||||
if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
|
||||
|
||||
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
|
||||
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
|
||||
fi
|
||||
|
||||
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
|
||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||
[ "$wanted" -lt "$RAM_MINIMUM" ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
|
||||
|
||||
if [[ "${RAM_SIZE,,}" == "max" ]]; then
|
||||
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE - RAM_SPARE ))
|
||||
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
|
||||
RAM_SIZE="${RAM_WANTED}G"
|
||||
fi
|
||||
|
||||
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
|
||||
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
|
||||
fi
|
||||
|
||||
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
|
||||
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
|
||||
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
|
||||
|
||||
# Print system info
|
||||
SYS="${SYS/-generic/}"
|
||||
FS="${FS/UNKNOWN //}"
|
||||
@@ -124,106 +161,74 @@ TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
|
||||
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
||||
echo
|
||||
|
||||
# Check available memory
|
||||
# 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
|
||||
if ! grep -qw "sse4_2" <<< "$flags"; then
|
||||
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
||||
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
||||
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
|
||||
|
||||
if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
|
||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
||||
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
|
||||
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
|
||||
info "$msg"
|
||||
fi
|
||||
|
||||
# Cleanup files
|
||||
rm -f /run/shm/qemu.*
|
||||
rm -f /run/shm/dsm.url
|
||||
rm -f "$QEMU_DIR"/qemu.*
|
||||
rm -f "$QEMU_DIR"/*.pid
|
||||
rm -f "$QEMU_DIR"/dsm.url
|
||||
|
||||
# Cleanup dirs
|
||||
rm -rf /tmp/dsm
|
||||
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
|
||||
|
||||
+8
-11
@@ -24,6 +24,8 @@ if [ -n "$HOST_MAC" ]; then
|
||||
|
||||
fi
|
||||
|
||||
HOST_PID="$QEMU_DIR/host.pid"
|
||||
|
||||
HOST_ARGS=()
|
||||
HOST_ARGS+=("-cpu=$CPU_CORES")
|
||||
HOST_ARGS+=("-cpu_arch=$HOST_CPU")
|
||||
@@ -37,15 +39,17 @@ if [[ "$HOST_DEBUG" == [Yy1]* ]]; then
|
||||
set -x
|
||||
./host.bin "${HOST_ARGS[@]}" &
|
||||
{ set +x; } 2>/dev/null
|
||||
echo "$!" > "$HOST_PID"
|
||||
echo
|
||||
else
|
||||
./host.bin "${HOST_ARGS[@]}" >/dev/null &
|
||||
echo "$!" > "$HOST_PID"
|
||||
fi
|
||||
|
||||
cnt=0
|
||||
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
|
||||
cnt=$((cnt + 1))
|
||||
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
|
||||
@@ -53,23 +57,16 @@ done
|
||||
|
||||
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
|
||||
cnt=$((cnt + 1))
|
||||
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
|
||||
done
|
||||
|
||||
# Configure serial ports
|
||||
|
||||
if [[ "$CONSOLE" != [Yy]* ]]; then
|
||||
SERIAL_OPTS="-serial pty"
|
||||
else
|
||||
SERIAL_OPTS="-serial mon:stdio"
|
||||
fi
|
||||
|
||||
SERIAL_OPTS+=" \
|
||||
SERIAL_OPTS="-serial mon:stdio \
|
||||
-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"
|
||||
|
||||
return 0
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: "${COM_PORT:="2210"}" # Comm port
|
||||
: "${WEB_PORT:="5000"}" # Webserver port
|
||||
: "${CHR_PORT:="12345"}" # Character port
|
||||
: "${WSD_PORT:="8004"}" # Websockets port
|
||||
|
||||
WEB_PID="/run/nginx.pid"
|
||||
WSD_PID="$QEMU_DIR/websocketd.pid"
|
||||
|
||||
cp -r /var/www/* "$QEMU_DIR"
|
||||
rm -f "$WSD_PID"
|
||||
rm -f "$WEB_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 "$!" > "$WSD_PID"
|
||||
|
||||
fi
|
||||
|
||||
return 0
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
lastmsg=""
|
||||
path="/run/shm/msg.html"
|
||||
|
||||
refresh() {
|
||||
|
||||
[ ! -f "$path" ] && return 0
|
||||
[ ! -s "$path" ] && return 0
|
||||
|
||||
msg=$(< "$path")
|
||||
msg="${msg%$'\n'}"
|
||||
|
||||
[ -z "$msg" ] && return 0
|
||||
[[ "$msg" == "$lastmsg" ]] && return 0
|
||||
|
||||
lastmsg="$msg"
|
||||
echo "s: $msg"
|
||||
return 0
|
||||
}
|
||||
|
||||
refresh
|
||||
|
||||
inotifywait -m "$path" |
|
||||
while read -r fp event fn; do
|
||||
case "${event,,}" in
|
||||
"modify"* ) refresh ;;
|
||||
"delete_self" ) echo "c: vnc" ;;
|
||||
esac
|
||||
done
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# You can override this hook to execute a script before startup!
|
||||
|
||||
return 0
|
||||
+184
-10
@@ -28,6 +28,7 @@ formatBytes() {
|
||||
|
||||
isAlive() {
|
||||
local pid="$1"
|
||||
[ -z "$pid" ] && return 1
|
||||
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
return 0
|
||||
@@ -36,23 +37,67 @@ isAlive() {
|
||||
return 1
|
||||
}
|
||||
|
||||
pKill() {
|
||||
waitPid() {
|
||||
local i=0
|
||||
local pid="$1"
|
||||
local timeout="${2:-10}"
|
||||
|
||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||
|
||||
while isAlive "$pid"; do
|
||||
while [ -n "$pid" ] && isAlive "$pid"; do
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
(( i >= timeout * 5 )) && return 1
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
waitPidFile() {
|
||||
local i=0
|
||||
local pid=""
|
||||
local file="$1"
|
||||
local timeout="${2:-10}"
|
||||
|
||||
[ ! -s "$file" ] && return 0
|
||||
! read -r pid <"$file" && return 0
|
||||
[ -z "$pid" ] && return 0
|
||||
|
||||
while [ -s "$file" ] && isAlive "$pid"; do
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
(( i >= timeout * 5 )) && return 1
|
||||
done
|
||||
|
||||
rm -f -- "$file"
|
||||
return 0
|
||||
}
|
||||
|
||||
pKill() {
|
||||
local pid="$1"
|
||||
local timeout="${2:-10}"
|
||||
|
||||
{ kill -15 -- "$pid" || :; } 2>/dev/null
|
||||
|
||||
if ! waitPid "$pid" "$timeout"; then
|
||||
warn "Timed out while waiting for PID $pid"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
fWait() {
|
||||
local i=0
|
||||
local name="$1"
|
||||
local timeout="${2:-10}"
|
||||
|
||||
[ -z "$name" ] && return 0
|
||||
|
||||
while pgrep -f -l "$name" >/dev/null; do
|
||||
sleep 0.2
|
||||
i=$((i + 1))
|
||||
if (( i >= timeout * 5 )); then
|
||||
warn "Timed out while waiting for process: $name"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
@@ -60,9 +105,75 @@ fWait() {
|
||||
|
||||
fKill() {
|
||||
local name="$1"
|
||||
local timeout="${2:-10}"
|
||||
|
||||
{ pkill -f "$name" || true; } 2>/dev/null
|
||||
fWait "$name"
|
||||
[ -z "$name" ] && return 0
|
||||
|
||||
{ pkill -f "$name" || :; } 2>/dev/null
|
||||
fWait "$name" "$timeout"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
sKill() {
|
||||
local pid=""
|
||||
local file="$1"
|
||||
|
||||
[ ! -s "$file" ] && return 0
|
||||
! read -r pid <"$file" && return 0
|
||||
[ -z "$pid" ] && return 0
|
||||
|
||||
if isAlive "$pid"; then
|
||||
{ kill -15 -- "$pid" || :; } 2>/dev/null
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
mKill() {
|
||||
local timeout=10
|
||||
local files=("$@")
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
sKill "$file"
|
||||
done
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
if ! waitPidFile "$file" "$timeout"; then
|
||||
warn "Timed out while waiting for PID file: $file"
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
setOwner() {
|
||||
local file="$1"
|
||||
local dir uid gid
|
||||
|
||||
[ ! -f "$file" ] && return 1
|
||||
|
||||
dir=$(dirname -- "$file")
|
||||
uid=$(stat -c '%u' "$dir")
|
||||
gid=$(stat -c '%g' "$dir")
|
||||
|
||||
! chown "$uid:$gid" "$file" && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
makeDir() {
|
||||
local path="$1"
|
||||
local dir uid gid
|
||||
|
||||
[ -d "$path" ] && return 0
|
||||
! mkdir -p "$path" && return 1
|
||||
|
||||
dir=$(dirname -- "$path")
|
||||
uid=$(stat -c '%u' "$dir")
|
||||
gid=$(stat -c '%g' "$dir")
|
||||
|
||||
! chown "$uid:$gid" "$path" && return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
@@ -123,11 +234,11 @@ cpu() {
|
||||
fi
|
||||
|
||||
cpu="${cpu// CPU/}"
|
||||
cpu="${cpu// [0-9] Core}"
|
||||
cpu="${cpu// [0-9][0-9] Core}"
|
||||
cpu="${cpu// [0-9][0-9][0-9] Core}"
|
||||
cpu="${cpu// [0-9]th Gen}"
|
||||
cpu="${cpu// [0-9][0-9]th Gen}"
|
||||
cpu="${cpu// [0-9][0-9] Core}"
|
||||
cpu="${cpu// [0-9] Core}"
|
||||
cpu="${cpu//[0-9][0-9]th Gen }"
|
||||
cpu="${cpu//[0-9]th Gen }"
|
||||
cpu="${cpu// Processor/}"
|
||||
cpu="${cpu// Quad core/}"
|
||||
cpu="${cpu// Dual core/}"
|
||||
@@ -147,6 +258,8 @@ cpu() {
|
||||
|
||||
hasDisk() {
|
||||
|
||||
[[ "${DISK_DISABLE:-}" == [Yy1]* ]] && return 1
|
||||
|
||||
[ -b "/disk" ] && return 0
|
||||
[ -b "/disk1" ] && return 0
|
||||
[ -b "/dev/disk1" ] && return 0
|
||||
@@ -159,4 +272,65 @@ hasDisk() {
|
||||
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
|
||||
|
||||
@@ -27,4 +27,18 @@ server {
|
||||
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/;
|
||||
}
|
||||
}
|
||||
|
||||
+110
-32
@@ -1,4 +1,5 @@
|
||||
var request;
|
||||
var booting = false;
|
||||
var interval = 1000;
|
||||
|
||||
function getInfo() {
|
||||
@@ -6,11 +7,14 @@ function getInfo() {
|
||||
var url = "msg.html";
|
||||
|
||||
try {
|
||||
if (request) {
|
||||
request.abort();
|
||||
}
|
||||
|
||||
if (window.XMLHttpRequest) {
|
||||
request = new XMLHttpRequest();
|
||||
} else {
|
||||
throw "XMLHttpRequest not available!";
|
||||
throw new Error("XMLHttpRequest not available!");
|
||||
}
|
||||
|
||||
request.onreadystatechange = processInfo;
|
||||
@@ -18,22 +22,49 @@ function getInfo() {
|
||||
request.send();
|
||||
|
||||
} catch (e) {
|
||||
var err = "Error: " + e.message;
|
||||
console.log(err);
|
||||
setError(err);
|
||||
setError("Error: " + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
try {
|
||||
|
||||
if (request.readyState != 4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var msg = request.responseText;
|
||||
if (msg == null || msg.length == 0) {
|
||||
setInfo("Booting DSM instance", true);
|
||||
schedule();
|
||||
|
||||
if (booting) {
|
||||
schedule();
|
||||
return true;
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -43,20 +74,11 @@ function processInfo() {
|
||||
if (msg.toLowerCase().indexOf("<html>") !== -1) {
|
||||
notFound = true;
|
||||
} else {
|
||||
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;
|
||||
} else {
|
||||
setInfo(msg);
|
||||
processMsg(msg);
|
||||
if (msg.toLowerCase().indexOf("href=") == -1) {
|
||||
schedule();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,44 +89,60 @@ function processInfo() {
|
||||
}
|
||||
|
||||
setError("Error: Received statuscode " + request.status);
|
||||
schedule();
|
||||
return false;
|
||||
|
||||
} catch (e) {
|
||||
var err = "Error: " + e.message;
|
||||
console.log(err);
|
||||
setError(err);
|
||||
setError("Error: " + e.message);
|
||||
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 {
|
||||
|
||||
if (msg == null || msg.length == 0) {
|
||||
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;
|
||||
if (!error) {
|
||||
el.style.visibility = 'visible';
|
||||
spin.style.visibility = 'visible';
|
||||
} else {
|
||||
el.style.visibility = 'hidden';
|
||||
spin.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
var p = "<p class=\"loading\">";
|
||||
loading = !!loading;
|
||||
if (loading) {
|
||||
msg = "<p class=\"loading\">" + msg + "</p>";
|
||||
msg = p + msg + "</p>";
|
||||
}
|
||||
|
||||
el = document.getElementById("info");
|
||||
|
||||
if (el.innerHTML != msg) {
|
||||
el.innerHTML = msg;
|
||||
if (msg.includes(p)) {
|
||||
if (el.innerHTML.includes(p)) {
|
||||
el.getElementsByClassName('loading')[0].textContent = extractContent(msg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
el.innerHTML = msg;
|
||||
return true;
|
||||
|
||||
} catch (e) {
|
||||
@@ -114,6 +152,7 @@ function setInfo(msg, loading, error) {
|
||||
}
|
||||
|
||||
function setError(text) {
|
||||
console.warn(text);
|
||||
return setInfo(text, false, true);
|
||||
}
|
||||
|
||||
@@ -123,8 +162,47 @@ function schedule() {
|
||||
|
||||
function reload() {
|
||||
setTimeout(() => {
|
||||
document.location.reload();
|
||||
window.location.reload();
|
||||
}, 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();
|
||||
connect();
|
||||
|
||||
Reference in New Issue
Block a user