Compare commits

..
80 Commits
Author SHA1 Message Date
Kroese bc0defd813 feat: Add custom .yml for Github Codespaces (#1066) 2025-10-15 11:39:36 +02:00
Kroese 48e7a9fff0 fix: Round down minimum disk size (#1065) 2025-10-15 11:37:05 +02:00
Kroese c2fa58ef27 feat: Fall back to slirp when passt fails (#1064) 2025-10-15 10:31:42 +02:00
Kroese ea49cb144b feat: Validate user port configuration (#1063) 2025-10-14 16:44:11 +02:00
Kroese b8e778a79d fix: Configure ports for Slirp networking (#1062) 2025-10-14 14:16:20 +02:00
Kroese c70e12f0a2 fix: Lower spare disk space (#1061)
Reduced spare disk space threshold from 2GB to 512MB.
2025-10-14 03:33:25 +02:00
Kroese 6281205912 feat: Add "max" setting for DISK_SIZE (#1060) 2025-10-14 01:22:44 +02:00
Kroese 1ffc5c55b2 fix: Show Passt output on error (#1059) 2025-10-13 14:11:54 +02:00
Kroese c3302e1720 fix: Rename physical to logical (#1058) 2025-10-13 09:28:25 +02:00
Kroese 25227944b5 fix: Do not reset loading animation (#1056) 2025-10-12 01:51:17 +02:00
Kroese 06650e916a build: Run check for all files (#1057)
Removed specific paths from pull request triggers.
2025-10-12 01:48:25 +02:00
Kroese 2e34dffed5 feat: Expose only selected ports with Passt (#1055) 2025-10-12 01:44:24 +02:00
Kroese 3db91f077f fix: Relay last status message (#1054) 2025-10-11 17:56:44 +02:00
Kroese fae14f4dd9 feat: Set listening interface for Passt (#1052) 2025-10-09 11:48:08 +02:00
Kroese d190b3ba87 feat: Use websocket for status messages (#1051) 2025-10-08 21:28:26 +02:00
Kroese 554f3295cb fix: Terminate tail on exit (#1049) 2025-10-08 08:13:27 +02:00
Kroese cdc4689d6a feat: Use the engine variable (#1048) 2025-10-07 12:41:42 +02:00
Kroese 21d18fd439 feat: Improve healthcheck (#1047) 2025-10-07 05:53:06 +02:00
Kroese fd5ec52226 fix: Check if logfile exists (#1046) 2025-10-07 02:21:49 +02:00
Kroese fc7ab22741 fix: Use gateway MAC for passt (#1045) 2025-10-07 02:15:26 +02:00
Kroese 59cf59faa4 feat: Improved networking implementation (#1044) 2025-10-07 00:58:34 +02:00
Kroese 705c6ce7f1 fix: Exclude 'tap' from bus check (#1043) 2025-10-05 17:06:57 +02:00
Kroese f2937ab507 feat: Allow large MTU sizes (#1042) 2025-10-04 10:35:20 +02:00
Kroese 399829cf3c feat: Make monitor port configurable (#1041) 2025-10-04 09:39:20 +02:00
xrh0905 b694d6faf8 feat: Support 8k sector sizes (#1040) 2025-10-03 12:57:08 +02:00
Kroese 7acd1f6cdb feat: Enhanced Dnsmasq logging (#1039) 2025-10-03 01:54:44 +02:00
Kroese 09ca3bf118 feat: Add debug trace option (#1038) 2025-10-02 17:18:53 +02:00
renovate[bot] 6cac45c397 chore(deps): update peter-evans/dockerhub-description action to v5 (#1037)
Update / dockerHubDescription (push) Has been cancelled
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 23:39:30 +02:00
Kroese f18663d840 feat: Network mode detection (#1036) 2025-10-01 13:05:13 +02:00
Kroese fefe1af9e6 fix: CPU detection (#1035)
Refactor CPU string processing to remove 'th Gen' suffix.
2025-09-30 17:18:43 +02:00
Kroese 87a8cf7513 feat: Support 32k sector sizes (#1034) 2025-09-30 10:49:07 +02:00
Kroese 7f31cb6023 fix: Detect host mode networking (#1033) 2025-09-30 10:32:00 +02:00
Kroese 138742c953 feat: Increase default disksize (#1030)
Update / dockerHubDescription (push) Has been cancelled
2025-09-28 18:22:44 +02:00
Kroese 2c6efc45f2 feat: Make webserver port configurable (#1028) 2025-09-27 11:44:13 +02:00
Kroese 24d795fbe3 buid: Add ethtool package (#1027) 2025-09-27 10:45:09 +02:00
Kroese 7fae62d286 feat: Detect host mode networking (#1026) 2025-09-27 10:44:02 +02:00
Kroese 2135df07ea docs: Additional info for user-mode networking (#1025) 2025-09-27 10:08:11 +02:00
Kroese 521beedf1c feat: Add note for MAC address availability (#1024) 2025-09-27 09:49:11 +02:00
Kroese e362c9a8a9 feat: Update error message for KVM acceleration (#1023) 2025-09-24 16:10:32 +02:00
Kroese 78817ecfc1 fix: Remove default model (#1022) 2025-09-23 16:04:44 +02:00
Kroese 8de7f56ff8 feat: Add KVM warning (#1021) 2025-09-23 16:02:42 +02:00
Kroese 2e2017470e fix: Syntax for arithmetic operations (#1020) 2025-09-22 20:24:23 +02:00
Kroese 0a0cd98de3 feat: Add "max" setting to automaticly set CPU cores and RAM (#1019) 2025-09-22 16:59:02 +02:00
renovate[bot] 17187dd23a chore(deps): update hadolint/hadolint-action action to v3.3.0 (#1018)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 16:56:58 +02:00
Kroese 10237b31cf revert: Simplify conditional checks (#1017)
This reverts commit 6883efb857.
2025-09-21 23:08:00 +02:00
Kroese 6883efb857 fix: Simplify conditional checks (#1016) 2025-09-21 15:09:52 +02:00
Kroese 1ca55cad8f feat: Limit CPU_CORES to amount of physical cores (#1015) 2025-09-21 13:41:20 +02:00
Kroese f841eb1ef1 fix: Simplify conditional checks (#1014) 2025-09-20 23:47:00 +02:00
Kroese 1ee49332f3 feat: Improve CPU detection (#1013) 2025-09-20 21:43:35 +02:00
Kroese 095e618785 feat: Make webserver optional (#1012) 2025-09-18 23:45:05 +02:00
Kroese 57a902ad9f feat: add UDP support to USER_PORTS (#1011)
Allow USER_PORTS entries in the form PORT or PORT/PROTO (tcp or udp). When the protocol is omitted, TCP is assumed for backward compatibility. This enables forwarding of UDP services when running the container in user‑mode networking.
2025-09-17 21:49:20 +02:00
xrh0905 a7e229aaae feat: Add IPQuery.io as additional comparison source for the country code (#1006) 2025-09-17 17:55:54 +02:00
Kroese 10466d7851 feat: Parse CPU flags (#1010) 2025-09-17 17:54:50 +02:00
Kroese 238ebd273b build: Disable automatic builds (#1009)
Removed push triggers and paths to ignore from build workflow.
2025-09-14 11:54:51 +02:00
renovate[bot] 0b08f4212e chore(deps): update hadolint/hadolint-action action to v3.2.0 (#1008)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 16:43:41 +02:00
Kroese 9aec5d20e3 feat: Check if storage folder is writeable (#1005)
Build / Check (push) Has been cancelled
Update / dockerHubDescription (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-08-27 22:50:23 +02:00
Kroese a19bf2f066 feat: Check if storage folder is writeable (#1004) 2025-08-27 22:48:23 +02:00
Kroese bee68ee548 feat: Add syntax parser directive (#1003) 2025-08-27 21:29:28 +02:00
xrh0905 520c70ab22 feat: Allow shutdown timeout override (#995)
Mitigation #987
2025-08-27 19:58:02 +02:00
dependabot[bot] 67a7fbc94b build(deps): Bump actions/checkout from 4 to 5 (#1001)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-27 19:56:41 +02:00
Kroese 1b8054e847 fix: Podman detection (#990)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-05-27 00:50:40 +02:00
Kroese 631a558a9f feat: Enable IPv6 support for user-mode container networking (#983)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-27 11:28:54 +02:00
Kroese d5805b4e08 feat: Check CPU vendor (#982) 2025-04-27 11:21:37 +02:00
Kroese 1dc15ce2e0 docs: Add input types (#981) 2025-04-23 12:54:22 +02:00
Kroese 4145e811df feat: Improve CPU detection (#980)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-20 21:43:02 +02:00
Kroese fe8615d7d3 docs: Add quotes around $PWD (#977)
Build / Check (push) Has been cancelled
Update / dockerHubDescription (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-16 12:10:59 +02:00
Kroese 27ea5506bc feat: Default to 2 CPU cores and 2GB of RAM (#976) 2025-04-16 11:56:02 +02:00
Kroese 394131a0d7 feat: Podman detection (#972)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-09 09:01:01 +02:00
Kroese 4d0d16023f feat: Podman detection (#971) 2025-04-09 08:58:02 +02:00
Kroese 60dd794b29 fix: Avoid pipe to head on find (#970) 2025-04-09 08:03:41 +02:00
Kroese f13d104968 build: Add root-user-action flag (#968)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-08 07:53:17 +02:00
Kroese 914099fd12 build: Add --root-user-action flag (#967)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-07 18:47:36 +02:00
Kroese 639725957d fix: Permission errors on QNAP (#966) 2025-04-07 18:36:47 +02:00
Kroese 64bbe82c4a fix: KVM warning (#965)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
Update / dockerHubDescription (push) Has been cancelled
2025-04-07 17:44:26 +02:00
Kroese 73a13e5697 fix: QNAP permission errors (#964) 2025-04-07 17:20:14 +02:00
Kroese e8ec5fb8ed feat: Support 4k sector sizes (#963) 2025-04-07 13:25:20 +02:00
Kroese 0b500ca377 docs: Disk pass-through (#960)
Update / dockerHubDescription (push) Has been cancelled
2025-04-03 10:22:26 +02:00
Kroese df58745ed8 fix: Ignore missing custom .pat after install (#959)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-03 01:13:14 +02:00
Kroese 55e8ab4930 docs: Github Codespaces (#956)
Update / dockerHubDescription (push) Has been cancelled
2025-03-27 01:48:40 +01:00
Kroese 4882fd3bf6 fix: Local file support (#955)
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-26 19:51:58 +01:00
31 changed files with 1024 additions and 487 deletions

No files matched your search

+1 -1
View File
@@ -2,5 +2,5 @@
"name": "dsm",
"service": "dsm",
"forwardPorts": [5000],
"dockerComposeFile": "compose.yml"
"dockerComposeFile": ".github/codespaces.yml"
}
+2
View File
@@ -21,6 +21,7 @@ body:
attributes:
label: Docker compose
description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations:
required: true
- type: textarea
@@ -28,6 +29,7 @@ body:
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`).
render: shell
validations:
required: true
- type: textarea
+2
View File
@@ -23,6 +23,7 @@ body:
attributes:
label: Docker compose
description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations:
required: true
- type: textarea
@@ -30,6 +31,7 @@ body:
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`).
render: shell
validations:
required: true
- type: textarea
+19
View File
@@ -0,0 +1,19 @@
services:
dsm:
container_name: dsm
image: vdsm/virtual-dsm
environment:
RAM_SIZE: "max"
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
+1 -15
View File
@@ -2,20 +2,6 @@ name: Build
on:
workflow_dispatch:
push:
branches:
- master
paths-ignore:
- '**/*.md'
- '**/*.yml'
- '**/*.js'
- '**/*.css'
- '**/*.html'
- 'web/**'
- '.gitignore'
- '.dockerignore'
- '.github/**'
- '.github/workflows/**'
concurrency:
group: build
@@ -36,7 +22,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0
-
+2 -2
View File
@@ -9,7 +9,7 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
-
name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
@@ -17,7 +17,7 @@ jobs:
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
-
name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.1.0
uses: hadolint/hadolint-action@v3.3.0
with:
dockerfile: Dockerfile
ignore: DL3008,DL3003,DL3006,DL3013
+2 -2
View File
@@ -12,10 +12,10 @@ jobs:
dockerHubDescription:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
-
name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4
uses: peter-evans/dockerhub-description@v5
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-5
View File
@@ -1,11 +1,6 @@
on:
workflow_dispatch:
pull_request:
paths:
- '**/*.sh'
- 'Dockerfile'
- '.github/workflows/test.yml'
- '.github/workflows/check.yml'
name: "Test"
permissions: {}
+14 -6
View File
@@ -1,3 +1,5 @@
# syntax=docker/dockerfile:1
FROM qemux/qemu-host:2.05 AS builder
# FROM golang as builder
@@ -9,6 +11,7 @@ FROM qemux/qemu-host:2.05 AS builder
FROM debian:trixie-slim
ARG TARGETARCH
ARG TARGETPLATFORM
ARG VERSION_ARG="0.0"
ARG DEBCONF_NOWARNINGS="yes"
@@ -26,6 +29,7 @@ RUN set -eu && \
unzip \
nginx \
procps \
ethtool \
python3 \
python3-pip \
python3-msgpack \
@@ -33,18 +37,22 @@ RUN set -eu && \
xz-utils \
iptables \
iproute2 \
apt-utils \
dnsmasq \
fakeroot \
apt-utils \
net-tools \
e2fsprogs \
qemu-utils \
websocketd \
iputils-ping \
inotify-tools \
ca-certificates \
netcat-openbsd \
qemu-system-x86 && \
wget "https://github.com/qemus/passt/releases/download/v2025_09_19/passt_2025_09_19_${TARGETARCH}.deb" -O /tmp/passt.deb -q && \
dpkg -i /tmp/passt.deb && \
apt-get clean && \
pip3 install --no-cache-dir --break-system-packages dissect.cstruct && \
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
mkdir -p /etc/qemu && \
echo "allow br0" > /etc/qemu/bridge.conf && \
unlink /etc/nginx/sites-enabled/default && \
@@ -55,15 +63,15 @@ RUN set -eu && \
COPY --chmod=755 ./src /run/
COPY --chmod=755 ./web /var/www/
COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/sites-enabled/web.conf
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/default.conf
ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py
VOLUME /storage
EXPOSE 22 139 445 5000
ENV RAM_SIZE="1G"
ENV DISK_SIZE="16G"
ENV CPU_CORES="1"
ENV RAM_SIZE="2G"
ENV CPU_CORES="2"
ENV DISK_SIZE="256G"
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh
+1 -1
View File
@@ -3,7 +3,7 @@ services:
container_name: dsm
image: vdsm/virtual-dsm
environment:
DISK_SIZE: "16G"
DISK_SIZE: "256G"
devices:
- /dev/kvm
- /dev/net/tun
+2 -2
View File
@@ -8,7 +8,7 @@ spec:
- ReadWriteOnce
resources:
requests:
storage: 16Gi
storage: 256Gi
---
apiVersion: apps/v1
kind: Deployment
@@ -31,7 +31,7 @@ spec:
image: vdsm/virtual-dsm
env:
- name: DISK_SIZE
value: "16G"
value: "256G"
ports:
- containerPort: 5000
name: http
+12 -32
View File
@@ -30,7 +30,7 @@ services:
container_name: dsm
image: vdsm/virtual-dsm
environment:
DISK_SIZE: "16G"
DISK_SIZE: "256G"
devices:
- /dev/kvm
- /dev/net/tun
@@ -47,7 +47,7 @@ services:
##### Via Docker CLI:
```bash
docker run -it --rm --name dsm -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v ${PWD:-.}/dsm:/storage --stop-timeout 120 vdsm/virtual-dsm
docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm
```
##### Via Kubernetes:
@@ -58,7 +58,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
##### Via Github Codespaces:
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?repo=619260050&machine=basicLinux32gb)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/vdsm/virtual-dsm)
## FAQ 💬
@@ -87,35 +87,24 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I change the size of the disk?
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
To expand the default size of 256 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
```yaml
environment:
DISK_SIZE: "128G"
DISK_SIZE: "512G"
```
> [!TIP]
> This can also be used to resize the existing disk to a larger capacity without any data loss.
### How do I create a growable disk?
By default, the entire capacity of the disk will be reserved in advance.
To create a growable disk that only allocates space that is actually used, add the following environment variable:
```yaml
environment:
DISK_FMT: "qcow2"
```
### How do I add multiple disks?
To create additional disks, modify your compose file like this:
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
DISK2_SIZE: "500G"
DISK3_SIZE: "750G"
volumes:
- ./example2:/storage2
- ./example3:/storage3
@@ -123,20 +112,19 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I pass-through a disk?
It is possible to pass-through a disk device directly, which can be useful when your host is a virtual machine, as it removes an extra layer and allows for easier image management on the host.
For use with physical disks this method provides little advantage over using an image and is not recommended. You can add the device to your compose file like this:
It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way:
```yaml
devices:
- /dev/sdc:/disk1
- /dev/sdb:/disk1
- /dev/sdc1:/disk2
```
Replace `/dev/sdc` with the name of the disk. It needs to be totally empty (without any partition table) otherwise DSM does not always format it into a volume.
Make sure it is totally empty (without any filesystem), otherwise DSM may not format it as a volume.
### How do I change the amount of CPU or RAM?
By default, the container will be allowed to use a maximum of 1 CPU core and 1 GB of RAM.
By default, the container will be allowed to use a maximum of 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:
@@ -264,14 +252,6 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
### How do I run Windows in a container?
You can use [dockur/windows](https://github.com/dockur/windows) for that. It shares many of the same features, and even has completely automatic installation.
### How do I run a Linux desktop in a container?
You can use [qemus/qemu](https://github.com/qemus/qemu) in that case.
### Is this project legal?
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project will be considered legal.
+7 -5
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
@@ -9,6 +10,7 @@ set -Eeuo pipefail
file="/run/shm/dsm.url"
address="/run/shm/qemu.ip"
gateway="/run/shm/qemu.gw"
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
@@ -16,13 +18,13 @@ location=$(<"$file")
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
if [[ "$location" == "20.20"* ]]; then
ip="20.20.20.1"
if [[ "$DHCP" == [Yy1]* ]]; then
ip=$(<"$address")
echo "Failed to reach DSM at http://$location"
else
ip=$(<"$gateway")
port="${location##*:}"
echo "Failed to reach DSM at port $port"
else
echo "Failed to reach DSM at http://$location"
ip=$(<"$address")
fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1
+1 -1
View File
@@ -37,7 +37,7 @@ if [[ "$RAM_CHECK" != [Nn]* ]]; then
fi
if [[ "$DEBUG" == [Yy1]* ]]; then
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi
return 0
+48 -23
View File
@@ -82,7 +82,7 @@ isCow() {
supportsDirect() {
local FS=$1
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
return 1
fi
@@ -98,10 +98,10 @@ createDisk() {
local FS=$5
local DATA_SIZE DIR SPACE GB FA
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
rm -f "$DISK_FILE"
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace
@@ -320,7 +320,7 @@ convertDisk() {
msg="Conversion of $DISK_DESC"
html "$msg completed..."
info "$msg to $DST_FMT completed succesfully!"
info "$msg to $DST_FMT completed successfully!"
return 0
}
@@ -368,6 +368,8 @@ createDevice () {
local DISK_FMT=$5
local DISK_IO=$6
local DISK_CACHE=$7
local DISK_SERIAL=$8
local DISK_SECTORS=$9
local DISK_ID="data$DISK_INDEX"
local index=""
@@ -381,29 +383,29 @@ createDevice () {
;;
"usb" )
result+=",if=none \
-device usb-storage,drive=${DISK_ID}${index}"
-device usb-storage,drive=${DISK_ID}${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"nvme" )
result+=",if=none \
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}"
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"ide" | "sata" )
result+=",if=none \
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \
-device ide-hd,drive=${DISK_ID},bus=ahci$DISK_INDEX.0,rotation_rate=$DISK_ROTATION${index}"
-device ide-hd,drive=${DISK_ID},bus=ahci$DISK_INDEX.0,rotation_rate=$DISK_ROTATION${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"blk" | "virtio-blk" )
result+=",if=none \
-device virtio-blk-pci,drive=${DISK_ID},bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}"
-device virtio-blk-pci,drive=${DISK_ID},bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"scsi" | "virtio-scsi" )
result+=",if=none \
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \
-device scsi-hd,drive=${DISK_ID},bus=${DISK_ID}b.0,channel=0,scsi-id=0,lun=0,rotation_rate=$DISK_ROTATION${index}"
-device scsi-hd,drive=${DISK_ID},bus=${DISK_ID}b.0,channel=0,scsi-id=0,lun=0,rotation_rate=$DISK_ROTATION${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
esac
@@ -422,7 +424,7 @@ addDisk () {
local DISK_FMT=$7
local DISK_IO=$8
local DISK_CACHE=$9
local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DISK_EXT=$(fmt2ext "$DISK_FMT")
local DISK_FILE="$DISK_BASE.$DISK_EXT"
@@ -430,6 +432,16 @@ addDisk () {
DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0
if [[ "${DISK_SPACE,,}" == "max" ]]; then
local SPARE=536870912
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
(( SPACE < SPARE )) && SPACE="$SPARE" || SPACE=$((SPACE-SPARE))
GB=$(( SPACE/1073741825 ))
DISK_SPACE="${GB}G"
fi
SPACE="${DISK_SPACE// /}"
[ -z "$SPACE" ] && SPACE="16G"
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
@@ -482,7 +494,7 @@ addDisk () {
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
}
@@ -497,12 +509,29 @@ addDevice () {
[ -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
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE")
local sectors=""
local result logical physical
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}"
if [ -n "$physical" ]; then
if [[ "$physical" != "512" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
else
warn "Failed to determine the sector size for $DISK_DEV"
fi
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
return 0
}
html "Initializing disks..."
msg="Initializing disks..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
@@ -514,11 +543,7 @@ case "${DISK_TYPE,,}" in
esac
if [ -z "$ALLOCATE" ]; then
if [[ "${DISK_FMT,,}" == "raw" ]]; then
ALLOCATE="Y"
else
ALLOCATE="N"
fi
ALLOCATE="N"
fi
if [[ "$ALLOCATE" == [Nn]* ]]; then
@@ -529,11 +554,11 @@ else
DISK_ALLOC="preallocation=falloc"
fi
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE")
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE")
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" "" "")
DISK1_FILE="$STORAGE/${DISK_NAME}"
if [[ ! -f "$DISK1_FILE.img" ]] && [[ -f "$STORAGE/data${DISK_SIZE}.img" ]]; then
if [ ! -f "$DISK1_FILE.img" ] && [ -f "$STORAGE/data${DISK_SIZE}.img" ]; then
# Fallback for legacy installs
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi
@@ -542,7 +567,7 @@ 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
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
@@ -555,7 +580,7 @@ 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
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
+7 -1
View File
@@ -8,7 +8,9 @@ set -Eeuo pipefail
: "${DISPLAY:="none"}" # Display type
: "${RENDERNODE:="/dev/dri/renderD128"}" # Render node
if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}')
if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64" ]]; then
[[ "${DISPLAY,,}" == "none" ]] && VGA="none"
DISPLAY_OPTS="-display $DISPLAY -vga $VGA"
@@ -16,6 +18,10 @@ if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
fi
msg="Configuring display drivers..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
DISPLAY_OPTS+=" -vga $VGA"
+4 -1
View File
@@ -1,13 +1,16 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${PLATFORM:="x64"}"
: "${APP:="Virtual DSM"}"
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
cd /run
. start.sh # Startup hook
. utils.sh # Load functions
. reset.sh # Initialize system
. server.sh # Start webserver
. install.sh # Run installation
. disk.sh # Initialize disks
. display.sh # Initialize graphics
@@ -30,7 +33,7 @@ fi
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
terminal
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
sleep 1 & wait $!
+28 -28
View File
@@ -13,32 +13,35 @@ else
fi
FN="boot.pat"
if [ -d "/$FN" ]; then
error "The file /$FN does not exist, please check that you mapped it to a valid path!" && exit 65
DIR=$(find / -maxdepth 1 -type d -iname "$FN" -print -quit)
[ ! -d "$DIR" ] && DIR=$(find "$STORAGE" -maxdepth 1 -type d -iname "$FN" -print -quit)
if [ -d "$DIR" ]; then
BASE="DSM_VirtualDSM" && URL="file://$DIR"
if [[ ! -s "$STORAGE/$BASE.boot.img" || ! -s "$STORAGE/$BASE.system.img" ]]; then
error "The bind $DIR maps to a file that does not exist!" && exit 65
fi
fi
FILE=$(find / -maxdepth 1 -type f -iname "$FN" | head -n 1)
[ ! -s "$FILE" ] && FILE=$(find "$STORAGE" -maxdepth 1 -type f -iname "$FN" | head -n 1)
FILE=$(find / -maxdepth 1 -type f -iname "$FN" -print -quit)
[ ! -s "$FILE" ] && FILE=$(find "$STORAGE" -maxdepth 1 -type f -iname "$FN" -print -quit)
[ -s "$FILE" ] && BASE="DSM_VirtualDSM" && URL="file://$FILE"
if [ -n "$URL" ]; then
if [ ! -s "$FILE" ]; then
BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
fi
if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }"
printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi
if [[ "${URL,,}" != "http"* ]] && [[ "${URL,,}" != "file:"* ]] ; then
URL="file:///$BASE.pat"
if [ ! -s "/$BASE.pat" ]; then
error "File '$BASE.pat' does not exist!" && exit 65
fi
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
URL="file://$STORAGE/$BASE.pat"
fi
fi
if [[ -s "$STORAGE/$BASE.boot.img" ]] && [[ -s "$STORAGE/$BASE.system.img" ]]; then
if [[ -s "$STORAGE/$BASE.boot.img" && -s "$STORAGE/$BASE.system.img" ]]; then
return 0 # Previous installation found
fi
@@ -63,8 +66,9 @@ fi
if [ ! -s "$FILE" ]; then
BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
BASE="${BASE//+/ }"
printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
@@ -86,7 +90,7 @@ if [[ "${FS,,}" == "fuse"* ]]; then
info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
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..."
fi
@@ -268,12 +272,9 @@ mv "$HDA.tgz" "$HDA.txz"
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
[ -s "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/"
if [ -s "$IDB.txz" ]; then
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb/"
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb"
mkdir -p "$INDEX_DB"
tar xpfJ "$IDB.txz" --absolute-names -C "$INDEX_DB"
fi
LABEL="1.44.1-42218"
@@ -282,6 +283,8 @@ NUMBLOCKS="2097152" # (16777216 * 512) / 4096
MSG="Installing system partition..."
fakeroot -- bash -c "set -Eeu;\
[ -s $HDP.txz ] && tar xpfJ $HDP.txz --absolute-names -C $MOUNT/;\
[ -s $IDB.txz ] && tar xpfJ $IDB.txz --absolute-names -C $INDEX_DB/;\
tar xpfJ $HDA.txz --absolute-names --skip-old-files -C $MOUNT/;\
printf '%b%s%b' '\E[1;34m❯ \E[1;36m' 'Install: $MSG' '\E[0m\n';\
mke2fs -q -t ext4 -b 4096 -d $MOUNT/ -L $LABEL -F -E offset=$OFFSET $SYSTEM $NUMBLOCKS"
@@ -298,7 +301,4 @@ fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
rm -rf "$TMP"
html "Booting DSM instance..."
sleep 1.2
return 0
+482 -168
View File
@@ -7,17 +7,25 @@ set -Eeuo pipefail
: "${MTU:=""}"
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
: "${USER_PORTS:=""}"
: "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}"
: "${ADAPTER:="virtio-net-pci"}"
: "${VM_NET_IP:=""}"
: "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="dsm"}"
: "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_IP:="20.20.20.21"}"
: "${VM_NET_BRIDGE:="docker"}"
: "${VM_NET_HOST:="VirtualDSM"}"
: "${VM_NET_MASK:="255.255.255.0"}"
: "${PASST:="passt"}"
: "${PASST_MTU:=""}"
: "${PASST_OPTS:=""}"
: "${PASST_DEBUG:=""}"
: "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ_DEBUG:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
@@ -29,6 +37,8 @@ ADD_ERR="Please add the following setting to your container:"
configureDHCP() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
# Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then
@@ -59,14 +69,15 @@ configureDHCP() {
fi ;;
esac
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size.."
warn "Failed to set MTU size to $MTU."
fi
fi
while ! ip link set "$VM_NET_TAP" up; do
info "Waiting for MAC address $VM_NET_MAC to become available..."
info "If you cloned this machine, please delete the 'dsm.mac' file to generate a different MAC address."
sleep 2
done
@@ -78,7 +89,7 @@ configureDHCP() {
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev)
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && return 1
[[ ! -e "$TAP_PATH" ]] && [[ -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
[[ ! -e "$TAP_PATH" && -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
if [[ ! -e "$TAP_PATH" ]]; then
{ mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
@@ -104,94 +115,284 @@ configureDHCP() {
configureDNS() {
# Create lease file for faster resolve
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
chmod 644 /var/lib/misc/dnsmasq.leases
local if="$1"
local ip="$2"
local mac="$3"
local host="$4"
local mask="$5"
local gateway="$6"
# dnsmasq configuration:
DNSMASQ_OPTS+=" --dhcp-authoritative"
echo "$gateway" > /run/shm/qemu.gw
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
# Set DHCP range and host
DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP"
DNSMASQ_OPTS+=" --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite"
local log="/var/log/dnsmasq.log"
rm -f "$log"
# Set DNS server and gateway
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,255.255.255.0"
DNSMASQ_OPTS+=" --dhcp-option=option:router,${VM_NET_IP%.*}.1"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1"
case "${NETWORK,,}" in
"nat" | "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:
DNSMASQ_OPTS+=" --dhcp-authoritative"
# Set DHCP range and host
DNSMASQ_OPTS+=" --dhcp-range=$ip,$ip"
DNSMASQ_OPTS+=" --dhcp-host=$mac,,$ip,$host,infinite"
# Set DNS server and gateway
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,$mask"
DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway"
esac
# Set interfaces
DNSMASQ_OPTS+=" --interface=$if"
DNSMASQ_OPTS+=" --bind-interfaces"
# Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1"
DNSMASQ_OPTS+=" --address=/host.lan/$gateway"
# Set local dns resolver to dnsmasq when needed
[ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"
# Enable logging to file
DNSMASQ_OPTS+=" --log-facility=$log"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
if [[ "${DEBUG_DNS:-}" == [Yy1]* ]]; then
DNSMASQ_OPTS+=" -d"
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
return 0
fi
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}"
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && return 1
local msg="Failed to start Dnsmasq, reason: $?"
[ -f "$log" ] && cat "$log"
error "$msg"
return 1
fi
return 0
}
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
fi
getUserPorts() {
local args=""
local list=$1
local ssh="22"
local dsm="5000"
[ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm"
list="${list//,/ }"
list="${list## }"
list="${list%% }"
for port in $list; do
args+="hostfwd=tcp::$port-$VM_NET_IP:$port,"
done
echo "${args%?}"
return 0
}
getHostPorts() {
local list=$1
local list=""
list+="$MON_PORT,"
list+="${HOST_PORTS// /},"
[ -z "$list" ] && echo "" && return 0
if [[ "$list" != *","* ]]; then
echo " ! --dport $list"
else
echo " -m multiport ! --dports $list"
fi
# Remove duplicates
list=$(echo "${list//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
echo "$list"
return 0
}
configureUser() {
getUserPorts() {
NET_OPTS="-netdev user,id=hostnet0,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,hostname=$VM_NET_HOST"
local ssh="22"
local dsm="5000,5001"
local forward
forward=$(getUserPorts "$USER_PORTS")
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 val="${hostport///tcp}"
if [[ "$num" == "${val///udp}" ]]; then
num=""
warn "Could not assign port ${val///udp} to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!"
fi
done
[ -n "$num" ] && ports+="$userport,"
done
# Remove duplicates
ports=$(echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
echo "$ports"
return 0
}
getSlirp() {
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-$VM_NET_IP:$num,"
proto="udp"
num="${port%/udp}"
fi
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
done
args=$(echo "$args" | sed 's/,*$//g')
echo "${args%?}"
return 0
}
configureSlirp() {
[[ "$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)
[ -n "$forward" ] && NET_OPTS+=",$forward"
if [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]]; then
echo "$gateway" > /run/shm/qemu.gw
else
[ ! -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
VM_NET_IP="$ip"
return 0
}
configurePasst() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
local log="/var/log/passt.log"
rm -f "$log"
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else
gateway="${ip%.*}.2"
fi
# passt configuration:
[ -z "$IP6" ] && PASST_OPTS+=" -4"
PASST_OPTS+=" -a $ip"
PASST_OPTS+=" -g $gateway"
PASST_OPTS+=" -n $VM_NET_MASK"
[ -n "$PASST_MTU" ] && PASST_OPTS+=" -m $PASST_MTU"
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 /var/run/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/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
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" ] && 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" ] && 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
}
configureNAT() {
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring NAT networking..."
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[[ "$PODMAN" == [Yy1]* ]] && return 1
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
@@ -199,50 +400,64 @@ configureNAT() {
fi
if [ ! -c /dev/net/tun ]; then
[[ "$PODMAN" != [Yy1]* ]] && error "$tuntap"
return 1
warn "$tuntap" && return 1
fi
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null; 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
[[ "$PODMAN" != [Yy1]* ]] && 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
fi
fi
local ip base
base=$(echo "$IP" | sed -r 's/([^.]*.){2}//')
if [[ "$IP" != "172.30."* ]]; then
ip="172.30.$base"
else
ip="172.31.$base"
fi
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else
gateway="${ip%.*}.2"
fi
# Create a bridge with a static IP for the VM guest
{ ip link add dev dockerbridge type bridge ; rc=$?; } || :
{ ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
if (( rc != 0 )); then
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then
error "Failed to add IP address pool!" && return 1
if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then
warn "failed to add IP address pool!" && return 1
fi
while ! ip link set dockerbridge up; do
while ! ip link set "$VM_NET_BRIDGE" up; do
info "Waiting for IP address to become available..."
sleep 2
done
# QEMU Works with taps, set tap to the bridge created
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
error "$tuntap" && return 1
warn "$tuntap" && return 1
fi
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size.."
warn "failed to set MTU size to $MTU."
fi
fi
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
warn "Failed to set gateway MAC address.."
warn "failed to set gateway MAC address.."
fi
while ! ip link set "$VM_NET_TAP" up promisc on; do
@@ -250,27 +465,39 @@ configureNAT() {
sleep 2
done
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
error "Failed to set IP link!" && return 1
if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
warn "failed to set master bridge!" && return 1
fi
# Add internet connection to the VM
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
if grep -wq "nf_tables" /proc/modules; 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
exclude=$(getHostPorts "$HOST_PORTS")
exclude=$(getHostPorts)
if [ -n "$exclude" ]; then
if [[ "$exclude" != *","* ]]; then
exclude=" ! --dport $exclude"
else
exclude=" -m multiport ! --dports $exclude"
fi
fi
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
error "$tables" && return 1
warn "$tables" && return 1
fi
# shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then
error "Failed to configure IP tables!" && return 1
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$ip"; then
warn "failed to configure IP tables!" && return 1
fi
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then
error "Failed to configure IP tables!" && return 1
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$ip"; then
warn "failed to configure IP tables!" && return 1
fi
if (( KERNEL > 4 )); then
@@ -287,8 +514,9 @@ configureNAT() {
NET_OPTS+=",script=no,downscript=no"
configureDNS || return 1
configureDNS "$VM_NET_BRIDGE" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0
}
@@ -296,26 +524,43 @@ closeBridge() {
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
[[ "${NETWORK,,}" == "user"* ]] && return 0
pid="/var/run/passt.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
case "${NETWORK,,}" in
"user"* | "passt" | "slirp" ) return 0 ;;
esac
ip link set "$VM_NET_TAP" down promisc off &> null || true
ip link delete "$VM_NET_TAP" &> null || true
ip link set dockerbridge down &> null || true
ip link delete dockerbridge &> null || true
ip link set "$VM_NET_BRIDGE" down &> null || true
ip link delete "$VM_NET_BRIDGE" &> null || true
return 0
}
closeWeb() {
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
# Shutdown websocket
local pid="/var/run/websocketd.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
return 0
}
closeNetwork() {
if [[ "$DHCP" == [Yy1]* ]]; then
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then
closeWeb
fi
[[ "$NETWORK" == [Nn]* ]] && return 0
@@ -336,19 +581,34 @@ closeNetwork() {
return 0
}
cleanUp() {
# Clean up old files
rm -f /etc/resolv.dnsmasq
rm -f /var/run/passt.pid
rm -f /var/run/dnsmasq.pid
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
info "Lingering interface will be removed..."
ip link delete "$VM_NET_TAP" || true
fi
return 0
}
checkOS() {
local name
local kernel
local os=""
local if="macvlan"
name=$(uname -a)
kernel=$(uname -a)
[[ "${name,,}" == *"darwin"* ]] && os="Docker Desktop for macOS"
[[ "${name,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows"
[[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
[[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
if [[ "$DHCP" == [Yy1]* ]]; then
if="macvtap"
[[ "${name,,}" == *"synology"* ]] && os="Synology Container Manager"
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
fi
if [ -n "$os" ]; then
@@ -376,22 +636,66 @@ getInfo() {
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
fi
BASE_IP="${VM_NET_IP%.*}."
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
{ IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1); rc=$?; } 2>/dev/null || :
if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27
fi
if [ -z "$MTU" ]; then
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
if (( rc != 0 )); then
error "Could not determine container IP address!" && exit 26
fi
if [ "$MTU" -gt "1500" ]; then
info "MTU size is too large: $MTU, ignoring..." && MTU="0"
IP6=""
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
{ IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up); rc=$?; } 2>/dev/null || :
(( rc != 0 )) && IP6=""
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
fi
local result nic bus
result=$(ethtool -i "$VM_NET_DEV")
nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $(2)}')
bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $(2)}')
if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus"
error "This container does not support host mode networking!"
exit 29
fi
if [[ "$DHCP" == [Yy1]* ]]; then
checkOS
if [[ "${nic,,}" == "ipvlan" ]]; then
error "This container does not support IPVLAN networking when DHCP=Y."
exit 29
fi
if [[ "${nic,,}" != "macvlan" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $nic"
error "The container needs to be in a MACVLAN network when DHCP=Y."
exit 29
fi
else
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
checkOS
fi
fi
local mtu=""
if [ -f "/sys/class/net/$VM_NET_DEV/mtu" ]; then
mtu=$(< "/sys/class/net/$VM_NET_DEV/mtu")
fi
[ -z "$MTU" ] && MTU="$mtu"
[ -z "$MTU" ] && MTU="0"
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
fi
fi
@@ -419,18 +723,28 @@ getInfo() {
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
IP6=""
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then
if [ -z "$NETWORK" ] || [[ "${NETWORK^^}" == "Y" ]]; then
# By default Podman has no permissions for NAT networking
NETWORK="user"
fi
fi
if [[ "$DEBUG" == [Yy1]* ]]; then
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi
[ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
echo "$IP" > /run/shm/qemu.ip
echo "$nic" > /run/shm/qemu.nic
return 0
}
@@ -444,84 +758,84 @@ if [[ "$NETWORK" == [Nn]* ]]; then
return 0
fi
getInfo
html "Initializing network..."
msg="Initializing network..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
if [[ "$DEBUG" == [Yy1]* ]]; then
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" ]] && [[ "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi
getInfo
cleanUp
if [[ "$IP" == "172.17."* ]]; then
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
fi
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
info "Lingering interface will be removed..."
ip link delete "$VM_NET_TAP" || true
fi
MSG="Booting DSM instance..."
html "$MSG"
if [[ "$DHCP" == [Yy1]* ]]; then
checkOS
if [[ "$IP" == "172."* ]]; then
warn "container IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
fi
# Configure for macvtap interface
configureDHCP || exit 20
MSG="Booting DSM instance..."
html "$MSG"
else
if [[ "$IP" != "172."* ]] && [[ "$IP" != "10.8"* ]] && [[ "$IP" != "10.9"* ]]; then
checkOS
if [[ "${WEB:-}" != [Nn]* ]]; then
sleep 1.2
closeWeb
fi
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
case "${NETWORK,,}" in
"user"* | "passt" | "slirp" ) ;;
"nat" | "tap" | "tun" | "tuntap" | "y" )
if [[ "${NETWORK,,}" != "user"* ]]; then
# Configure tap interface
if ! configureNAT; then
# Configure for tap interface
if ! configureNAT; then
closeBridge
NETWORK="user"
msg="falling back to user-mode networking!"
msg="failed to setup NAT networking, $msg"
closeBridge
NETWORK="user"
msg="falling back to user-mode networking!"
if [[ "$PODMAN" != [Yy1]* ]]; then
msg="an error occured, $msg"
else
msg="podman detected, $msg"
fi
warn "$msg"
[ -z "$USER_PORTS" ] && info "Notice: port mapping will not work without \"USER_PORTS\" now."
fi ;;
fi
esac
fi
[[ "${NETWORK,,}" == "user"* ]] && NETWORK="passt"
if [[ "${NETWORK,,}" == "user"* ]]; then
case "${NETWORK,,}" in
"nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
"passt" )
# Configure for user-mode networking (slirp)
configureUser || exit 24
# Configure for user-mode networking (passt)
if ! configurePasst; then
error "Failed to configure user-mode networking!"
exit 24
fi ;;
fi
"slirp" )
# Configure for user-mode networking (slirp)
if ! configureSlirp; then
error "Failed to configure user-mode networking!"
exit 24
fi ;;
*)
error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
esac
case "${NETWORK,,}" in
"passt" | "slirp" )
if [ -z "$USER_PORTS" ]; then
info "Notice: because user-mode networking is active, if you need to expose ports, add them to the \"USER_PORTS\" variable."
fi ;;
esac
fi
NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
[[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
[[ "$MTU" != "0" && "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
return 0
+7 -7
View File
@@ -1,15 +1,15 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${API_TIMEOUT:="50"}" # API Call timeout
: "${QEMU_TIMEOUT:="50"}" # QEMU Termination timeout
# Configure QEMU for graceful shutdown
API_CMD=6
API_TIMEOUT=50
API_HOST="127.0.0.1:2210"
API_HOST="127.0.0.1:$COM_PORT"
QEMU_TERM=""
QEMU_PORT=7100
QEMU_TIMEOUT=50
QEMU_DIR="/run/shm"
QEMU_PID="$QEMU_DIR/qemu.pid"
QEMU_LOG="$QEMU_DIR/qemu.log"
@@ -83,7 +83,7 @@ terminal() {
fi
if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$MON_PORT" | tr -d '\000')
dev="${dev#*serial0}"
dev="${dev#*pty:}"
dev="${dev%%$'\n'*}"
@@ -127,7 +127,7 @@ _graceful_shutdown() {
fi
# Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null
# Send shutdown command to guest agent via serial port
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
@@ -172,7 +172,7 @@ _graceful_shutdown() {
MON_OPTS="\
-pidfile $QEMU_PID \
-name $PROCESS,process=$PROCESS,debug-threads=on \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
-monitor telnet:localhost:$MON_PORT,server,nowait,nodelay"
if [[ "$CONSOLE" != [Yy]* ]]; then
+5 -7
View File
@@ -11,6 +11,7 @@ error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: $1" "\E[0m\n" >&2; }
file="/run/shm/dsm.url"
info="/run/shm/msg.html"
driver="/run/shm/qemu.nic"
page="/run/shm/index.html"
address="/run/shm/qemu.ip"
shutdown="/run/shm/qemu.end"
@@ -56,11 +57,7 @@ do
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue
if [ -z "$ip" ]; then
[[ "$DHCP" == [Yy1]* ]] && continue
ip="20.20.20.21"
fi
[ -z "$ip" ] && continue
echo "$ip:$port" > $file
done
@@ -69,7 +66,7 @@ done
location=$(<"$file")
if [[ "$location" != "20.20"* ]]; then
if [[ "$DHCP" == [Yy1]* ]]; then
msg="http://$location"
title="<title>Virtual DSM</title>"
@@ -88,10 +85,11 @@ if [[ "$location" != "20.20"* ]]; then
else
nic=$(<"$driver")
ip=$(<"$address")
port="${location##*:}"
if [[ "$ip" == "172."* ]]; then
if [[ "${nic,,}" != "macvlan" ]]; then
msg="port $port"
else
msg="http://$ip:$port"
+27 -38
View File
@@ -3,11 +3,9 @@ set -Eeuo pipefail
# Docker environment variables
: "${KVM:="Y"}"
: "${HOST_CPU:=""}"
: "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}"
: "${DEF_MODEL:="qemu64"}"
CLOCKSOURCE="tsc"
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
@@ -27,40 +25,7 @@ else
esac
fi
if [[ "${ARCH,,}" != "amd64" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, this will cause a major loss of performance."
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(device file missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(no write access)"
else
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(vmx/svm disabled)"
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, this will cause a major loss of performance."
else
error "KVM acceleration not available $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ on how to diagnose the cause, or continue without KVM by setting KVM=N (not recommended)."
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if [[ "$KVM" != [Nn]* ]]; then
@@ -69,7 +34,7 @@ if [[ "$KVM" != [Nn]* ]]; 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..."
[ -z "$CPU_MODEL" ] && CPU_MODEL="$DEF_MODEL"
[ -z "$CPU_MODEL" ] && CPU_MODEL="qemu64"
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi
@@ -112,7 +77,7 @@ else
CPU_MODEL="max"
CPU_FEATURES+=",migratable=no"
else
CPU_MODEL="$DEF_MODEL"
CPU_MODEL="qemu64"
fi
fi
@@ -120,6 +85,30 @@ else
fi
if [[ "$ARGUMENTS" == *"-cpu host,"* ]]; then
args="${ARGUMENTS} "
prefix="${args/-cpu host,*/}"
suffix="${args/*-cpu host,/}"
param="${suffix%% *}"
suffix="${suffix#* }"
args="${prefix}${suffix}"
ARGUMENTS="${args::-1}"
if [ -z "$CPU_FLAGS" ]; then
CPU_FLAGS="$param"
else
CPU_FLAGS+=",$param"
fi
else
if [[ "$ARGUMENTS" == *"-cpu host"* ]]; then
ARGUMENTS="${ARGUMENTS//-cpu host/}"
fi
fi
if [ -z "$CPU_FLAGS" ]; then
if [ -z "$CPU_FEATURES" ]; then
CPU_FLAGS="$CPU_MODEL"
+1 -1
View File
@@ -25,7 +25,7 @@ do
if [ -s "$file" ]; then
bytes=$(du -sb "$file" | cut -f1)
if (( bytes > 1000 )); then
if [ -z "$total" ] || [[ "$total" == "0" ]]; then
if [ -z "$total" ] || [[ "$total" == "0" ]] || [ "$bytes" -gt "$total" ]; then
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
else
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
+92 -85
View File
@@ -2,36 +2,45 @@
set -Eeuo pipefail
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
[ ! -f "/run/entry.sh" ] && error "Script must be run inside the container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
echo "❯ Starting $APP for Docker v$(</run/version)..."
echo "❯ For support visit $SUPPORT"
# Docker environment variables
: "${TZ:=""}" # System local timezone
: "${TZ:=""}" # System timezone
: "${KVM:="Y"}" # KVM acceleration
: "${DEBUG:="N"}" # Disable debugging mode
: "${COUNTRY:=""}" # Country code for mirror
: "${CONSOLE:="N"}" # Disable console mode
: "${ALLOCATE:=""}" # Preallocate diskspace
: "${ARGUMENTS:=""}" # Extra QEMU parameters
: "${CPU_CORES:="1"}" # Amount of CPU cores
: "${RAM_SIZE:="1G"}" # Maximum RAM amount
: "${CPU_CORES:="2"}" # Amount of CPU cores
: "${RAM_SIZE:="2G"}" # Maximum RAM amount
: "${RAM_CHECK:="Y"}" # Check available RAM
: "${DISK_SIZE:="16G"}" # Initial data disk size
: "${STORAGE:="/storage"}" # Storage folder location
# Helper variables
PODMAN="N"
ENGINE="Docker"
PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}"
if [ -f "/run/.containerenv" ]; then
PODMAN="Y"
ENGINE="Podman"
fi
echo "❯ Starting $APP for $ENGINE v$(</run/version)..."
echo "❯ For support visit $SUPPORT"
INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html"
FOOTER1="$APP for Docker v$(</run/version)"
FOOTER1="$APP for $ENGINE v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
CPU=$(cpu)
@@ -48,8 +57,15 @@ else
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
fi
CPU_CORES="${CPU_CORES// /}"
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
if [ "$CPU_CORES" -gt "$CORES" ]; then
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available, so will be limited to ${CORES}."
CPU_CORES="$CORES"
fi
# Check system
if [ ! -d "/dev/shm" ]; then
@@ -69,10 +85,14 @@ if [ ! -d "$STORAGE" ]; then
error "Storage folder ($STORAGE) not found!" && exit 13
fi
if [ ! -w "$STORAGE" ]; then
error "Storage folder ($STORAGE) is not writeable!" && exit 13
fi
# Check filesystem
FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
DISK_IO="threads"
DISK_CACHE="writeback"
fi
@@ -85,6 +105,12 @@ RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
RAM_SIZE="${RAM_SIZE// /}"
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
if [[ "${RAM_SIZE,,}" == "max" ]]; then
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE - RAM_SPARE ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
RAM_SIZE="${RAM_WANTED}G"
fi
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
fi
@@ -116,6 +142,63 @@ if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); th
info "$msg"
fi
# Check KVM support
if [[ "${PLATFORM,,}" == "x64" ]]; then
TARGET="amd64"
else
TARGET="arm64"
fi
if [[ "$KVM" == [Nn]* ]]; then
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
else
if [[ "${ARCH,,}" != "$TARGET" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ${PLATFORM^^} instructions, so the machine will run about 10 times slower."
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(/dev/kvm is unwriteable)"
else
if [[ "${PLATFORM,,}" == "x64" ]]; then
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
# Cleanup files
rm -f /run/shm/qemu.*
rm -f /run/shm/dsm.url
@@ -124,80 +207,4 @@ rm -f /run/shm/dsm.url
rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp"
getCountry() {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.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
}
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen 5000 default_server;/listen [::]:5000 default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
else
sed -i "s/listen [::]:5000 default_server ipv6only=off;/listen 5000 default_server;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
return 0
+3 -3
View File
@@ -45,7 +45,7 @@ fi
cnt=0
sleep 0.2
while ! nc -z -w2 127.0.0.1 2210 > /dev/null 2>&1; do
while ! nc -z -w2 127.0.0.1 "$COM_PORT" > /dev/null 2>&1; do
sleep 0.1
cnt=$((cnt + 1))
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
@@ -53,7 +53,7 @@ done
cnt=0
while ! nc -z -w2 127.0.0.1 12345 > /dev/null 2>&1; do
while ! nc -z -w2 127.0.0.1 "$CHR_PORT" > /dev/null 2>&1; do
sleep 0.1
cnt=$((cnt + 1))
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
@@ -69,7 +69,7 @@ fi
SERIAL_OPTS+=" \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
return 0
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${COM_PORT:="2210"}" # Comm port
: "${MON_PORT:="7100"}" # Monitor port
: "${WEB_PORT:="5000"}" # Webserver port
: "${CHR_PORT:="12345"}" # Character port
: "${WSD_PORT:="8004"}" # Websockets port
cp -r /var/www/* /run/shm
rm -f /var/run/websocketd.pid
html "Starting $APP for $ENGINE..."
if [[ "${WEB:-}" != [Nn]* ]]; then
mkdir -p /etc/nginx/sites-enabled
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
nginx -e stderr
# Start websocket server
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
echo "$!" > /var/run/websocketd.pid
fi
return 0
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -Eeuo pipefail
path="/run/shm/msg.html"
if [ -f "$path" ] && [ -s "$path" ]; then
echo -n "s: " && cat "$path"
fi
inotifywait -m "$path" |
while read -r fp event fn; do
case "${event,,}" in
"modify"* ) echo -n "s: " && cat "$path" ;;
"delete_self" ) echo "c: vnc" ;;
esac
done
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# You can override this hook to execute a script before startup!
return 0
+73 -21
View File
@@ -113,41 +113,32 @@ cpu() {
local cpu=""
ret=$(lscpu)
if grep -qi "model name" <<< "$ret"; then
cpu=$(echo "$ret" | grep -m 1 -i 'model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
if [ -z "${cpu// /}" ] && grep -qi "model:" <<< "$ret"; then
cpu=$(echo "$ret" | grep -m 1 -i 'model:' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
cpu="${cpu// CPU/}"
cpu="${cpu// 4 Core/}"
cpu="${cpu// 6 Core/}"
cpu="${cpu// 8 Core/}"
cpu="${cpu// 10 Core/}"
cpu="${cpu// 12 Core/}"
cpu="${cpu// 16 Core/}"
cpu="${cpu// 32 Core/}"
cpu="${cpu// 64 Core/}"
cpu="${cpu//7th Gen /}"
cpu="${cpu//8th Gen /}"
cpu="${cpu//9th Gen /}"
cpu="${cpu//10th Gen /}"
cpu="${cpu//11th Gen /}"
cpu="${cpu//12th Gen /}"
cpu="${cpu//13th Gen /}"
cpu="${cpu//14th Gen /}"
cpu="${cpu//15th Gen /}"
cpu="${cpu// [0-9] Core}"
cpu="${cpu// [0-9][0-9] Core}"
cpu="${cpu// [0-9][0-9][0-9] Core}"
cpu="${cpu//[0-9]th Gen }"
cpu="${cpu//[0-9][0-9]th Gen }"
cpu="${cpu// Processor/}"
cpu="${cpu// Quad core/}"
cpu="${cpu// Dual core/}"
cpu="${cpu// Octa core/}"
cpu="${cpu// Hexa core/}"
cpu="${cpu// Core TM/ Core}"
cpu="${cpu// with Radeon Graphics/}"
cpu="${cpu// with Radeon Vega Graphics/}"
cpu="${cpu// with Radeon Vega Mobile Gfx/}"
cpu="${cpu// w Radeon [0-9][0-9][0-9]M Graphics/}"
[ -z "${cpu// /}" ] && cpu="Unknown"
echo "$cpu"
@@ -168,4 +159,65 @@ hasDisk() {
return 1
}
getCountry() {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.ipquery.io/?format=json" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0
}
addPackage() {
local pkg=$1
local desc=$2
if apt-mark showinstall | grep -qx "$pkg"; then
return 0
fi
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
[ -z "$COUNTRY" ] && setCountry
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
return 0
+14
View File
@@ -27,4 +27,18 @@ server {
index index.html;
}
location /status {
proxy_http_version 1.1;
proxy_set_header Connection 'upgrade';
proxy_set_header Upgrade $http_upgrade;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_pass http://127.0.0.1:8004/;
}
}
+106 -32
View File
@@ -1,4 +1,5 @@
var request;
var booting = false;
var interval = 1000;
function getInfo() {
@@ -6,7 +7,6 @@ function getInfo() {
var url = "msg.html";
try {
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
@@ -18,22 +18,49 @@ function getInfo() {
request.send();
} catch (e) {
var err = "Error: " + e.message;
console.log(err);
setError(err);
setError("Error: " + e.message);
}
}
function getURL() {
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, '');
return protocol + "//" + window.location.host + path;
}
function processMsg(msg) {
if (msg.toLowerCase().indexOf("href=") !== -1) {
var div = document.createElement("div");
div.innerHTML = msg;
var url = div.querySelector("a").href;
setTimeout(() => {
window.location.assign(url);
}, 3000);
}
setInfo(msg);
return true;
}
function processInfo() {
try {
if (request.readyState != 4) {
return true;
}
var msg = request.responseText;
if (msg == null || msg.length == 0) {
setInfo("Booting DSM instance", true);
schedule();
if (booting) {
schedule();
return true;
}
window.location.reload();
return false;
}
@@ -43,20 +70,11 @@ function processInfo() {
if (msg.toLowerCase().indexOf("<html>") !== -1) {
notFound = true;
} else {
if (msg.toLowerCase().indexOf("href=") !== -1) {
var div = document.createElement("div");
div.innerHTML = msg;
var url = div.querySelector("a").href;
setTimeout(() => {
window.location.assign(url);
}, 3000);
setInfo(msg);
return true;
} else {
setInfo(msg);
processMsg(msg);
if (msg.toLowerCase().indexOf("href=") == -1) {
schedule();
return true;
}
return true;
}
}
@@ -67,44 +85,60 @@ function processInfo() {
}
setError("Error: Received statuscode " + request.status);
schedule();
return false;
} catch (e) {
var err = "Error: " + e.message;
console.log(err);
setError(err);
setError("Error: " + e.message);
return false;
}
}
function setInfo(msg, loading, error) {
function extractContent(s) {
var span = document.createElement('span');
span.innerHTML = s;
return span.textContent || span.innerText;
};
function setInfo(msg, loading, error) {
try {
if (msg == null || msg.length == 0) {
return false;
}
var el = document.getElementById("spinner");
if (msg.includes("Booting ")) {
booting = true;
}
var el = document.getElementById("info");
if (el.innerText == msg || el.innerHTML == msg) {
return true;
}
var spin = document.getElementById("spinner");
error = !!error;
if (!error) {
el.style.visibility = 'visible';
spin.style.visibility = 'visible';
} else {
el.style.visibility = 'hidden';
spin.style.visibility = 'hidden';
}
var p = "<p class=\"loading\">";
loading = !!loading;
if (loading) {
msg = "<p class=\"loading\">" + msg + "</p>";
msg = p + msg + "</p>";
}
el = document.getElementById("info");
if (el.innerHTML != msg) {
el.innerHTML = msg;
if (msg.includes(p)) {
if (el.innerHTML.includes(p)) {
el.getElementsByClassName('loading')[0].textContent = extractContent(msg);
return true;
}
}
el.innerHTML = msg;
return true;
} catch (e) {
@@ -114,6 +148,7 @@ function setInfo(msg, loading, error) {
}
function setError(text) {
console.warn(text);
return setInfo(text, false, true);
}
@@ -123,8 +158,47 @@ function schedule() {
function reload() {
setTimeout(() => {
document.location.reload();
window.location.reload();
}, 3000);
}
function connect() {
var wsUrl = getURL() + "/status";
var ws = new WebSocket(wsUrl);
ws.onmessage = function(e) {
var pos = e.data.indexOf(":");
var cmd = e.data.substring(0, pos);
var msg = e.data.substring(pos + 2);
switch (cmd) {
case "s":
processMsg(msg);
break;
case "e":
setError(msg);
break;
default:
console.warn("Unknown event: " + cmd);
break;
}
};
ws.onclose = function(e) {
setTimeout(function() {
connect();
}, interval);
};
ws.onerror = function(e) {
ws.close();
if (!booting) {
window.location.reload();
}
};
}
schedule();
connect();