Compare commits

...

119 Commits

Author SHA1 Message Date
Kroese
3db91f077f fix: Relay last status message (#1054) 2025-10-11 17:56:44 +02:00
Kroese
fae14f4dd9 feat: Set listening interface for Passt (#1052) 2025-10-09 11:48:08 +02:00
Kroese
d190b3ba87 feat: Use websocket for status messages (#1051) 2025-10-08 21:28:26 +02:00
Kroese
554f3295cb fix: Terminate tail on exit (#1049) 2025-10-08 08:13:27 +02:00
Kroese
cdc4689d6a feat: Use the engine variable (#1048) 2025-10-07 12:41:42 +02:00
Kroese
21d18fd439 feat: Improve healthcheck (#1047) 2025-10-07 05:53:06 +02:00
Kroese
fd5ec52226 fix: Check if logfile exists (#1046) 2025-10-07 02:21:49 +02:00
Kroese
fc7ab22741 fix: Use gateway MAC for passt (#1045) 2025-10-07 02:15:26 +02:00
Kroese
59cf59faa4 feat: Improved networking implementation (#1044) 2025-10-07 00:58:34 +02:00
Kroese
705c6ce7f1 fix: Exclude 'tap' from bus check (#1043) 2025-10-05 17:06:57 +02:00
Kroese
f2937ab507 feat: Allow large MTU sizes (#1042) 2025-10-04 10:35:20 +02:00
Kroese
399829cf3c feat: Make monitor port configurable (#1041) 2025-10-04 09:39:20 +02:00
xrh0905
b694d6faf8 feat: Support 8k sector sizes (#1040) 2025-10-03 12:57:08 +02:00
Kroese
7acd1f6cdb feat: Enhanced Dnsmasq logging (#1039) 2025-10-03 01:54:44 +02:00
Kroese
09ca3bf118 feat: Add debug trace option (#1038) 2025-10-02 17:18:53 +02:00
renovate[bot]
6cac45c397 chore(deps): update peter-evans/dockerhub-description action to v5 (#1037)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 23:39:30 +02:00
Kroese
f18663d840 feat: Network mode detection (#1036) 2025-10-01 13:05:13 +02:00
Kroese
fefe1af9e6 fix: CPU detection (#1035)
Refactor CPU string processing to remove 'th Gen' suffix.
2025-09-30 17:18:43 +02:00
Kroese
87a8cf7513 feat: Support 32k sector sizes (#1034) 2025-09-30 10:49:07 +02:00
Kroese
7f31cb6023 fix: Detect host mode networking (#1033) 2025-09-30 10:32:00 +02:00
Kroese
138742c953 feat: Increase default disksize (#1030)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-09-28 18:22:44 +02:00
Kroese
2c6efc45f2 feat: Make webserver port configurable (#1028) 2025-09-27 11:44:13 +02:00
Kroese
24d795fbe3 buid: Add ethtool package (#1027) 2025-09-27 10:45:09 +02:00
Kroese
7fae62d286 feat: Detect host mode networking (#1026) 2025-09-27 10:44:02 +02:00
Kroese
2135df07ea docs: Additional info for user-mode networking (#1025) 2025-09-27 10:08:11 +02:00
Kroese
521beedf1c feat: Add note for MAC address availability (#1024) 2025-09-27 09:49:11 +02:00
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)
Some checks failed
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)
Some checks failed
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)
Some checks failed
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)
Some checks failed
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)
Some checks failed
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)
Some checks failed
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)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-04-08 07:53:17 +02:00
Kroese
914099fd12 build: Add --root-user-action flag (#967)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
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) 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)
Some checks failed
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
Update / dockerHubDescription (push) Has been cancelled
2025-04-07 13:25:20 +02:00
Kroese
0b500ca377 docs: Disk pass-through (#960)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-04-03 10:22:26 +02:00
Kroese
df58745ed8 fix: Ignore missing custom .pat after install (#959)
Some checks failed
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)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-27 01:48:40 +01:00
Kroese
4882fd3bf6 fix: Local file support (#955)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-26 19:51:58 +01:00
Kroese
3e69f4bcc9 feat: New extraction method (#954)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-26 09:24:13 +01:00
Kroese
39428909d6 docs: Installation (#953)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-26 02:47:28 +01:00
Kroese
71d2ed40db docs: Github Codespaces (#952)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-25 14:49:46 +01:00
Kroese
9ad21a28a6 docs: Disk pass-through (#951)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-25 12:18:38 +01:00
Kroese
0c7acd6e71 fix: Remove non-printable characters (#949)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-24 13:52:38 +01:00
Kroese
ad26129375 docs: KVM information (#947)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-20 23:21:41 +01:00
Kroese
4e7822fedd docs: Compatibility chart (#946) 2025-03-20 23:07:14 +01:00
Kroese
4dfcbe4ab1 docs: Add Podman (#945) 2025-03-20 20:09:53 +01:00
Kroese
8fb6f1f9ad feat: Podman detection (#944)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-20 11:54:26 +01:00
Kroese
11d4fafa6d fix: User-mode networking (#943)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-20 03:07:01 +01:00
Kroese
be15557798 fix: User-mode networking (#942) 2025-03-20 03:05:50 +01:00
Kroese
1e83757494 fix: Set user-mode IP address (#941)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-19 12:55:53 +01:00
Kroese
fad463a439 feat: Add devcontainer (#940)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-19 09:17:48 +01:00
Kroese
608563d029 feat: Add devcontainer (#939)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-18 19:25:27 +01:00
Kroese
5fcd22b09f docs: Formatting (#938)
Some checks failed
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
Update / dockerHubDescription (push) Has been cancelled
2025-03-18 14:24:05 +01:00
Kroese
65548da2ef docs: Github Codespaces (#937) 2025-03-18 14:10:08 +01:00
Kroese
111e513dac docs: Github Codespaces (#936) 2025-03-18 14:09:10 +01:00
Kroese
7fce3a98fe feat: Refactor helper functions (#935) 2025-03-18 12:57:52 +01:00
Kroese
4018eeadb4 fix: Remove port 80 (#934)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
Update / dockerHubDescription (push) Waiting to run
2025-03-18 05:03:54 +01:00
Kroese
a8e4647fa6 feat: IPv6 support (#933) 2025-03-18 03:59:58 +01:00
Kroese
36d3fca4fc fix: Fallback to POSIX fallocate (#932)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-17 11:19:04 +01:00
Kroese
89b28f36d3 docs: Disk pass-through (#930)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-16 12:30:06 +01:00
Kroese
b4f7d70a7a docs: Readme (#929)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-16 06:52:14 +01:00
Kroese
902bafbd0c docs: Readme (#928) 2025-03-16 06:40:38 +01:00
Kroese
7ad5c7fa70 fix: Display available memory (#926)
Some checks failed
Update / dockerHubDescription (push) Waiting to run
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-15 14:09:17 +01:00
Kroese
4d29f056b7 fix: Format filesizes (#925)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-14 18:53:53 +01:00
Kroese
36af9a3483 feat: Validate configured RAM (#924)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-14 14:42:08 +01:00
Kroese
5e6f931433 feat: Detect clock source (#923) 2025-03-14 14:16:10 +01:00
Kroese
beadfe720c feat: Improve CPU detection (#921)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-14 05:23:49 +01:00
Kroese
92b4bfc383 feat: Validate configured RAM (#920)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-13 11:35:08 +01:00
Kroese
092ed23085 feat: Check CPU core configuration (#919) 2025-03-13 10:47:25 +01:00
Kroese
c70fa3d00a feat: Validate specified size (#918)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-12 21:17:47 +01:00
Kroese
fea0ba09f6 docs: Use relative paths (#917)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-12 12:31:50 +01:00
Kroese
7d2af63eac docs: Kubernetes deployment (#916)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-07 00:42:29 +01:00
Kroese
2e73bf560e docs: Add link to download (#915) 2025-03-07 00:22:29 +01:00
Kroese
bfc8d7a9c6 fix: Network shutdown (#912)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-05 04:59:57 +01:00
Kroese
a1e9936572 fix: Set MTU value (#911)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-04 15:37:37 +01:00
Kroese
3675a50129 fix: Gateway MAC generation (#910)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-03 13:57:53 +01:00
Kroese
91229152bd feat: Set MTU size for TAP interface (#909) 2025-03-03 13:40:15 +01:00
Kroese
a1993e590a feat: Automaticly match MTU size (#908) 2025-03-03 12:20:16 +01:00
Kroese
52fe712b6f docs: Readme (#907)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-01 14:23:51 +01:00
Kroese
7f400b6b59 feat: Improve CPU detection (#905)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-02-27 11:52:48 +01:00
Kroese
9cb88a99e6 feat: Allow bridge networks (#904) 2025-02-27 11:46:38 +01:00
Kroese
b967a471b5 fix: Add e2fsprogs package (#902)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-26 10:03:46 +01:00
Kroese
f40127df01 feat: Make app name configurable (#900)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-25 15:13:57 +01:00
Kroese
8c5e0ee274 fix: Generate local MAC address (#899)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-24 21:01:55 +01:00
Kroese
2adf0b292b fix: Preserve gateway MAC address (#898)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-24 03:48:42 +01:00
Kroese
1c9b793c75 fix: Move nginx config (#893)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-02-15 02:36:07 +01:00
Kroese
00c4ef7795 feat: Remove existing TAP interface (#892)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-14 20:19:12 +01:00
Kroese
619657adf2 docs: Add restart policy (#888)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-02-10 00:31:32 +01:00
Kroese
41db7c1035 feat: Improve CPU detection (#884)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-02-06 02:19:39 +01:00
29 changed files with 1379 additions and 822 deletions

6
.devcontainer.json Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "dsm",
"service": "dsm",
"forwardPorts": [5000],
"dockerComposeFile": "compose.yml"
}

View File

@@ -21,6 +21,7 @@ body:
attributes: attributes:
label: Docker compose label: Docker compose
description: The compose file (or otherwise the `docker run` command used). description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations: validations:
required: true required: true
- type: textarea - type: textarea
@@ -28,6 +29,7 @@ body:
attributes: attributes:
label: Docker log label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`). description: The logfile of the container (as shown by `docker logs dsm`).
render: shell
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@@ -23,6 +23,7 @@ body:
attributes: attributes:
label: Docker compose label: Docker compose
description: The compose file (or otherwise the `docker run` command used). description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations: validations:
required: true required: true
- type: textarea - type: textarea
@@ -30,6 +31,7 @@ body:
attributes: attributes:
label: Docker log label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`). description: The logfile of the container (as shown by `docker logs dsm`).
render: shell
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@@ -2,20 +2,6 @@ name: Build
on: on:
workflow_dispatch: workflow_dispatch:
push:
branches:
- master
paths-ignore:
- '**/*.md'
- '**/*.yml'
- '**/*.js'
- '**/*.css'
- '**/*.html'
- 'web/**'
- '.gitignore'
- '.dockerignore'
- '.github/**'
- '.github/workflows/**'
concurrency: concurrency:
group: build group: build
@@ -36,7 +22,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
- -

View File

@@ -9,7 +9,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v5
- -
name: Run ShellCheck name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
@@ -17,8 +17,8 @@ jobs:
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028 SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
- -
name: Lint Dockerfile name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.1.0 uses: hadolint/hadolint-action@v3.3.0
with: with:
dockerfile: Dockerfile dockerfile: Dockerfile
ignore: DL3008,DL3003,DL3006 ignore: DL3008,DL3003,DL3006,DL3013
failure-threshold: warning failure-threshold: warning

View File

@@ -12,10 +12,10 @@ jobs:
dockerHubDescription: dockerHubDescription:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v5
- -
name: Docker Hub Description name: Docker Hub Description
uses: peter-evans/dockerhub-description@v4 uses: peter-evans/dockerhub-description@v5
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@@ -1,3 +1,5 @@
# syntax=docker/dockerfile:1
FROM qemux/qemu-host:2.05 AS builder FROM qemux/qemu-host:2.05 AS builder
# FROM golang as builder # FROM golang as builder
@@ -9,38 +11,50 @@ FROM qemux/qemu-host:2.05 AS builder
FROM debian:trixie-slim FROM debian:trixie-slim
ARG TARGETARCH
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
ARG DEBCONF_NONINTERACTIVE_SEEN="true" ARG DEBCONF_NONINTERACTIVE_SEEN="true"
RUN set -eu && extra="" && \ RUN set -eu && \
if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi && \
apt-get update && \ apt-get update && \
apt-get --no-install-recommends -y install \ apt-get --no-install-recommends -y install \
jq \ jq \
tini \ tini \
curl \ curl \
cpio \
wget \ wget \
fdisk \ fdisk \
unzip \ unzip \
nginx \ nginx \
procps \ procps \
ethtool \
python3 \
python3-pip \
python3-msgpack \
python3-pysodium \
xz-utils \ xz-utils \
iptables \ iptables \
iproute2 \ iproute2 \
apt-utils \
dnsmasq \ dnsmasq \
fakeroot \ fakeroot \
apt-utils \
net-tools \ net-tools \
e2fsprogs \
qemu-utils \ qemu-utils \
websocketd \
iputils-ping \
inotify-tools \
ca-certificates \ ca-certificates \
netcat-openbsd \ netcat-openbsd \
qemu-system-x86 \ qemu-system-x86 && \
"$extra" && \ wget "https://github.com/qemus/passt/releases/download/v2025_09_19/passt_2025_09_19_${TARGETARCH}.deb" -O /tmp/passt.deb -q && \
dpkg -i /tmp/passt.deb && \
apt-get clean && \ apt-get clean && \
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
mkdir -p /etc/qemu && \
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" > /run/version && \
@@ -49,14 +63,15 @@ RUN set -eu && extra="" && \
COPY --chmod=755 ./src /run/ COPY --chmod=755 ./src /run/
COPY --chmod=755 ./web /var/www/ COPY --chmod=755 ./web /var/www/
COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin
COPY --chmod=744 ./web/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 VOLUME /storage
EXPOSE 22 139 445 5000 EXPOSE 22 139 445 5000
ENV RAM_SIZE="1G" ENV RAM_SIZE="2G"
ENV DISK_SIZE="16G" ENV CPU_CORES="2"
ENV CPU_CORES="1" ENV DISK_SIZE="256G"
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh

View File

@@ -1,16 +1,17 @@
services: services:
dsm: dsm:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm:latest image: vdsm/virtual-dsm
environment: environment:
DISK_SIZE: "16G" DISK_SIZE: "256G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
ports: ports:
- 5000:5000 - 5000:5000
volumes: volumes:
- /var/dsm:/storage - ./dsm:/storage
stop_grace_period: 2m restart: always
stop_grace_period: 2m

View File

@@ -8,60 +8,70 @@ spec:
- ReadWriteOnce - ReadWriteOnce
resources: resources:
requests: requests:
storage: 16Gi storage: 256Gi
--- ---
apiVersion: v1 apiVersion: apps/v1
kind: Pod kind: Deployment
metadata: metadata:
name: dsm name: dsm
labels: labels:
name: dsm name: dsm
spec: spec:
containers: replicas: 1
- name: dsm selector:
image: vdsm/virtual-dsm matchLabels:
env: app: dsm
- name: RAM_SIZE template:
value: "1G" metadata:
- name: CPU_CORES labels:
value: "1" app: dsm
- name: DISK_SIZE spec:
value: "16G" containers:
ports: - name: dsm
- containerPort: 5000 image: vdsm/virtual-dsm
securityContext: env:
capabilities: - name: DISK_SIZE
add: value: "256G"
- NET_ADMIN ports:
privileged: true - containerPort: 5000
volumeMounts: name: http
- mountPath: /storage protocol: TCP
name: storage securityContext:
- mountPath: /dev/kvm capabilities:
name: dev-kvm add:
- mountPath: /dev/net/tun - NET_ADMIN
name: dev-tun privileged: true
terminationGracePeriodSeconds: 120 volumeMounts:
volumes: - mountPath: /storage
- name: storage name: storage
persistentVolumeClaim: - mountPath: /dev/kvm
claimName: dsm-pvc name: dev-kvm
- hostPath: - mountPath: /dev/net/tun
path: /dev/kvm name: dev-tun
name: dev-kvm terminationGracePeriodSeconds: 120
- hostPath: volumes:
path: /dev/net/tun - name: storage
type: CharDevice persistentVolumeClaim:
name: dev-tun claimName: dsm-pvc
- hostPath:
path: /dev/kvm
name: dev-kvm
- hostPath:
path: /dev/net/tun
type: CharDevice
name: dev-tun
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
name: dsm name: dsm
spec: spec:
internalTrafficPolicy: Cluster
ports: ports:
- name: tcp-5000 - name: http
port: 5000 port: 5000
protocol: TCP
targetPort: 5000
selector: selector:
name: dsm app: dsm
type: NodePort type: ClusterIP

112
readme.md
View File

@@ -22,7 +22,7 @@ Virtual DSM in a Docker container.
## Usage 🐳 ## Usage 🐳
Via Docker Compose: ##### Via Docker Compose:
```yaml ```yaml
services: services:
@@ -30,7 +30,7 @@ services:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
environment: environment:
DISK_SIZE: "16G" DISK_SIZE: "256G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun
@@ -39,31 +39,26 @@ services:
ports: ports:
- 5000:5000 - 5000:5000
volumes: volumes:
- /var/dsm:/storage - ./dsm:/storage
restart: always
stop_grace_period: 2m stop_grace_period: 2m
``` ```
Via Docker CLI: ##### Via Docker CLI:
```bash ```bash
docker run -it --rm -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN --stop-timeout 120 vdsm/virtual-dsm docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm
``` ```
Via Kubernetes: ##### Via Kubernetes:
```shell ```shell
kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/master/kubernetes.yml kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/master/kubernetes.yml
``` ```
## Compatibility ⚙️ ##### Via Github Codespaces:
| **Product** | **Platform** | | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/vdsm/virtual-dsm)
|---|---|---|
| Docker Engine | Linux| ✅ |
| Docker Desktop | Linux | ❌ |
| Docker Desktop | macOS | ❌ |
| Docker Desktop | Windows 11 | ✅ |
| Docker Desktop | Windows 10 | ❌ |
## FAQ 💬 ## FAQ 💬
@@ -71,9 +66,11 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
Very simple! These are the steps: Very simple! These are the steps:
- Start the container and connect to [port 5000](http://localhost:5000) using your web browser. - Start the container and connect to [port 5000](http://127.0.0.1:5000/) using your web browser.
- Wait until DSM is ready, choose an username and password, and you will be taken to the desktop. - Wait until DSM finishes its installation
- Choose an username and password, and you will be taken to the desktop.
Enjoy your brand new NAS, and don't forget to star this repo! Enjoy your brand new NAS, and don't forget to star this repo!
@@ -83,70 +80,51 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
```yaml ```yaml
volumes: volumes:
- /var/dsm:/storage - ./dsm:/storage
``` ```
Replace the example path `/var/dsm` with the desired storage folder. Replace the example path `./dsm` with the desired storage folder or named volume.
### How do I change the size of the disk? ### How do I change the size of the disk?
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity: To expand the default size of 256 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
```yaml ```yaml
environment: environment:
DISK_SIZE: "128G" DISK_SIZE: "512G"
``` ```
> [!TIP] > [!TIP]
> This can also be used to resize the existing disk to a larger capacity without any data loss. > This can also be used to resize the existing disk to a larger capacity without any data loss.
### How do I create a growable disk?
By default, the entire capacity of the disk is 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"
```
> [!NOTE]
> This may reduce the write performance of the disk.
### How do I add multiple disks? ### How do I add multiple disks?
To create additional disks, modify your compose file like this: To create additional disks, modify your compose file like this:
```yaml ```yaml
environment: environment:
DISK2_SIZE: "32G" DISK2_SIZE: "500G"
DISK3_SIZE: "64G" DISK3_SIZE: "750G"
volumes: volumes:
- /home/example:/storage2 - ./example2:/storage2
- /mnt/data/example:/storage3 - ./example3:/storage3
``` ```
### How do I pass-through a disk? ### How do I pass-through a disk?
It is possible to pass-through a disk device directly, by adding it to your compose file in this way: It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way:
```yaml ```yaml
devices: devices:
- /dev/disk/by-uuid/12345-12345-12345-12345-12345:/disk2 - /dev/sdb:/disk1
- /dev/sdc1:/disk2
``` ```
Make sure to bind the disk via its UUID (obtainable via `lsblk -o name,uuid`) instead of its name (`/dev/sdc`), to prevent ever binding the wrong disk when the drive letters happen to change. Make sure it is totally empty (without any filesystem), otherwise DSM may not format it as a volume.
> [!IMPORTANT]
> The device needs to be totally empty (without any partition table) otherwise DSM does not always format it into a volume.
> [!CAUTION]
> Do NOT use this feature with the goal of sharing files from the host, they will all be lost without warning when DSM creates the volume.
### 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 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: If you want to adjust this, you can specify the desired amount using the following environment variables:
@@ -158,9 +136,16 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I verify if my system supports KVM? ### How do I verify if my system supports KVM?
Only Linux and Windows 11 support KVM virtualization, macOS and Windows 10 do not unfortunately. First check if your software is compatible using this chart:
You can run the following commands in Linux to check your system: | **Product** | **Linux** | **Win11** | **Win10** | **macOS** |
|---|---|---|---|---|
| Docker CLI | ✅ | ✅ | ❌ | ❌ |
| Docker Desktop | ❌ | ✅ | ❌ | ❌ |
| Podman CLI | ✅ | ✅ | ❌ | ❌ |
| Podman Desktop | ✅ | ✅ | ❌ | ❌ |
After that you can run the following commands in Linux to check your system:
```bash ```bash
sudo apt install cpu-checker sudo apt install cpu-checker
@@ -175,11 +160,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's. - you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's.
If you do not receive any error from `kvm-ok` but the container still complains about KVM, please check whether: If you did not receive any error from `kvm-ok` but the container still complains about a missing KVM device, it could help to add `privileged: true` to your compose file (or `sudo` to your `docker` command) to rule out any permission issue.
- you are not using "Docker Desktop for Linux" as it does not support KVM, instead make use of Docker Engine directly.
- it could help to add `privileged: true` to your compose file (or `sudo` to your `docker run` command), to rule out any permission issue.
### How do I assign an individual IP address to the container? ### How do I assign an individual IP address to the container?
@@ -222,7 +203,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
After configuring the container for [macvlan](#how-do-i-assign-an-individual-ip-address-to-the-container), it is possible for DSM to become part of your home network by requesting an IP from your router, just like your other devices. After configuring the container for [macvlan](#how-do-i-assign-an-individual-ip-address-to-the-container), it is possible for DSM to become part of your home network by requesting an IP from your router, just like your other devices.
To enable this mode, add the following lines to your compose file: To enable this mode, in which the container and DSM will have separate IP addresses, add the following lines to your compose file:
```yaml ```yaml
environment: environment:
@@ -233,9 +214,6 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- 'c *:* rwm' - 'c *:* rwm'
``` ```
> [!NOTE]
> In this mode, the container and DSM will each have their own separate IPs.
### How do I pass-through the GPU? ### How do I pass-through the GPU?
To pass-through your Intel GPU, add the following lines to your compose file: To pass-through your Intel GPU, add the following lines to your compose file:
@@ -247,28 +225,28 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- /dev/dri - /dev/dri
``` ```
> [!TIP] > [!NOTE]
> This can be used to enable the facial recognition function in Synology Photos for example. > This can be used to enable the facial recognition function in Synology Photos, but does not provide hardware transcoding for video.
### How do I install a specific version of vDSM? ### How do I install a specific version of vDSM?
By default, version 7.2 will be installed, but if you prefer an older version, you can add its download URL to your compose file as follows: By default, version 7.2 will be installed, but if you prefer an older version, you can add the download URL of the `.pat` file to your compose file as follows:
```yaml ```yaml
environment: environment:
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat" URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
``` ```
With this method, it is even possible to switch between different versions while keeping all your file data intact. With this method, it is even possible to switch back and forth between versions while keeping your file data intact.
If you don't have internet access, it's also possible to skip the download by setting URL to: Alternatively, you can also skip the download and use a local file instead, by binding it in your compose file in this way:
```yaml ```yaml
environment: volumes:
URL: "DSM_VirtualDSM_42218.pat" - ./DSM_VirtualDSM_42218.pat:/boot.pat
``` ```
after placing a file called `DSM_VirtualDSM_42218.pat` in your `/storage` folder. Replace the example path `./DSM_VirtualDSM_42218.pat` with the filename of your desired `.pat` file. The value of `URL` will be ignored in this case.
### What are the differences compared to the standard DSM? ### What are the differences compared to the standard DSM?

View File

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

View File

@@ -17,21 +17,27 @@ ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
if [[ "$RAM_CHECK" != [Nn]* ]]; then if [[ "$RAM_CHECK" != [Nn]* ]]; then
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}') RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
AVAIL_GB=$(( RAM_AVAIL/1073741824 )) AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
error "Your configured RAM_SIZE of $WANTED_GB GB is too high for the $AVAIL_GB GB of memory available, please set a lower value." 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."
exit 17 [[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
fi info "$msg"
else
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
warn "your configured RAM_SIZE of $WANTED_GB GB is very close to the $AVAIL_GB GB of memory available, please consider a lower value." 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
fi fi
if [[ "$DEBUG" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
printf "Arguments:\n\n%s" "${ARGS// -/$'\n-'}" && echo printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi fi
return 0 return 0

View File

@@ -82,7 +82,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
fi fi
@@ -96,7 +96,7 @@ createDisk() {
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 FA local DATA_SIZE DIR SPACE GB FA
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
@@ -109,16 +109,16 @@ createDisk() {
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1) SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if (( DATA_SIZE > SPACE )); then if (( DATA_SIZE > SPACE )); then
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) GB=$(formatBytes "$SPACE")
error "Not enough free space to create a $DISK_DESC of $DISK_SPACE in $DIR, it has only $SPACE_GB 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..."
info "Creating a $DISK_SPACE $DISK_STYLE $DISK_DESC image in $DISK_FMT format..." info "Creating a ${DISK_SPACE/G/ GB} $DISK_STYLE $DISK_DESC image in $DISK_FMT format..."
local FAIL="Could not create a $DISK_STYLE $DISK_FMT $DISK_DESC image of $DISK_SPACE ($DISK_FILE)" local FAIL="Could not create a $DISK_STYLE $DISK_FMT $DISK_DESC image of ${DISK_SPACE/G/ GB} ($DISK_FILE)"
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
@@ -141,10 +141,12 @@ createDisk() {
else else
# Create an empty file # Create an empty file
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
rm -f "$DISK_FILE" if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
error "$FAIL" && exit 77 rm -f "$DISK_FILE"
error "$FAIL" && exit 77
fi
fi fi
fi fi
@@ -180,7 +182,7 @@ resizeDisk() {
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 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")
@@ -194,17 +196,17 @@ resizeDisk() {
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1) SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if (( REQ > SPACE )); then if (( REQ > SPACE )); then
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) GB=$(formatBytes "$SPACE")
error "Not enough free space to resize $DISK_DESC to $DISK_SPACE in $DIR, it has only $SPACE_GB 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
local GB=$(( (CUR_SIZE + 1073741823)/1073741824 )) GB=$(formatBytes "$CUR_SIZE")
MSG="Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..." MSG="Resizing $DISK_DESC from $GB to ${DISK_SPACE/G/ GB}..."
info "$MSG" && html "$MSG" info "$MSG" && html "$MSG"
local FAIL="Could not resize the $DISK_STYLE $DISK_FMT $DISK_DESC image from ${GB}G to $DISK_SPACE ($DISK_FILE)" local FAIL="Could not resize the $DISK_STYLE $DISK_FMT $DISK_DESC image from ${GB} to ${DISK_SPACE/G/ GB} ($DISK_FILE)"
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
@@ -219,9 +221,11 @@ resizeDisk() {
else else
# Resize file by allocating more space # Resize file by allocating more space
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
error "$FAIL" && exit 75 if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
error "$FAIL" && exit 75
fi
fi fi
fi fi
@@ -257,7 +261,7 @@ convertDisk() {
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
local DIR CUR_SIZE SPACE local DIR CUR_SIZE SPACE GB
# Check free diskspace # Check free diskspace
DIR=$(dirname "$TMP_FILE") DIR=$(dirname "$TMP_FILE")
@@ -265,8 +269,8 @@ convertDisk() {
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1) SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if (( CUR_SIZE > SPACE )); then if (( CUR_SIZE > SPACE )); then
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) GB=$(formatBytes "$SPACE")
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $SPACE_GB 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
@@ -296,8 +300,10 @@ convertDisk() {
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
# Work around qemu-img bug # Work around qemu-img bug
CUR_SIZE=$(stat -c%s "$TMP_FILE") CUR_SIZE=$(stat -c%s "$TMP_FILE")
if ! fallocate -l "$CUR_SIZE" "$TMP_FILE"; then if ! fallocate -l "$CUR_SIZE" "$TMP_FILE" &>/dev/null; then
if ! fallocate -l -x "$CUR_SIZE" "$TMP_FILE"; then
error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE" error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
fi
fi fi
fi fi
fi fi
@@ -314,7 +320,7 @@ convertDisk() {
msg="Conversion of $DISK_DESC" msg="Conversion of $DISK_DESC"
html "$msg completed..." html "$msg completed..."
info "$msg to $DST_FMT completed succesfully!" info "$msg to $DST_FMT completed successfully!"
return 0 return 0
} }
@@ -362,6 +368,8 @@ createDevice () {
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_SECTORS=$9
local DISK_ID="data$DISK_INDEX" local DISK_ID="data$DISK_INDEX"
local index="" local index=""
@@ -375,29 +383,29 @@ createDevice () {
;; ;;
"usb" ) "usb" )
result+=",if=none \ result+=",if=none \
-device usb-storage,drive=${DISK_ID}${index}" -device usb-storage,drive=${DISK_ID}${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result" echo "$result"
;; ;;
"nvme" ) "nvme" )
result+=",if=none \ 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" echo "$result"
;; ;;
"ide" | "sata" ) "ide" | "sata" )
result+=",if=none \ result+=",if=none \
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \ -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" echo "$result"
;; ;;
"blk" | "virtio-blk" ) "blk" | "virtio-blk" )
result+=",if=none \ 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" echo "$result"
;; ;;
"scsi" | "virtio-scsi" ) "scsi" | "virtio-scsi" )
result+=",if=none \ result+=",if=none \
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \ -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" echo "$result"
;; ;;
esac esac
@@ -416,7 +424,7 @@ addDisk () {
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 DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DISK_EXT=$(fmt2ext "$DISK_FMT") DISK_EXT=$(fmt2ext "$DISK_FMT")
local DISK_FILE="$DISK_BASE.$DISK_EXT" local DISK_FILE="$DISK_BASE.$DISK_EXT"
@@ -424,15 +432,17 @@ addDisk () {
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G" SPACE="${DISK_SPACE// /}"
DISK_SPACE=$(echo "${DISK_SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') [ -z "$SPACE" ] && SPACE="16G"
[[ -z "${DISK_SPACE//[0-9]}" ]] && DISK_SPACE="${DISK_SPACE}G" [ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
if (( DATA_SIZE < 1 )); then if ! numfmt --from=iec "$SPACE" &>/dev/null; then
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73 error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
fi fi
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 ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi fi
@@ -465,16 +475,16 @@ 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" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $? resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi fi
else else
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $? createDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
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
} }
@@ -489,12 +499,29 @@ addDevice () {
[ -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
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 return 0
} }
html "Initializing disks..." msg="Initializing disks..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS="" [ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi" [ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
@@ -506,11 +533,7 @@ case "${DISK_TYPE,,}" in
esac esac
if [ -z "$ALLOCATE" ]; then if [ -z "$ALLOCATE" ]; then
if [[ "${DISK_FMT,,}" == "raw" ]]; then ALLOCATE="N"
ALLOCATE="Y"
else
ALLOCATE="N"
fi
fi fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
@@ -521,11 +544,11 @@ else
DISK_ALLOC="preallocation=falloc" DISK_ALLOC="preallocation=falloc"
fi 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" "" "")
DISK1_FILE="$STORAGE/${DISK_NAME}" DISK1_FILE="$STORAGE/${DISK_NAME}"
if [[ ! -f "$DISK1_FILE.img" ]] && [[ -f "$STORAGE/data${DISK_SIZE}.img" ]]; then if [ ! -f "$DISK1_FILE.img" ] && [ -f "$STORAGE/data${DISK_SIZE}.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img" mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi fi
@@ -534,7 +557,7 @@ DISK2_FILE="/storage2/${DISK_NAME}2"
if [ ! -f "$DISK2_FILE.img" ]; then if [ ! -f "$DISK2_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage2/data.img" 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") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "$DISK1_FILE.img") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then
@@ -547,7 +570,7 @@ DISK3_FILE="/storage3/${DISK_NAME}3"
if [ ! -f "$DISK3_FILE.img" ]; then if [ ! -f "$DISK3_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage3/data.img" 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") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "$DISK1_FILE.img") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then

View File

@@ -8,7 +8,9 @@ set -Eeuo pipefail
: "${DISPLAY:="none"}" # Display type : "${DISPLAY:="none"}" # Display type
: "${RENDERNODE:="/dev/dri/renderD128"}" # Render node : "${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,,}" == "none" ]] && VGA="none"
DISPLAY_OPTS="-display $DISPLAY -vga $VGA" DISPLAY_OPTS="-display $DISPLAY -vga $VGA"
@@ -16,6 +18,10 @@ if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
fi fi
msg="Configuring display drivers..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE" DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
DISPLAY_OPTS+=" -vga $VGA" DISPLAY_OPTS+=" -vga $VGA"

View File

@@ -1,12 +1,16 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
APP="Virtual DSM" : "${PLATFORM:="x64"}"
SUPPORT="https://github.com/vdsm/virtual-dsm" : "${APP:="Virtual DSM"}"
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
cd /run cd /run
. start.sh # Startup hook
. 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
@@ -29,7 +33,7 @@ fi
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15 (( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
terminal terminal
tail -fn +0 "$QEMU_LOG" 2>/dev/null & tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || : cat "$QEMU_TERM" 2>/dev/null & wait $! || :
sleep 1 & wait $! sleep 1 & wait $!

View File

@@ -5,29 +5,43 @@ set -Eeuo pipefail
if [ -f "$STORAGE/dsm.ver" ]; then if [ -f "$STORAGE/dsm.ver" ]; then
BASE=$(<"$STORAGE/dsm.ver") BASE=$(<"$STORAGE/dsm.ver")
BASE="${BASE//[![:print:]]/}"
[ -z "$BASE" ] && BASE="DSM_VirtualDSM_69057" [ -z "$BASE" ] && BASE="DSM_VirtualDSM_69057"
else else
# Fallback for old installs # Fallback for old installs
BASE="DSM_VirtualDSM_42962" BASE="DSM_VirtualDSM_42962"
fi fi
if [ -n "$URL" ]; then FN="boot.pat"
BASE=$(basename "$URL" .pat) DIR=$(find / -maxdepth 1 -type d -iname "$FN" -print -quit)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then [ ! -d "$DIR" ] && DIR=$(find "$STORAGE" -maxdepth 1 -type d -iname "$FN" -print -quit)
BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}" if [ -d "$DIR" ]; then
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g') BASE="DSM_VirtualDSM" && URL="file://$DIR"
fi if [[ ! -s "$STORAGE/$BASE.boot.img" || ! -s "$STORAGE/$BASE.system.img" ]]; then
if [[ "${URL,,}" != "http"* ]]; then error "The bind $DIR maps to a file that does not exist!" && exit 65
if [ -s "$STORAGE/$BASE.pat" ]; then
URL="file://$STORAGE/$BASE.pat"
else
error "File $STORAGE/$BASE.pat does not exist!" && exit 65
fi
fi fi
fi fi
if [[ -s "$STORAGE/$BASE.boot.img" ]] && [[ -s "$STORAGE/$BASE.system.img" ]]; then 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" ] && [ ! -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
[ ! -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
return 0 # Previous installation found return 0 # Previous installation found
fi fi
@@ -46,11 +60,16 @@ if [ -z "$DL" ]; then
[[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL" [[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL"
fi fi
[ -z "$URL" ] && URL="$DL/release/7.2.2/72806/DSM_VirtualDSM_72806.pat" if [ -z "$URL" ]; then
URL="$DL/release/7.2.2/72806/DSM_VirtualDSM_72806.pat"
fi
BASE=$(basename "${URL%%\?*}" .pat) if [ ! -s "$FILE" ]; then
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}" BASE=$(basename "${URL%%\?*}" .pat)
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 if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$STORAGE/$BASE.pat" rm -f "$STORAGE/$BASE.pat"
@@ -71,7 +90,7 @@ if [[ "${FS,,}" == "fuse"* ]]; then
info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!" info "Warning: 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..." info "Warning: the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi fi
@@ -85,9 +104,9 @@ else
TMP="/tmp/dsm" TMP="/tmp/dsm"
TMP_SPACE=2147483648 TMP_SPACE=2147483648
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1) SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
SPACE_MB=$(( (SPACE + 1048575)/1048576 )) SPACE_MB=$(formatBytes "$SPACE")
if (( TMP_SPACE > SPACE )); then if (( TMP_SPACE > SPACE )); then
error "Not enough free space inside the container, have $SPACE_MB 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
fi fi
@@ -96,13 +115,13 @@ 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)
SPACE_MB=$(( (SPACE + 1048575)/1048576 )) SPACE_MB=$(formatBytes "$SPACE" "down")
(( ROOT_SPACE > SPACE )) && error "Not enough free space inside the container, have $SPACE_MB MB available but need at least 500 MB." && exit 96 (( ROOT_SPACE > SPACE )) && error "Not enough free space inside the container, have $SPACE_MB available but need at least 500 MB." && exit 96
MIN_SPACE=15032385536 MIN_SPACE=15032385536
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) SPACE_GB=$(formatBytes "$SPACE")
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB GB available but need at least 14 GB." && exit 94 (( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB available but need at least 14 GB." && exit 94
# Check if output is to interactive TTY # Check if output is to interactive TTY
if [ -t 1 ]; then if [ -t 1 ]; then
@@ -111,128 +130,16 @@ else
PROGRESS="--progress=dot:giga" PROGRESS="--progress=dot:giga"
fi fi
# Download the required files from the Synology website if [[ "$URL" == "file://"* ]]; then
MSG="Copying DSM"
ROOT="Y" ERR="Failed to copy ${URL:7}"
RD="$TMP/rd.gz" info "Install: Copying installation image..."
RDC="$STORAGE/dsm.rd" else
MSG="Downloading DSM"
if [ ! -s "$RDC" ] && [[ "$URL" == "file://"* ]] && [[ "${URL,,}" == *"_42218.pat" ]]; then ERR="Failed to download $URL"
info "Install: Downloading $BASE.pat..."
rm -f "$RD"
rm -f "$RDC"
tar --extract --file="${URL:7}" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
cp "$RD" "$RDC"
fi fi
if [ ! -s "$RDC" ]; then
rm -f "$RD"
rm -f "$RDC"
MSG="Downloading installer"
info "Install: $MSG..." && html "$MSG..."
SIZE=5394188
POS="65627648-71021835"
VERIFY="b4215a4b213ff5154db0488f92c87864"
LOC="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
[[ "${URL,,}" == *"_42218.pat" ]] && LOC="$URL"
/run/progress.sh "$RD" "$SIZE" "$MSG ([P])..." &
{ curl -r "$POS" -sfk --connect-timeout 10 -S -o "$RD" "$LOC"; rc=$?; } || :
fKill "progress.sh"
ERR="Failed to download $LOC"
(( rc == 3 )) && error "$ERR , cannot write file (disk full?)" && exit 60
(( rc == 4 )) && error "$ERR , network failure!" && exit 60
(( rc == 8 )) && error "$ERR , server issued an error response!" && exit 60
if (( rc != 0 )); then
if (( rc != 22 )) && (( rc != 56 )); then
error "$ERR , reason: $rc" && exit 60
fi
SUM="skip"
else
SUM=$(md5sum "$RD" | cut -f 1 -d " ")
fi
if [ "$SUM" != "$VERIFY" ]; then
PAT="/install.pat"
SIZE=379637760
rm -f "$RD"
rm -f "$PAT"
html "$MSG..."
/run/progress.sh "$PAT" "$SIZE" "$MSG ([P])..." &
{ wget "$LOC" -O "$PAT" -q --no-check-certificate --timeout=10 --no-http-keep-alive --show-progress "$PROGRESS"; rc=$?; } || :
fKill "progress.sh"
ERR="Failed to download $LOC"
(( rc == 3 )) && error "$ERR , cannot write file (disk full?)" && exit 60
(( rc == 4 )) && error "$ERR , network failure!" && exit 60
(( rc == 8 )) && error "$ERR , server issued an error response!" && exit 60
(( rc != 0 )) && error "$ERR , reason: $rc" && exit 60
tar --extract --file="$PAT" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
rm "$PAT"
fi
cp "$RD" "$RDC"
fi
if [ -f "$RDC" ]; then
{ xz -dc <"$RDC" >"$TMP/rd" 2>/dev/null; rc=$?; } || :
(( rc != 1 )) && error "Failed to unxz $RDC on $FS, reason $rc" && exit 91
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
if (( rc != 0 )); then
ROOT="N"
{ (cd "$TMP" && fakeroot cpio -idmu <"$TMP/rd" 2>/dev/null); rc=$?; } || :
(( rc != 0 )) && error "Failed to extract $RDC on $FS, reason $rc" && exit 92
fi
rm -rf /run/extract && mkdir -p /run/extract
for file in $TMP/usr/lib/libcurl.so.4 \
$TMP/usr/lib/libmbedcrypto.so.5 \
$TMP/usr/lib/libmbedtls.so.13 \
$TMP/usr/lib/libmbedx509.so.1 \
$TMP/usr/lib/libmsgpackc.so.2 \
$TMP/usr/lib/libsodium.so \
$TMP/usr/lib/libsynocodesign-ng-virtual-junior-wins.so.7 \
$TMP/usr/syno/bin/scemd; do
cp "$file" /run/extract/
done
if [ "$ARCH" != "amd64" ]; then
mkdir -p /lib64/
cp "$TMP/usr/lib/libc.so.6" /lib64/
cp "$TMP/usr/lib/libpthread.so.0" /lib64/
cp "$TMP/usr/lib/ld-linux-x86-64.so.2" /lib64/
fi
mv /run/extract/scemd /run/extract/syno_extract_system_patch
chmod +x /run/extract/syno_extract_system_patch
fi
rm -rf "$TMP" && mkdir -p "$TMP"
info "Install: Downloading $BASE.pat..."
MSG="Downloading DSM"
ERR="Failed to download $URL"
html "$MSG..." html "$MSG..."
PAT="/$BASE.pat" PAT="/$BASE.pat"
@@ -240,6 +147,10 @@ rm -f "$PAT"
if [[ "$URL" == "file://"* ]]; then if [[ "$URL" == "file://"* ]]; then
if [ ! -f "${URL:7}" ]; then
error "File '${URL:7}' does not exist!" && exit 65
fi
cp "${URL:7}" "$PAT" cp "${URL:7}" "$PAT"
else else
@@ -270,7 +181,7 @@ if ((SIZE<250000000)); then
error "The specified PAT file is probably an update pack as it's too small." && exit 62 error "The specified PAT file is probably an update pack as it's too small." && exit 62
fi fi
MSG="Extracting downloaded image..." MSG="Extracting installation image..."
info "Install: $MSG" && html "$MSG" info "Install: $MSG" && html "$MSG"
if { tar tf "$PAT"; } >/dev/null 2>&1; then if { tar tf "$PAT"; } >/dev/null 2>&1; then
@@ -279,22 +190,15 @@ if { tar tf "$PAT"; } >/dev/null 2>&1; then
else else
export LD_LIBRARY_PATH="/run/extract" { (cd "$TMP" && python3 /run/extract.py -i "$PAT" -d 2>/run/extract.log); rc=$?; } || :
if [ "$ARCH" == "amd64" ]; then if (( rc != 0 )); then
{ /run/extract/syno_extract_system_patch "$PAT" "$TMP/."; rc=$?; } || : cat /run/extract.log
else error "Failed to extract PAT file, reason $rc" && exit 63
{ qemu-x86_64 /run/extract/syno_extract_system_patch "$PAT" "$TMP/."; rc=$?; } || :
fi fi
export LD_LIBRARY_PATH=""
(( rc != 0 )) && error "Failed to extract PAT file, reason $rc" && exit 63
fi fi
rm -rf /run/extract
MSG="Preparing system partition..." MSG="Preparing system partition..."
info "Install: $MSG" && html "$MSG" info "Install: $MSG" && html "$MSG"
@@ -310,10 +214,10 @@ rm -f "$SYSTEM"
# Check free diskspace # Check free diskspace
SYSTEM_SIZE=10738466816 SYSTEM_SIZE=10738466816
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_MB=$(( (SPACE + 1048575)/1048576 )) SPACE_MB=$(formatBytes "$SPACE")
if (( SYSTEM_SIZE > SPACE )); then if (( SYSTEM_SIZE > SPACE )); then
error "Not enough free space in $STORAGE to create a 10 GB system disk, have only $SPACE_MB MB available." && exit 97 error "Not enough free space in $STORAGE to create a 10 GB system disk, have only $SPACE_MB available." && exit 97
fi fi
if ! touch "$SYSTEM"; then if ! touch "$SYSTEM"; then
@@ -328,10 +232,12 @@ if [[ "${FS,,}" == "btrfs" ]]; then
fi fi
fi fi
if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM" &>/dev/null; then
if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then if ! fallocate -l -x "$SYSTEM_SIZE" "$SYSTEM"; then
rm -f "$SYSTEM" if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then
error "Could not allocate file $SYSTEM for the system disk." && exit 98 rm -f "$SYSTEM"
error "Could not allocate file $SYSTEM for the system disk." && exit 98
fi
fi fi
fi fi
@@ -366,12 +272,9 @@ mv "$HDA.tgz" "$HDA.txz"
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/" [ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"* rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
[ -s "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/"
if [ -s "$IDB.txz" ]; then if [ -s "$IDB.txz" ]; then
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb/" INDEX_DB="$MOUNT/usr/syno/synoman/indexdb"
mkdir -p "$INDEX_DB" mkdir -p "$INDEX_DB"
tar xpfJ "$IDB.txz" --absolute-names -C "$INDEX_DB"
fi fi
LABEL="1.44.1-42218" LABEL="1.44.1-42218"
@@ -379,22 +282,12 @@ OFFSET="1048576" # 2048 * 512
NUMBLOCKS="2097152" # (16777216 * 512) / 4096 NUMBLOCKS="2097152" # (16777216 * 512) / 4096
MSG="Installing system partition..." MSG="Installing system partition..."
if [[ "$ROOT" != [Nn]* ]]; then fakeroot -- bash -c "set -Eeu;\
[ -s $HDP.txz ] && tar xpfJ $HDP.txz --absolute-names -C $MOUNT/;\
tar xpfJ "$HDA.txz" --absolute-names --skip-old-files -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/;\
info "Install: $MSG" && html "$MSG" 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"
mke2fs -q -t ext4 -b 4096 -d "$MOUNT/" -L "$LABEL" -F -E "offset=$OFFSET" "$SYSTEM" "$NUMBLOCKS"
else
fakeroot -- bash -c "set -Eeu;\
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"
fi
rm -rf "$MOUNT" rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE/dsm.ver" echo "$BASE" > "$STORAGE/dsm.ver"
@@ -408,7 +301,4 @@ fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img" mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
rm -rf "$TMP" rm -rf "$TMP"
html "Booting DSM instance..."
sleep 1.2
return 0 return 0

View File

@@ -4,19 +4,28 @@ set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: "${MAC:=""}" : "${MAC:=""}"
: "${MTU:=""}"
: "${DHCP:="N"}" : "${DHCP:="N"}"
: "${NETWORK:="Y"}" : "${NETWORK:="Y"}"
: "${USER_PORTS:=""}"
: "${HOST_PORTS:=""}" : "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}"
: "${ADAPTER:="virtio-net-pci"}" : "${ADAPTER:="virtio-net-pci"}"
: "${VM_NET_IP:=""}"
: "${VM_NET_DEV:=""}" : "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="dsm"}" : "${VM_NET_TAP:="dsm"}"
: "${VM_NET_MAC:="$MAC"}" : "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_IP:="20.20.20.21"}" : "${VM_NET_BRIDGE:="docker"}"
: "${VM_NET_HOST:="VirtualDSM"}" : "${VM_NET_HOST:="VirtualDSM"}"
: "${VM_NET_MASK:="255.255.255.0"}"
: "${PASST:="passt"}"
: "${PASST_MTU:=""}"
: "${PASST_OPTS:=""}"
: "${PASST_DEBUG:=""}"
: "${DNSMASQ_OPTS:=""}" : "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ_DEBUG:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}" : "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}" : "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
@@ -28,6 +37,8 @@ ADD_ERR="Please add the following setting to your container:"
configureDHCP() { configureDHCP() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
# Create the necessary file structure for /dev/vhost-net # Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then if mknod /dev/vhost-net c 10 238; then
@@ -58,8 +69,15 @@ configureDHCP() {
fi ;; fi ;;
esac esac
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size to $MTU."
fi
fi
while ! ip link set "$VM_NET_TAP" up; do while ! ip link set "$VM_NET_TAP" up; do
info "Waiting for MAC address $VM_NET_MAC to become available..." info "Waiting for MAC address $VM_NET_MAC to become available..."
info "If you cloned this machine, please delete the 'dsm.mac' file to generate a different MAC address."
sleep 2 sleep 2
done done
@@ -71,7 +89,7 @@ configureDHCP() {
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev) 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 (( 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 if [[ ! -e "$TAP_PATH" ]]; then
{ mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || : { mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
@@ -97,23 +115,69 @@ configureDHCP() {
configureDNS() { configureDNS() {
# dnsmasq configuration: local if="$1"
DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0" local ip="$2"
local mac="$3"
local host="$4"
local mask="$5"
local gateway="$6"
# Create lease file for faster resolve echo "$gateway" > /run/shm/qemu.gw
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
# Set DNS server and gateway [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1 --dhcp-option=option:router,${VM_NET_IP%.*}.1" [[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
local log="/var/log/dnsmasq.log"
rm -f "$log"
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 # Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1" DNSMASQ_OPTS+=" --address=/host.lan/$gateway"
# Set local dns resolver to dnsmasq when needed
[ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"
# Enable logging to file
DNSMASQ_OPTS+=" --log-facility=$log"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}"
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && return 1
local msg="Failed to start Dnsmasq, reason: $?"
[ -f "$log" ] && cat "$log"
error "$msg"
return 1
fi
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
fi fi
return 0 return 0
@@ -133,7 +197,18 @@ getUserPorts() {
list="${list%% }" list="${list%% }"
for port in $list; do for port in $list; do
args+="hostfwd=tcp::$port-$VM_NET_IP:$port," proto="tcp"
num="$port"
if [[ "$port" == */udp ]]; then
proto="udp"
num="${port%/udp}"
elif [[ "$port" == */tcp ]]; then
proto="tcp"
num="${port%/tcp}"
fi
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
done done
echo "${args%?}" echo "${args%?}"
@@ -142,37 +217,144 @@ getUserPorts() {
getHostPorts() { getHostPorts() {
local list=$1 local list="$1"
list=$(echo "${list// /}" | sed 's/,*$//g')
[ -z "$list" ] && echo "" && return 0 [ -z "$list" ] && list="$MON_PORT" || list+=",$MON_PORT"
if [[ "${NETWORK,,}" == "passt" ]]; then
local DNS_PORT="53"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
[ -z "$list" ] && list="$DNS_PORT" || list+=",$DNS_PORT"
fi
[ -z "$list" ] && list="$COM_PORT" || list+=",$COM_PORT"
[ -z "$list" ] && list="$CHR_PORT" || list+=",$CHR_PORT"
[ -z "$list" ] && list="$WSD_PORT" || list+=",$WSD_PORT"
if [[ "$list" != *","* ]]; then
echo " ! --dport $list"
else
echo " -m multiport ! --dports $list"
fi fi
echo "$list"
return 0 return 0
} }
configureUser() { configureSlirp() {
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" [[ "$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 local forward
forward=$(getUserPorts "$USER_PORTS") forward=$(getUserPorts "${USER_PORTS:-}")
[ -n "$forward" ] && NET_OPTS+=",$forward" [ -n "$forward" ] && NET_OPTS+=",$forward"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
cp /etc/resolv.conf /etc/resolv.dnsmasq
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0
}
configurePasst() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
local log="/var/log/passt.log"
rm -f "$log"
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else
gateway="${ip%.*}.2"
fi
# passt configuration:
[ -z "$IP6" ] && PASST_OPTS+=" -4"
PASST_OPTS+=" -a $ip"
PASST_OPTS+=" -g $gateway"
PASST_OPTS+=" -n $VM_NET_MASK"
[ -n "$PASST_MTU" ] && PASST_OPTS+=" -m $PASST_MTU"
exclude=$(getHostPorts "$HOST_PORTS")
if [ -z "$exclude" ]; then
exclude="%${VM_NET_DEV}/all"
else
exclude="%${VM_NET_DEV}/~${exclude//,/,~}"
fi
PASST_OPTS+=" -t $exclude"
PASST_OPTS+=" -u $exclude"
PASST_OPTS+=" -H $VM_NET_HOST"
PASST_OPTS+=" -M $GATEWAY_MAC"
PASST_OPTS+=" -P /var/run/passt.pid"
PASST_OPTS+=" -l $log"
PASST_OPTS+=" -q"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
cp /etc/resolv.conf /etc/resolv.dnsmasq
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi
PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then
local msg="Failed to start passt, reason: $?"
[ -f "$log" ] && cat "$log"
error "$msg"
return 1
fi
if [[ "$PASST_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
else
if [[ "$DEBUG" == [Yy1]* ]]; then
[ -f "$log" ] && cat "$log" && echo ""
fi
fi
NET_OPTS="-netdev stream,id=hostnet0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket"
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0 return 0
} }
configureNAT() { configureNAT() {
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun" local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat" local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring NAT networking..."
# Create the necessary file structure for /dev/net/tun # Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
[[ "$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
@@ -180,37 +362,64 @@ configureNAT() {
fi fi
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
error "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
# Check port forwarding flag # Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null; 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
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && return 1 warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
return 1
fi fi
fi fi
# Create a bridge with a static IP for the VM guest 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
{ ip link add dev dockerbridge type bridge ; rc=$?; } || : [ -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 "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1 warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi fi
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then
error "Failed to add IP address!" && return 1 warn "failed to add IP address pool!" && return 1
fi fi
while ! ip link set dockerbridge up; do while ! ip link set "$VM_NET_BRIDGE" up; do
info "Waiting for IP address to become available..." info "Waiting for IP address to become available..."
sleep 2 sleep 2
done done
# QEMU Works with taps, set tap to the bridge created # QEMU Works with taps, set tap to the bridge created
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
error "$tuntap" && return 1 warn "$tuntap" && return 1
fi
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "failed to set MTU size to $MTU."
fi
fi
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
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
@@ -218,27 +427,39 @@ configureNAT() {
sleep 2 sleep 2
done done
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
error "Failed to set IP link!" && return 1 warn "failed to set master bridge!" && return 1
fi fi
# Add internet connection to the VM if grep -wq "nf_tables" /proc/modules; then
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null update-alternatives --set iptables /usr/sbin/iptables-nft > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null update-alternatives --set ip6tables /usr/sbin/ip6tables-nft > /dev/null
else
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
fi
exclude=$(getHostPorts "$HOST_PORTS") exclude=$(getHostPorts "$HOST_PORTS")
if [ -n "$exclude" ]; then
if [[ "$exclude" != *","* ]]; then
exclude=" ! --dport $exclude"
else
exclude=" -m multiport ! --dports $exclude"
fi
fi
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
error "$tables" && return 1 warn "$tables" && return 1
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$ip"; then
error "Failed to configure IP tables!" && return 1 warn "failed to configure IP tables!" && return 1
fi fi
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$ip"; then
error "Failed to configure IP tables!" && return 1 warn "failed to configure IP tables!" && return 1
fi fi
if (( KERNEL > 4 )); then if (( KERNEL > 4 )); then
@@ -255,19 +476,53 @@ 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
}
closeBridge() {
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
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 "$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 return 0
} }
closeNetwork() { closeNetwork() {
if [[ "$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
@@ -275,24 +530,29 @@ closeNetwork() {
exec 30<&- || true exec 30<&- || true
exec 40<&- || true exec 40<&- || true
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" != [Yy1]* ]]; then
ip link set "$VM_NET_TAP" down || true closeBridge
return 0
fi
ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true
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 ip link delete "$VM_NET_TAP" || true
else
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
[[ "${NETWORK,,}" == "user"* ]] && return 0
ip link set "$VM_NET_TAP" down promisc off || true
ip link delete "$VM_NET_TAP" || true
ip link set dockerbridge down || true
ip link delete dockerbridge || true
fi fi
return 0 return 0
@@ -300,17 +560,17 @@ closeNetwork() {
checkOS() { checkOS() {
local name local kernel
local os="" local os=""
local if="macvlan" local if="macvlan"
name=$(uname -a) kernel=$(uname -a)
[[ "${name,,}" == *"darwin"* ]] && os="Docker Desktop for macOS" [[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
[[ "${name,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows" [[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
if="macvtap" if="macvtap"
[[ "${name,,}" == *"synology"* ]] && os="Synology Container Manager" [[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
fi fi
if [ -n "$os" ]; then if [ -n "$os" ]; then
@@ -335,12 +595,77 @@ getInfo() {
if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then
error "Network interface '$VM_NET_DEV' does not exist inside the container!" error "Network interface '$VM_NET_DEV' does not exist inside the container!"
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 27 error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
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)
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)
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 [ "$MTU" -gt "1500" ]; then
[[ "$DEBUG" == [Yy1]* ]] && echo "MTU size is too large: $MTU, ignoring..."
MTU="0"
fi
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
fi
fi fi
if [ -z "$VM_NET_MAC" ]; then if [ -z "$VM_NET_MAC" ]; then
local file="$STORAGE/dsm.mac" local file="$STORAGE/dsm.mac"
[ -s "$file" ] && VM_NET_MAC=$(<"$file") [ -s "$file" ] && VM_NET_MAC=$(<"$file")
VM_NET_MAC="${VM_NET_MAC//[![:print:]]/}"
if [ -z "$VM_NET_MAC" ]; then if [ -z "$VM_NET_MAC" ]; then
# 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/')
@@ -360,9 +685,28 @@ getInfo() {
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28 error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi fi
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}') 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/)
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
echo "$IP" > /run/shm/qemu.ip echo "$IP" > /run/shm/qemu.ip
echo "$nic" > /run/shm/qemu.nic
return 0 return 0
} }
@@ -376,70 +720,79 @@ 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
info "Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC" cleanUp
[ -f /etc/resolv.conf ] && grep '^nameserver*' /etc/resolv.conf
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
MSG="Booting DSM instance..."
html "$MSG"
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 if [[ "${WEB:-}" != [Nn]* ]]; then
checkOS sleep 1.2
closeWeb
fi fi
# Shutdown nginx case "${NETWORK,,}" in
nginx -s stop 2> /dev/null "user"* | "passt" | "slirp" ) ;;
fWait "nginx" "nat" | "tap" | "tun" | "tuntap" | "y" )
if [[ "${NETWORK,,}" != "user"* ]]; then # Configure tap interface
if ! configureNAT; then
# Configure for tap interface closeBridge
if ! configureNAT; then NETWORK="user"
msg="falling back to user-mode networking!"
msg="failed to setup NAT networking, $msg"
NETWORK="user" fi ;;
warn "falling back to usermode networking! Performance will be bad and port mapping will not work."
ip link set "$VM_NET_TAP" down promisc off &> null || true esac
ip link delete "$VM_NET_TAP" &> null || true
ip link set dockerbridge down &> null || true [[ "${NETWORK,,}" == "user"* ]] && NETWORK="passt"
ip link delete dockerbridge &> null || true
fi case "${NETWORK,,}" in
"nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
"passt" )
fi # Configure for user-mode networking (passt)
if ! configurePasst; then
error "Failed to configure user-mode networking!"
exit 24
fi ;;
if [[ "${NETWORK,,}" == "user"* ]]; then "slirp" )
# Configure for usermode networking (slirp) # Configure for user-mode networking (slirp)
configureUser || exit 24 if ! configureSlirp; then
error "Failed to configure user-mode networking!"
exit 24
fi
fi if [ -z "$USER_PORTS" ]; then
info "Notice: slirp networking is active, so when you want to expose ports, you will need to map them using this variable: \"USER_PORTS=5000,5001\"."
fi ;;
*)
error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
esac
fi fi
NET_OPTS+=" -device $ADAPTER,romfile=,netdev=hostnet0,mac=$VM_NET_MAC,id=net0" NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
[[ "$MTU" != "0" && "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
return 0 return 0

View File

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

View File

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

View File

@@ -3,56 +3,38 @@ set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: "${KVM:="Y"}"
: "${HOST_CPU:=""}" : "${HOST_CPU:=""}"
: "${CPU_FLAGS:=""}" : "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}" : "${CPU_MODEL:=""}"
: "${DEF_MODEL:="qemu64"}"
if [[ "${ARCH,,}" != "amd64" ]]; then CLOCKSOURCE="tsc"
KVM="N" [[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, this will cause a major loss of performance." CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
if [ ! -f "$CLOCK" ]; then
warn "file \"$CLOCK\" cannot not found?"
else
result=$(<"$CLOCK")
result="${result//[![:print:]]/}"
case "${result,,}" in
"${CLOCKSOURCE,,}" ) ;;
"kvm-clock" ) info "Nested KVM virtualization detected.." ;;
"hyperv_clocksource_tsc_page" ) info "Nested Hyper-V virtualization detected.." ;;
"hpet" ) warn "unsupported clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
*) warn "unexpected clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
esac
fi fi
if [[ "$KVM" != [Nn]* ]]; then flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
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
if [[ "$KVM" != [Nn]* ]]; then if [[ "$KVM" != [Nn]* ]]; then
CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor" CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor"
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
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..." 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" CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi fi
@@ -61,15 +43,6 @@ if [[ "$KVM" != [Nn]* ]]; then
CPU_FEATURES+=",migratable=no" CPU_FEATURES+=",migratable=no"
fi fi
if [ -f "$CLOCK" ]; then
CLOCK=$(<"$CLOCK")
if [[ "${CLOCK,,}" != "tsc" ]]; then
warn "unexpected clocksource: $CLOCK"
fi
else
warn "file \"$CLOCK\" cannot not found?"
fi
if grep -qw "svm" <<< "$flags"; then if grep -qw "svm" <<< "$flags"; then
# AMD processor # AMD processor
@@ -104,7 +77,7 @@ else
CPU_MODEL="max" CPU_MODEL="max"
CPU_FEATURES+=",migratable=no" CPU_FEATURES+=",migratable=no"
else else
CPU_MODEL="$DEF_MODEL" CPU_MODEL="qemu64"
fi fi
fi fi
@@ -112,6 +85,30 @@ else
fi 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_FLAGS" ]; then
if [ -z "$CPU_FEATURES" ]; then if [ -z "$CPU_FEATURES" ]; then
CPU_FLAGS="$CPU_MODEL" CPU_FLAGS="$CPU_MODEL"
@@ -127,7 +124,7 @@ else
fi fi
if [ -z "$HOST_CPU" ]; then if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep -m 1 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g') [[ "${CPU,,}" != "unknown" ]] && HOST_CPU="$CPU"
fi fi
if [ -n "$HOST_CPU" ]; then if [ -n "$HOST_CPU" ]; then

View File

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

View File

@@ -1,45 +1,49 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "${1:-}" "\E[0m\n"; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: ${1:-}" "\E[0m\n" >&2; }
warn () { printf "%b%s%b" "\E[1;31m " "Warning: ${1:-}" "\E[0m\n" >&2; }
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
: "${COMMIT:="N"}" # Commit to local image
: "${COUNTRY:=""}" # Country code for mirror : "${COUNTRY:=""}" # Country code for mirror
: "${CONSOLE:="N"}" # Disable console mode : "${CONSOLE:="N"}" # Disable console mode
: "${ALLOCATE:=""}" # Preallocate diskspace : "${ALLOCATE:=""}" # Preallocate diskspace
: "${ARGUMENTS:=""}" # Extra QEMU parameters : "${ARGUMENTS:=""}" # Extra QEMU parameters
: "${CPU_CORES:="1"}" # Amount of CPU cores : "${CPU_CORES:="2"}" # Amount of CPU cores
: "${RAM_SIZE:="1G"}" # Maximum RAM amount : "${RAM_SIZE:="2G"}" # Maximum RAM amount
: "${RAM_CHECK:="Y"}" # Check available RAM : "${RAM_CHECK:="Y"}" # Check available RAM
: "${DISK_SIZE:="16G"}" # Initial data disk size : "${DISK_SIZE:="16G"}" # Initial data disk size
: "${STORAGE:="/storage"}" # Storage folder location : "${STORAGE:="/storage"}" # Storage folder location
# Helper variables # Helper variables
PODMAN="N"
ENGINE="Docker"
PROCESS="${APP,,}" PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}" PROCESS="${PROCESS// /-}"
if [ -f "/run/.containerenv" ]; then
PODMAN="Y"
ENGINE="Podman"
fi
echo " Starting $APP for $ENGINE v$(</run/version)..."
echo " For support visit $SUPPORT"
INFO="/run/shm/msg.html" INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html" PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html" TEMPLATE="/var/www/index.html"
FOOTER1="$APP for Docker v$(</run/version)" FOOTER1="$APP for $ENGINE v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>" FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
CPI=$(lscpu) CPU=$(cpu)
SYS=$(uname -r) SYS=$(uname -r)
HOST=$(hostname -s) HOST=$(hostname -s)
KERNEL=$(echo "$SYS" | cut -b 1) KERNEL=$(echo "$SYS" | cut -b 1)
@@ -47,30 +51,21 @@ 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)" <<< "$CPI"; then if ! grep -qi "socket(s)" <<< "$(lscpu)"; then
SOCKETS=1 SOCKETS=1
else else
SOCKETS=$(echo "$CPI" | grep -m 1 -i 'socket(s)' | awk '{print $(2)}') SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
fi fi
if ! grep -qi "model name" <<< "$CPI"; then CPU_CORES="${CPU_CORES// /}"
CPU="" [[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
else [ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
CPU=$(echo "$CPI" | 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:" <<< "$CPI"; then if [ "$CPU_CORES" -gt "$CORES" ]; then
CPU=$(echo "$CPI" | 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') warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of physical cores, so will be limited to ${CORES}."
CPU_CORES="$CORES"
fi fi
CPU="${CPU// CPU/}"
CPU="${CPU// 16 Core/}"
CPU="${CPU// Processor/}"
CPU="${CPU// Quad core/}"
CPU="${CPU// Core TM/ Core}"
CPU="${CPU// with Radeon Graphics/}"
[ -z "${CPU// /}" ] && CPU="Unknown"
# Check system # Check system
if [ ! -d "/dev/shm" ]; then if [ ! -d "/dev/shm" ]; then
@@ -81,19 +76,23 @@ fi
# Check folder # Check folder
if [[ "$COMMIT" != [Nn]* ]]; then if [[ "${COMMIT:-}" == [Yy1]* ]]; then
STORAGE="/local" STORAGE="/local"
mkdir -p "$STORAGE" mkdir -p "$STORAGE"
else fi
if [ ! -d "$STORAGE" ]; then
error "Storage folder ($STORAGE) not found!" && exit 13 if [ ! -d "$STORAGE" ]; then
fi error "Storage folder ($STORAGE) not found!" && exit 13
fi
if [ ! -w "$STORAGE" ]; then
error "Storage folder ($STORAGE) is not writeable!" && exit 13
fi fi
# Check filesystem # Check filesystem
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
DISK_IO="threads" DISK_IO="threads"
DISK_CACHE="writeback" DISK_CACHE="writeback"
fi fi
@@ -102,11 +101,24 @@ fi
RAM_SPARE=500000000 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_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
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') 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=$(numfmt --from=iec "$RAM_SIZE")
AVAIL_GB=$(( RAM_AVAIL/1073741824 )) [ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
TOTAL_GB=$(( (RAM_TOTAL + 1073741823)/1073741824 ))
WANTED_GB=$(( (RAM_WANTED + 1073741823)/1073741824 ))
# Print system info # Print system info
SYS="${SYS/-generic/}" SYS="${SYS/-generic/}"
@@ -114,20 +126,79 @@ FS="${FS/UNKNOWN //}"
FS="${FS/ext2\/ext3/ext4}" FS="${FS/ext2\/ext3/ext4}"
FS=$(echo "$FS" | sed 's/[)(]//g') FS=$(echo "$FS" | sed 's/[)(]//g')
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) SPACE_GB=$(formatBytes "$SPACE" "down")
AVAIL_MEM=$(formatBytes "$RAM_AVAIL" "down")
TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
echo " CPU: ${CPU} | RAM: $AVAIL_GB/$TOTAL_GB GB | DISK: $SPACE_GB GB (${FS}) | KERNEL: ${SYS}..." echo " CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
echo echo
# Check memory # Check available memory
if [[ "$RAM_CHECK" != [Nn]* ]]; then if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
error "Your configured RAM_SIZE of $WANTED_GB GB is too high for the $AVAIL_GB GB of memory available, please set a lower value." 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."
exit 17 [[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
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
fi fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(/dev/kvm is unwriteable)"
else
if [[ "${PLATFORM,,}" == "x64" ]]; then
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
# Cleanup files # Cleanup files
rm -f /run/shm/qemu.* rm -f /run/shm/qemu.*
rm -f /run/shm/dsm.url rm -f /run/shm/dsm.url
@@ -136,154 +207,4 @@ rm -f /run/shm/dsm.url
rm -rf /tmp/dsm rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp" rm -rf "$STORAGE/tmp"
# Helper functions
isAlive() {
local pid=$1
if kill -0 "$pid" 2>/dev/null; then
return 0
fi
return 1
}
pKill() {
local pid=$1
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 0.2
done
return 0
}
fWait() {
local name=$1
while pgrep -f -l "$name" >/dev/null; do
sleep 0.2
done
return 0
}
fKill() {
local name=$1
{ pkill -f "$name" || true; } 2>/dev/null
fWait "$name"
return 0
}
escape () {
local s
s=${1//&/\&amp;}
s=${s//</\&lt;}
s=${s//>/\&gt;}
s=${s//'"'/\&quot;}
printf -- %s "$s"
return 0
}
html()
{
local title
local body
local script
local footer
title=$(escape "$APP")
title="<title>$title</title>"
footer=$(escape "$FOOTER1")
body=$(escape "$1")
if [[ "$body" == *"..." ]]; then
body="<p class=\"loading\">${body/.../}</p>"
fi
[ -n "${2:-}" ] && script="$2" || script=""
local HTML
HTML=$(<"$TEMPLATE")
HTML="${HTML/\[1\]/$title}"
HTML="${HTML/\[2\]/$script}"
HTML="${HTML/\[3\]/$body}"
HTML="${HTML/\[4\]/$footer}"
HTML="${HTML/\[5\]/$FOOTER2}"
echo "$HTML" > "$PAGE"
echo "$body" > "$INFO"
return 0
}
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
}
# Start webserver
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
return 0 return 0

View File

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

39
src/server.sh Normal file
View File

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

16
src/socket.sh Normal file
View File

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

6
src/start.sh Normal file
View File

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

223
src/utils.sh Normal file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Helper functions
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "${1:-}" "\E[0m\n"; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: ${1:-}" "\E[0m\n" >&2; }
warn () { printf "%b%s%b" "\E[1;31m " "Warning: ${1:-}" "\E[0m\n" >&2; }
formatBytes() {
local result
result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;')
local unit="${result//[0-9. ]}"
result="${result//[a-zA-Z ]/}"
if [[ "${2:-}" == "up" ]]; then
if [[ "$result" == *"."* ]]; then
result="${result%%.*}"
result=$((result+1))
fi
else
if [[ "${2:-}" == "down" ]]; then
result="${result%%.*}"
fi
fi
echo "$result $unit"
return 0
}
isAlive() {
local pid="$1"
if kill -0 "$pid" 2>/dev/null; then
return 0
fi
return 1
}
pKill() {
local pid="$1"
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 0.2
done
return 0
}
fWait() {
local name="$1"
while pgrep -f -l "$name" >/dev/null; do
sleep 0.2
done
return 0
}
fKill() {
local name="$1"
{ pkill -f "$name" || true; } 2>/dev/null
fWait "$name"
return 0
}
escape () {
local s
s=${1//&/\&amp;}
s=${s//</\&lt;}
s=${s//>/\&gt;}
s=${s//'"'/\&quot;}
printf -- %s "$s"
return 0
}
html() {
local title
local body
local script
local footer
title=$(escape "$APP")
title="<title>$title</title>"
footer=$(escape "$FOOTER1")
body=$(escape "$1")
if [[ "$body" == *"..." ]]; then
body="<p class=\"loading\">${body/.../}</p>"
fi
[ -n "${2:-}" ] && script="$2" || script=""
local HTML
HTML=$(<"$TEMPLATE")
HTML="${HTML/\[1\]/$title}"
HTML="${HTML/\[2\]/$script}"
HTML="${HTML/\[3\]/$body}"
HTML="${HTML/\[4\]/$footer}"
HTML="${HTML/\[5\]/$FOOTER2}"
echo "$HTML" > "$PAGE"
echo "$body" > "$INFO"
return 0
}
cpu() {
local ret
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// [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"
return 0
}
hasDisk() {
[ -b "/disk" ] && return 0
[ -b "/disk1" ] && return 0
[ -b "/dev/disk1" ] && return 0
[ -b "${DEVICE:-}" ] && return 0
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
[ -s "$STORAGE/$DISK_NAME.img" ] && return 0
[ -s "$STORAGE/$DISK_NAME.qcow2" ] && return 0
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

View File

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

View File

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