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
|
.dockerignore
|
||||||
|
.devcontainer
|
||||||
.git
|
.git
|
||||||
.github
|
.github
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|||||||
@@ -22,13 +22,13 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
-
|
-
|
||||||
name: Docker metadata
|
name: Docker metadata
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
context: git
|
context: git
|
||||||
images: |
|
images: |
|
||||||
@@ -43,23 +43,23 @@ jobs:
|
|||||||
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
|
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
|
||||||
-
|
-
|
||||||
name: Set up Docker Buildx
|
name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v4
|
||||||
-
|
-
|
||||||
name: Login into Docker Hub
|
name: Login into Docker Hub
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Login to GitHub Container Registry
|
name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Build Docker image
|
name: Build Docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
-
|
-
|
||||||
name: Checkout
|
name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
-
|
-
|
||||||
name: Run ShellCheck
|
name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
@@ -22,3 +22,8 @@ jobs:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
ignore: DL3008,DL3003,DL3006,DL3013
|
ignore: DL3008,DL3003,DL3006,DL3013
|
||||||
failure-threshold: warning
|
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:
|
dockerHubDescription:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
-
|
||||||
-
|
name: Checkout repo
|
||||||
name: Docker Hub Description
|
uses: actions/checkout@v6
|
||||||
uses: peter-evans/dockerhub-description@v4
|
-
|
||||||
with:
|
name: Docker Hub Description
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
uses: peter-evans/dockerhub-description@v5
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
with:
|
||||||
repository: ${{ secrets.DOCKERHUB_REPO }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
short-description: ${{ github.event.repository.description }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
readme-filepath: ./readme.md
|
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:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- '**/*.sh'
|
|
||||||
- 'Dockerfile'
|
|
||||||
- '.github/workflows/test.yml'
|
|
||||||
- '.github/workflows/check.yml'
|
|
||||||
|
|
||||||
name: "Test"
|
name: "Test"
|
||||||
permissions: {}
|
permissions: {}
|
||||||
|
|||||||
+9
-2
@@ -11,8 +11,11 @@ FROM qemux/qemu-host:2.05 AS builder
|
|||||||
|
|
||||||
FROM debian:trixie-slim
|
FROM debian:trixie-slim
|
||||||
|
|
||||||
|
ARG TARGETARCH
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG VERSION_ARG="0.0"
|
ARG VERSION_ARG="0.0"
|
||||||
|
ARG VERSION_PASST="2026_05_26"
|
||||||
|
|
||||||
ARG DEBCONF_NOWARNINGS="yes"
|
ARG DEBCONF_NOWARNINGS="yes"
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
||||||
@@ -36,23 +39,27 @@ RUN set -eu && \
|
|||||||
xz-utils \
|
xz-utils \
|
||||||
iptables \
|
iptables \
|
||||||
iproute2 \
|
iproute2 \
|
||||||
apt-utils \
|
|
||||||
dnsmasq \
|
dnsmasq \
|
||||||
fakeroot \
|
fakeroot \
|
||||||
|
apt-utils \
|
||||||
net-tools \
|
net-tools \
|
||||||
e2fsprogs \
|
e2fsprogs \
|
||||||
qemu-utils \
|
qemu-utils \
|
||||||
|
websocketd \
|
||||||
iputils-ping \
|
iputils-ping \
|
||||||
|
inotify-tools \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
netcat-openbsd \
|
netcat-openbsd \
|
||||||
qemu-system-x86 && \
|
qemu-system-x86 && \
|
||||||
|
wget "https://github.com/qemus/passt/releases/download/v${VERSION_PASST}/passt_${VERSION_PASST}_${TARGETARCH}.deb" -O /tmp/passt.deb -q --timeout=10 && \
|
||||||
|
dpkg -i /tmp/passt.deb && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
|
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
|
||||||
mkdir -p /etc/qemu && \
|
mkdir -p /etc/qemu && \
|
||||||
echo "allow br0" > /etc/qemu/bridge.conf && \
|
echo "allow br0" > /etc/qemu/bridge.conf && \
|
||||||
unlink /etc/nginx/sites-enabled/default && \
|
unlink /etc/nginx/sites-enabled/default && \
|
||||||
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \
|
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/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
COPY --chmod=755 ./src /run/
|
COPY --chmod=755 ./src /run/
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ services:
|
|||||||
##### Via Docker CLI:
|
##### Via Docker CLI:
|
||||||
|
|
||||||
```bash
|
```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:
|
##### 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?
|
### 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:
|
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
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
: "${DHCP:="N"}"
|
||||||
: "${NETWORK:="Y"}"
|
: "${NETWORK:="Y"}"
|
||||||
|
|
||||||
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
|
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down..." && exit 1
|
||||||
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
|
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet..." && exit 0
|
||||||
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled.." && exit 0
|
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled." && exit 0
|
||||||
|
|
||||||
file="/run/shm/dsm.url"
|
file="/run/shm/dsm.url"
|
||||||
address="/run/shm/qemu.ip"
|
address="/run/shm/qemu.ip"
|
||||||
|
gateway="/run/shm/qemu.gw"
|
||||||
|
|
||||||
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
|
[ ! -s "$file" ] && echo "DSM has not enabled networking yet..." && exit 1
|
||||||
|
|
||||||
location=$(<"$file")
|
location=$(<"$file")
|
||||||
|
|
||||||
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
||||||
|
|
||||||
if [[ "$location" == "20.20"* ]]; then
|
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||||
ip="20.20.20.1"
|
ip=$(<"$address")
|
||||||
|
echo "Failed to reach DSM at http://$location"
|
||||||
|
else
|
||||||
|
ip=$(<"$gateway")
|
||||||
port="${location##*:}"
|
port="${location##*:}"
|
||||||
echo "Failed to reach DSM at port $port"
|
echo "Failed to reach DSM at port $port"
|
||||||
else
|
|
||||||
echo "Failed to reach DSM at http://$location"
|
|
||||||
ip=$(<"$address")
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1
|
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1
|
||||||
|
|||||||
+1
-28
@@ -3,6 +3,7 @@ set -Eeuo pipefail
|
|||||||
|
|
||||||
DEF_OPTS="-nodefaults -boot strict=on"
|
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')
|
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"
|
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}"
|
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"
|
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="$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 ' ')
|
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
|
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 "$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
|
[ ! -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() {
|
fmt2ext() {
|
||||||
local DISK_FMT=$1
|
local DISK_FMT="$1"
|
||||||
|
|
||||||
case "${DISK_FMT,,}" in
|
case "${DISK_FMT,,}" in
|
||||||
qcow2)
|
qcow2)
|
||||||
@@ -34,7 +42,7 @@ fmt2ext() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ext2fmt() {
|
ext2fmt() {
|
||||||
local DISK_EXT=$1
|
local DISK_EXT="$1"
|
||||||
|
|
||||||
case "${DISK_EXT,,}" in
|
case "${DISK_EXT,,}" in
|
||||||
qcow2)
|
qcow2)
|
||||||
@@ -50,7 +58,7 @@ ext2fmt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSize() {
|
getSize() {
|
||||||
local DISK_FILE=$1
|
local DISK_FILE="$1"
|
||||||
local DISK_EXT DISK_FMT
|
local DISK_EXT DISK_FMT
|
||||||
|
|
||||||
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
||||||
@@ -70,7 +78,7 @@ getSize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
isCow() {
|
isCow() {
|
||||||
local FS=$1
|
local FS="$1"
|
||||||
|
|
||||||
if [[ "${FS,,}" == "btrfs" ]]; then
|
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||||
return 0
|
return 0
|
||||||
@@ -80,7 +88,7 @@ isCow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
supportsDirect() {
|
supportsDirect() {
|
||||||
local FS=$1
|
local FS="$1"
|
||||||
|
|
||||||
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
||||||
return 1
|
return 1
|
||||||
@@ -91,17 +99,17 @@ supportsDirect() {
|
|||||||
|
|
||||||
createDisk() {
|
createDisk() {
|
||||||
|
|
||||||
local DISK_FILE=$1
|
local DISK_FILE="$1"
|
||||||
local DISK_SPACE=$2
|
local DISK_SPACE="$2"
|
||||||
local DISK_DESC=$3
|
local DISK_DESC="$3"
|
||||||
local DISK_FMT=$4
|
local DISK_FMT="$4"
|
||||||
local FS=$5
|
local FS="$5"
|
||||||
local DATA_SIZE DIR SPACE GB FA
|
local DATA_SIZE DIR SPACE GB FA
|
||||||
|
|
||||||
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
|
||||||
|
|
||||||
rm -f "$DISK_FILE"
|
rm -f "$DISK_FILE"
|
||||||
|
|
||||||
|
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||||
|
|
||||||
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||||
|
|
||||||
# Check free diskspace
|
# 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 "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
|
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
html "Creating a $DISK_DESC image..."
|
html "Creating a $DISK_DESC image..."
|
||||||
@@ -177,16 +186,16 @@ createDisk() {
|
|||||||
|
|
||||||
resizeDisk() {
|
resizeDisk() {
|
||||||
|
|
||||||
local DISK_FILE=$1
|
local DISK_FILE="$1"
|
||||||
local DISK_SPACE=$2
|
local DISK_SPACE="$2"
|
||||||
local DISK_DESC=$3
|
local DISK_DESC="$3"
|
||||||
local DISK_FMT=$4
|
local DISK_FMT="$4"
|
||||||
local FS=$5
|
local FS="$5"
|
||||||
local CUR_SIZE DATA_SIZE DIR SPACE GB
|
local CUR_SIZE DATA_SIZE DIR SPACE GB
|
||||||
|
|
||||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||||
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
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
|
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
|
||||||
|
|
||||||
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
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 "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
|
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
GB=$(formatBytes "$CUR_SIZE")
|
GB=$(formatBytes "$CUR_SIZE")
|
||||||
@@ -245,26 +255,28 @@ resizeDisk() {
|
|||||||
|
|
||||||
convertDisk() {
|
convertDisk() {
|
||||||
|
|
||||||
local SOURCE_FILE=$1
|
local SOURCE_FILE="$1"
|
||||||
local SOURCE_FMT=$2
|
local SOURCE_FMT="$2"
|
||||||
local DST_FILE=$3
|
local DST_FILE="$3"
|
||||||
local DST_FMT=$4
|
local DST_FMT="$4"
|
||||||
local DISK_BASE=$5
|
local DISK_BASE="$5"
|
||||||
local DISK_DESC=$6
|
local DISK_DESC="$6"
|
||||||
local FS=$7
|
local FS="$7"
|
||||||
|
|
||||||
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
|
[ -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"
|
local TMP_FILE="$DISK_BASE.tmp"
|
||||||
rm -f "$TMP_FILE"
|
rm -f "$TMP_FILE"
|
||||||
|
|
||||||
|
local DIR FA
|
||||||
|
DIR=$(dirname "$TMP_FILE")
|
||||||
|
|
||||||
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||||
|
|
||||||
local DIR CUR_SIZE SPACE GB
|
local CUR_SIZE SPACE GB
|
||||||
|
|
||||||
# Check free diskspace
|
# Check free diskspace
|
||||||
DIR=$(dirname "$TMP_FILE")
|
|
||||||
CUR_SIZE=$(getSize "$SOURCE_FILE")
|
CUR_SIZE=$(getSize "$SOURCE_FILE")
|
||||||
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
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 "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
|
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local msg="Converting $DISK_DESC to $DST_FMT"
|
local msg="Converting $DISK_DESC to $DST_FMT"
|
||||||
@@ -327,31 +340,31 @@ convertDisk() {
|
|||||||
|
|
||||||
checkFS () {
|
checkFS () {
|
||||||
|
|
||||||
local FS=$1
|
local FS="$1"
|
||||||
local DISK_FILE=$2
|
local DISK_FILE="$2"
|
||||||
local DISK_DESC=$3
|
local DISK_DESC="$3"
|
||||||
local DIR FA
|
local DIR FA
|
||||||
|
|
||||||
DIR=$(dirname "$DISK_FILE")
|
DIR=$(dirname "$DISK_FILE")
|
||||||
[ ! -d "$DIR" ] && return 0
|
[ ! -d "$DIR" ] && return 0
|
||||||
|
|
||||||
if [[ "${FS,,}" == "overlay"* ]]; then
|
if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
|
||||||
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
warn "the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${FS,,}" == "fuse"* ]]; then
|
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
|
fi
|
||||||
|
|
||||||
if ! supportsDirect "$FS"; then
|
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
|
fi
|
||||||
|
|
||||||
if isCow "$FS"; then
|
if isCow "$FS"; then
|
||||||
if [ -f "$DISK_FILE" ]; then
|
if [ -f "$DISK_FILE" ]; then
|
||||||
FA=$(lsattr "$DISK_FILE")
|
FA=$(lsattr "$DISK_FILE")
|
||||||
if [[ "$FA" != *"C"* ]]; then
|
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
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -361,15 +374,15 @@ checkFS () {
|
|||||||
|
|
||||||
createDevice () {
|
createDevice () {
|
||||||
|
|
||||||
local DISK_FILE=$1
|
local DISK_FILE="$1"
|
||||||
local DISK_TYPE=$2
|
local DISK_TYPE="$2"
|
||||||
local DISK_INDEX=$3
|
local DISK_INDEX="$3"
|
||||||
local DISK_ADDRESS=$4
|
local DISK_ADDRESS="$4"
|
||||||
local DISK_FMT=$5
|
local DISK_FMT="$5"
|
||||||
local DISK_IO=$6
|
local DISK_IO="$6"
|
||||||
local DISK_CACHE=$7
|
local DISK_CACHE="$7"
|
||||||
local DISK_SERIAL=$8
|
local DISK_SERIAL="$8"
|
||||||
local DISK_SECTORS=$9
|
local DISK_SECTORS="$9"
|
||||||
local DISK_ID="data$DISK_INDEX"
|
local DISK_ID="data$DISK_INDEX"
|
||||||
|
|
||||||
local index=""
|
local index=""
|
||||||
@@ -413,18 +426,32 @@ createDevice () {
|
|||||||
return 0
|
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 () {
|
addDisk () {
|
||||||
|
|
||||||
local DISK_BASE=$1
|
local DISK_BASE="$1"
|
||||||
local DISK_TYPE=$2
|
local DISK_TYPE="$2"
|
||||||
local DISK_DESC=$3
|
local DISK_DESC="$3"
|
||||||
local DISK_SPACE=$4
|
local DISK_SPACE="$4"
|
||||||
local DISK_INDEX=$5
|
local DISK_INDEX="$5"
|
||||||
local DISK_ADDRESS=$6
|
local DISK_ADDRESS="$6"
|
||||||
local DISK_FMT=$7
|
local DISK_FMT="$7"
|
||||||
local DISK_IO=$8
|
local DISK_IO="$8"
|
||||||
local DISK_CACHE=$9
|
local DISK_CACHE="$9"
|
||||||
local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
|
local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE LEFT FREE USED
|
||||||
|
|
||||||
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
||||||
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
||||||
@@ -432,8 +459,25 @@ addDisk () {
|
|||||||
DIR=$(dirname "$DISK_FILE")
|
DIR=$(dirname "$DISK_FILE")
|
||||||
[ ! -d "$DIR" ] && return 0
|
[ ! -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// /}"
|
SPACE="${DISK_SPACE// /}"
|
||||||
[ -z "$SPACE" ] && SPACE="16G"
|
[ -z "$SPACE" ] && SPACE="256G"
|
||||||
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
||||||
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/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")
|
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
||||||
|
|
||||||
if (( DATA_SIZE < 6442450944 )); then
|
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
|
fi
|
||||||
|
|
||||||
FS=$(stat -f -c %T "$DIR")
|
FS=$(stat -f -c %T "$DIR")
|
||||||
@@ -455,7 +499,7 @@ addDisk () {
|
|||||||
DISK_CACHE="writeback"
|
DISK_CACHE="writeback"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! [ -s "$DISK_FILE" ] ; then
|
if [ ! -s "$DISK_FILE" ] ; then
|
||||||
|
|
||||||
if [[ "${DISK_FMT,,}" != "raw" ]]; then
|
if [[ "${DISK_FMT,,}" != "raw" ]]; then
|
||||||
PREV_FMT="raw"
|
PREV_FMT="raw"
|
||||||
@@ -468,6 +512,7 @@ addDisk () {
|
|||||||
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
|
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
|
||||||
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
|
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -s "$DISK_FILE" ]; then
|
if [ -s "$DISK_FILE" ]; then
|
||||||
@@ -475,7 +520,18 @@ addDisk () {
|
|||||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||||
|
|
||||||
if (( DATA_SIZE > CUR_SIZE )); then
|
if (( DATA_SIZE > CUR_SIZE )); then
|
||||||
|
|
||||||
resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
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
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
@@ -484,6 +540,38 @@ addDisk () {
|
|||||||
|
|
||||||
fi
|
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" "" "")
|
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "")
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -491,31 +579,34 @@ addDisk () {
|
|||||||
|
|
||||||
addDevice () {
|
addDevice () {
|
||||||
|
|
||||||
local DISK_DEV=$1
|
local DISK_DEV="$1"
|
||||||
local DISK_TYPE=$2
|
local DISK_TYPE="$2"
|
||||||
local DISK_INDEX=$3
|
local DISK_INDEX="$3"
|
||||||
local DISK_ADDRESS=$4
|
local DISK_ADDRESS="$4"
|
||||||
|
|
||||||
[ -z "$DISK_DEV" ] && return 0
|
[ -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
|
[ ! -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 sectors=""
|
||||||
local result logical physical
|
local dev_type=""
|
||||||
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
|
dev_type=$(lsblk -no TYPE "$DISK_DEV" 2>/dev/null | head -n1)
|
||||||
logical="${result%% *}"
|
|
||||||
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
|
|
||||||
physical="${physical%% *}"
|
|
||||||
|
|
||||||
if [ -n "$physical" ]; then
|
# Only detect and apply sector sizes for partitions, not whole disks
|
||||||
if [[ "$physical" == "512" || "$physical" == "4096" ]]; then
|
# Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk
|
||||||
if [[ "$physical" == "4096" ]]; then
|
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"
|
sectors=",logical_block_size=$logical,physical_block_size=$physical"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
warn "Unknown physical sector size: $physical for $DISK_DEV"
|
warn "Failed to determine the sector size for $DISK_DEV"
|
||||||
fi
|
fi
|
||||||
else
|
|
||||||
warn "Failed to determine the sector size for $DISK_DEV"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
|
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
|
||||||
@@ -523,11 +614,16 @@ addDevice () {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
html "Initializing disks..."
|
|
||||||
|
|
||||||
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
|
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
|
||||||
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
|
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
|
||||||
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
|
[ -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
|
case "${DISK_TYPE,,}" in
|
||||||
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
"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 "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
|
||||||
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
|
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
|
||||||
|
|
||||||
|
if [[ "$DISK_DISABLE" == [Yy1]* ]]; then
|
||||||
|
finishDisks
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
DISK1_FILE="$STORAGE/${DISK_NAME}"
|
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"
|
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"
|
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"
|
DISK4_FILE="/storage4/${DISK_NAME}4"
|
||||||
|
|
||||||
: "${DISK2_SIZE:=""}"
|
: "${DISK2_SIZE:=""}"
|
||||||
@@ -627,7 +699,6 @@ else
|
|||||||
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DISK_OPTS+=" -object iothread,id=io2"
|
finishDisks
|
||||||
|
|
||||||
html "Initialized disks successfully..."
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64"
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
msg="Configuring display drivers..."
|
||||||
|
html "$msg"
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
||||||
|
|
||||||
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
|
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
|
||||||
DISPLAY_OPTS+=" -vga $VGA"
|
DISPLAY_OPTS+=" -vga $VGA"
|
||||||
|
|
||||||
|
|||||||
+18
-9
@@ -1,13 +1,16 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
: "${PLATFORM:="x64"}"
|
||||||
: "${APP:="Virtual DSM"}"
|
: "${APP:="Virtual DSM"}"
|
||||||
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
|
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
|
||||||
|
|
||||||
cd /run
|
cd /run
|
||||||
|
|
||||||
|
. start.sh # Startup hook
|
||||||
. utils.sh # Load functions
|
. utils.sh # Load functions
|
||||||
. reset.sh # Initialize system
|
. reset.sh # Initialize system
|
||||||
|
. server.sh # Start webserver
|
||||||
. install.sh # Run installation
|
. install.sh # Run installation
|
||||||
. disk.sh # Initialize disks
|
. disk.sh # Initialize disks
|
||||||
. display.sh # Initialize graphics
|
. display.sh # Initialize graphics
|
||||||
@@ -15,23 +18,29 @@ cd /run
|
|||||||
. proc.sh # Initialize processor
|
. proc.sh # Initialize processor
|
||||||
. serial.sh # Initialize serialport
|
. serial.sh # Initialize serialport
|
||||||
. power.sh # Configure shutdown
|
. power.sh # Configure shutdown
|
||||||
|
. memory.sh # Check available memory
|
||||||
. config.sh # Configure arguments
|
. config.sh # Configure arguments
|
||||||
|
. finish.sh # Finish initialization
|
||||||
|
|
||||||
trap - ERR
|
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..."
|
info "Booting $APP using QEMU v$version..."
|
||||||
|
|
||||||
if [[ "$CONSOLE" == [Yy]* ]]; then
|
if [[ "$SHUTDOWN" != [Yy1]* ]]; then
|
||||||
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
|
exec "${cmd[@]}" ${ARGS:+ $ARGS}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || :
|
if [ ! -t 1 ] || [ ! -c /dev/tty ]; then
|
||||||
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
|
"${cmd[@]}" ${ARGS:+ $ARGS} &
|
||||||
|
else
|
||||||
|
"${cmd[@]}" ${ARGS:+ $ARGS} </dev/tty >/dev/tty &
|
||||||
|
fi
|
||||||
|
|
||||||
terminal
|
rc=0
|
||||||
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
wait $! || rc=$?
|
||||||
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
|
[ -f "$QEMU_END" ] && exit "$rc"
|
||||||
|
|
||||||
sleep 1 & wait $!
|
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)
|
BASE=$(basename "$URL" .pat)
|
||||||
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
|
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
|
||||||
BASE=$(basename "${URL%%\?*}" .pat)
|
BASE=$(basename "${URL%%\?*}" .pat)
|
||||||
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
|
printf -v BASE '%b' "${BASE//%/\\x}"
|
||||||
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
BASE="${BASE//[!A-Za-z0-9._-]/_}"
|
||||||
fi
|
fi
|
||||||
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then
|
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then
|
||||||
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
|
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
|
||||||
@@ -65,8 +65,8 @@ fi
|
|||||||
|
|
||||||
if [ ! -s "$FILE" ]; then
|
if [ ! -s "$FILE" ]; then
|
||||||
BASE=$(basename "${URL%%\?*}" .pat)
|
BASE=$(basename "${URL%%\?*}" .pat)
|
||||||
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
|
printf -v BASE '%b' "${BASE//%/\\x}"
|
||||||
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
BASE="${BASE//[!A-Za-z0-9._-]/_}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
|
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
|
||||||
@@ -80,16 +80,16 @@ rm -f "$STORAGE/$BASE.system.img"
|
|||||||
# Check filesystem
|
# Check filesystem
|
||||||
FS=$(stat -f -c %T "$STORAGE")
|
FS=$(stat -f -c %T "$STORAGE")
|
||||||
|
|
||||||
if [[ "${FS,,}" == "overlay"* ]]; then
|
if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
|
||||||
info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
|
warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${FS,,}" == "fuse"* ]]; then
|
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
|
fi
|
||||||
|
|
||||||
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
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
|
fi
|
||||||
|
|
||||||
if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then
|
if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then
|
||||||
@@ -98,6 +98,10 @@ fi
|
|||||||
|
|
||||||
if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then
|
if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then
|
||||||
TMP="$STORAGE/tmp"
|
TMP="$STORAGE/tmp"
|
||||||
|
rm -rf "$TMP"
|
||||||
|
if ! makeDir "$TMP"; then
|
||||||
|
error "Failed to create directory \"$TMP\" !" && exit 93
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
TMP="/tmp/dsm"
|
TMP="/tmp/dsm"
|
||||||
TMP_SPACE=2147483648
|
TMP_SPACE=2147483648
|
||||||
@@ -106,10 +110,9 @@ else
|
|||||||
if (( TMP_SPACE > SPACE )); then
|
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
|
error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93
|
||||||
fi
|
fi
|
||||||
|
rm -rf "$TMP" && mkdir -p "$TMP"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf "$TMP" && mkdir -p "$TMP"
|
|
||||||
|
|
||||||
# Check free diskspace
|
# Check free diskspace
|
||||||
ROOT_SPACE=536870912
|
ROOT_SPACE=536870912
|
||||||
SPACE=$(df --output=avail -B 1 / | tail -n 1)
|
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
|
error "Could not create file $SYSTEM for the system disk." && exit 98
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
! setOwner "$SYSTEM" && error "Failed to set the owner for \"$SYSTEM\" !"
|
||||||
|
|
||||||
if [[ "${FS,,}" == "btrfs" ]]; then
|
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||||
{ chattr +C "$SYSTEM"; } || :
|
{ chattr +C "$SYSTEM"; } || :
|
||||||
FA=$(lsattr "$SYSTEM")
|
FA=$(lsattr "$SYSTEM")
|
||||||
@@ -254,7 +259,11 @@ PART="$TMP/partition.fdisk"
|
|||||||
sfdisk -q "$SYSTEM" < "$PART"
|
sfdisk -q "$SYSTEM" < "$PART"
|
||||||
|
|
||||||
MOUNT="$TMP/system"
|
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..."
|
MSG="Extracting system partition..."
|
||||||
info "Install: $MSG" && html "$MSG"
|
info "Install: $MSG" && html "$MSG"
|
||||||
@@ -289,6 +298,7 @@ fakeroot -- bash -c "set -Eeu;\
|
|||||||
|
|
||||||
rm -rf "$MOUNT"
|
rm -rf "$MOUNT"
|
||||||
echo "$BASE" > "$STORAGE/dsm.ver"
|
echo "$BASE" > "$STORAGE/dsm.ver"
|
||||||
|
! setOwner "$STORAGE/dsm.ver" && error "Failed to set the owner for \"$STORAGE/dsm.ver\" !"
|
||||||
|
|
||||||
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
||||||
rm -f "$PAT"
|
rm -f "$PAT"
|
||||||
@@ -296,10 +306,13 @@ else
|
|||||||
mv -f "$PAT" "$STORAGE/$BASE.pat"
|
mv -f "$PAT" "$STORAGE/$BASE.pat"
|
||||||
fi
|
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"
|
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"
|
rm -rf "$TMP"
|
||||||
|
|
||||||
html "Booting DSM instance..."
|
|
||||||
sleep 1.2
|
|
||||||
|
|
||||||
return 0
|
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:=""}"
|
: "${MTU:=""}"
|
||||||
: "${DHCP:="N"}"
|
: "${DHCP:="N"}"
|
||||||
: "${NETWORK:="Y"}"
|
: "${NETWORK:="Y"}"
|
||||||
: "${USER_PORTS:=""}"
|
|
||||||
: "${HOST_PORTS:=""}"
|
: "${HOST_PORTS:=""}"
|
||||||
|
: "${USER_PORTS:=""}"
|
||||||
: "${ADAPTER:="virtio-net-pci"}"
|
: "${ADAPTER:="virtio-net-pci"}"
|
||||||
|
|
||||||
|
: "${VM_NET_IP:=""}"
|
||||||
: "${VM_NET_DEV:=""}"
|
: "${VM_NET_DEV:=""}"
|
||||||
: "${VM_NET_TAP:="dsm"}"
|
: "${VM_NET_TAP:="dsm"}"
|
||||||
: "${VM_NET_MAC:="$MAC"}"
|
: "${VM_NET_MAC:="$MAC"}"
|
||||||
: "${VM_NET_IP:="20.20.20.21"}"
|
: "${VM_NET_BRIDGE:="docker"}"
|
||||||
: "${VM_NET_HOST:="VirtualDSM"}"
|
: "${VM_NET_HOST:="VirtualDSM"}"
|
||||||
|
: "${VM_NET_MASK:="255.255.255.0"}"
|
||||||
|
|
||||||
|
: "${PASST:="/run/passt"}"
|
||||||
|
: "${PASST_MTU:=""}"
|
||||||
|
: "${PASST_OPTS:=""}"
|
||||||
|
: "${PASST_DEBUG:=""}"
|
||||||
|
: "${PASST_PID:="/var/run/passt.pid"}"
|
||||||
|
|
||||||
: "${DNSMASQ_OPTS:=""}"
|
: "${DNSMASQ_OPTS:=""}"
|
||||||
|
: "${DNSMASQ_DEBUG:=""}"
|
||||||
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
|
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
|
||||||
|
: "${DNSMASQ_PID:="/var/run/dnsmasq.pid"}"
|
||||||
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
|
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
|
||||||
|
|
||||||
ADD_ERR="Please add the following setting to your container:"
|
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() {
|
configureDHCP() {
|
||||||
|
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
|
||||||
|
|
||||||
# Create the necessary file structure for /dev/vhost-net
|
# Create the necessary file structure for /dev/vhost-net
|
||||||
if [ ! -c /dev/vhost-net ]; then
|
if [ ! -c /dev/vhost-net ]; then
|
||||||
if mknod /dev/vhost-net c 10 238; then
|
if mknod /dev/vhost-net c 10 238; then
|
||||||
@@ -61,7 +73,7 @@ configureDHCP() {
|
|||||||
|
|
||||||
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
||||||
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
||||||
warn "Failed to set MTU size.."
|
warn "Failed to set MTU size to $MTU."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -105,98 +117,319 @@ configureDHCP() {
|
|||||||
|
|
||||||
configureDNS() {
|
configureDNS() {
|
||||||
|
|
||||||
# Create lease file for faster resolve
|
local fa="$1"
|
||||||
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
|
local ip="$2"
|
||||||
chmod 644 /var/lib/misc/dnsmasq.leases
|
local mac="$3"
|
||||||
|
local host="$4"
|
||||||
|
local mask="$5"
|
||||||
|
local gateway="$6"
|
||||||
|
local arguments="$DNSMASQ_OPTS"
|
||||||
|
|
||||||
# dnsmasq configuration:
|
echo "$gateway" > /run/shm/qemu.gw
|
||||||
DNSMASQ_OPTS+=" --dhcp-authoritative"
|
|
||||||
|
|
||||||
# Set DHCP range and host
|
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
|
||||||
DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP"
|
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
|
||||||
DNSMASQ_OPTS+=" --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite"
|
|
||||||
|
|
||||||
# Set DNS server and gateway
|
[ -s "$DNSMASQ_PID" ] && pKill "$(<"$DNSMASQ_PID")"
|
||||||
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,255.255.255.0"
|
rm -f "$DNSMASQ_PID"
|
||||||
DNSMASQ_OPTS+=" --dhcp-option=option:router,${VM_NET_IP%.*}.1"
|
|
||||||
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1"
|
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
|
# 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
|
# Enable logging to file
|
||||||
DNSMASQ_OPTS+=" -d"
|
local log="/var/log/dnsmasq.log"
|
||||||
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
|
rm -f "$log"
|
||||||
return 0
|
arguments+=" --log-facility=$log"
|
||||||
fi
|
|
||||||
|
|
||||||
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
|
arguments=$(echo "$arguments" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
||||||
error "Failed to start dnsmasq, reason: $?" && return 1
|
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${arguments// -/$'\n-'}"
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
{ $DNSMASQ ${arguments:+ $arguments}; rc=$?; } || :
|
||||||
}
|
|
||||||
|
|
||||||
getUserPorts() {
|
if (( rc != 0 )); then
|
||||||
|
|
||||||
local args=""
|
local msg="Failed to start Dnsmasq, reason: $rc"
|
||||||
local list=$1
|
|
||||||
local ssh="22"
|
|
||||||
local dsm="5000"
|
|
||||||
|
|
||||||
[ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm"
|
if [[ "${NETWORK,,}" == "slirp" || "${NETWORK,,}" == "passt" || "$ROOTLESS" != [Yy1]* || "$DEBUG" == [Yy1]* ]]; then
|
||||||
|
[ -f "$log" ] && [ -s "$log" ] && cat "$log"
|
||||||
list="${list//,/ }"
|
error "$msg"
|
||||||
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}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
|
return 1
|
||||||
done
|
fi
|
||||||
|
|
||||||
|
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
|
||||||
|
tail -fn +0 "$log" --pid=$$ &
|
||||||
|
fi
|
||||||
|
|
||||||
echo "${args%?}"
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
getHostPorts() {
|
getHostPorts() {
|
||||||
|
|
||||||
local list="$1"
|
local list="${HOST_PORTS// /},"
|
||||||
|
|
||||||
[ -z "$list" ] && echo "" && return 0
|
# Remove duplicates
|
||||||
|
list=$(echo "${list//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
|
||||||
if [[ "$list" != *","* ]]; then
|
|
||||||
echo " ! --dport $list"
|
|
||||||
else
|
|
||||||
echo " -m multiport ! --dports $list"
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
echo "$list"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
configureUser() {
|
getUserPorts() {
|
||||||
|
|
||||||
if [ -z "$IP6" ]; then
|
local ssh="22"
|
||||||
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 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
|
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
|
fi
|
||||||
|
|
||||||
local forward
|
VM_NET_IP="$ip"
|
||||||
forward=$(getUserPorts "$USER_PORTS")
|
return 0
|
||||||
[ -n "$forward" ] && NET_OPTS+=",$forward"
|
}
|
||||||
|
|
||||||
|
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
|
return 0
|
||||||
}
|
}
|
||||||
@@ -204,11 +437,12 @@ configureUser() {
|
|||||||
configureNAT() {
|
configureNAT() {
|
||||||
|
|
||||||
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
|
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
|
||||||
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
||||||
|
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring NAT networking..."
|
||||||
|
|
||||||
# Create the necessary file structure for /dev/net/tun
|
# Create the necessary file structure for /dev/net/tun
|
||||||
if [ ! -c /dev/net/tun ]; then
|
if [ ! -c /dev/net/tun ]; then
|
||||||
[[ "$PODMAN" == [Yy1]* ]] && return 1
|
|
||||||
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
|
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
|
||||||
if mknod /dev/net/tun c 10 200; then
|
if mknod /dev/net/tun c 10 200; then
|
||||||
chmod 666 /dev/net/tun
|
chmod 666 /dev/net/tun
|
||||||
@@ -216,50 +450,71 @@ configureNAT() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -c /dev/net/tun ]; then
|
if [ ! -c /dev/net/tun ]; then
|
||||||
error "$tuntap" && return 1
|
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||||
|
warn "$tuntap" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check port forwarding flag
|
# Check port forwarding flag
|
||||||
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||||
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
|
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
|
||||||
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||||
[[ "$PODMAN" == [Yy1]* ]] && return 1
|
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||||
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
|
warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
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
|
# Create a bridge with a static IP for the VM guest
|
||||||
{ ip link add dev dockerbridge type bridge ; rc=$?; } || :
|
{ ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
|
||||||
|
|
||||||
if (( rc != 0 )); then
|
if (( rc != 0 )); then
|
||||||
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
|
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||||
|
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then
|
if ! ip address add "$gateway/24" broadcast "$broadcast" dev "$VM_NET_BRIDGE"; then
|
||||||
error "Failed to add IP address pool!" && return 1
|
warn "failed to add IP address pool!" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while ! ip link set dockerbridge up; do
|
while ! ip link set "$VM_NET_BRIDGE" up; do
|
||||||
info "Waiting for IP address to become available..."
|
info "Waiting for IP address to become available..."
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
# QEMU Works with taps, set tap to the bridge created
|
# Set tap to the bridge created
|
||||||
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
|
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
|
||||||
error "$tuntap" && return 1
|
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
|
||||||
|
warn "$tuntap" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
||||||
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
||||||
warn "Failed to set MTU size.."
|
warn "failed to set MTU size to $MTU."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
|
||||||
|
|
||||||
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
|
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
|
||||||
warn "Failed to set gateway MAC address.."
|
warn "failed to set gateway MAC address."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while ! ip link set "$VM_NET_TAP" up promisc on; do
|
while ! ip link set "$VM_NET_TAP" up promisc on; do
|
||||||
@@ -267,32 +522,53 @@ configureNAT() {
|
|||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
|
if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
|
||||||
error "Failed to set IP link!" && return 1
|
warn "failed to set master bridge!" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add internet connection to the VM
|
# Flush existing tables
|
||||||
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
|
clearTables
|
||||||
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
|
|
||||||
|
|
||||||
exclude=$(getHostPorts "$HOST_PORTS")
|
exclude=$(getHostPorts)
|
||||||
|
|
||||||
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
|
if [ -n "$exclude" ]; then
|
||||||
error "$tables" && return 1
|
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
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then
|
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -m comment --comment "remove" -j DNAT --to "$ip"; then
|
||||||
error "Failed to configure IP tables!" && return 1
|
warn "failed to configure IP tables!" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then
|
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -m comment --comment "remove" -j DNAT --to "$ip"; then
|
||||||
error "Failed to configure IP tables!" && return 1
|
warn "failed to configure IP tables!" && return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if (( KERNEL > 4 )); then
|
if (( KERNEL > 4 )); then
|
||||||
# Hack for guest VMs complaining about "bad udp checksums in 5 packets"
|
# 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
|
fi
|
||||||
|
|
||||||
NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP"
|
NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP"
|
||||||
@@ -304,23 +580,31 @@ configureNAT() {
|
|||||||
|
|
||||||
NET_OPTS+=",script=no,downscript=no"
|
NET_OPTS+=",script=no,downscript=no"
|
||||||
|
|
||||||
configureDNS || return 1
|
configureDNS "$VM_NET_BRIDGE" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
|
||||||
|
|
||||||
|
VM_NET_IP="$ip"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
closeBridge() {
|
closeBridge() {
|
||||||
|
|
||||||
local pid="/var/run/dnsmasq.pid"
|
local pids=( "$PASST_PID" "$DNSMASQ_PID" )
|
||||||
[ -s "$pid" ] && pKill "$(<"$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 set "$VM_NET_BRIDGE" down &> /dev/null || :
|
||||||
ip link delete "$VM_NET_TAP" &> null || true
|
ip link delete "$VM_NET_BRIDGE" &> /dev/null || :
|
||||||
|
|
||||||
ip link set dockerbridge down &> null || true
|
clearTables
|
||||||
ip link delete dockerbridge &> null || true
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
closeWeb() {
|
||||||
|
|
||||||
|
local pids=( "$WEB_PID" "$WSD_PID" )
|
||||||
|
mKill "${pids[@]}"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -328,11 +612,7 @@ closeBridge() {
|
|||||||
closeNetwork() {
|
closeNetwork() {
|
||||||
|
|
||||||
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then
|
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then
|
||||||
|
closeWeb
|
||||||
# Shutdown nginx
|
|
||||||
nginx -s stop 2> /dev/null
|
|
||||||
fWait "nginx"
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[[ "$NETWORK" == [Nn]* ]] && return 0
|
[[ "$NETWORK" == [Nn]* ]] && return 0
|
||||||
@@ -340,36 +620,41 @@ closeNetwork() {
|
|||||||
exec 30<&- || true
|
exec 30<&- || true
|
||||||
exec 40<&- || true
|
exec 40<&- || true
|
||||||
|
|
||||||
if [[ "$DHCP" != [Yy1]* ]]; then
|
closeBridge
|
||||||
|
|
||||||
closeBridge
|
return 0
|
||||||
return 0
|
}
|
||||||
|
|
||||||
fi
|
cleanUp() {
|
||||||
|
|
||||||
ip link set "$VM_NET_TAP" down || true
|
closeBridge
|
||||||
ip link delete "$VM_NET_TAP" || true
|
|
||||||
|
# Clean up old files
|
||||||
|
rm -f "$PASST_PID"
|
||||||
|
rm -f "$DNSMASQ_PID"
|
||||||
|
rm -f /etc/resolv.dnsmasq
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
checkOS() {
|
checkOS() {
|
||||||
|
|
||||||
local kernel
|
|
||||||
local os=""
|
local os=""
|
||||||
local if="macvlan"
|
local kernel=""
|
||||||
|
local iface="macvlan"
|
||||||
|
|
||||||
kernel=$(uname -a)
|
kernel=$(uname -a)
|
||||||
|
|
||||||
[[ "${kernel,,}" == *"darwin"* ]] && os="Docker Desktop for macOS"
|
[[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
|
||||||
[[ "${kernel,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows"
|
[[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
|
||||||
|
|
||||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||||
if="macvtap"
|
iface="macvtap"
|
||||||
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
|
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$os" ]; then
|
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
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -383,8 +668,8 @@ getInfo() {
|
|||||||
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
|
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
|
||||||
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
|
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
|
||||||
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
|
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
|
||||||
# Automaticly detect the default network interface
|
# Automatically detect the default network interface
|
||||||
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
|
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1; exit }' /proc/net/route)
|
||||||
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
|
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -393,29 +678,77 @@ getInfo() {
|
|||||||
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
|
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
|
||||||
fi
|
fi
|
||||||
|
|
||||||
NIC=$(ethtool -i "$VM_NET_DEV" | grep -m 1 -i 'driver:' | awk '{print $(2)}')
|
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
|
||||||
|
{ IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1); } 2>/dev/null || :
|
||||||
|
[ -z "$IP" ] && [[ "$DHCP" != [Yy1]* ]] && error "Could not determine container IPv4 address!" && exit 26
|
||||||
|
|
||||||
if [[ "${NIC,,}" != "veth" ]]; then
|
IP6=""
|
||||||
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $NIC"
|
# shellcheck disable=SC2143
|
||||||
error "This container does not support host mode networking!" && exit 29
|
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
||||||
|
{ IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up); rc=$?; } 2>/dev/null || :
|
||||||
|
(( rc != 0 )) && IP6=""
|
||||||
|
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BASE_IP="${VM_NET_IP%.*}."
|
local result nic bus
|
||||||
|
result=$(ethtool -i "$VM_NET_DEV")
|
||||||
|
nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $2}')
|
||||||
|
bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $2}')
|
||||||
|
|
||||||
if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then
|
if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
|
||||||
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27
|
[[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus"
|
||||||
|
error "This container does not support host mode networking!"
|
||||||
|
exit 29
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "$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
|
BNF="/proc/sys/net/bridge/bridge-nf-call-iptables"
|
||||||
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
|
|
||||||
|
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
|
fi
|
||||||
|
|
||||||
if [ "$MTU" -gt "1500" ]; then
|
local mtu=""
|
||||||
info "MTU size is too large: $MTU, ignoring..." && MTU="0"
|
|
||||||
|
if [ -f "/sys/class/net/$VM_NET_DEV/mtu" ]; then
|
||||||
|
mtu=$(< "/sys/class/net/$VM_NET_DEV/mtu")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
[ -z "$MTU" ] && MTU="$mtu"
|
||||||
|
[ -z "$MTU" ] && MTU="0"
|
||||||
|
|
||||||
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
|
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
|
||||||
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
if [[ "$MTU" != "0" ]] && [ "$MTU" -lt "1500" ]; then
|
||||||
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
|
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -428,6 +761,7 @@ getInfo() {
|
|||||||
# Generate MAC address based on Docker container ID in hostname
|
# Generate MAC address based on Docker container ID in hostname
|
||||||
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
|
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
|
||||||
echo "${VM_NET_MAC^^}" > "$file"
|
echo "${VM_NET_MAC^^}" > "$file"
|
||||||
|
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -443,18 +777,21 @@ getInfo() {
|
|||||||
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
|
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
|
||||||
fi
|
fi
|
||||||
|
|
||||||
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
|
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
||||||
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
|
|
||||||
|
|
||||||
IP6=""
|
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||||
# shellcheck disable=SC2143
|
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
|
||||||
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
|
||||||
IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
|
info "$line"
|
||||||
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
|
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
|
fi
|
||||||
|
|
||||||
[ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
|
echo "$IP" > "$QEMU_DIR"/qemu.ip
|
||||||
echo "$IP" > /run/shm/qemu.ip
|
echo "$nic" > "$QEMU_DIR"//qemu.nic
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -468,84 +805,82 @@ if [[ "$NETWORK" == [Nn]* ]]; then
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
getInfo
|
msg="Initializing network..."
|
||||||
html "Initializing network..."
|
html "$msg"
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
||||||
|
|
||||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
getInfo
|
||||||
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
|
cleanUp
|
||||||
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
|
|
||||||
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
|
|
||||||
info "$line"
|
|
||||||
if [ -f /etc/resolv.conf ]; then
|
|
||||||
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
|
|
||||||
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$IP" == "172.17."* ]]; then
|
if [[ "$IP" == "172.17."* ]]; then
|
||||||
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
|
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
|
MSG="Booting DSM instance..."
|
||||||
info "Lingering interface will be removed..."
|
html "$MSG"
|
||||||
ip link delete "$VM_NET_TAP" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||||
|
|
||||||
checkOS
|
|
||||||
|
|
||||||
if [[ "$IP" == "172."* ]]; then
|
|
||||||
warn "container IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure for macvtap interface
|
# Configure for macvtap interface
|
||||||
configureDHCP || exit 20
|
configureDHCP || exit 20
|
||||||
|
|
||||||
MSG="Booting DSM instance..."
|
|
||||||
html "$MSG"
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
|
|
||||||
checkOS
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${WEB:-}" != [Nn]* ]]; then
|
if [[ "${WEB:-}" != [Nn]* ]]; then
|
||||||
|
sleep 1.2
|
||||||
# Shutdown nginx
|
closeWeb
|
||||||
nginx -s stop 2> /dev/null
|
|
||||||
fWait "nginx"
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${NETWORK,,}" != "user"* ]]; then
|
case "${NETWORK,,}" in
|
||||||
|
"passt" | "slirp" | "user"* ) ;;
|
||||||
|
"tap" | "tun" | "tuntap" | "y" | "" )
|
||||||
|
|
||||||
# Configure for tap interface
|
# Configure tap interface
|
||||||
if ! configureNAT; then
|
if ! configureNAT; then
|
||||||
|
|
||||||
closeBridge
|
closeBridge
|
||||||
NETWORK="user"
|
NETWORK="user"
|
||||||
msg="falling back to user-mode networking!"
|
|
||||||
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\"."
|
|
||||||
|
|
||||||
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)
|
case "${NETWORK,,}" in
|
||||||
configureUser || exit 24
|
"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
|
fi
|
||||||
|
|
||||||
|
|||||||
+97
-110
@@ -1,133 +1,112 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
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
|
# Configure QEMU for graceful shutdown
|
||||||
|
|
||||||
API_CMD=6
|
API_CMD=6
|
||||||
API_HOST="127.0.0.1:2210"
|
API_HOST="127.0.0.1:$COM_PORT"
|
||||||
: "${API_TIMEOUT:="50"}" # API Call timeout
|
|
||||||
|
# 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"
|
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() {
|
_trap() {
|
||||||
func="$1" ; shift
|
local func="$1"; shift
|
||||||
for sig ; do
|
local sig
|
||||||
|
TRAP_PID=$BASHPID
|
||||||
|
|
||||||
|
for sig; do
|
||||||
trap "$func $sig" "$sig"
|
trap "$func $sig" "$sig"
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app() {
|
||||||
|
echo "$APP" && return 0
|
||||||
|
}
|
||||||
|
|
||||||
finish() {
|
finish() {
|
||||||
|
|
||||||
local pid
|
local i=0
|
||||||
|
local pid=""
|
||||||
local reason=$1
|
local reason=$1
|
||||||
|
local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \
|
||||||
|
"${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" )
|
||||||
|
|
||||||
touch "$QEMU_END"
|
touch "$QEMU_END"
|
||||||
|
|
||||||
if [ -s "$QEMU_PID" ]; then
|
if [ -s "$QEMU_PID" ]; then
|
||||||
|
if read -r pid <"$QEMU_PID"; then
|
||||||
pid=$(<"$QEMU_PID")
|
if [ -n "$pid" ] && isAlive "$pid"; then
|
||||||
echo && error "Forcefully terminating QEMU process, reason: $reason..."
|
local display="$reason"
|
||||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
case "$reason" in
|
||||||
|
129 ) display="SIGHUP" ;;
|
||||||
while isAlive "$pid"; do
|
130 ) display="SIGINT" ;;
|
||||||
sleep 1
|
131 ) display="SIGQUIT" ;;
|
||||||
# Workaround for zombie pid
|
134 ) display="SIGABRT" ;;
|
||||||
[ ! -s "$QEMU_PID" ] && break
|
143 ) display="SIGTERM" ;;
|
||||||
done
|
esac
|
||||||
fi
|
error "Forcefully terminating $(app), reason: $display..."
|
||||||
|
{ disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null
|
||||||
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"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dev="${msg#*/dev/p}"
|
|
||||||
dev="/dev/p${dev%% *}"
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -c "$dev" ]; then
|
mKill "${pids[@]}"
|
||||||
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
|
fKill "print.sh"
|
||||||
dev="${dev#*serial0}"
|
|
||||||
dev="${dev#*pty:}"
|
closeNetwork
|
||||||
dev="${dev%%$'\n'*}"
|
|
||||||
dev="${dev%%$'\r'*}"
|
if ! waitPidFile "$QEMU_PID" 10; then
|
||||||
|
warn "Timed out while waiting for $(app) to exit!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -c "$dev" ]; then
|
(( reason != 1 )) && echo && echo "❯ Shutdown completed!"
|
||||||
error "Device '$dev' not found!"
|
exit "$reason"
|
||||||
finish 34 && return 34
|
|
||||||
fi
|
|
||||||
|
|
||||||
QEMU_TERM="$dev"
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_graceful_shutdown() {
|
graceful_shutdown() {
|
||||||
|
|
||||||
local code=$?
|
local sig="$1"
|
||||||
local pid url response
|
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
|
if [ -f "$QEMU_END" ]; then
|
||||||
echo && info "Received $1 signal while already shutting down..."
|
echo && info "Received $1 signal while already shutting down..."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
set +e
|
||||||
|
start=$SECONDS
|
||||||
touch "$QEMU_END"
|
touch "$QEMU_END"
|
||||||
echo && info "Received $1 signal, sending shutdown command..."
|
echo && info "Received $1 signal, sending shutdown command..."
|
||||||
|
|
||||||
if [ ! -s "$QEMU_PID" ]; then
|
if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then
|
||||||
echo && error "QEMU PID file does not exist?"
|
warn "QEMU PID file ($QEMU_PID) does not exist?"
|
||||||
finish "$code" && return "$code"
|
finish "$code"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pid=$(<"$QEMU_PID")
|
if [ -z "$pid" ] || ! isAlive "$pid"; then
|
||||||
|
warn "QEMU process with PID $pid does not exist?"
|
||||||
if ! isAlive "$pid"; then
|
finish "$code"
|
||||||
echo && error "QEMU process does not exist?"
|
|
||||||
finish "$code" && return "$code"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Don't send the powerdown signal because vDSM ignores ACPI signals
|
# Don't send the powerdown signal because vDSM ignores ACPI signals
|
||||||
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
|
# nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || :
|
||||||
|
|
||||||
# Send shutdown command to guest agent via serial port
|
# Send shutdown command to guest agent via serial port
|
||||||
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
|
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
|
||||||
@@ -142,44 +121,52 @@ _graceful_shutdown() {
|
|||||||
response="${response#*message\"\: \"}"
|
response="${response#*message\"\: \"}"
|
||||||
[ -z "$response" ] && response="second signal"
|
[ -z "$response" ] && response="second signal"
|
||||||
echo && error "Forcefully terminating because of: ${response%%\"*}"
|
echo && error "Forcefully terminating because of: ${response%%\"*}"
|
||||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
{ kill -15 -- "$pid" || :; } 2>/dev/null
|
||||||
|
|
||||||
fi
|
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
|
! isAlive "$pid" && break
|
||||||
|
|
||||||
sleep 1
|
|
||||||
cnt=$((cnt+1))
|
|
||||||
|
|
||||||
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
|
|
||||||
|
|
||||||
# Workaround for zombie pid
|
# Workaround for zombie pid
|
||||||
[ ! -s "$QEMU_PID" ] && break
|
[ ! -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
|
done
|
||||||
|
|
||||||
if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
|
finish "$code"
|
||||||
echo && error "Shutdown timeout reached, aborting..."
|
|
||||||
fi
|
|
||||||
|
|
||||||
finish "$code" && return "$code"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MON_OPTS="\
|
[[ "$SHUTDOWN" != [Yy1]* ]] && return 0
|
||||||
-pidfile $QEMU_PID \
|
[ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
|
||||||
-name $PROCESS,process=$PROCESS,debug-threads=on \
|
|
||||||
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
|
|
||||||
|
|
||||||
if [[ "$CONSOLE" != [Yy]* ]]; then
|
_trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT
|
||||||
|
|
||||||
MON_OPTS+=" -daemonize -D $QEMU_LOG"
|
|
||||||
|
|
||||||
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
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"
|
file="/run/shm/dsm.url"
|
||||||
info="/run/shm/msg.html"
|
info="/run/shm/msg.html"
|
||||||
|
driver="/run/shm/qemu.nic"
|
||||||
page="/run/shm/index.html"
|
page="/run/shm/index.html"
|
||||||
address="/run/shm/qemu.ip"
|
address="/run/shm/qemu.ip"
|
||||||
shutdown="/run/shm/qemu.end"
|
shutdown="/run/shm/qemu.end"
|
||||||
@@ -56,11 +57,7 @@ do
|
|||||||
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
||||||
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue
|
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue
|
||||||
|
|
||||||
if [ -z "$ip" ]; then
|
[ -z "$ip" ] && continue
|
||||||
[[ "$DHCP" == [Yy1]* ]] && continue
|
|
||||||
ip="20.20.20.21"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$ip:$port" > $file
|
echo "$ip:$port" > $file
|
||||||
|
|
||||||
done
|
done
|
||||||
@@ -69,7 +66,7 @@ done
|
|||||||
|
|
||||||
location=$(<"$file")
|
location=$(<"$file")
|
||||||
|
|
||||||
if [[ "$location" != "20.20"* ]]; then
|
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||||
|
|
||||||
msg="http://$location"
|
msg="http://$location"
|
||||||
title="<title>Virtual DSM</title>"
|
title="<title>Virtual DSM</title>"
|
||||||
@@ -88,10 +85,11 @@ if [[ "$location" != "20.20"* ]]; then
|
|||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
|
nic=$(<"$driver")
|
||||||
ip=$(<"$address")
|
ip=$(<"$address")
|
||||||
port="${location##*:}"
|
port="${location##*:}"
|
||||||
|
|
||||||
if [[ "$ip" == "172."* ]]; then
|
if [[ "${nic,,}" != "macvlan" ]]; then
|
||||||
msg="port $port"
|
msg="port $port"
|
||||||
else
|
else
|
||||||
msg="http://$ip:$port"
|
msg="http://$ip:$port"
|
||||||
|
|||||||
+4
-51
@@ -3,7 +3,6 @@ set -Eeuo pipefail
|
|||||||
|
|
||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
|
|
||||||
: "${KVM:="Y"}"
|
|
||||||
: "${HOST_CPU:=""}"
|
: "${HOST_CPU:=""}"
|
||||||
: "${CPU_FLAGS:=""}"
|
: "${CPU_FLAGS:=""}"
|
||||||
: "${CPU_MODEL:=""}"
|
: "${CPU_MODEL:=""}"
|
||||||
@@ -13,7 +12,7 @@ CLOCKSOURCE="tsc"
|
|||||||
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
||||||
|
|
||||||
if [ ! -f "$CLOCK" ]; then
|
if [ ! -f "$CLOCK" ]; then
|
||||||
warn "file \"$CLOCK\" cannot not found?"
|
warn "file \"$CLOCK\" cannot be found?"
|
||||||
else
|
else
|
||||||
result=$(<"$CLOCK")
|
result=$(<"$CLOCK")
|
||||||
result="${result//[![:print:]]/}"
|
result="${result//[![:print:]]/}"
|
||||||
@@ -26,52 +25,7 @@ else
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$KVM" == [Nn]* ]]; then
|
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
||||||
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
|
|
||||||
else
|
|
||||||
if [[ "${ARCH,,}" != "amd64" ]]; then
|
|
||||||
KVM="N"
|
|
||||||
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, so the machine will run about 10 times slower."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$KVM" != [Nn]* ]]; then
|
|
||||||
|
|
||||||
KVM_ERR=""
|
|
||||||
|
|
||||||
if [ ! -e /dev/kvm ]; then
|
|
||||||
KVM_ERR="(/dev/kvm is missing)"
|
|
||||||
else
|
|
||||||
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
|
||||||
KVM_ERR="(/dev/kvm is unwriteable)"
|
|
||||||
else
|
|
||||||
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
|
||||||
if ! grep -qw "vmx\|svm" <<< "$flags"; then
|
|
||||||
KVM_ERR="(not enabled in BIOS)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$KVM_ERR" ]; then
|
|
||||||
KVM="N"
|
|
||||||
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
|
||||||
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
|
|
||||||
else
|
|
||||||
kernel=$(uname -a)
|
|
||||||
case "${kernel,,}" in
|
|
||||||
*"microsoft"* )
|
|
||||||
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
|
|
||||||
*"synology"* )
|
|
||||||
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
|
|
||||||
*)
|
|
||||||
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
|
|
||||||
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
|
|
||||||
esac
|
|
||||||
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$KVM" != [Nn]* ]]; then
|
if [[ "$KVM" != [Nn]* ]]; then
|
||||||
|
|
||||||
@@ -79,9 +33,8 @@ if [[ "$KVM" != [Nn]* ]]; then
|
|||||||
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
|
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
|
||||||
|
|
||||||
if ! grep -qw "sse4_2" <<< "$flags"; then
|
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..."
|
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
||||||
[ -z "$CPU_MODEL" ] && CPU_MODEL="qemu64"
|
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
||||||
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$CPU_MODEL" ]; then
|
if [ -z "$CPU_MODEL" ]; then
|
||||||
|
|||||||
+21
-14
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
info="/run/shm/msg.html"
|
||||||
|
|
||||||
escape () {
|
escape () {
|
||||||
local s
|
local s
|
||||||
s=${1//&/\&}
|
s=${1//&/\&}
|
||||||
@@ -11,28 +13,33 @@ escape () {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
file="$1"
|
path="$1"
|
||||||
total="$2"
|
total="$2"
|
||||||
body=$(escape "$3")
|
body=$(escape "$3")
|
||||||
info="/run/shm/msg.html"
|
|
||||||
|
|
||||||
if [[ "$body" == *"..." ]]; then
|
if [[ "$body" == *"..." ]]; then
|
||||||
body="<p class=\"loading\">${body/.../}</p>"
|
body="<p class=\"loading\">${body::-3}</p>"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
while true
|
while true
|
||||||
do
|
do
|
||||||
if [ -s "$file" ]; then
|
|
||||||
bytes=$(du -sb "$file" | cut -f1)
|
if [ ! -s "$path" ] && [ ! -d "$path" ]; then
|
||||||
if (( bytes > 1000 )); then
|
bytes="0"
|
||||||
if [ -z "$total" ] || [[ "$total" == "0" ]]; then
|
else
|
||||||
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
|
bytes=$(du -sb "$path" | cut -f1)
|
||||||
else
|
|
||||||
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
|
|
||||||
size="$size%"
|
|
||||||
fi
|
|
||||||
echo "${body//(\[P\])/($size)}"> "$info"
|
|
||||||
fi
|
|
||||||
fi
|
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 $!
|
sleep 1 & wait $!
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|||||||
+130
-125
@@ -2,19 +2,17 @@
|
|||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
|
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
|
||||||
|
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG
|
||||||
|
|
||||||
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
|
[ ! -f "/run/entry.sh" ] && error "Script must be run inside the container!" && exit 11
|
||||||
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
|
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
|
||||||
|
|
||||||
echo "❯ Starting $APP for Docker v$(</run/version)..."
|
|
||||||
echo "❯ For support visit $SUPPORT"
|
|
||||||
|
|
||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
|
|
||||||
: "${TZ:=""}" # System local timezone
|
: "${TZ:=""}" # System timezone
|
||||||
|
: "${KVM:="Y"}" # KVM acceleration
|
||||||
: "${DEBUG:="N"}" # Disable debugging mode
|
: "${DEBUG:="N"}" # Disable debugging mode
|
||||||
: "${COUNTRY:=""}" # Country code for mirror
|
: "${COUNTRY:=""}" # Country code for mirror
|
||||||
: "${CONSOLE:="N"}" # Disable console mode
|
|
||||||
: "${ALLOCATE:=""}" # Preallocate diskspace
|
: "${ALLOCATE:=""}" # Preallocate diskspace
|
||||||
: "${ARGUMENTS:=""}" # Extra QEMU parameters
|
: "${ARGUMENTS:=""}" # Extra QEMU parameters
|
||||||
: "${CPU_CORES:="2"}" # Amount of CPU cores
|
: "${CPU_CORES:="2"}" # Amount of CPU cores
|
||||||
@@ -25,15 +23,47 @@ echo "❯ For support visit $SUPPORT"
|
|||||||
|
|
||||||
# Helper variables
|
# Helper variables
|
||||||
|
|
||||||
|
ROOTLESS="N"
|
||||||
|
PRIVILEGED="N"
|
||||||
|
ENGINE="Docker"
|
||||||
PROCESS="${APP,,}"
|
PROCESS="${APP,,}"
|
||||||
PROCESS="${PROCESS// /-}"
|
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"
|
INFO="/run/shm/msg.html"
|
||||||
PAGE="/run/shm/index.html"
|
PAGE="/run/shm/index.html"
|
||||||
TEMPLATE="/var/www/index.html"
|
TEMPLATE="/var/www/index.html"
|
||||||
FOOTER1="$APP for Docker v$(</run/version)"
|
FOOTER1="$APP for $ENGINE v$(</etc/version)"
|
||||||
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
||||||
|
|
||||||
|
SOCKETS=1
|
||||||
CPU=$(cpu)
|
CPU=$(cpu)
|
||||||
SYS=$(uname -r)
|
SYS=$(uname -r)
|
||||||
HOST=$(hostname -s)
|
HOST=$(hostname -s)
|
||||||
@@ -42,33 +72,39 @@ MINOR=$(echo "$SYS" | cut -d '.' -f2)
|
|||||||
ARCH=$(dpkg --print-architecture)
|
ARCH=$(dpkg --print-architecture)
|
||||||
CORES=$(grep -c '^processor' /proc/cpuinfo)
|
CORES=$(grep -c '^processor' /proc/cpuinfo)
|
||||||
|
|
||||||
if ! grep -qi "socket(s)" <<< "$(lscpu)"; then
|
if grep -qi "socket(s)" <<< "$(lscpu)"; then
|
||||||
SOCKETS=1
|
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $2}')
|
||||||
else
|
[ -z "${SOCKETS##*[!0-9]*}" ] && SOCKETS=1
|
||||||
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
|
[ "$SOCKETS" -lt "1" ] && SOCKETS=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CPU_CORES="${CPU_CORES// /}"
|
CPU_CORES="${CPU_CORES// /}"
|
||||||
|
[ -z "$CPU_CORES" ] && CPU_CORES=2
|
||||||
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
|
[[ "${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
|
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"
|
CPU_CORES="$CORES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check system
|
# Check system
|
||||||
|
|
||||||
|
QEMU_DIR="/run/shm"
|
||||||
|
|
||||||
if [ ! -d "/dev/shm" ]; then
|
if [ ! -d "/dev/shm" ]; then
|
||||||
error "Directory /dev/shm not found!" && exit 14
|
error "Directory /dev/shm not found!" && exit 14
|
||||||
else
|
else
|
||||||
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
|
[ ! -d "$QEMU_DIR" ] && ln -s /dev/shm "$QEMU_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
QEMU_PID="$QEMU_DIR/qemu.pid"
|
||||||
|
|
||||||
# Check folder
|
# Check folder
|
||||||
|
|
||||||
if [[ "${COMMIT:-}" == [Yy1]* ]]; then
|
if [[ "${STORAGE,,}" != "/storage" ]]; then
|
||||||
STORAGE="/local"
|
|
||||||
mkdir -p "$STORAGE"
|
mkdir -p "$STORAGE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -77,7 +113,9 @@ if [ ! -d "$STORAGE" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -w "$STORAGE" ]; then
|
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
|
fi
|
||||||
|
|
||||||
# Check filesystem
|
# Check filesystem
|
||||||
@@ -89,28 +127,27 @@ if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Read memory
|
# Read memory
|
||||||
RAM_SPARE=500000000
|
|
||||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||||
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
||||||
|
|
||||||
|
RAM_SPARE=500000000
|
||||||
|
RAM_MINIMUM=136314880
|
||||||
RAM_SIZE="${RAM_SIZE// /}"
|
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
|
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
|
# Print system info
|
||||||
SYS="${SYS/-generic/}"
|
SYS="${SYS/-generic/}"
|
||||||
FS="${FS/UNKNOWN //}"
|
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 "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
||||||
echo
|
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
|
fi
|
||||||
|
|
||||||
# Cleanup files
|
# Cleanup files
|
||||||
rm -f /run/shm/qemu.*
|
rm -f "$QEMU_DIR"/qemu.*
|
||||||
rm -f /run/shm/dsm.url
|
rm -f "$QEMU_DIR"/*.pid
|
||||||
|
rm -f "$QEMU_DIR"/dsm.url
|
||||||
|
|
||||||
# Cleanup dirs
|
# Cleanup dirs
|
||||||
rm -rf /tmp/dsm
|
rm -rf /tmp/dsm
|
||||||
rm -rf "$STORAGE/tmp"
|
rm -rf "$STORAGE/tmp"
|
||||||
|
|
||||||
getCountry() {
|
|
||||||
local url=$1
|
|
||||||
local query=$2
|
|
||||||
local rc json result
|
|
||||||
|
|
||||||
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
|
|
||||||
(( rc != 0 )) && return 0
|
|
||||||
|
|
||||||
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
|
|
||||||
(( rc != 0 )) && return 0
|
|
||||||
|
|
||||||
[[ ${#result} -ne 2 ]] && return 0
|
|
||||||
[[ "${result^^}" == "XX" ]] && return 0
|
|
||||||
|
|
||||||
COUNTRY="${result^^}"
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
setCountry() {
|
|
||||||
|
|
||||||
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
|
|
||||||
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
|
|
||||||
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
|
|
||||||
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
|
|
||||||
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
|
|
||||||
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
|
|
||||||
|
|
||||||
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
|
|
||||||
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
|
|
||||||
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
|
|
||||||
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
|
|
||||||
[ -z "$COUNTRY" ] && getCountry "https://api.ipquery.io/?format=json" ".location.country_code"
|
|
||||||
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
addPackage() {
|
|
||||||
local pkg=$1
|
|
||||||
local desc=$2
|
|
||||||
|
|
||||||
if apt-mark showinstall | grep -qx "$pkg"; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
MSG="Installing $desc..."
|
|
||||||
info "$MSG" && html "$MSG"
|
|
||||||
|
|
||||||
[ -z "$COUNTRY" ] && setCountry
|
|
||||||
|
|
||||||
if [[ "${COUNTRY^^}" == "CN" ]]; then
|
|
||||||
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get -qq update
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
: "${WEB_PORT:="5000"}" # Webserver port
|
|
||||||
|
|
||||||
cp -r /var/www/* /run/shm
|
|
||||||
html "Starting $APP for Docker..."
|
|
||||||
|
|
||||||
if [[ "${WEB:-}" != [Nn]* ]]; then
|
|
||||||
|
|
||||||
mkdir -p /etc/nginx/sites-enabled
|
|
||||||
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
|
|
||||||
|
|
||||||
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
|
|
||||||
|
|
||||||
# shellcheck disable=SC2143
|
|
||||||
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
|
||||||
|
|
||||||
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start webserver
|
|
||||||
nginx -e stderr
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+8
-11
@@ -24,6 +24,8 @@ if [ -n "$HOST_MAC" ]; then
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
HOST_PID="$QEMU_DIR/host.pid"
|
||||||
|
|
||||||
HOST_ARGS=()
|
HOST_ARGS=()
|
||||||
HOST_ARGS+=("-cpu=$CPU_CORES")
|
HOST_ARGS+=("-cpu=$CPU_CORES")
|
||||||
HOST_ARGS+=("-cpu_arch=$HOST_CPU")
|
HOST_ARGS+=("-cpu_arch=$HOST_CPU")
|
||||||
@@ -37,15 +39,17 @@ if [[ "$HOST_DEBUG" == [Yy1]* ]]; then
|
|||||||
set -x
|
set -x
|
||||||
./host.bin "${HOST_ARGS[@]}" &
|
./host.bin "${HOST_ARGS[@]}" &
|
||||||
{ set +x; } 2>/dev/null
|
{ set +x; } 2>/dev/null
|
||||||
|
echo "$!" > "$HOST_PID"
|
||||||
echo
|
echo
|
||||||
else
|
else
|
||||||
./host.bin "${HOST_ARGS[@]}" >/dev/null &
|
./host.bin "${HOST_ARGS[@]}" >/dev/null &
|
||||||
|
echo "$!" > "$HOST_PID"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cnt=0
|
cnt=0
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
|
|
||||||
while ! nc -z -w2 127.0.0.1 2210 > /dev/null 2>&1; do
|
while ! nc -z -w2 127.0.0.1 "$COM_PORT" > /dev/null 2>&1; do
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
cnt=$((cnt + 1))
|
cnt=$((cnt + 1))
|
||||||
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
|
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
|
||||||
@@ -53,23 +57,16 @@ done
|
|||||||
|
|
||||||
cnt=0
|
cnt=0
|
||||||
|
|
||||||
while ! nc -z -w2 127.0.0.1 12345 > /dev/null 2>&1; do
|
while ! nc -z -w2 127.0.0.1 "$CHR_PORT" > /dev/null 2>&1; do
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
cnt=$((cnt + 1))
|
cnt=$((cnt + 1))
|
||||||
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
|
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
|
||||||
done
|
done
|
||||||
|
|
||||||
# Configure serial ports
|
# Configure serial ports
|
||||||
|
SERIAL_OPTS="-serial mon:stdio \
|
||||||
if [[ "$CONSOLE" != [Yy]* ]]; then
|
|
||||||
SERIAL_OPTS="-serial pty"
|
|
||||||
else
|
|
||||||
SERIAL_OPTS="-serial mon:stdio"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SERIAL_OPTS+=" \
|
|
||||||
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
|
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
|
||||||
-chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \
|
-chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \
|
||||||
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
|
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -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() {
|
isAlive() {
|
||||||
local pid="$1"
|
local pid="$1"
|
||||||
|
[ -z "$pid" ] && return 1
|
||||||
|
|
||||||
if kill -0 "$pid" 2>/dev/null; then
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
return 0
|
return 0
|
||||||
@@ -36,23 +37,67 @@ isAlive() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pKill() {
|
waitPid() {
|
||||||
|
local i=0
|
||||||
local pid="$1"
|
local pid="$1"
|
||||||
|
local timeout="${2:-10}"
|
||||||
|
|
||||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
while [ -n "$pid" ] && isAlive "$pid"; do
|
||||||
|
|
||||||
while isAlive "$pid"; do
|
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
|
i=$((i + 1))
|
||||||
|
(( i >= timeout * 5 )) && return 1
|
||||||
done
|
done
|
||||||
|
|
||||||
return 0
|
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() {
|
fWait() {
|
||||||
|
local i=0
|
||||||
local name="$1"
|
local name="$1"
|
||||||
|
local timeout="${2:-10}"
|
||||||
|
|
||||||
|
[ -z "$name" ] && return 0
|
||||||
|
|
||||||
while pgrep -f -l "$name" >/dev/null; do
|
while pgrep -f -l "$name" >/dev/null; do
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
|
i=$((i + 1))
|
||||||
|
if (( i >= timeout * 5 )); then
|
||||||
|
warn "Timed out while waiting for process: $name"
|
||||||
|
break
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@@ -60,9 +105,75 @@ fWait() {
|
|||||||
|
|
||||||
fKill() {
|
fKill() {
|
||||||
local name="$1"
|
local name="$1"
|
||||||
|
local timeout="${2:-10}"
|
||||||
|
|
||||||
{ pkill -f "$name" || true; } 2>/dev/null
|
[ -z "$name" ] && return 0
|
||||||
fWait "$name"
|
|
||||||
|
{ 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
|
return 0
|
||||||
}
|
}
|
||||||
@@ -123,11 +234,11 @@ cpu() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cpu="${cpu// CPU/}"
|
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][0-9][0-9] Core}"
|
||||||
cpu="${cpu// [0-9]th Gen}"
|
cpu="${cpu// [0-9][0-9] Core}"
|
||||||
cpu="${cpu// [0-9][0-9]th Gen}"
|
cpu="${cpu// [0-9] Core}"
|
||||||
|
cpu="${cpu//[0-9][0-9]th Gen }"
|
||||||
|
cpu="${cpu//[0-9]th Gen }"
|
||||||
cpu="${cpu// Processor/}"
|
cpu="${cpu// Processor/}"
|
||||||
cpu="${cpu// Quad core/}"
|
cpu="${cpu// Quad core/}"
|
||||||
cpu="${cpu// Dual core/}"
|
cpu="${cpu// Dual core/}"
|
||||||
@@ -147,6 +258,8 @@ cpu() {
|
|||||||
|
|
||||||
hasDisk() {
|
hasDisk() {
|
||||||
|
|
||||||
|
[[ "${DISK_DISABLE:-}" == [Yy1]* ]] && return 1
|
||||||
|
|
||||||
[ -b "/disk" ] && return 0
|
[ -b "/disk" ] && return 0
|
||||||
[ -b "/disk1" ] && return 0
|
[ -b "/disk1" ] && return 0
|
||||||
[ -b "/dev/disk1" ] && return 0
|
[ -b "/dev/disk1" ] && return 0
|
||||||
@@ -159,4 +272,65 @@ hasDisk() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCountry() {
|
||||||
|
local url=$1
|
||||||
|
local query=$2
|
||||||
|
local rc json result
|
||||||
|
|
||||||
|
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
|
||||||
|
(( rc != 0 )) && return 0
|
||||||
|
|
||||||
|
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
|
||||||
|
(( rc != 0 )) && return 0
|
||||||
|
|
||||||
|
[[ ${#result} -ne 2 ]] && return 0
|
||||||
|
[[ "${result^^}" == "XX" ]] && return 0
|
||||||
|
|
||||||
|
COUNTRY="${result^^}"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
setCountry() {
|
||||||
|
|
||||||
|
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
|
||||||
|
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
|
||||||
|
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
|
||||||
|
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
|
||||||
|
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
|
||||||
|
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
|
||||||
|
|
||||||
|
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
|
||||||
|
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
|
||||||
|
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
|
||||||
|
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
|
||||||
|
[ -z "$COUNTRY" ] && getCountry "https://api.ipquery.io/?format=json" ".location.country_code"
|
||||||
|
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
addPackage() {
|
||||||
|
local pkg=$1
|
||||||
|
local desc=$2
|
||||||
|
|
||||||
|
if apt-mark showinstall | grep -qx "$pkg"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
MSG="Installing $desc..."
|
||||||
|
info "$MSG" && html "$MSG"
|
||||||
|
|
||||||
|
[ -z "$COUNTRY" ] && setCountry
|
||||||
|
|
||||||
|
if [[ "${COUNTRY^^}" == "CN" ]]; then
|
||||||
|
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get -qq update
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@@ -27,4 +27,18 @@ server {
|
|||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /status {
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_read_timeout 3600s;
|
||||||
|
proxy_send_timeout 3600s;
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:8004/;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-32
@@ -1,4 +1,5 @@
|
|||||||
var request;
|
var request;
|
||||||
|
var booting = false;
|
||||||
var interval = 1000;
|
var interval = 1000;
|
||||||
|
|
||||||
function getInfo() {
|
function getInfo() {
|
||||||
@@ -6,11 +7,14 @@ function getInfo() {
|
|||||||
var url = "msg.html";
|
var url = "msg.html";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (request) {
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
|
||||||
if (window.XMLHttpRequest) {
|
if (window.XMLHttpRequest) {
|
||||||
request = new XMLHttpRequest();
|
request = new XMLHttpRequest();
|
||||||
} else {
|
} else {
|
||||||
throw "XMLHttpRequest not available!";
|
throw new Error("XMLHttpRequest not available!");
|
||||||
}
|
}
|
||||||
|
|
||||||
request.onreadystatechange = processInfo;
|
request.onreadystatechange = processInfo;
|
||||||
@@ -18,22 +22,49 @@ function getInfo() {
|
|||||||
request.send();
|
request.send();
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var err = "Error: " + e.message;
|
setError("Error: " + e.message);
|
||||||
console.log(err);
|
|
||||||
setError(err);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getURL() {
|
||||||
|
|
||||||
|
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, '');
|
||||||
|
|
||||||
|
return protocol + "//" + window.location.host + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processMsg(msg) {
|
||||||
|
|
||||||
|
if (msg.toLowerCase().indexOf("href=") !== -1) {
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.innerHTML = msg;
|
||||||
|
var url = div.querySelector("a").href;
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.assign(url);
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
setInfo(msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function processInfo() {
|
function processInfo() {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (request.readyState != 4) {
|
if (request.readyState != 4) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = request.responseText;
|
var msg = request.responseText;
|
||||||
if (msg == null || msg.length == 0) {
|
if (msg == null || msg.length == 0) {
|
||||||
setInfo("Booting DSM instance", true);
|
|
||||||
schedule();
|
if (booting) {
|
||||||
|
schedule();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.reload();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,20 +74,11 @@ function processInfo() {
|
|||||||
if (msg.toLowerCase().indexOf("<html>") !== -1) {
|
if (msg.toLowerCase().indexOf("<html>") !== -1) {
|
||||||
notFound = true;
|
notFound = true;
|
||||||
} else {
|
} else {
|
||||||
if (msg.toLowerCase().indexOf("href=") !== -1) {
|
processMsg(msg);
|
||||||
var div = document.createElement("div");
|
if (msg.toLowerCase().indexOf("href=") == -1) {
|
||||||
div.innerHTML = msg;
|
|
||||||
var url = div.querySelector("a").href;
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.assign(url);
|
|
||||||
}, 3000);
|
|
||||||
setInfo(msg);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
setInfo(msg);
|
|
||||||
schedule();
|
schedule();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,44 +89,60 @@ function processInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setError("Error: Received statuscode " + request.status);
|
setError("Error: Received statuscode " + request.status);
|
||||||
schedule();
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
var err = "Error: " + e.message;
|
setError("Error: " + e.message);
|
||||||
console.log(err);
|
|
||||||
setError(err);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setInfo(msg, loading, error) {
|
function extractContent(s) {
|
||||||
|
var span = document.createElement('span');
|
||||||
|
span.innerHTML = s;
|
||||||
|
return span.textContent || span.innerText;
|
||||||
|
};
|
||||||
|
|
||||||
|
function setInfo(msg, loading, error) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (msg == null || msg.length == 0) {
|
if (msg == null || msg.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var el = document.getElementById("spinner");
|
if (msg.includes("Booting ")) {
|
||||||
|
booting = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var el = document.getElementById("info");
|
||||||
|
|
||||||
|
if (el.innerText == msg || el.innerHTML == msg) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var spin = document.getElementById("spinner");
|
||||||
|
|
||||||
error = !!error;
|
error = !!error;
|
||||||
if (!error) {
|
if (!error) {
|
||||||
el.style.visibility = 'visible';
|
spin.style.visibility = 'visible';
|
||||||
} else {
|
} else {
|
||||||
el.style.visibility = 'hidden';
|
spin.style.visibility = 'hidden';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var p = "<p class=\"loading\">";
|
||||||
loading = !!loading;
|
loading = !!loading;
|
||||||
if (loading) {
|
if (loading) {
|
||||||
msg = "<p class=\"loading\">" + msg + "</p>";
|
msg = p + msg + "</p>";
|
||||||
}
|
}
|
||||||
|
|
||||||
el = document.getElementById("info");
|
if (msg.includes(p)) {
|
||||||
|
if (el.innerHTML.includes(p)) {
|
||||||
if (el.innerHTML != msg) {
|
el.getElementsByClassName('loading')[0].textContent = extractContent(msg);
|
||||||
el.innerHTML = msg;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
el.innerHTML = msg;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -114,6 +152,7 @@ function setInfo(msg, loading, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setError(text) {
|
function setError(text) {
|
||||||
|
console.warn(text);
|
||||||
return setInfo(text, false, true);
|
return setInfo(text, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,8 +162,47 @@ function schedule() {
|
|||||||
|
|
||||||
function reload() {
|
function reload() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
document.location.reload();
|
window.location.reload();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
|
||||||
|
var wsUrl = getURL() + "/status";
|
||||||
|
var ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
ws.onmessage = function(e) {
|
||||||
|
|
||||||
|
var pos = e.data.indexOf(":");
|
||||||
|
var cmd = e.data.substring(0, pos);
|
||||||
|
var msg = e.data.substring(pos + 2);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case "s":
|
||||||
|
processMsg(msg);
|
||||||
|
break;
|
||||||
|
case "e":
|
||||||
|
setError(msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn("Unknown event: " + cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onclose = function(e) {
|
||||||
|
setTimeout(function() {
|
||||||
|
connect();
|
||||||
|
}, interval);
|
||||||
|
};
|
||||||
|
|
||||||
|
ws.onerror = function(e) {
|
||||||
|
ws.close();
|
||||||
|
if (!booting) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
schedule();
|
schedule();
|
||||||
|
connect();
|
||||||
|
|||||||
Reference in New Issue
Block a user