Compare commits

...
68 Commits
Author SHA1 Message Date
KroeseandGitHub f45b308c78 feat: Sanitize environment variables (#1192) 2026-07-03 17:14:59 +02:00
CopilotandGitHub d5821cd782 feat: Normalize boolean option handling (#1191) 2026-07-03 15:17:14 +02:00
KroeseandGitHub 312816b18e feat: Implement strip() function (#1190)
Added strip function to remove whitespace and quotes.
2026-07-03 14:48:58 +02:00
KroeseandGitHub f42a3f1662 feat: Add enabled() function (#1189) 2026-07-03 14:06:42 +02:00
KroeseandGitHub 558e5022ca docs: Readme (#1188) 2026-07-02 20:26:15 +02:00
KroeseandGitHub e03822f26e feat: Improve shutdown logic (#1187) 2026-07-02 20:14:05 +02:00
KroeseandGitHub 7b90ca30ba feat: Improve socket cleanup (#1186) 2026-07-02 18:51:40 +02:00
KroeseandGitHub 22c01bf2f1 docs: Readme (#1185) 2026-07-02 12:28:35 +02:00
KroeseandGitHub 1f601db8b0 build: Update Passt to v2026_06_11 (#1183) 2026-06-28 12:43:44 +02:00
KroeseandGitHub e7e2c35cb7 build: Run review steps in parallel (#1182) 2026-06-25 22:31:34 +02:00
KroeseandGitHub 50089112c0 docs: Update stargazers chart (#1181) 2026-06-25 14:21:53 +02:00
KroeseandGitHub f9538ce53b feat: Improve IPv6 check (#1180) 2026-06-19 13:24:54 +02:00
renovate[bot]andGitHub 49d25a13d0 chore(deps): update actions/checkout action to v7 (#1179) 2026-06-19 11:48:11 +02:00
KroeseandGitHub 7c9d2b1fd5 feat: Improved start and stop logic (#1178) 2026-06-05 02:37:20 +02:00
KroeseandGitHub b68d243b6a fix: Add fail_level to reviewdog actions (#1177) 2026-06-04 23:45:01 +02:00
KroeseandGitHub 79e85f9fd1 fix: Provide exitcode on shutdown (#1176) 2026-05-31 00:32:49 +02:00
KroeseandGitHub 476048a4ee fix: Update paths for QEMU files (#1175) 2026-05-30 23:50:39 +02:00
KroeseandGitHub f983dc3e46 fix: Remove stale QEMU files (#1174) 2026-05-30 23:05:16 +02:00
KroeseandGitHub 38f9c49200 feat: Added finishDisks function (#1173) 2026-05-30 20:51:58 +02:00
KroeseandGitHub 7157717a85 feat: Added option to disable data disks (#1171) 2026-05-30 19:52:54 +02:00
KroeseandGitHub 031674424d fix: Handle iptables-save errors better (#1170) 2026-05-30 18:58:12 +02:00
KroeseandGitHub 28e6f8da78 fix: IP address parsing (#1168) 2026-05-29 17:13:47 +02:00
KroeseandGitHub d5802607b5 fix: Throw object for XMLHttpRequest (#1166) 2026-05-29 16:54:45 +02:00
KroeseandGitHub 7168f2f843 feat: Update version file path (#1165) 2026-05-29 16:08:23 +02:00
KroeseandGitHub 819dd29025 fix: Network interface detection (#1164) 2026-05-29 14:34:34 +02:00
KroeseandGitHub 48ef47b85b fix: Previous XHR not aborted (#1163) 2026-05-29 14:33:23 +02:00
KroeseandGitHub 10c902bc9b build: Update Passt to v2026_05_26 (#1162) 2026-05-29 12:52:20 +02:00
KroeseandGitHub d6de10efe1 feat: Flush iptables on container restarts (#1161) 2026-05-29 05:00:56 +02:00
KroeseandGitHub 6d1d9d92b4 feat: Improve iptable rules (#1160) 2026-05-28 23:00:17 +02:00
KroeseandGitHub 799649e78c docs: Improve low diskspace warning (#1158) 2026-05-24 14:29:17 +02:00
KroeseandGitHub 12060c72b2 feat: Update warning message (#1157) 2026-05-23 20:31:37 +02:00
KroeseandGitHub 6eadd1b953 fix: Timeout comparison operator (#1155) 2026-05-17 13:01:57 +02:00
KroeseandGitHub 342ca4da03 fix: Wrong IP was passed to getSlirp (#1154) 2026-05-17 12:27:58 +02:00
KroeseandGitHub b0b6190560 feat: Add warning when macvlan parent is a bridge (#1153)
Fixes #1152
2026-05-17 06:56:55 +02:00
KroeseandGitHub 075410e4cf fix: CPU cores validation logic (#1151) 2026-05-16 20:59:57 +02:00
KroeseandGitHub 2a388b434b feat: Improve Dnsmasq error handling (#1149) 2026-05-16 11:35:04 +02:00
KroeseandGitHub 280c4037f3 fix: CPU cores validation logic (#1150) 2026-05-16 11:33:23 +02:00
KroeseandGitHub 007bdc735d fix: Warning message for missing clock source (#1148) 2026-05-16 10:58:37 +02:00
KroeseandGitHub 41fc0a6a10 fix: Undeclared variable in ConvertDisk (#1147) 2026-05-16 10:11:37 +02:00
KroeseandGitHub 7753e4ac74 fix: Slirp portmapping was missing last port (#1146) 2026-05-16 02:22:44 +02:00
KroeseandGitHub f74bf35202 fix: Socket detection logic (#1145) 2026-05-16 02:17:55 +02:00
KroeseandGitHub aa472c96c6 feat: Use iptables multiport extension (#1144) 2026-05-15 23:28:01 +02:00
KroeseandGitHub 0dcb880800 fix: Do not remove QEMU files (#1143) 2026-05-15 22:43:44 +02:00
KroeseandGitHub 8daa6973b7 feat: Do not require NET_RAW for dnsmasq (#1142) 2026-05-15 21:30:16 +02:00
KroeseandGitHub 3884befa56 feat: Improve memory allocation logic (#1141) 2026-05-15 17:58:41 +02:00
KroeseandGitHub 85b83f8eb1 feat: Remove reliability on iptables multiport extension (#1140) 2026-05-15 17:44:35 +02:00
KroeseandGitHub f395dfaa66 feat: Removed legacy code (#1139) 2026-05-14 20:59:36 +02:00
KroeseandGitHub b3124075ea feat: Refactor QEMU PID variables (#1138) 2026-05-10 17:34:22 +02:00
KroeseandGitHub aed909bdee fix: NFT tables detection (#1137) 2026-05-10 17:24:02 +02:00
KroeseandGitHub d4cf5778e2 build: Update Passt to v2026_05_07 (#1136) 2026-05-10 17:20:26 +02:00
KroeseandGitHub 48de7dd00b fix: Size comparison variables (#1135) 2026-05-10 17:16:20 +02:00
dependabot[bot]andGitHub 686e64da3f build(deps): Bump docker/setup-buildx-action from 3 to 4 (#1130) 2026-05-10 17:15:02 +02:00
dependabot[bot]andGitHub 7517dc8b3e build(deps): Bump docker/metadata-action from 5 to 6 (#1131) 2026-05-10 17:13:41 +02:00
renovate[bot]andGitHub 660fe715e4 chore(deps): update grantbirki/json-yaml-validate action to v5 (#1134) 2026-05-10 17:06:56 +02:00
dependabot[bot]andGitHub 04ea29ec77 build(deps): Bump docker/build-push-action from 6 to 7 (#1132) 2026-05-10 17:05:14 +02:00
dependabot[bot]andGitHub 02c891cb16 build(deps): Bump docker/login-action from 3 to 4 (#1129) 2026-05-10 17:04:00 +02:00
Quang-Linh LEandGitHub 0b5d21357e fiz: Ignore sector size for whole disk passthrough (#1121) 2026-01-13 11:40:35 +01:00
KroeseandGitHub e0545b37d7 fix: Avoid duplicating dnsmasq arguments (#1113) 2025-11-22 04:28:11 +01:00
renovate[bot]andGitHub 4161c21082 chore(deps): update actions/checkout action to v6 (#1112) 2025-11-21 12:39:39 +01:00
KroeseandGitHub 48d9a1771d fix: Update Codespaces configuration (#1110) 2025-11-14 06:05:19 +01:00
KroeseandGitHub 471cdbb338 fix: Workaround AppArmor profile for passt (#1108) 2025-11-12 07:03:22 +01:00
KroeseandGitHub e77bca202b fix: Spelling mistake (#1105) 2025-11-06 03:46:35 +01:00
KroeseandGitHub 2e6c01e934 feat: Detect if container is running in privileged mode (#1104) 2025-11-06 03:39:46 +01:00
KroeseandGitHub 302c991c0c fix: Change condition for OverlayFS warning (#1103) 2025-11-06 03:22:47 +01:00
KroeseandGitHub a89007ee03 build: Use Github token (#1100) 2025-10-29 14:05:53 +01:00
KroeseandGitHub 8a89149d58 feat: Check for SSE4 instruction set (#1099) 2025-10-29 08:32:42 +01:00
KroeseandGitHub 5e8bbc2868 fix: Remove unnecessary operation (#1097) 2025-10-24 04:30:21 +02:00
KroeseandGitHub 4e48920309 fix: Do not assume Podman never has privileges (#1096) 2025-10-24 01:19:38 +02:00
23 changed files with 763 additions and 522 deletions
+2 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "Virtual DSM", "name": "Virtual DSM",
"service": "vdsm", "service": "dsm",
"forwardPorts": [5000], "forwardPorts": [5000],
"portsAttributes": { "portsAttributes": {
"5000": { "5000": {
@@ -11,7 +11,5 @@
"otherPortsAttributes": { "otherPortsAttributes": {
"onAutoForward": "ignore" "onAutoForward": "ignore"
}, },
"dockerComposeFile": "codespaces.yml", "dockerComposeFile": "codespaces.yml"
"workspaceFolder": "/workspaces/vdsm",
"initializeCommand": "docker system prune --all --force"
} }
+6 -6
View File
@@ -22,13 +22,13 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v7
with: with:
fetch-depth: 0 fetch-depth: 0
- -
name: Docker metadata name: Docker metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
context: git context: git
images: | images: |
@@ -43,23 +43,23 @@ jobs:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
- -
name: Set up Docker Buildx name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- -
name: Login into Docker Hub name: Login into Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- -
name: Login to GitHub Container Registry name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- -
name: Build Docker image name: Build Docker image
uses: docker/build-push-action@v6 uses: docker/build-push-action@v7
with: with:
context: . context: .
push: true push: true
+5 -4
View File
@@ -7,9 +7,10 @@ jobs:
name: shellcheck name: shellcheck
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v7
- parallel:
- -
name: Run ShellCheck name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
@@ -24,6 +25,6 @@ jobs:
failure-threshold: warning failure-threshold: warning
- -
name: Validate JSON and YML files name: Validate JSON and YML files
uses: GrantBirki/json-yaml-validate@v4 uses: GrantBirki/json-yaml-validate@v5.0.0
with: with:
yaml_exclude_regex: ".*\\kubernetes\\.yml$" yaml_exclude_regex: ".*\\kubernetes\\.yml$"
+12 -10
View File
@@ -12,13 +12,15 @@ jobs:
dockerHubDescription: dockerHubDescription:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 -
- name: Checkout repo
name: Docker Hub Description uses: actions/checkout@v7
uses: peter-evans/dockerhub-description@v5 -
with: name: Docker Hub Description
username: ${{ secrets.DOCKERHUB_USERNAME }} uses: peter-evans/dockerhub-description@v5
password: ${{ secrets.DOCKERHUB_TOKEN }} with:
repository: ${{ secrets.DOCKERHUB_REPO }} username: ${{ secrets.DOCKERHUB_USERNAME }}
short-description: ${{ github.event.repository.description }} password: ${{ secrets.DOCKERHUB_TOKEN }}
readme-filepath: ./readme.md repository: ${{ secrets.DOCKERHUB_REPO }}
short-description: ${{ github.event.repository.description }}
readme-filepath: ./readme.md
+15 -10
View File
@@ -13,9 +13,10 @@ jobs:
name: review name: review
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v7
- parallel:
- -
name: Spelling name: Spelling
uses: reviewdog/action-misspell@v1 uses: reviewdog/action-misspell@v1
@@ -26,41 +27,45 @@ jobs:
*.md *.md
*.sh *.sh
reporter: github-pr-review reporter: github-pr-review
github_token: ${{ secrets.REPO_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
- -
name: Hadolint name: Hadolint
uses: reviewdog/action-hadolint@v1 uses: reviewdog/action-hadolint@v1
with: with:
level: warning level: warning
fail_level: error
reporter: github-pr-review reporter: github-pr-review
hadolint_ignore: DL3008 DL3003 DL3006 DL3013 hadolint_ignore: DL3008 DL3003 DL3006 DL3013
github_token: ${{ secrets.REPO_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
- -
name: YamlLint name: YamlLint
uses: reviewdog/action-yamllint@v1 uses: reviewdog/action-yamllint@v1
with: with:
level: warning level: warning
reporter: github-pr-review reporter: github-pr-review
github_token: ${{ secrets.REPO_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
- -
name: ActionLint name: ActionLint
uses: reviewdog/action-actionlint@v1 uses: reviewdog/action-actionlint@v1
with: with:
level: warning level: warning
reporter: github-pr-review reporter: github-pr-review
github_token: ${{ secrets.REPO_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
- -
name: Shellformat name: Shellformat
uses: reviewdog/action-shfmt@v1 uses: reviewdog/action-shfmt@v1
if: false
with: with:
level: warning level: warning
fail_on_error: "true"
shfmt_flags: "-i 2 -ci -bn" shfmt_flags: "-i 2 -ci -bn"
github_token: ${{ secrets.REPO_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
- -
name: Shellcheck name: Shellcheck
uses: reviewdog/action-shellcheck@v1 uses: reviewdog/action-shellcheck@v1
with: with:
level: warning level: warning
fail_level: error
reporter: github-pr-review reporter: github-pr-review
shellcheck_flags: -x -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028 shellcheck_flags: -x -e SC1091 -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
github_token: ${{ secrets.REPO_ACCESS_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
+5 -11
View File
@@ -1,19 +1,13 @@
# syntax=docker/dockerfile:1 # syntax=docker/dockerfile:1
FROM qemux/qemu-host:2.05 AS builder FROM qemux/qemu-host:2.05 AS builder
# FROM golang as builder
# WORKDIR /
# RUN git clone https://github.com/qemus/qemu-host.git
# WORKDIR /qemu-host/src
# RUN go mod download
# RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host.bin .
FROM debian:trixie-slim FROM debian:trixie-slim
ARG TARGETARCH ARG TARGETARCH
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
ARG VERSION_PASST="2026_06_11"
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"
@@ -39,7 +33,7 @@ RUN set -eu && \
iproute2 \ iproute2 \
dnsmasq \ dnsmasq \
fakeroot \ fakeroot \
apt-utils \ apt-utils \
net-tools \ net-tools \
e2fsprogs \ e2fsprogs \
qemu-utils \ qemu-utils \
@@ -49,7 +43,7 @@ RUN set -eu && \
ca-certificates \ ca-certificates \
netcat-openbsd \ netcat-openbsd \
qemu-system-x86 && \ qemu-system-x86 && \
wget "https://github.com/qemus/passt/releases/download/v2025_09_19/passt_2025_09_19_${TARGETARCH}.deb" -O /tmp/passt.deb -q && \ wget "https://github.com/qemus/passt/releases/download/v${VERSION_PASST}/passt_${VERSION_PASST}_${TARGETARCH}.deb" -O /tmp/passt.deb -q --timeout=10 && \
dpkg -i /tmp/passt.deb && \ dpkg -i /tmp/passt.deb && \
apt-get clean && \ apt-get clean && \
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \ pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
@@ -57,7 +51,7 @@ RUN set -eu && \
echo "allow br0" > /etc/qemu/bridge.conf && \ echo "allow br0" > /etc/qemu/bridge.conf && \
unlink /etc/nginx/sites-enabled/default && \ unlink /etc/nginx/sites-enabled/default && \
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \ sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \
echo "$VERSION_ARG" > /run/version && \ echo "$VERSION_ARG" > /etc/version && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY --chmod=755 ./src /run/ COPY --chmod=755 ./src /run/
+23 -14
View File
@@ -18,11 +18,11 @@ Virtual DSM in a Docker container.
- Multiple disks - Multiple disks
- KVM acceleration - KVM acceleration
- Upgrades supported - Automatic download
## Usage 🐳 ## Usage 🐳
##### Via Docker Compose: ##### Docker Compose:
```yaml ```yaml
services: services:
@@ -44,22 +44,31 @@ services:
stop_grace_period: 2m stop_grace_period: 2m
``` ```
##### Via Docker CLI: ##### Docker CLI:
```bash ```bash
docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 docker.io/vdsm/virtual-dsm docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 docker.io/vdsm/virtual-dsm
``` ```
##### Via Kubernetes: ##### 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
``` ```
##### Via Github Codespaces: ##### GitHub Codespaces:
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/vdsm/virtual-dsm) [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/vdsm/virtual-dsm)
## Requirements ⚙️
- A Linux host with KVM support, or Docker Desktop / Podman on Windows 11 with nested virtualization enabled.
- At least 2 GB of RAM available.
- At least 32 GB of free disk space.
> [!NOTE]
> Docker Desktop on macOS and Windows 10 do not currently provide the required KVM support for this image.
## FAQ 💬 ## FAQ 💬
### How do I use it? ### How do I use it?
@@ -95,7 +104,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
``` ```
> [!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 an existing disk to a larger capacity without any data loss.
### How do I add multiple disks? ### How do I add multiple disks?
@@ -110,9 +119,9 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- ./example3:/storage3 - ./example3:/storage3
``` ```
### How do I pass-through a disk? ### How do I pass through a disk?
It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way: You can pass through disk devices or partitions directly by adding them to your compose file in this way:
```yaml ```yaml
devices: devices:
@@ -158,7 +167,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- you enabled "nested virtualization" if you are running the container inside a virtual machine. - you enabled "nested virtualization" if you are running the container inside a virtual machine.
- 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 VPSs.
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. 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.
@@ -214,9 +223,9 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
- 'c *:* rwm' - 'c *:* rwm'
``` ```
### 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:
```yaml ```yaml
environment: environment:
@@ -254,12 +263,12 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### Is this project legal? ### Is this project legal?
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project will be considered legal. Yes, this project contains only open-source code and does not distribute any material owned by Synology. Neither does it try to circumvent any copyright protection measures.
However, by installing Synology's Virtual DSM, you must accept their end-user license agreement, which does not permit installation on non-Synology hardware. So only run this container on an official Synology NAS, as any other use will be a violation of their terms and conditions. However, by installing Synology's Virtual DSM, you must accept their end-user license agreement, which does not permit installation on non-Synology hardware. So only run this container on an official Synology NAS, as any other use will be a violation of their terms and conditions.
## Stars 🌟 ## Stars 🌟
[![Stars](https://starchart.cc/vdsm/virtual-dsm.svg?variant=adaptive)](https://starchart.cc/vdsm/virtual-dsm) [![Stargazers](https://raw.githubusercontent.com/star-stats/stars/refs/heads/data/charts/vdsm-virtual-dsm.svg)](https://github.com/vdsm/virtual-dsm/stargazers)
## Disclaimer ⚖️ ## Disclaimer ⚖️
+4 -4
View File
@@ -4,15 +4,15 @@ set -Eeuo pipefail
: "${DHCP:="N"}" : "${DHCP:="N"}"
: "${NETWORK:="Y"}" : "${NETWORK:="Y"}"
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1 [ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down..." && exit 1
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0 [ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet..." && exit 0
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled.." && exit 0 [[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled." && exit 0
file="/run/shm/dsm.url" file="/run/shm/dsm.url"
address="/run/shm/qemu.ip" address="/run/shm/qemu.ip"
gateway="/run/shm/qemu.gw" gateway="/run/shm/qemu.gw"
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1 [ ! -s "$file" ] && echo "DSM has not enabled networking yet..." && exit 1
location=$(<"$file") location=$(<"$file")
+1
View File
@@ -3,6 +3,7 @@ set -Eeuo pipefail
DEF_OPTS="-nodefaults -boot strict=on" DEF_OPTS="-nodefaults -boot strict=on"
RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
MON_OPTS="-name $PROCESS,process=$PROCESS,debug-threads=on -pidfile $QEMU_PID"
CPU_OPTS="-cpu $CPU_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1" CPU_OPTS="-cpu $CPU_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
MAC_OPTS="-machine type=q35,smm=off,usb=off,vmport=off,dump-guest-core=off,hpet=off${KVM_OPTS}" MAC_OPTS="-machine type=q35,smm=off,usb=off,vmport=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4" DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
+73 -64
View File
@@ -11,6 +11,15 @@ set -Eeuo pipefail
: "${DISK_DISCARD:="on"}" # Controls whether unmap (TRIM) commands are passed to the host. : "${DISK_DISCARD:="on"}" # Controls whether unmap (TRIM) commands are passed to the host.
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD : "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
# Sanitize all variables
DISK_IO=$(strip "$DISK_IO")
DISK_FMT=$(strip "$DISK_FMT")
DISK_TYPE=$(strip "$DISK_TYPE")
DISK_FLAGS=$(strip "$DISK_FLAGS")
DISK_CACHE=$(strip "$DISK_CACHE")
DISK_DISCARD=$(strip "$DISK_DISCARD")
DISK_ROTATION=$(strip "$DISK_ROTATION")
BOOT="$STORAGE/$BASE.boot.img" BOOT="$STORAGE/$BASE.boot.img"
SYSTEM="$STORAGE/$BASE.system.img" SYSTEM="$STORAGE/$BASE.system.img"
@@ -110,7 +119,7 @@ createDisk() {
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
if [[ "$ALLOCATE" != [Nn]* ]]; then if ! disabled "$ALLOCATE"; then
# Check free diskspace # Check free diskspace
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
@@ -139,7 +148,7 @@ createDisk() {
{ chattr +C "$DISK_FILE"; } || : { chattr +C "$DISK_FILE"; } || :
fi fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if disabled "$ALLOCATE"; then
# Create an empty file # Create an empty file
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
@@ -198,7 +207,7 @@ resizeDisk() {
local REQ=$(( DATA_SIZE - CUR_SIZE )) local REQ=$(( DATA_SIZE - CUR_SIZE ))
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71 (( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
if [[ "$ALLOCATE" != [Nn]* ]]; then if ! disabled "$ALLOCATE"; then
# Check free diskspace # Check free diskspace
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
@@ -221,7 +230,7 @@ resizeDisk() {
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
if [[ "$ALLOCATE" == [Nn]* ]]; then if disabled "$ALLOCATE"; then
# Resize file by changing its length # Resize file by changing its length
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
@@ -264,17 +273,19 @@ convertDisk() {
local FS="$7" local FS="$7"
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79 [ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79 [ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exist?" && exit 79
local TMP_FILE="$DISK_BASE.tmp" local TMP_FILE="$DISK_BASE.tmp"
rm -f "$TMP_FILE" rm -f "$TMP_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then local DIR FA
DIR=$(dirname "$TMP_FILE")
local DIR CUR_SIZE SPACE GB if ! disabled "$ALLOCATE"; then
local CUR_SIZE SPACE GB
# Check free diskspace # Check free diskspace
DIR=$(dirname "$TMP_FILE")
CUR_SIZE=$(getSize "$SOURCE_FILE") CUR_SIZE=$(getSize "$SOURCE_FILE")
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1) SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
@@ -295,7 +306,7 @@ convertDisk() {
isCow "$FS" && DISK_PARAM+=",nocow=on" isCow "$FS" && DISK_PARAM+=",nocow=on"
if [[ "$DST_FMT" != "raw" ]]; then if [[ "$DST_FMT" != "raw" ]]; then
if [[ "$ALLOCATE" == [Nn]* ]]; then if disabled "$ALLOCATE"; then
CONV_FLAGS+=" -c" CONV_FLAGS+=" -c"
fi fi
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS" [ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
@@ -308,7 +319,7 @@ convertDisk() {
fi fi
if [[ "$DST_FMT" == "raw" ]]; then if [[ "$DST_FMT" == "raw" ]]; then
if [[ "$ALLOCATE" != [Nn]* ]]; then if ! disabled "$ALLOCATE"; 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" &>/dev/null; then if ! fallocate -l "$CUR_SIZE" "$TMP_FILE" &>/dev/null; then
@@ -346,7 +357,7 @@ checkFS () {
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
if [[ "${FS,,}" == "overlay"* && "$PODMAN" != [Yy1]* ]]; then if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
warn "the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!" warn "the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
@@ -424,6 +435,20 @@ createDevice () {
return 0 return 0
} }
finishDisks () {
case "${DISK_TYPE,,}" in
"blk" | "scsi" | "virtio-blk" | "virtio-scsi" )
DISK_OPTS+=" -object iothread,id=io2" ;;
esac
if ! enabled "$DISK_DISABLE"; then
html "Initialized disks successfully..."
fi
return 0
}
addDisk () { addDisk () {
local DISK_BASE="$1" local DISK_BASE="$1"
@@ -524,7 +549,7 @@ addDisk () {
fi fi
if [ -f "$DISK_FILE" ] && [[ "$ALLOCATE" == [Nn]* ]]; then if [ -f "$DISK_FILE" ] && disabled "$ALLOCATE"; then
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
USED=$(du -sB 1 "$DISK_FILE" | cut -f1) USED=$(du -sB 1 "$DISK_FILE" | cut -f1)
@@ -536,16 +561,15 @@ addDisk () {
GB=$(formatBytes "$FREE") GB=$(formatBytes "$FREE")
LEFT=$(formatBytes "$LEFT") LEFT=$(formatBytes "$LEFT")
CUR_SIZE=$(formatBytes "$CUR_SIZE") CUR_SIZE=$(formatBytes "$CUR_SIZE")
msg="the virtual size of the ${DISK_DESC,,} is $CUR_SIZE" msg="The virtual size of the ${DISK_DESC,,} is $CUR_SIZE"
if [[ "$USED" == "0" ]]; then if [ -n "$USED" ] && [[ "$USED" != "0" ]]; then
msg+=","
else
USED=$(formatBytes "$USED") USED=$(formatBytes "$USED")
msg+=" (of which $USED is used)," msg+=" (of which $USED is used)"
fi fi
warn "$msg but there is only $GB of free space left in $DIR, make at least $LEFT more room available!" info "$msg, but there is only $GB of free space remaining in $DIR now."
info "Please consider making at least $LEFT more space available in $DIR for future expansions."
fi fi
@@ -573,18 +597,25 @@ addDevice () {
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55 [ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
local sectors="" local sectors=""
local result logical physical local dev_type=""
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-) dev_type=$(lsblk -no TYPE "$DISK_DEV" 2>/dev/null | head -n1)
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}"
if [ -n "$physical" ]; then # Only detect and apply sector sizes for partitions, not whole disks
if [[ "$physical" != "512" ]]; then # Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk
sectors=",logical_block_size=$logical,physical_block_size=$physical" if [[ "$dev_type" == "part" ]]; then
local result logical physical
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}"
if [ -n "$physical" ]; then
if [[ "$physical" != "512" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
else
warn "Failed to determine the sector size for $DISK_DEV"
fi fi
else
warn "Failed to determine the sector size for $DISK_DEV"
fi fi
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors") DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
@@ -592,13 +623,16 @@ addDevice () {
return 0 return 0
} }
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"
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data" [ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
[ -z "${DISK_DISABLE:-}" ] && DISK_DISABLE=""
if ! enabled "$DISK_DISABLE"; then
msg="Initializing disks..."
html "$msg"
enabled "$DEBUG" && echo "$msg"
fi
case "${DISK_TYPE,,}" in case "${DISK_TYPE,,}" in
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;; "ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
@@ -609,7 +643,7 @@ if [ -z "$ALLOCATE" ]; then
ALLOCATE="N" ALLOCATE="N"
fi fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if disabled "$ALLOCATE"; then
DISK_STYLE="growable" DISK_STYLE="growable"
DISK_ALLOC="preallocation=off" DISK_ALLOC="preallocation=off"
else else
@@ -620,38 +654,14 @@ fi
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "") DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "") DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
if enabled "$DISK_DISABLE"; then
finishDisks
return 0
fi
DISK1_FILE="$STORAGE/${DISK_NAME}" DISK1_FILE="$STORAGE/${DISK_NAME}"
if [ ! -f "$DISK1_FILE.img" ] && [ -f "$STORAGE/data${DISK_SIZE}.img" ]; then
# Fallback for legacy installs
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi
DISK2_FILE="/storage2/${DISK_NAME}2" DISK2_FILE="/storage2/${DISK_NAME}2"
if [ ! -f "$DISK2_FILE.img" ]; then
# Fallback for legacy installs
FALLBACK="/storage2/data.img"
if [[ -f "$DISK1_FILE.img" && -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then
mv "$FALLBACK" "$DISK2_FILE.img"
fi
fi
fi
DISK3_FILE="/storage3/${DISK_NAME}3" DISK3_FILE="/storage3/${DISK_NAME}3"
if [ ! -f "$DISK3_FILE.img" ]; then
# Fallback for legacy installs
FALLBACK="/storage3/data.img"
if [[ -f "$DISK1_FILE.img" && -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then
mv "$FALLBACK" "$DISK3_FILE.img"
fi
fi
fi
DISK4_FILE="/storage4/${DISK_NAME}4" DISK4_FILE="/storage4/${DISK_NAME}4"
: "${DISK2_SIZE:=""}" : "${DISK2_SIZE:=""}"
@@ -698,7 +708,6 @@ else
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $? addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi fi
DISK_OPTS+=" -object iothread,id=io2" finishDisks
html "Initialized disks successfully..."
return 0 return 0
+7 -2
View File
@@ -8,9 +8,14 @@ set -Eeuo pipefail
: "${DISPLAY:="none"}" # Display type : "${DISPLAY:="none"}" # Display type
: "${RENDERNODE:="/dev/dri/renderD128"}" # Render node : "${RENDERNODE:="/dev/dri/renderD128"}" # Render node
# Sanitize variables
VGA=$(strip "$VGA")
DISPLAY=$(strip "$DISPLAY")
RENDERNODE=$(strip "$RENDERNODE")
CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}') CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}')
if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64" ]]; then if ! enabled "$GPU" || [[ "$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"
@@ -20,7 +25,7 @@ fi
msg="Configuring display drivers..." msg="Configuring display drivers..."
html "$msg" html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg" enabled "$DEBUG" && echo "$msg"
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE" DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
DISPLAY_OPTS+=" -vga $VGA" DISPLAY_OPTS+=" -vga $VGA"
+13 -9
View File
@@ -24,19 +24,23 @@ cd /run
trap - ERR trap - ERR
version=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }') cmd=(qemu-system-x86_64)
version=$("${cmd[@]}" --version | awk 'NR==1 { print $4 }')
info "Booting $APP using QEMU v$version..." info "Booting $APP using QEMU v$version..."
if [[ "$CONSOLE" == [Yy]* ]]; then if ! enabled "$SHUTDOWN"; then
exec qemu-system-x86_64 ${ARGS:+ $ARGS} exec "${cmd[@]}" ${ARGS:+ $ARGS}
fi fi
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || : if [ ! -t 1 ] || [ ! -c /dev/tty ]; then
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15 "${cmd[@]}" ${ARGS:+ $ARGS} &
else
"${cmd[@]}" ${ARGS:+ $ARGS} </dev/tty >/dev/tty &
fi
terminal rc=0
tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null & wait $! || rc=$?
cat "$QEMU_TERM" 2>/dev/null & wait $! || : [ -f "$QEMU_END" ] && exit "$rc"
sleep 1 & wait $! sleep 1 & wait $!
[ ! -f "$QEMU_END" ] && finish 0 finish "$rc"
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
if [[ "$DEBUG" == [Yy1]* ]]; then if enabled "$DEBUG"; then
printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}" printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi fi
+3 -3
View File
@@ -27,11 +27,12 @@ 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" ] && FILE=$(find "$STORAGE" -maxdepth 1 -type f -iname "$FN" -print -quit)
[ -s "$FILE" ] && BASE="DSM_VirtualDSM" && URL="file://$FILE" [ -s "$FILE" ] && BASE="DSM_VirtualDSM" && URL="file://$FILE"
URL=$(strip "$URL")
if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
BASE=$(basename "$URL" .pat) BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }"
printf -v BASE '%b' "${BASE//%/\\x}" printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}" BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
@@ -66,7 +67,6 @@ fi
if [ ! -s "$FILE" ]; then if [ ! -s "$FILE" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }"
printf -v BASE '%b' "${BASE//%/\\x}" printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}" BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
@@ -82,7 +82,7 @@ rm -f "$STORAGE/$BASE.system.img"
# Check filesystem # Check filesystem
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "overlay"* && "$PODMAN" != [Yy1]* ]]; then if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then
warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!" warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
+37 -45
View File
@@ -1,14 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
msg="Checking memory..."
enabled "$DEBUG" && echo "$msg"
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}') RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
if [[ "$RAM_CHECK" != [Nn]* && "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then if ! disabled "$RAM_CHECK" && [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
AVAIL_MEM=$(formatBytes "$RAM_AVAIL") wanted=$(numfmt --from=iec "$RAM_SIZE")
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then if (( (wanted + RAM_SPARE) > RAM_AVAIL )); then
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available," msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of free memory available,"
if [[ "${FS,,}" == "zfs" ]]; then if [[ "${FS,,}" == "zfs" ]]; then
info "$msg but since ZFS is active this will be ignored." info "$msg but since ZFS is active this will be ignored."
else else
@@ -16,8 +20,8 @@ if [[ "$RAM_CHECK" != [Nn]* && "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "h
warn "$msg it will automatically be adjusted to a lower amount." warn "$msg it will automatically be adjusted to a lower amount."
fi fi
else else
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then if (( (wanted + (RAM_SPARE * 3)) > RAM_AVAIL )); then
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available," msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of free memory available,"
if [[ "${FS,,}" == "zfs" ]]; then if [[ "${FS,,}" == "zfs" ]]; then
info "$msg but since ZFS is active this will be ignored." info "$msg but since ZFS is active this will be ignored."
else else
@@ -30,57 +34,45 @@ fi
if [[ "${RAM_SIZE,,}" == "half" ]]; then if [[ "${RAM_SIZE,,}" == "half" ]]; then
RAM_WANTED=$(( RAM_AVAIL / 2 )) if (( (RAM_AVAIL / 2) > RAM_SPARE )); then
RAM_WANTED=$(( RAM_WANTED / 1073741825 )) wanted=$(( (RAM_AVAIL / 2) / 1048577 ))
RAM_SIZE="${wanted}M"
if (( "$RAM_WANTED" < 1 )); then info "Allocated $wanted MB of RAM for the virtual machine."
RAM_WANTED=$(( RAM_AVAIL / 2 ))
RAM_WANTED=$(( RAM_WANTED / 1048577 ))
RAM_SIZE="${RAM_WANTED}M"
else else
RAM_SIZE="${RAM_WANTED}G" RAM_SIZE="max"
fi fi
fi fi
if [[ "${RAM_SIZE,,}" == "max" ]]; then if [[ "${RAM_SIZE,,}" == "max" ]]; then
RAM_WANTED=$(( RAM_AVAIL - (RAM_SPARE * 3) )) if (( RAM_AVAIL < (RAM_SPARE * 2) )); then
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then wanted=$(( RAM_AVAIL / 2 ))
RAM_WANTED=$(( RAM_AVAIL - (RAM_SPARE * 2) ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE ))
RAM_WANTED=$(( RAM_WANTED / 1048577 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL ))
RAM_WANTED=$(( RAM_WANTED / 1048577 ))
fi
RAM_SIZE="${RAM_WANTED}M"
else
RAM_SIZE="${RAM_WANTED}G"
fi
else
RAM_SIZE="${RAM_WANTED}G"
fi
else else
RAM_SIZE="${RAM_WANTED}G"
wanted=$(( RAM_AVAIL - (RAM_SPARE * 3) ))
if (( wanted < (RAM_SPARE * 6) )); then
wanted=$(( RAM_AVAIL - RAM_SPARE ))
fi
fi fi
wanted=$(( wanted / 1048577 ))
RAM_SIZE="${wanted}M"
info "Allocated $wanted MB of RAM for the virtual machine."
fi
wanted=$(numfmt --from=iec "$RAM_SIZE")
if [ "$wanted" -lt "$RAM_MINIMUM" ]; then
wanted=$(( wanted / 1048577 ))
error "Not enough memory available, there is only $wanted MB left!"
exit 16
fi fi
return 0 return 0
+217 -162
View File
@@ -19,16 +19,28 @@ set -Eeuo pipefail
: "${VM_NET_HOST:="VirtualDSM"}" : "${VM_NET_HOST:="VirtualDSM"}"
: "${VM_NET_MASK:="255.255.255.0"}" : "${VM_NET_MASK:="255.255.255.0"}"
: "${PASST:="passt"}" : "${PASST:="/run/passt"}"
: "${PASST_MTU:=""}" : "${PASST_MTU:=""}"
: "${PASST_OPTS:=""}" : "${PASST_OPTS:=""}"
: "${PASST_DEBUG:=""}" : "${PASST_DEBUG:=""}"
: "${PASST_PID:="/var/run/passt.pid"}"
: "${PASST_SOCKET:="/tmp/passt.socket"}"
: "${DNSMASQ_OPTS:=""}" : "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ_DEBUG:=""}" : "${DNSMASQ_DEBUG:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}" : "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_PID:="/var/run/dnsmasq.pid"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}" : "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
# Sanitize variables
MAC=$(strip "$MAC")
MTU=$(strip "$MTU")
ADAPTER=$(strip "$ADAPTER")
NETWORK=$(strip "$NETWORK")
PASST_MTU=$(strip "$PASST_MTU")
HOST_PORTS=$(strip "$HOST_PORTS")
USER_PORTS=$(strip "$USER_PORTS")
ADD_ERR="Please add the following setting to your container:" ADD_ERR="Please add the following setting to your container:"
# ###################################### # ######################################
@@ -37,7 +49,7 @@ ADD_ERR="Please add the following setting to your container:"
configureDHCP() { configureDHCP() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..." enabled "$DEBUG" && 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
@@ -115,20 +127,21 @@ configureDHCP() {
configureDNS() { configureDNS() {
local if="$1" local fa="$1"
local ip="$2" local ip="$2"
local mac="$3" local mac="$3"
local host="$4" local host="$4"
local mask="$5" local mask="$5"
local gateway="$6" local gateway="$6"
local arguments="$DNSMASQ_OPTS"
echo "$gateway" > /run/shm/qemu.gw echo "$gateway" > /run/shm/qemu.gw
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
local log="/var/log/dnsmasq.log" enabled "${DNSMASQ_DISABLE:-}" && return 0
rm -f "$log" enabled "$DEBUG" && echo "Starting dnsmasq daemon..."
[ -s "$DNSMASQ_PID" ] && pKill "$(<"$DNSMASQ_PID")"
rm -f "$DNSMASQ_PID"
case "${NETWORK,,}" in case "${NETWORK,,}" in
"tap" | "tun" | "tuntap" | "y" ) "tap" | "tun" | "tuntap" | "y" )
@@ -138,45 +151,55 @@ configureDNS() {
chmod 644 /var/lib/misc/dnsmasq.leases chmod 644 /var/lib/misc/dnsmasq.leases
# dnsmasq configuration: # dnsmasq configuration:
DNSMASQ_OPTS+=" --dhcp-authoritative" arguments+=" --dhcp-authoritative"
# Set DHCP range and host # Set DHCP range and host
DNSMASQ_OPTS+=" --dhcp-range=$ip,$ip" arguments+=" --dhcp-range=$ip,$ip"
DNSMASQ_OPTS+=" --dhcp-host=$mac,,$ip,$host,infinite" arguments+=" --dhcp-host=$mac,,$ip,$host,infinite"
# Set DNS server and gateway # Set DNS server and gateway
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,$mask" arguments+=" --dhcp-option=option:netmask,$mask"
DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway" arguments+=" --dhcp-option=option:router,$gateway"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway" arguments+=" --dhcp-option=option:dns-server,$gateway"
esac esac
# Set interfaces # Set interfaces
DNSMASQ_OPTS+=" --interface=$if" arguments+=" --interface=$fa"
DNSMASQ_OPTS+=" --bind-interfaces" arguments+=" --bind-interfaces"
# Workaround NET_RAW capability
arguments+=" --no-ping"
# Add DNS entry for container # Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/$gateway" arguments+=" --address=/host.lan/$gateway"
# Set local dns resolver to dnsmasq when needed # Set local dns resolver to dnsmasq when needed
[ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq" [ -f /etc/resolv.dnsmasq ] && arguments+=" --resolv-file=/etc/resolv.dnsmasq"
# Enable logging to file # Enable logging to file
DNSMASQ_OPTS+=" --log-facility=$log" local log="/var/log/dnsmasq.log"
rm -f "$log"
arguments+=" --log-facility=$log"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') arguments=$(echo "$arguments" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}" enabled "$DEBUG" && printf "Dnsmasq arguments:\n\n%s\n\n" "${arguments// -/$'\n-'}"
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then { $DNSMASQ ${arguments:+ $arguments}; rc=$?; } || :
local msg="Failed to start Dnsmasq, reason: $?" if (( rc != 0 )); then
[ -f "$log" ] && cat "$log"
error "$msg" local msg="Failed to start Dnsmasq, reason: $rc"
if [[ "${NETWORK,,}" == "slirp" || "${NETWORK,,}" == "passt" ]] || ! enabled "$ROOTLESS" || enabled "$DEBUG"; then
[ -f "$log" ] && [ -s "$log" ] && cat "$log"
error "$msg"
fi
return 1 return 1
fi fi
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then if enabled "$DNSMASQ_DEBUG"; then
tail -fn +0 "$log" --pid=$$ & tail -fn +0 "$log" --pid=$$ &
fi fi
@@ -185,9 +208,7 @@ configureDNS() {
getHostPorts() { getHostPorts() {
local list="" local list="${HOST_PORTS// /},"
list+="$MON_PORT,"
list+="${HOST_PORTS// /},"
# Remove duplicates # Remove duplicates
list=$(echo "${list//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g') list=$(echo "${list//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
@@ -235,14 +256,13 @@ getUserPorts() {
done done
# Remove duplicates # Remove duplicates
ports=$(echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g') echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g'
echo "$ports"
return 0 return 0
} }
getSlirp() { getSlirp() {
local ip="$1"
local args="" local args=""
local list="" local list=""
@@ -258,24 +278,22 @@ getSlirp() {
proto="udp" proto="udp"
num="${port%/udp}" num="${port%/udp}"
elif [[ "$port" != *"/tcp" ]]; then elif [[ "$port" != *"/tcp" ]]; then
args+="hostfwd=$proto::$num-$VM_NET_IP:$num," args+="hostfwd=$proto::$num-$ip:$num,"
proto="udp" proto="udp"
num="${port%/udp}" num="${port%/udp}"
fi fi
args+="hostfwd=$proto::$num-$VM_NET_IP:$num," args+="hostfwd=$proto::$num-$ip:$num,"
done done
args=$(echo "$args" | sed 's/,*$//g') echo "$args" | sed 's/,*$//g'
echo "${args%?}"
return 0 return 0
} }
configureSlirp() { configureSlirp() {
NETWORK="slirp" NETWORK="slirp"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..." enabled "$DEBUG" && echo "Configuring slirp networking..."
local ip="$IP" local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP" [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
@@ -289,15 +307,18 @@ configureSlirp() {
NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=$gateway,net=${gateway%.*}.0/24,dhcpstart=$ip,${ipv6}hostname=$VM_NET_HOST" 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=$(getSlirp) forward=$(getSlirp "$ip")
[ -n "$forward" ] && NET_OPTS+=",$forward" [ -n "$forward" ] && NET_OPTS+=",$forward"
if [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]]; then if enabled "${DNSMASQ_DISABLE:-}"; then
echo "$gateway" > /run/shm/qemu.gw echo "$gateway" > /run/shm/qemu.gw
else else
[ ! -f /etc/resolv.dnsmasq ] && cp /etc/resolv.conf /etc/resolv.dnsmasq [ ! -f /etc/resolv.dnsmasq ] && cp /etc/resolv.conf /etc/resolv.dnsmasq
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1 configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf printf '%s\n' \
'nameserver 127.0.0.1' \
'search .' \
'options ndots:0' > /etc/resolv.conf
fi fi
VM_NET_IP="$ip" VM_NET_IP="$ip"
@@ -307,14 +328,11 @@ configureSlirp() {
configurePasst() { configurePasst() {
NETWORK="passt" NETWORK="passt"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..." enabled "$DEBUG" && echo "Configuring user-mode networking..."
local log="/var/log/passt.log" local log="/tmp/passt.log"
rm -f "$log" rm -f "$log"
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
local ip="$IP" local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP" [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
@@ -346,47 +364,50 @@ configurePasst() {
PASST_OPTS+=" -H $VM_NET_HOST" PASST_OPTS+=" -H $VM_NET_HOST"
PASST_OPTS+=" -M $GATEWAY_MAC" PASST_OPTS+=" -M $GATEWAY_MAC"
PASST_OPTS+=" -P $PASST_PID"
local uid gid PASST_OPTS+=" -s $PASST_SOCKET"
uid=$(id -u)
gid=$(id -g)
PASST_OPTS+=" --runas $uid:$gid"
PASST_OPTS+=" -P /var/run/passt.pid"
PASST_OPTS+=" -l $log" PASST_OPTS+=" -l $log"
PASST_OPTS+=" -q" PASST_OPTS+=" -q"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then if ! enabled "${DNSMASQ_DISABLE:-}"; then
[ ! -f /etc/resolv.dnsmasq ] && cp /etc/resolv.conf /etc/resolv.dnsmasq [ ! -f /etc/resolv.dnsmasq ] && cp /etc/resolv.conf /etc/resolv.dnsmasq
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf printf '%s\n' \
'nameserver 127.0.0.1' \
'search .' \
'options ndots:0' > /etc/resolv.conf
fi fi
PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') 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 if enabled "$DEBUG" || enabled "$PASST_DEBUG"; then
printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
fi
[ ! -f "$PASST" ] && cp /usr/bin/passt* /run
if ! "$PASST" ${PASST_OPTS:+$PASST_OPTS} >/dev/null 2>&1; then
rm -f "$log" rm -f "$log"
PASST_OPTS="${PASST_OPTS/ -q/}" PASST_OPTS="${PASST_OPTS/ -q/}"
{ $PASST ${PASST_OPTS:+ $PASST_OPTS}; rc=$?; } || : { "$PASST" ${PASST_OPTS:+$PASST_OPTS}; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
[ -f "$log" ] && cat "$log" [ -f "$log" ] && [ -s "$log" ] && cat "$log"
warn "failed to start passt ($rc), falling back to slirp networking!" warn "failed to start passt ($rc), falling back to slirp networking!"
configureSlirp && return 0 || return 1 configureSlirp && return 0 || return 1
fi fi
fi fi
if [[ "$PASST_DEBUG" == [Yy1]* ]]; then if enabled "$PASST_DEBUG"; then
tail -fn +0 "$log" --pid=$$ & tail -fn +0 "$log" --pid=$$ &
else else
if [[ "$DEBUG" == [Yy1]* ]]; then if enabled "$DEBUG"; then
[ -f "$log" ] && cat "$log" && echo "" [ -f "$log" ] && [ -s "$log" ] && cat "$log" && echo ""
fi fi
fi fi
NET_OPTS="-netdev stream,id=hostnet0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket" NET_OPTS="-netdev stream,id=hostnet0,server=off,addr.type=unix,addr.path=$PASST_SOCKET"
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1 configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
@@ -394,16 +415,51 @@ configurePasst() {
return 0 return 0
} }
clearTables() {
local table="" line rules
# Choose between iptables or nftables
if command -v iptables-nft >/dev/null 2>&1 && iptables-nft -V >/dev/null 2>&1; then
update-alternatives --set iptables /usr/sbin/iptables-nft > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft > /dev/null
else
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
fi
# Store the current iptables ruleset
! rules=$(iptables-save 2> /dev/null) && return 0
[ -z "$rules" ] && return 0
# Delete every rule tagged with our unique identifier, leaving all other rules intact.
while IFS= read -r line; do
case "$line" in
\*nat) table="nat" ;;
\*filter) table="filter" ;;
\*mangle) table="mangle" ;;
\*raw) table="raw" ;;
esac
if [[ "$line" == -A* ]]; then
local re="--comment[[:space:]]+\"?remove\"?([[:space:]]|\$)"
if [[ "$line" =~ $re ]]; then
read -ra args <<< "${line/-A /-D }"
iptables -t "$table" "${args[@]}" &> /dev/null || :
fi
fi
done <<< "$rules"
return 0
}
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..." enabled "$DEBUG" && 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
@@ -411,6 +467,7 @@ configureNAT() {
fi fi
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
@@ -418,13 +475,15 @@ configureNAT() {
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || : { sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
return 1 return 1
fi fi
fi fi
local ip base local ip base gateway exclude
base=$(echo "$IP" | sed -r 's/([^.]*.){2}//') base=$(cut -d. -f3,4 <<< "$IP")
if [[ "$IP" != "172.30."* ]]; then if [[ "$IP" != "172.30."* ]]; then
ip="172.30.$base" ip="172.30.$base"
else else
@@ -433,21 +492,24 @@ configureNAT() {
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP" [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1" gateway="${ip%.*}.1"
else else
gateway="${ip%.*}.2" gateway="${ip%.*}.2"
fi fi
local subnet="${ip%.*}.0/24"
local broadcast="${ip%.*}.255"
# Create a bridge with a static IP for the VM guest # Create a bridge with a static IP for the VM guest
{ ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || : { ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "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 "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then if ! ip address add "$gateway/24" broadcast "$broadcast" dev "$VM_NET_BRIDGE"; then
warn "failed to add IP address pool!" && return 1 warn "failed to add IP address pool!" && return 1
fi fi
@@ -456,8 +518,9 @@ configureNAT() {
sleep 2 sleep 2
done done
# QEMU Works with taps, set tap to the bridge created # Set tap to the bridge created
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
warn "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
@@ -468,7 +531,7 @@ configureNAT() {
fi fi
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
warn "failed to set gateway MAC address.." warn "failed to set gateway MAC address."
fi fi
while ! ip link set "$VM_NET_TAP" up promisc on; do while ! ip link set "$VM_NET_TAP" up promisc on; do
@@ -480,13 +543,8 @@ configureNAT() {
warn "failed to set master bridge!" && return 1 warn "failed to set master bridge!" && return 1
fi fi
if grep -wq "nf_tables" /proc/modules; then # Flush existing tables
update-alternatives --set iptables /usr/sbin/iptables-nft > /dev/null clearTables
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) exclude=$(getHostPorts)
@@ -498,22 +556,36 @@ configureNAT() {
fi fi
fi fi
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then # NAT traffic from bridge subnet to Docker uplink
warn "$tables" && return 1 if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -s "$subnet" ! -d "$subnet" -m comment --comment "remove" -j MASQUERADE > /dev/null 2>&1; then
enabled "$ROOTLESS" && ! enabled "$DEBUG" && return 1
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -s "$subnet" ! -d "$subnet" -m comment --comment "remove" -j MASQUERADE; then
warn "$tables" && return 1
fi
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$ip"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -m comment --comment "remove" -j DNAT --to "$ip"; then
warn "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 "$ip"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -m comment --comment "remove" -j DNAT --to "$ip"; then
warn "failed to configure IP tables!" && return 1 warn "failed to configure IP tables!" && return 1
fi fi
if (( KERNEL > 4 )); then if (( KERNEL > 4 )); then
# Hack for guest VMs complaining about "bad udp checksums in 5 packets" # Hack for guest VMs complaining about "bad udp checksums in 5 packets"
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true iptables -A POSTROUTING -t mangle -p udp --dport bootpc -m comment --comment "remove" -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
fi
# Allow forwarding from bridge -> dev
if ! iptables -A FORWARD -i "$VM_NET_BRIDGE" -o "$VM_NET_DEV" -m comment --comment "remove" -j ACCEPT; then
warn "failed to configure IP tables!" && return 1
fi
# Allow return traffic
if ! iptables -A FORWARD -i "$VM_NET_DEV" -o "$VM_NET_BRIDGE" -m conntrack --ctstate RELATED,ESTABLISHED -m comment --comment "remove" -j ACCEPT; then
warn "failed to configure IP tables!" && return 1
fi fi
NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP" NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP"
@@ -533,97 +605,72 @@ configureNAT() {
closeBridge() { closeBridge() {
local pid="/var/run/dnsmasq.pid" local pids=( "$PASST_PID" "$DNSMASQ_PID" )
[ -s "$pid" ] && pKill "$(<"$pid")" mKill "${pids[@]}"
rm -f "$pid"
pid="/var/run/passt.pid" ip link set "$VM_NET_TAP" down promisc off &> /dev/null || :
[ -s "$pid" ] && pKill "$(<"$pid")" ip link delete "$VM_NET_TAP" &> /dev/null || :
rm -f "$pid"
case "${NETWORK,,}" in ip link set "$VM_NET_BRIDGE" down &> /dev/null || :
"user"* | "passt" | "slirp" ) return 0 ;; ip link delete "$VM_NET_BRIDGE" &> /dev/null || :
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
clearTables
return 0 return 0
} }
closeWeb() { closeWeb() {
# Shutdown nginx local pids=( "$WEB_PID" "$WSD_PID" )
nginx -s stop 2> /dev/null mKill "${pids[@]}"
fWait "nginx"
# Shutdown websocket
local pid="/var/run/websocketd.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
return 0 return 0
} }
closeNetwork() { closeNetwork() {
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then if ! disabled "${WEB:-}" && enabled "$DHCP"; then
closeWeb closeWeb
fi fi
[[ "$NETWORK" == [Nn]* ]] && return 0 disabled "$NETWORK" && return 0
exec 30<&- || true exec 30<&- || true
exec 40<&- || true exec 40<&- || true
if [[ "$DHCP" != [Yy1]* ]]; then closeBridge
closeBridge
return 0
fi
ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true
return 0 return 0
} }
cleanUp() { cleanUp() {
# Clean up old files closeBridge
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 # Clean up old files
info "Lingering interface will be removed..." rm -f "$PASST_PID" "$PASST_SOCKET"
ip link delete "$VM_NET_TAP" || true rm -f "$DNSMASQ_PID" /etc/resolv.dnsmasq
fi
return 0 return 0
} }
checkOS() { checkOS() {
local kernel
local os="" local os=""
local if="macvlan" local kernel=""
local iface="macvlan"
kernel=$(uname -a) kernel=$(uname -a)
[[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS" [[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
[[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows" [[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
if [[ "$DHCP" == [Yy1]* ]]; then if enabled "$DHCP"; then
if="macvtap" iface="macvtap"
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager" [[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
fi fi
if [ -n "$os" ]; then if [ -n "$os" ]; then
warn "you are using $os which does not support $if, please revert to bridge networking!" warn "you are using $os which does not support $iface, please revert to bridge networking!"
fi fi
return 0 return 0
@@ -637,8 +684,8 @@ getInfo() {
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1" [ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2" [ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3" [ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
# Automaticly detect the default network interface # Automatically detect the default network interface
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route) [ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1; exit }' /proc/net/route)
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0" [ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
fi fi
@@ -648,15 +695,12 @@ getInfo() {
fi fi
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1) GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
{ IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1); rc=$?; } 2>/dev/null || : { IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1); } 2>/dev/null || :
[ -z "$IP" ] && ! enabled "$DHCP" && error "Could not determine container IPv4 address!" && exit 26
if (( rc != 0 )); then
error "Could not determine container IP address!" && exit 26
fi
IP6="" IP6=""
# shellcheck disable=SC2143 # shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then if [ -f /proc/net/if_inet6 ] && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)" != "1" ]] && [ -n "$(ifconfig -a | grep inet6)" ]; then
{ IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up); rc=$?; } 2>/dev/null || : { IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up); rc=$?; } 2>/dev/null || :
(( rc != 0 )) && IP6="" (( rc != 0 )) && IP6=""
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1) [ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
@@ -664,16 +708,16 @@ getInfo() {
local result nic bus local result nic bus
result=$(ethtool -i "$VM_NET_DEV") result=$(ethtool -i "$VM_NET_DEV")
nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $(2)}') nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $2}')
bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $(2)}') bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $2}')
if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus" enabled "$DEBUG" && info "Detected BUS: $bus"
error "This container does not support host mode networking!" error "This container does not support host mode networking!"
exit 29 exit 29
fi fi
if [[ "$DHCP" == [Yy1]* ]]; then if enabled "$DHCP"; then
checkOS checkOS
@@ -683,11 +727,25 @@ getInfo() {
fi fi
if [[ "${nic,,}" != "macvlan" ]]; then if [[ "${nic,,}" != "macvlan" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $nic" enabled "$DEBUG" && info "Detected NIC: $nic"
error "The container needs to be in a MACVLAN network when DHCP=Y." error "The container needs to be in a MACVLAN network when DHCP=Y."
exit 29 exit 29
fi fi
if uname -a | grep -Eqi 'unraid|truenas'; then
# Check if host exposes the bridge-nf sysctl
# (only visible if br_netfilter is loaded and /proc/sys is accessible)
BNF="/proc/sys/net/bridge/bridge-nf-call-iptables"
if [[ -r "$BNF" ]] && [[ "$(cat "$BNF")" != "0" ]]; then
warn "external LAN clients may not be able to reach this container, because net.bridge.bridge-nf-call-iptables=1."
warn "you can fix this issue by running 'sysctl -w net.bridge.bridge-nf-call-iptables=0' on the host system."
fi
fi
else else
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
@@ -737,26 +795,19 @@ getInfo() {
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/') GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then if enabled "$DEBUG"; 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" line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)" [[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line" info "$line"
if [ -f /etc/resolv.conf ]; then if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g') nameservers=$(grep '^nameserver ' /etc/resolv.conf | sed 's/^nameserver //' | paste -sd ',' | sed 's/,/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers" [ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi fi
echo echo
fi fi
echo "$IP" > /run/shm/qemu.ip echo "$IP" > "$QEMU_DIR"/qemu.ip
echo "$nic" > /run/shm/qemu.nic echo "$nic" > "$QEMU_DIR"//qemu.nic
return 0 return 0
} }
@@ -765,14 +816,14 @@ getInfo() {
# Configure Network # Configure Network
# ###################################### # ######################################
if [[ "$NETWORK" == [Nn]* ]]; then if disabled "$NETWORK"; then
NET_OPTS="" NET_OPTS=""
return 0 return 0
fi fi
msg="Initializing network..." msg="Initializing network..."
html "$msg" html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg" enabled "$DEBUG" && echo "$msg"
getInfo getInfo
cleanUp cleanUp
@@ -784,36 +835,40 @@ fi
MSG="Booting DSM instance..." MSG="Booting DSM instance..."
html "$MSG" html "$MSG"
if [[ "$DHCP" == [Yy1]* ]]; then if enabled "$DHCP"; then
# Configure for macvtap interface # Configure for macvtap interface
configureDHCP || exit 20 configureDHCP || exit 20
else else
if [[ "${WEB:-}" != [Nn]* ]]; then if ! disabled "${WEB:-}"; then
sleep 1.2 sleep 1.2
closeWeb closeWeb
fi fi
case "${NETWORK,,}" in case "${NETWORK,,}" in
"passt" | "slirp" | "user"* ) ;; "passt" | "slirp" | "user"* ) ;;
"tap" | "tun" | "tuntap" | "y" ) "tap" | "tun" | "tuntap" | "y" | "" )
# Configure tap interface # Configure tap interface
if ! configureNAT; then if ! configureNAT; then
closeBridge closeBridge
NETWORK="user" NETWORK="user"
msg="falling back to user-mode networking!"
msg="failed to setup NAT networking, $msg" if ! enabled "$ROOTLESS" || enabled "$DEBUG"; then
msg="falling back to user-mode networking!"
msg="failed to setup NAT networking, $msg"
warn "$msg"
fi
fi ;; fi ;;
esac esac
case "${NETWORK,,}" in case "${NETWORK,,}" in
"tap" | "tun" | "tuntap" | "y" ) ;; "tap" | "tun" | "tuntap" | "y" | "" ) ;;
"passt" | "user"* ) "passt" | "user"* )
# Configure for user-mode networking (passt) # Configure for user-mode networking (passt)
+113 -119
View File
@@ -1,146 +1,115 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${API_TIMEOUT:="50"}" # API Call timeout : "${SHUTDOWN:="Y"}" # Graceful ACPI shutdown
: "${QEMU_TIMEOUT:="50"}" # QEMU Termination timeout : "${TIMEOUT:="115"}" # QEMU termination timeout
: "${API_TIMEOUT:="90"}" # External API call timeout
# Configure QEMU for graceful shutdown # Configure QEMU for graceful shutdown
API_CMD=6 API_CMD=6
API_HOST="127.0.0.1:$COM_PORT" API_HOST="127.0.0.1:$COM_PORT"
QEMU_TERM="" # Configure QEMU for graceful shutdown
QEMU_DIR="/run/shm"
QEMU_PID="$QEMU_DIR/qemu.pid"
QEMU_LOG="$QEMU_DIR/qemu.log"
QEMU_OUT="$QEMU_DIR/qemu.out"
QEMU_END="$QEMU_DIR/qemu.end" QEMU_END="$QEMU_DIR/qemu.end"
if [[ "$KVM" == [Nn]* ]]; then
API_TIMEOUT=$(( API_TIMEOUT*2 ))
QEMU_TIMEOUT=$(( QEMU_TIMEOUT*2 ))
fi
touch "$QEMU_LOG"
_trap() { _trap() {
func="$1" ; shift local func="$1"; shift
for sig ; do local sig
TRAP_PID=$BASHPID
for sig; do
trap "$func $sig" "$sig" trap "$func $sig" "$sig"
done done
} }
app() {
echo "$APP" && return 0
}
finish() { finish() {
local pid local i=0
local cnt=0 local pid=""
local reason=$1 local reason=$1
local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \
"${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" )
touch "$QEMU_END" touch "$QEMU_END"
if [ -s "$QEMU_PID" ]; then if [ -s "$QEMU_PID" ]; then
if read -r pid <"$QEMU_PID"; then
pid=$(<"$QEMU_PID") if [ -n "$pid" ] && isAlive "$pid"; then
echo && error "Forcefully terminating Virtual DSM, reason: $reason..." local display="$reason"
{ kill -15 "$pid" || true; } 2>/dev/null case "$reason" in
129 ) display="SIGHUP" ;;
while isAlive "$pid"; do 130 ) display="SIGINT" ;;
131 ) display="SIGQUIT" ;;
sleep 1 134 ) display="SIGABRT" ;;
cnt=$((cnt+1)) 143 ) display="SIGTERM" ;;
esac
# Workaround for zombie pid error "Forcefully terminating $(app), reason: $display..."
[ ! -s "$QEMU_PID" ] && break { disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null
if [ "$cnt" == "5" ]; then
echo && error "QEMU did not terminate itself, forcefully killing process..."
{ kill -9 "$pid" || true; } 2>/dev/null
fi fi
done
fi
fKill "print.sh"
fKill "host.bin"
closeNetwork
sleep 1
echo && echo " Shutdown completed!"
exit "$reason"
}
terminal() {
local dev=""
if [ -s "$QEMU_OUT" ]; then
local msg
msg=$(<"$QEMU_OUT")
if [ -n "$msg" ]; then
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
echo "$msg"
fi
dev="${msg#*/dev/p}"
dev="/dev/p${dev%% *}"
fi fi
fi fi
if [ ! -c "$dev" ]; then mKill "${pids[@]}"
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$MON_PORT" | tr -d '\000') fKill "print.sh"
dev="${dev#*serial0}"
dev="${dev#*pty:}" closeNetwork
dev="${dev%%$'\n'*}"
dev="${dev%%$'\r'*}" if ! waitPidFile "$QEMU_PID" 10; then
warn "Timed out while waiting for $(app) to exit!"
fi fi
if [ ! -c "$dev" ]; then (( reason != 1 )) && echo && echo " Shutdown completed!"
error "Device '$dev' not found!" exit "$reason"
finish 34 && return 34
fi
QEMU_TERM="$dev"
return 0
} }
_graceful_shutdown() { graceful_shutdown() {
local code=$? local sig="$1"
local pid url response local pid=""
local code=0
local start url response elapsed
set +e [[ $BASHPID != "$TRAP_PID" ]] && return
case "$sig" in
SIGHUP) code=129 ;;
SIGINT) code=130 ;;
SIGQUIT) code=131 ;;
SIGABRT) code=134 ;;
SIGTERM) code=143 ;;
esac
if [ -f "$QEMU_END" ]; then if [ -f "$QEMU_END" ]; then
echo && info "Received $1 signal while already shutting down..." echo && info "Received $1 signal while already shutting down..."
return return
fi fi
set +e
start=$SECONDS
touch "$QEMU_END" touch "$QEMU_END"
echo && info "Received $1 signal, sending shutdown command..." echo && info "Received $1 signal, sending shutdown command..."
if [ ! -s "$QEMU_PID" ]; then if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then
echo && error "QEMU PID file does not exist?" warn "QEMU PID file ($QEMU_PID) does not exist?"
finish "$code" && return "$code" finish "$code"
fi fi
pid=$(<"$QEMU_PID") if [ -z "$pid" ] || ! isAlive "$pid"; then
warn "QEMU process with PID $pid does not exist?"
if ! isAlive "$pid"; then finish "$code"
echo && error "QEMU process does not exist?"
finish "$code" && return "$code"
fi fi
# Don't send the powerdown signal because vDSM ignores ACPI signals # Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null # nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || :
# Send shutdown command to guest agent via serial port # Send shutdown command to guest agent via serial port
API_TIMEOUT=$(strip "$API_TIMEOUT")
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT" url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
response=$(curl -sk -m "$(( API_TIMEOUT+2 ))" -S "$url" 2>&1) response=$(curl -sk -m "$(( API_TIMEOUT+2 ))" -S "$url" 2>&1)
@@ -153,44 +122,69 @@ _graceful_shutdown() {
response="${response#*message\"\: \"}" response="${response#*message\"\: \"}"
[ -z "$response" ] && response="second signal" [ -z "$response" ] && response="second signal"
echo && error "Forcefully terminating because of: ${response%%\"*}" echo && error "Forcefully terminating because of: ${response%%\"*}"
{ kill -15 "$pid" || true; } 2>/dev/null kill -15 -- "$pid" 2>/dev/null || :
fi fi
local cnt=0 local name
name="$(app)"
while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do local term_grace=3 # seconds before loop ends to send SIGTERM
local cleanup_grace=3 # seconds reserved after the loop for cleanup
TIMEOUT=$(strip "$TIMEOUT")
if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]]; then
TIMEOUT=115
fi
if (( TIMEOUT >= 30 )); then
term_grace=5
cleanup_grace=5
elif (( TIMEOUT >= 15 )); then
term_grace=4
cleanup_grace=4
fi
local cnt=0 sigterm_at=0 min wait_until elapsed timeout_left
elapsed=$((SECONDS - start))
timeout_left=$((TIMEOUT - elapsed))
min=$((term_grace + cleanup_grace + 1))
(( timeout_left < min )) && timeout_left=$min
wait_until=$((timeout_left - cleanup_grace))
sigterm_at=$((wait_until - term_grace))
while (( cnt <= wait_until )); do
sleep 1 &
local slp=$!
# Stop waiting if the process has exited
! isAlive "$pid" && break ! isAlive "$pid" && break
sleep 1 # Workaround for stale/zombie QEMU pid file
cnt=$((cnt+1))
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
# Workaround for zombie pid
[ ! -s "$QEMU_PID" ] && break [ ! -s "$QEMU_PID" ] && break
if (( cnt == sigterm_at )); then
info "${name^} is still running, sending SIGTERM... ($cnt/$wait_until)"
kill -15 -- "$pid" 2>/dev/null || :
elif (( cnt > 0 )) && enabled "${DEBUG:-}"; then
info "Waiting for $name to shut down... ($cnt/$wait_until)"
fi
wait "$slp"
(( cnt++ ))
done done
if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then finish "$code"
echo && error "Shutdown timeout reached, aborting..."
fi
finish "$code" && return "$code"
} }
MON_OPTS="\ ! enabled "$SHUTDOWN" && return 0
-pidfile $QEMU_PID \ [ -n "${QEMU_TIMEOUT:-}" ] && TIMEOUT="$QEMU_TIMEOUT"
-name $PROCESS,process=$PROCESS,debug-threads=on \
-monitor telnet:localhost:$MON_PORT,server,nowait,nodelay"
if [[ "$CONSOLE" != [Yy]* ]]; then _trap graceful_shutdown SIGTERM SIGHUP SIGABRT SIGQUIT
MON_OPTS+=" -daemonize -D $QEMU_LOG"
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
fi
return 0 return 0
+8 -5
View File
@@ -7,12 +7,16 @@ set -Eeuo pipefail
: "${CPU_FLAGS:=""}" : "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}" : "${CPU_MODEL:=""}"
HOST_CPU=$(strip "$HOST_CPU")
CPU_FLAGS=$(strip "$CPU_FLAGS")
CPU_MODEL=$(strip "$CPU_MODEL")
CLOCKSOURCE="tsc" CLOCKSOURCE="tsc"
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter" [[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource" CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
if [ ! -f "$CLOCK" ]; then if [ ! -f "$CLOCK" ]; then
warn "file \"$CLOCK\" cannot not found?" warn "file \"$CLOCK\" cannot be found?"
else else
result=$(<"$CLOCK") result=$(<"$CLOCK")
result="${result//[![:print:]]/}" result="${result//[![:print:]]/}"
@@ -27,15 +31,14 @@ fi
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo) flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if [[ "$KVM" != [Nn]* ]]; then if ! disabled "$KVM"; then
CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor" CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor"
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard" KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
if ! grep -qw "sse4_2" <<< "$flags"; then if ! grep -qw "sse4_2" <<< "$flags"; then
info "Your CPU does not have the SSE4 instruction set that Virtual DSM requires, it will be emulated..." error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
[ -z "$CPU_MODEL" ] && CPU_MODEL="qemu64" ! enabled "$DEBUG" && exit 88
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi fi
if [ -z "$CPU_MODEL" ]; then if [ -z "$CPU_MODEL" ]; then
+64 -26
View File
@@ -2,7 +2,7 @@
set -Eeuo pipefail set -Eeuo pipefail
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG enabled "${TRACE:-}" && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG
[ ! -f "/run/entry.sh" ] && error "Script must be run inside the 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
@@ -13,7 +13,6 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
: "${KVM:="Y"}" # KVM acceleration : "${KVM:="Y"}" # KVM acceleration
: "${DEBUG:="N"}" # Disable debugging mode : "${DEBUG:="N"}" # Disable debugging mode
: "${COUNTRY:=""}" # Country code for mirror : "${COUNTRY:=""}" # Country code for mirror
: "${CONSOLE:="N"}" # Disable console mode
: "${ALLOCATE:=""}" # Preallocate diskspace : "${ALLOCATE:=""}" # Preallocate diskspace
: "${ARGUMENTS:=""}" # Extra QEMU parameters : "${ARGUMENTS:=""}" # Extra QEMU parameters
: "${CPU_CORES:="2"}" # Amount of CPU cores : "${CPU_CORES:="2"}" # Amount of CPU cores
@@ -22,27 +21,54 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
: "${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 # Sanitize variables
TZ=$(strip "$TZ")
STORAGE=$(strip "$STORAGE")
COUNTRY=$(strip "$COUNTRY")
DISK_SIZE=$(strip "$DISK_SIZE")
PODMAN="N" # Helper variables
ROOTLESS="N"
PRIVILEGED="N"
ENGINE="Docker" ENGINE="Docker"
PROCESS="${APP,,}" PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}" PROCESS="${PROCESS// /-}"
if [ -f "/run/.containerenv" ]; then if [ -f "/run/.containerenv" ]; then
PODMAN="Y" ENGINE="${container:-}"
ENGINE="Podman" if [[ "${ENGINE,,}" == *"podman"* ]]; then
ROOTLESS="Y"
ENGINE="Podman"
else
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
fi
fi fi
echo " Starting $APP for $ENGINE v$(</run/version)..." echo " Starting $APP for $ENGINE v$(</etc/version)..."
echo " For support visit $SUPPORT" echo " For support visit $SUPPORT"
# Get the capability bounding set
CAP_BND=$(grep '^CapBnd:' /proc/$$/status | awk '{print $2}')
CAP_BND=$(printf "%d" "0x${CAP_BND}")
# Get the last capability number
LAST_CAP=$(cat /proc/sys/kernel/cap_last_cap)
# Calculate the maximum capability value
MAX_CAP=$(((1 << (LAST_CAP + 1)) - 1))
if [ "${CAP_BND}" -eq "${MAX_CAP}" ]; then
ROOTLESS="N"
PRIVILEGED="Y"
fi
INFO="/run/shm/msg.html" INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html" PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html" TEMPLATE="/var/www/index.html"
FOOTER1="$APP for $ENGINE v$(</run/version)" FOOTER1="$APP for $ENGINE v$(</etc/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>" FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
SOCKETS=1
CPU=$(cpu) CPU=$(cpu)
SYS=$(uname -r) SYS=$(uname -r)
HOST=$(hostname -s) HOST=$(hostname -s)
@@ -51,31 +77,36 @@ MINOR=$(echo "$SYS" | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture) ARCH=$(dpkg --print-architecture)
CORES=$(grep -c '^processor' /proc/cpuinfo) CORES=$(grep -c '^processor' /proc/cpuinfo)
if ! grep -qi "socket(s)" <<< "$(lscpu)"; then if grep -qi "socket(s)" <<< "$(lscpu)"; then
SOCKETS=1 SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $2}')
else [ -z "${SOCKETS##*[!0-9]*}" ] && SOCKETS=1
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}') [ "$SOCKETS" -lt "1" ] && SOCKETS=1
fi fi
CPU_CORES="${CPU_CORES// /}" CPU_CORES=$(strip "$CPU_CORES")
[ -z "$CPU_CORES" ] && CPU_CORES=2
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES" [[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
[[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 )) [[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 ))
[[ "${CPU_CORES,,}" == "0" ]] && CPU_CORES="1" [ -z "${CPU_CORES##*[!0-9]*}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
[ "$CPU_CORES" -lt "1" ] && CPU_CORES=1
if [ "$CPU_CORES" -gt "$CORES" ]; then if [ "$CPU_CORES" -gt "$CORES" ]; then
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available, so will be limited to ${CORES}." warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available (${CORES}) and will be limited."
CPU_CORES="$CORES" CPU_CORES="$CORES"
fi fi
# Check system # Check system
QEMU_DIR="/run/shm"
if [ ! -d "/dev/shm" ]; then if [ ! -d "/dev/shm" ]; then
error "Directory /dev/shm not found!" && exit 14 error "Directory /dev/shm not found!" && exit 14
else else
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm [ ! -d "$QEMU_DIR" ] && ln -s /dev/shm "$QEMU_DIR"
fi fi
QEMU_PID="$QEMU_DIR/qemu.pid"
# Check folder # Check folder
if [[ "${STORAGE,,}" != "/storage" ]]; then if [[ "${STORAGE,,}" != "/storage" ]]; then
@@ -101,12 +132,15 @@ if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
fi fi
# Read memory # Read memory
RAM_SPARE=500000000
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}') RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}') RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
RAM_SPARE=500000000
RAM_MINIMUM=136314880
RAM_SIZE=$(strip "$RAM_SIZE")
RAM_SIZE="${RAM_SIZE// /}" RAM_SIZE="${RAM_SIZE// /}"
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16 [ -z "$RAM_SIZE" ] && RAM_SIZE="2G"
if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
@@ -116,8 +150,8 @@ if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
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 ! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE") wanted=$(numfmt --from=iec "$RAM_SIZE")
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16 [ "$wanted" -lt "$RAM_MINIMUM" ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
fi fi
@@ -142,7 +176,7 @@ else
TARGET="arm64" TARGET="arm64"
fi fi
if [[ "$KVM" == [Nn]* ]]; then if disabled "$KVM"; then
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!" warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
else else
if [[ "${ARCH,,}" != "$TARGET" ]]; then if [[ "${ARCH,,}" != "$TARGET" ]]; then
@@ -151,7 +185,7 @@ else
fi fi
fi fi
if [[ "$KVM" != [Nn]* ]]; then if ! disabled "$KVM"; then
KVM_ERR="" KVM_ERR=""
@@ -166,6 +200,10 @@ if [[ "$KVM" != [Nn]* ]]; then
if ! grep -qw "vmx\|svm" <<< "$flags"; then if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)" KVM_ERR="(not enabled in BIOS)"
fi fi
if ! grep -qw "sse4_2" <<< "$flags"; then
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
! enabled "$DEBUG" && exit 88
fi
fi fi
fi fi
fi fi
@@ -185,15 +223,15 @@ if [[ "$KVM" != [Nn]* ]]; then
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower." 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)." ;; error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
esac esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88 ! enabled "$DEBUG" && exit 88
fi fi
fi fi
fi fi
# Cleanup files # Cleanup files
rm -f /run/shm/qemu.* rm -f "$QEMU_DIR"/dsm.url
rm -f /run/shm/dsm.url rm -f "$QEMU_DIR"/qemu.* "$QEMU_DIR"/*.pid "$QEMU_DIR"/*.sock
# Cleanup dirs # Cleanup dirs
rm -rf /tmp/dsm rm -rf /tmp/dsm
+13 -10
View File
@@ -5,10 +5,16 @@ set -Eeuo pipefail
: "${HOST_MAC:=""}" : "${HOST_MAC:=""}"
: "${HOST_DEBUG:=""}" : "${HOST_DEBUG:=""}"
: "${HOST_SERIAL:=""}"
: "${HOST_MODEL:=""}" : "${HOST_MODEL:=""}"
: "${HOST_SERIAL:=""}"
: "${GUEST_SERIAL:=""}" : "${GUEST_SERIAL:=""}"
# Sanitize variables
HOST_MAC=$(strip "$HOST_MAC")
HOST_MODEL=$(strip "$HOST_MODEL")
HOST_SERIAL=$(strip "$HOST_SERIAL")
GUEST_SERIAL=$(strip "$GUEST_SERIAL")
if [ -n "$HOST_MAC" ]; then if [ -n "$HOST_MAC" ]; then
HOST_MAC="${HOST_MAC//-/:}" HOST_MAC="${HOST_MAC//-/:}"
@@ -24,6 +30,8 @@ if [ -n "$HOST_MAC" ]; then
fi fi
HOST_PID="$QEMU_DIR/host.pid"
HOST_ARGS=() HOST_ARGS=()
HOST_ARGS+=("-cpu=$CPU_CORES") HOST_ARGS+=("-cpu=$CPU_CORES")
HOST_ARGS+=("-cpu_arch=$HOST_CPU") HOST_ARGS+=("-cpu_arch=$HOST_CPU")
@@ -33,13 +41,15 @@ HOST_ARGS+=("-cpu_arch=$HOST_CPU")
[ -n "$HOST_SERIAL" ] && HOST_ARGS+=("-hostsn=$HOST_SERIAL") [ -n "$HOST_SERIAL" ] && HOST_ARGS+=("-hostsn=$HOST_SERIAL")
[ -n "$GUEST_SERIAL" ] && HOST_ARGS+=("-guestsn=$GUEST_SERIAL") [ -n "$GUEST_SERIAL" ] && HOST_ARGS+=("-guestsn=$GUEST_SERIAL")
if [[ "$HOST_DEBUG" == [Yy1]* ]]; then if enabled "$HOST_DEBUG"; then
set -x set -x
./host.bin "${HOST_ARGS[@]}" & ./host.bin "${HOST_ARGS[@]}" &
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
echo "$!" > "$HOST_PID"
echo echo
else else
./host.bin "${HOST_ARGS[@]}" >/dev/null & ./host.bin "${HOST_ARGS[@]}" >/dev/null &
echo "$!" > "$HOST_PID"
fi fi
cnt=0 cnt=0
@@ -60,14 +70,7 @@ while ! nc -z -w2 127.0.0.1 "$CHR_PORT" > /dev/null 2>&1; do
done done
# Configure serial ports # Configure serial ports
SERIAL_OPTS="-serial mon:stdio \
if [[ "$CONSOLE" != [Yy]* ]]; then
SERIAL_OPTS="-serial pty"
else
SERIAL_OPTS="-serial mon:stdio"
fi
SERIAL_OPTS+=" \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \ -device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,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"
+14 -6
View File
@@ -2,17 +2,25 @@
set -Eeuo pipefail set -Eeuo pipefail
: "${COM_PORT:="2210"}" # Comm port : "${COM_PORT:="2210"}" # Comm port
: "${MON_PORT:="7100"}" # Monitor port
: "${WEB_PORT:="5000"}" # Webserver port : "${WEB_PORT:="5000"}" # Webserver port
: "${CHR_PORT:="12345"}" # Character port : "${CHR_PORT:="12345"}" # Character port
: "${WSD_PORT:="8004"}" # Websockets port : "${WSD_PORT:="8004"}" # Websockets port
cp -r /var/www/* /run/shm # Sanitize port variables
rm -f /var/run/websocketd.pid COM_PORT=$(strip "$COM_PORT")
WEB_PORT=$(strip "$WEB_PORT")
CHR_PORT=$(strip "$CHR_PORT")
WSD_PORT=$(strip "$WSD_PORT")
WEB_PID="/run/nginx.pid"
WSD_PID="$QEMU_DIR/websocketd.pid"
cp -r /var/www/* "$QEMU_DIR"
rm -f "$WSD_PID" "$WEB_PID"
html "Starting $APP for $ENGINE..." html "Starting $APP for $ENGINE..."
if [[ "${WEB:-}" != [Nn]* ]]; then if ! disabled "${WEB:-}"; then
mkdir -p /etc/nginx/sites-enabled mkdir -p /etc/nginx/sites-enabled
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
@@ -21,7 +29,7 @@ if [[ "${WEB:-}" != [Nn]* ]]; then
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 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 # shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then if [ -f /proc/net/if_inet6 ] && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)" != "1" ]] && [ -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 sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
@@ -32,7 +40,7 @@ if [[ "${WEB:-}" != [Nn]* ]]; then
# Start websocket server # Start websocket server
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log & websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
echo "$!" > /var/run/websocketd.pid echo "$!" > "$WSD_PID"
fi fi
+122 -6
View File
@@ -7,6 +7,40 @@ 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; } 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; } warn () { printf "%b%s%b" "\E[1;31m " "Warning: ${1:-}" "\E[0m\n" >&2; }
strip() {
local value="${1:-}"
# Remove surrounding whitespace
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
# Remove leading/trailing single/double quotes
value="${value%\"}"
value="${value#\"}"
value="${value%\'}"
value="${value#\'}"
# Remove surrounding whitespace again
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}
enabled() {
case "$(strip "${1:-}")" in
Y|y|YES|Yes|yes|TRUE|True|true|1|ON|On|on) return 0 ;;
*) return 1 ;;
esac
}
disabled() {
case "$(strip "${1:-}")" in
N|n|NO|No|no|FALSE|False|false|0|OFF|Off|off) return 0 ;;
*) return 1 ;;
esac
}
formatBytes() { formatBytes() {
local result local result
result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;') result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;')
@@ -28,6 +62,7 @@ formatBytes() {
isAlive() { isAlive() {
local pid="$1" local pid="$1"
[ -z "$pid" ] && return 1
if kill -0 "$pid" 2>/dev/null; then if kill -0 "$pid" 2>/dev/null; then
return 0 return 0
@@ -36,23 +71,67 @@ isAlive() {
return 1 return 1
} }
pKill() { waitPid() {
local i=0
local pid="$1" local pid="$1"
local timeout="${2:-10}"
{ kill -15 "$pid" || true; } 2>/dev/null while [ -n "$pid" ] && isAlive "$pid"; do
while isAlive "$pid"; do
sleep 0.2 sleep 0.2
i=$((i + 1))
(( i >= timeout * 5 )) && return 1
done done
return 0 return 0
} }
waitPidFile() {
local i=0
local pid=""
local file="$1"
local timeout="${2:-10}"
[ ! -s "$file" ] && return 0
! read -r pid <"$file" && return 0
[ -z "$pid" ] && return 0
while [ -s "$file" ] && isAlive "$pid"; do
sleep 0.2
i=$((i + 1))
(( i >= timeout * 5 )) && return 1
done
rm -f -- "$file"
return 0
}
pKill() {
local pid="$1"
local timeout="${2:-10}"
{ kill -15 -- "$pid" || :; } 2>/dev/null
if ! waitPid "$pid" "$timeout"; then
warn "Timed out while waiting for PID $pid"
fi
return 0
}
fWait() { fWait() {
local i=0
local name="$1" local name="$1"
local timeout="${2:-10}"
[ -z "$name" ] && return 0
while pgrep -f -l "$name" >/dev/null; do while pgrep -f -l "$name" >/dev/null; do
sleep 0.2 sleep 0.2
i=$((i + 1))
if (( i >= timeout * 5 )); then
warn "Timed out while waiting for process: $name"
break
fi
done done
return 0 return 0
@@ -60,9 +139,44 @@ fWait() {
fKill() { fKill() {
local name="$1" local name="$1"
local timeout="${2:-10}"
{ pkill -f "$name" || true; } 2>/dev/null [ -z "$name" ] && return 0
fWait "$name"
{ pkill -f "$name" || :; } 2>/dev/null
fWait "$name" "$timeout"
return 0
}
sKill() {
local pid=""
local file="$1"
[ ! -s "$file" ] && return 0
! read -r pid <"$file" && return 0
[ -z "$pid" ] && return 0
if isAlive "$pid"; then
{ kill -15 -- "$pid" || :; } 2>/dev/null
fi
return 0
}
mKill() {
local timeout=10
local files=("$@")
for file in "${files[@]}"; do
sKill "$file"
done
for file in "${files[@]}"; do
if ! waitPidFile "$file" "$timeout"; then
warn "Timed out while waiting for PID file: $file"
fi
done
return 0 return 0
} }
@@ -178,6 +292,8 @@ cpu() {
hasDisk() { hasDisk() {
enabled "${DISK_DISABLE:-}" && return 1
[ -b "/disk" ] && return 0 [ -b "/disk" ] && return 0
[ -b "/disk1" ] && return 0 [ -b "/disk1" ] && return 0
[ -b "/dev/disk1" ] && return 0 [ -b "/dev/disk1" ] && return 0
+5 -1
View File
@@ -7,10 +7,14 @@ function getInfo() {
var url = "msg.html"; var url = "msg.html";
try { try {
if (request) {
request.abort();
}
if (window.XMLHttpRequest) { if (window.XMLHttpRequest) {
request = new XMLHttpRequest(); request = new XMLHttpRequest();
} else { } else {
throw "XMLHttpRequest not available!"; throw new Error("XMLHttpRequest not available!");
} }
request.onreadystatechange = processInfo; request.onreadystatechange = processInfo;