Compare commits

..

84 Commits
v4.34 ... v5.03

Author SHA1 Message Date
Kroese
7c2e2fc7b1 feat: Enable IO threading
feat: Enable IO threading
2023-12-19 06:16:28 +01:00
Kroese
9bac0c94a8 feat: Detect COW on btrfs 2023-12-19 06:11:50 +01:00
Kroese
631568681e feat: Detect COW on BTRFS 2023-12-19 06:01:50 +01:00
Kroese
6599861dbb feat: Detect COW on BTRFS 2023-12-19 05:46:49 +01:00
Kroese
3bcd831531 fix: Display timeout 2023-12-19 04:30:01 +01:00
Kroese
fafd4a4fca feat: Enable IO threading 2023-12-19 03:59:19 +01:00
Kroese
5b69178f08 docs: Allocation 2023-12-19 03:33:41 +01:00
Kroese
479a30369d feat: Enable IO threading 2023-12-19 02:34:48 +01:00
Kroese
ba522a4acb feat: Increase clustersize for qcow2
* feat: Increase clustersize for qcow2
2023-12-18 17:34:40 +01:00
Kroese
3ab6d2c418 docs: Allocation 2023-12-18 16:48:01 +01:00
Kroese
b71b2245cb fix: Check diskspace
* fix: Check diskspace
2023-12-18 16:44:26 +01:00
Kroese
bbea0eb429 docs: Readme 2023-12-18 12:39:11 +01:00
Kroese
c28c5cfbaa docs: Allocation 2023-12-18 12:36:03 +01:00
Kroese
be3fd30cb2 feat: Honor allocation flag for qcow2
* feat: Honor allocation flag for qcow2
2023-12-18 12:18:28 +01:00
Kroese
e3942da906 feat: Set NOCOW flag for btrfs
feat: Set NOCOW flag for btrfs
2023-12-17 11:42:08 +01:00
Kroese
d66be1a228 fix: Do not set flags on resize 2023-12-17 11:25:42 +01:00
Kroese
72085d3711 fix: Set qcow2 flags 2023-12-17 11:17:26 +01:00
Kroese
e7cdbb1db5 feat: Optimize qcow2 flags 2023-12-17 10:51:30 +01:00
Kroese
7a592e0cea docs: DHCP mode 2023-12-17 10:08:55 +01:00
Kroese
107a4b87d5 fix: Quotes 2023-12-17 10:04:00 +01:00
Kroese
8b0ec3bef7 fix: Remove healthcheck exception 2023-12-17 09:57:50 +01:00
Kroese
aaded40a4f fix: Cleanup server files 2023-12-17 09:54:07 +01:00
Kroese
7f77bb88ab fix: Grammar 2023-12-17 09:50:07 +01:00
Kroese
95c3b2caad fix: Default folder 2023-12-17 09:39:19 +01:00
Kroese
150c450bf6 docs: Default folder 2023-12-17 09:37:36 +01:00
Kroese
932c23afba fix: Check SSE4.2 2023-12-16 15:57:30 +01:00
Kroese
2e0107e46f fix: Bash shebang
* fix: Bash shebang
2023-12-16 07:42:57 +01:00
Kroese
d22a3a4c7d feat: Support more CPU models
feat: Support more CPU models
2023-12-16 06:44:58 +01:00
Kroese
f93f870626 feat: CPU features 2023-12-16 06:31:43 +01:00
Kroese
a2e55c5dda fix: Configuration variables 2023-12-16 05:11:20 +01:00
Kroese
32748509ea fix: CPU configuration 2023-12-16 05:08:34 +01:00
Kroese
970a662170 fix: CPU configuration 2023-12-16 05:04:17 +01:00
Kroese
8925323a6e feat: KVM flag 2023-12-16 04:55:40 +01:00
Kroese
10915a601c feat: Display emulated CPU 2023-12-16 04:44:15 +01:00
Kroese
2cc1af19b1 fix: Configure CPU 2023-12-16 04:38:15 +01:00
Kroese
6670ca4fe1 fix: Configuration 2023-12-16 04:36:56 +01:00
Kroese
d3f77c848c feat: Emulate SSE4.2 on ARM64 2023-12-16 04:36:00 +01:00
Kroese
3f2ca67051 feat: Emulate SSE4.2 on ARM64 2023-12-16 04:22:21 +01:00
Kroese
3812101366 feat: Add KVM flag 2023-12-16 04:02:11 +01:00
Kroese
db72acfc4f feat: Install DSM 7.2 on ARM64 2023-12-16 03:12:55 +01:00
Kroese
1b3d760f5f fix: Set CPU model 2023-12-16 03:07:08 +01:00
Kroese
539f5de6d9 fix: Add qemu-user package on arm64 2023-12-16 02:45:16 +01:00
Kroese
fe2d072056 fix: Check for SSE4.2 support 2023-12-16 02:05:54 +01:00
Kroese
469ee67942 fix: Error codes 2023-12-16 01:42:02 +01:00
Kroese
95991d8f5d fix: Error codes 2023-12-16 01:36:33 +01:00
Kroese
7e12585429 fix: Error codes 2023-12-16 01:30:34 +01:00
Kroese
c335078aac feat: Display emulated CPU 2023-12-16 01:00:42 +01:00
Kroese
f1fbbb5623 feat: Configure CPU model 2023-12-16 00:35:08 +01:00
Kroese
b6502e0a38 fix: Simplify healthcheck
fix: Simplify healthcheck
2023-12-15 09:17:53 +01:00
Kroese
2fab3e5897 fix: Simplify healthcheck 2023-12-15 09:01:19 +01:00
Kroese
a4ea89d6e7 fix: Extended error message 2023-12-15 08:49:37 +01:00
Kroese
c451f253fa build: Release token (#454) 2023-12-14 03:45:32 +01:00
Kroese
03121b6c6d build: Renovate (#452) 2023-12-11 00:32:18 +01:00
Kroese
007d20c315 fix: Message variables
* fix: Message variables
2023-12-10 16:54:02 +01:00
Kroese
26d6fa9fcc feat: Improve shutdown
* feat: Improve shutdown
2023-12-10 15:58:37 +01:00
Kroese
b9f3e52ba4 feat: Improve shutdown (#448)
* feat: Improve shutdown
2023-12-10 09:22:35 +01:00
Kroese
03d2665725 fix: Local variables (#447)
* fix: Local variables

* fix: Keep location
2023-12-10 00:26:13 +01:00
Kroese
ba7fd2fe4a fix: Error checking (#446) 2023-12-09 21:45:31 +01:00
Kroese
a8bcae16a4 fix: Remove curly braces
* fix: Remove curly braces
2023-12-09 21:19:08 +01:00
Kroese
2f19d31a81 feat: Show conversion progress (#444)
* feat: Show conversion progress
2023-12-09 15:15:25 +01:00
Kroese
54692e3a75 fix: Move progress (#443) 2023-12-09 06:19:07 +01:00
Kroese
029235a34d docs: Readme (#442) 2023-12-09 05:52:21 +01:00
Kroese
180573d69f fix: Diskspace warning (#441) 2023-12-09 05:40:10 +01:00
Kroese
8fa900335a feat: Add qcow2 disk format (#440)
* feat: Add qcow2 disk format
2023-12-09 02:55:39 +01:00
Kroese
a527080ccd feat: Print curl error (#438)
* feat: Print curl error
2023-12-07 23:36:57 +01:00
Kroese
ce6d60c611 Parse JSON with JQ (#437)
* feat: Parse JSON with JQ
2023-12-07 23:18:47 +01:00
Kroese
ff9fd9b377 fix: Set timeout (#435) 2023-12-06 07:16:30 +01:00
Kroese
9e61be15e6 build: Platforms (#433) 2023-12-06 06:39:57 +01:00
Kroese
b1d53b42ca fix: File support 2023-12-06 05:56:25 +01:00
Kroese
143a2151fb fix: Increase timeout (#431)
* fix: Increase timeout
2023-12-06 05:37:03 +01:00
Kroese
7fd29e30b3 fix: Match package 2023-12-05 07:53:03 +01:00
Kroese
efe46e1fdc feat: Mirror selection
* feat: Country detection
2023-12-05 06:56:20 +01:00
Kroese
2cf4ca07f4 fix: Set request header 2023-12-04 17:14:55 +01:00
Kroese
b88207f0dd fix: Check diskspace 2023-12-04 15:32:17 +01:00
Kroese
70e10b1d56 fix: Deduce mirror from URL
* fix: Deduce mirror from URL
2023-12-04 15:10:49 +01:00
Kroese
ced994d94a fix: Force JSON response 2023-12-04 14:35:59 +01:00
Kroese
354bd2429b feat: Country detection
* feat: Country detection
2023-12-04 14:21:37 +01:00
Kroese
c1d3d15d4e fix: DNS resolution
* fix: DNS resolution
2023-12-04 07:20:40 +01:00
Kroese
95b2b83ac6 fix: DNS resolution 2023-12-03 08:33:52 +01:00
Kroese
c3c4d966b4 feat: Print available diskspace
* feat: Print available diskspace
2023-12-03 08:29:09 +01:00
Kroese
a768fecfde fix: Default RAM size
* fix: Default RAM size
2023-12-01 03:55:25 +01:00
Kroese
01e41a4014 build: Annotations (#414) 2023-11-30 14:04:47 +01:00
Kroese
eb4852683b build: Annotations (#413) 2023-11-30 13:53:00 +01:00
Kroese
6218333fec feat: Console mode
* feat: Console mode

* fix: Increase timeout
2023-11-29 20:24:28 +01:00
18 changed files with 781 additions and 509 deletions

View File

@@ -1,6 +1,4 @@
{ {
"extends": [ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
"config:base", "extends": ["config:recommended", ":disableDependencyDashboard"]
":disableDependencyDashboard"
]
} }

View File

@@ -49,6 +49,8 @@ jobs:
type=raw,value=${{ vars.MAJOR }}.${{ vars.MINOR }} type=raw,value=${{ vars.MAJOR }}.${{ vars.MINOR }}
labels: | labels: |
org.opencontainers.image.title=${{ vars.NAME }} org.opencontainers.image.title=${{ vars.NAME }}
env:
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@v3
@@ -72,21 +74,19 @@ jobs:
context: . context: .
push: true push: true
provenance: false provenance: false
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64,linux/arm
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.labels }} annotations: ${{ steps.meta.outputs.annotations }}
build-args: | build-args: |
VCS_REF=${GITHUB_SHA::8}
VERSION_ARG=${{ steps.meta.outputs.version }} VERSION_ARG=${{ steps.meta.outputs.version }}
- -
name: Create a release name: Create a release
uses: action-pack/github-release@v2 uses: action-pack/github-release@v2
env:
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with: with:
tag: "v${{ steps.meta.outputs.version }}" tag: "v${{ steps.meta.outputs.version }}"
title: "v${{ steps.meta.outputs.version }}" title: "v${{ steps.meta.outputs.version }}"
token: ${{ secrets.REPO_ACCESS_TOKEN }}
- -
name: Increment version variable name: Increment version variable
uses: action-pack/bump@v2 uses: action-pack/bump@v2

View File

@@ -9,12 +9,15 @@ FROM qemux/qemu-host as builder
FROM debian:trixie-slim FROM debian:trixie-slim
ARG TARGETPLATFORM
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND noninteractive ARG DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get -y upgrade && \ RUN apt-get update && apt-get -y upgrade \
apt-get --no-install-recommends -y install \ && if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi \
tini \ && apt-get --no-install-recommends -y install \
jq \
tini \
curl \ curl \
cpio \ cpio \
wget \ wget \
@@ -27,9 +30,11 @@ RUN apt-get update && apt-get -y upgrade && \
iproute2 \ iproute2 \
dnsmasq \ dnsmasq \
net-tools \ net-tools \
qemu-utils \
ca-certificates \ ca-certificates \
netcat-openbsd \ netcat-openbsd \
qemu-system-x86 \ qemu-system-x86 \
"$extra" \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@@ -45,9 +50,9 @@ EXPOSE 139
EXPOSE 445 EXPOSE 445
EXPOSE 5000 EXPOSE 5000
ENV CPU_CORES "1" ENV RAM_SIZE "1G"
ENV DISK_SIZE "16G" ENV DISK_SIZE "16G"
ENV RAM_SIZE "512M" ENV CPU_CORES "1"
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
RUN echo "$VERSION_ARG" > /run/version RUN echo "$VERSION_ARG" > /run/version

View File

@@ -4,9 +4,9 @@ services:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm:latest image: vdsm/virtual-dsm:latest
environment: environment:
CPU_CORES: "1"
DISK_SIZE: "16G" DISK_SIZE: "16G"
RAM_SIZE: "512M" RAM_SIZE: "1G"
CPU_CORES: "1"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun
@@ -18,6 +18,6 @@ services:
ports: ports:
- 5000:5000 - 5000:5000
volumes: volumes:
- /opt/dsm:/storage - /var/dsm:/storage
restart: on-failure restart: on-failure
stop_grace_period: 1m stop_grace_period: 2m

View File

@@ -15,7 +15,7 @@ Virtual DSM in a docker container.
## Features ## Features
- Multi-platform - Multiple disks
- KVM acceleration - KVM acceleration
- GPU passthrough - GPU passthrough
- Upgrades supported - Upgrades supported
@@ -39,9 +39,9 @@ services:
ports: ports:
- 5000:5000 - 5000:5000
volumes: volumes:
- /opt/dsm:/storage - /var/dsm:/storage
restart: on-failure restart: on-failure
stop_grace_period: 1m stop_grace_period: 2m
``` ```
Via `docker run` Via `docker run`
@@ -58,21 +58,21 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
```yaml ```yaml
environment: environment:
DISK_SIZE: "256G" DISK_SIZE: "128G"
``` ```
This can also be used to resize the existing disk to a larger capacity without any data loss. This can also be used to resize the existing disk to a larger capacity without any data loss.
* ### How do I change the location of the virtual disk? * ### How do I change the location of the virtual disk?
To change the virtual disk's location from the default Docker volume, include the following bind mount in your compose file: To change the location of the virtual disk, include the following bind mount in your compose file:
```yaml ```yaml
volumes: volumes:
- /home/user/data:/storage - /var/dsm:/storage
``` ```
Replace the example path `/home/user/data` with the desired storage folder. Replace the example path `/var/dsm` with the desired storage folder.
* ### How do I add multiple disks? * ### How do I add multiple disks?
@@ -87,25 +87,30 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
- /mnt/data/example:/storage3 - /mnt/data/example:/storage3
``` ```
* ### How do I change the space reserved by the virtual disk? * ### How do I create a growable disk?
By default, the entire disk space is reserved in advance. To create a growable disk that only reserves the space that is actually used, add the following environment variable: By default, the entire capacity of the disk is reserved in advance.
To create a growable disk that only allocates space that is actually used, add the following environment variables:
```yaml ```yaml
environment: environment:
ALLOCATE: "N" ALLOCATE: "N"
DISK_FMT: "qcow2"
``` ```
Keep in mind that this will not affect any of your existing disks, it only applies to newly created disks. Please note that this may reduce the write performance of the disk.
* ### How do I increase the amount of CPU or RAM? * ### How do I increase the amount of CPU or RAM?
By default, a single core and 512 MB of RAM are allocated to the container. To increase this, add the following environment variables: By default, a single core and 1 GB of RAM are allocated to the container.
To increase this, add the following environment variables:
```yaml ```yaml
environment: environment:
RAM_SIZE: "4G"
CPU_CORES: "4" CPU_CORES: "4"
RAM_SIZE: "2048M"
``` ```
* ### How do I verify if my system supports KVM? * ### How do I verify if my system supports KVM?
@@ -170,9 +175,9 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
- 'c *:* rwm' - 'c *:* rwm'
``` ```
Please note that even if you don't need DHCP, it's still recommended to enable this feature as it prevents NAT issues and increases performance by using a `macvtap` interface. Please note that even if you don't want DHCP, it's still recommended to enable this feature as it prevents NAT issues and increases performance by using a `macvtap` interface. In that case just set a static IP from the DSM control panel after you enabled this mode.
* ### How do I passthrough my GPU? * ### How do I passthrough the GPU?
To passthrough your Intel GPU, add the following lines to your compose file: To passthrough your Intel GPU, add the following lines to your compose file:

View File

@@ -1,61 +1,17 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -u set -Eeuo pipefail
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0 [ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1 [ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1
file="/run/dsm.url" file="/run/dsm.url"
[ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
if [ ! -f "$file" ]; then location=$(cat "$file")
# Retrieve IP from guest VM for Docker healthcheck
RESPONSE=$(curl -s -m 16 -S http://127.0.0.1:2210/read?command=10 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then
echo "Failed to connect to guest: $RESPONSE" && exit 1
fi
# Retrieve the HTTP port number
if [[ ! "${RESPONSE}" =~ "\"http_port\"" ]] ; then
echo "Failed to parse response from guest: $RESPONSE" && exit 1
fi
rest=${RESPONSE#*http_port}
rest=${rest#*:}
rest=${rest%%,*}
PORT=${rest%%\"*}
[ -z "${PORT}" ] && echo "Guest has not set a portnumber yet.." && exit 1
# Retrieve the IP address
if [[ ! "${RESPONSE}" =~ "eth0" ]] ; then
echo "Failed to parse response from guest: $RESPONSE" && exit 1
fi
rest=${RESPONSE#*eth0}
rest=${rest#*ip}
rest=${rest#*:}
rest=${rest#*\"}
IP=${rest%%\"*}
[ -z "${IP}" ] && echo "Guest has not received an IP yet.." && exit 1
echo "${IP}:${PORT}" > $file
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
echo "Failed to reach page at http://$location" && exit 1
fi fi
LOCATION=$(cat "$file") echo "Healthcheck OK"
if ! curl -m 20 -ILfSs "http://${LOCATION}/" > /dev/null; then
rm -f $file
echo "Failed to reach http://${LOCATION}"
exit 1
fi
if [[ "$LOCATION" == "20.20"* ]]; then
echo "Healthcheck OK"
else
echo "Healthcheck OK ( ${LOCATION%:*} )"
fi
exit 0 exit 0

View File

@@ -1,40 +1,20 @@
#!/bin/bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
KVM_ERR=""
KVM_OPTS=""
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi
if [ -n "${KVM_ERR}" ]; then
if [ "$ARCH" == "amd64" ]; then
error "KVM acceleration not detected ${KVM_ERR}, see the FAQ about this."
[[ "${DEBUG}" != [Yy1]* ]] && exit 88
fi
else
KVM_OPTS=",accel=kvm -enable-kvm -cpu host"
fi
DEF_OPTS="-nographic -nodefaults -boot strict=on -display none" DEF_OPTS="-nographic -nodefaults -boot strict=on -display none"
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')
CPU_OPTS="-smp ${CPU_CORES},sockets=1,dies=1,cores=${CPU_CORES},threads=1" CPU_OPTS="-cpu $CPU_MODEL -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}" MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4" EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom" EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
EXTRA_OPTS="$EXTRA_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c" EXTRA_OPTS="$EXTRA_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
if [[ "${GPU}" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then if [[ "$GPU" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then
DEF_OPTS="-nodefaults -boot strict=on -display egl-headless,rendernode=/dev/dri/renderD128" DEF_OPTS="-nodefaults -boot strict=on -display egl-headless,rendernode=/dev/dri/renderD128"
DEF_OPTS="${DEF_OPTS} -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1" DEF_OPTS="$DEF_OPTS -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1"
fi fi
ARGS="${DEF_OPTS} ${CPU_OPTS} ${RAM_OPTS} ${MAC_OPTS} ${MON_OPTS} ${SERIAL_OPTS} ${NET_OPTS} ${DISK_OPTS} ${EXTRA_OPTS} ${ARGUMENTS}" ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $EXTRA_OPTS $ARGUMENTS"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ') ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
return 0 return 0

61
src/cpu.sh Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: ${HOST_CPU:=''}
: ${CPU_MODEL:='host'}
: ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'}
KVM_ERR=""
KVM_OPTS=""
if [[ "$ARCH" == "amd64" && "$KVM" != [Nn]* ]]; then
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi
if [ -n "$KVM_ERR" ]; then
error "KVM acceleration not detected $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ on how to enable it, or skip this error by setting KVM=N (not recommended)."
[[ "$DEBUG" != [Yy1]* ]] && exit 88
[[ "$CPU_MODEL" == "host"* ]] && CPU_MODEL="max,$CPU_FEATURES"
else
KVM_OPTS=",accel=kvm -enable-kvm"
fi
if [ -n "$KVM_OPTS" ]; then
if ! grep -qE '^flags.* (sse4_2)' /proc/cpuinfo; then
error "Your host CPU does not have the SSE4.2 instruction set that Virtual DSM requires to boot."
error "Disable KVM by setting KVM=N to emulate a compatible CPU, at the cost of performance."
[[ "$DEBUG" != [Yy1]* ]] && exit 89
fi
fi
else
[[ "$CPU_MODEL" == "host"* ]] && CPU_MODEL="max,$CPU_FEATURES"
fi
if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
if [ -n "$HOST_CPU" ]; then
HOST_CPU="${HOST_CPU%%,*},,"
else
HOST_CPU="QEMU, Virtual CPU,"
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="$HOST_CPU X86_64"
else
HOST_CPU="$HOST_CPU $ARCH"
fi
fi
return 0

View File

@@ -3,10 +3,12 @@ set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: ${DISK_IO:='native'} # I/O Mode, can be set to 'native', 'threads' or 'io_turing' : ${DISK_IO:='native'} # I/O Mode, can be set to 'native', 'threads' or 'io_turing'
: ${DISK_CACHE:='none'} # Caching mode, can be set to 'writeback' for better performance : ${DISK_FMT:='raw'} # Disk file format, 'raw' by default for best performance
: ${DISK_DISCARD:='on'} # Controls whether unmap (TRIM) commands are passed to the host. : ${DISK_CACHE:='none'} # Caching mode, can be set to 'writeback' for better performance
: ${DISK_ROTATION:='1'} # Rotation rate, set to 1 for SSD storage and increase for HDD : ${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_FLAGS:='nocow=on'} # Specifies the options for use with the qcow2 disk format
BOOT="$STORAGE/$BASE.boot.img" BOOT="$STORAGE/$BASE.boot.img"
SYSTEM="$STORAGE/$BASE.system.img" SYSTEM="$STORAGE/$BASE.system.img"
@@ -15,155 +17,358 @@ SYSTEM="$STORAGE/$BASE.system.img"
[ ! -f "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82 [ ! -f "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
DISK_OPTS="\ DISK_OPTS="\
-device virtio-scsi-pci,id=hw-synoboot,bus=pcie.0,addr=0xa \ -object iothread,id=io1 -object iothread,id=io2 \
-drive file=${BOOT},if=none,id=drive-synoboot,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -device virtio-scsi-pci,id=hw-synoboot,iothread=io1,bus=pcie.0,addr=0xa \
-device scsi-hd,bus=hw-synoboot.0,channel=0,scsi-id=0,lun=0,drive=drive-synoboot,id=synoboot0,rotation_rate=${DISK_ROTATION},bootindex=1 \ -drive file=$BOOT,if=none,id=drive-synoboot,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device virtio-scsi-pci,id=hw-synosys,bus=pcie.0,addr=0xb \ -device scsi-hd,bus=hw-synoboot.0,channel=0,scsi-id=0,lun=0,drive=drive-synoboot,id=synoboot0,rotation_rate=$DISK_ROTATION,bootindex=1 \
-drive file=${SYSTEM},if=none,id=drive-synosys,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -device virtio-scsi-pci,id=hw-synosys,iothread=io1,bus=pcie.0,addr=0xb \
-device scsi-hd,bus=hw-synosys.0,channel=0,scsi-id=0,lun=0,drive=drive-synosys,id=synosys0,rotation_rate=${DISK_ROTATION},bootindex=2" -drive file=$SYSTEM,if=none,id=drive-synosys,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-synosys.0,channel=0,scsi-id=0,lun=0,drive=drive-synosys,id=synosys0,rotation_rate=$DISK_ROTATION,bootindex=2"
addDisk () { fmt2ext() {
local DISK_FMT=$1
local GB case "${DISK_FMT,,}" in
local DIR qcow2)
local REQ echo "qcow2"
local SPACE ;;
local CUR_SIZE raw)
local DATA_SIZE echo "img"
local DISK_ID=$1 ;;
local DISK_FILE=$2 *)
error "Unrecognized disk format: $DISK_FMT" && exit 78
;;
esac
}
ext2fmt() {
local DISK_EXT=$1
case "${DISK_EXT,,}" in
qcow2)
echo "qcow2"
;;
img)
echo "raw"
;;
*)
error "Unrecognized file extension: .$DISK_EXT" && exit 78
;;
esac
}
getSize() {
local DISK_FILE=$1
local DISK_EXT DISK_FMT
DISK_EXT="$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')"
DISK_FMT="$(ext2fmt "$DISK_EXT")"
case "${DISK_FMT,,}" in
raw)
stat -c%s "$DISK_FILE"
;;
qcow2)
qemu-img info "$DISK_FILE" -f "$DISK_FMT" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/'
;;
*)
error "Unrecognized disk format: $DISK_FMT" && exit 78
;;
esac
}
createDisk() {
local DISK_FILE=$1
local DISK_SPACE=$2
local DISK_DESC=$3 local DISK_DESC=$3
local DISK_SPACE=$4 local DISK_FMT=$4
local DISK_INDEX=$5 local DIR SPACE DATA_SIZE
local DISK_ADDRESS=$6
DIR=$(dirname "${DISK_FILE}") if [[ "$ALLOCATE" != [Nn]* ]]; then
[ ! -d "${DIR}" ] && return 0
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G" # Check free diskspace
DISK_SPACE=$(echo "${DISK_SPACE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') DIR=$(dirname "$DISK_FILE")
DATA_SIZE=$(numfmt --from=iec "${DISK_SPACE}") SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
if (( DATA_SIZE < 6442450944 )); then if (( DATA_SIZE > SPACE )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83 error "Not enough free space to create a $DISK_DESC of $DISK_SPACE in $DIR, it has only $SPACE_GB GB available..."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi fi
if [ -f "${DISK_FILE}" ]; then local FAIL="Could not create a $DISK_SPACE $DISK_FMT file for $DISK_DESC ($DISK_FILE)"
CUR_SIZE=$(stat -c%s "${DISK_FILE}") case "${DISK_FMT,,}" in
raw)
if [[ "$ALLOCATE" == [Nn]* ]]; then
if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then # Create an empty file
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
GB=$(( (CUR_SIZE + 1073741823)/1073741824 )) rm -f "$DISK_FILE"
info "Resizing ${DISK_DESC} from ${GB}G to ${DISK_SPACE} .." error "$FAIL" && exit 77
if [[ "${ALLOCATE}" == [Nn]* ]]; then
# Resize file by changing its length
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
error "Could not resize ${DISK_DESC} file (${DISK_FILE}) to ${DISK_SPACE} .." && exit 85
fi fi
else else
REQ=$((DATA_SIZE-CUR_SIZE)) # Create an empty file
if ! fallocate -l "$DISK_SPACE" "$DISK_FILE"; then
# Check free diskspace if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1) rm -f "$DISK_FILE"
error "$FAIL" && exit 77
if (( REQ > SPACE )); then
error "Not enough free space to resize ${DISK_DESC} to ${DISK_SPACE} .."
error "Specify a smaller size or disable preallocation with ALLOCATE=N." && exit 84
fi
# Resize file by allocating more space
if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
error "Could not resize ${DISK_DESC} file (${DISK_FILE}) to ${DISK_SPACE} .." && exit 85
fi fi
fi fi
fi fi
fi ;;
fi qcow2)
local DISK_OPTS="$DISK_ALLOC"
if [ ! -f "${DISK_FILE}" ]; then [ -n "$DISK_FLAGS" ] && DISK_OPTS="$DISK_OPTS,$DISK_FLAGS"
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_OPTS" -- "$DISK_FILE" "$DISK_SPACE" ; then
if [[ "${ALLOCATE}" == [Nn]* ]]; then rm -f "$DISK_FILE"
error "$FAIL" && exit 70
# Create an empty file
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
rm -f "${DISK_FILE}"
error "Could not create a file for ${DISK_DESC} (${DISK_FILE})" && exit 87
fi fi
;;
else esac
# Check free diskspace
SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1)
if (( DATA_SIZE > SPACE )); then
error "Not enough free space to create ${DISK_DESC} of ${DISK_SPACE} .."
error "Specify a smaller size or disable preallocation with ALLOCATE=N." && exit 86
fi
# Create an empty file
if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
rm -f "${DISK_FILE}"
error "Could not create a file for ${DISK_DESC} (${DISK_FILE}) of ${DISK_SPACE} .." && exit 87
fi
fi
fi
fi
DISK_OPTS="${DISK_OPTS} \
-device virtio-scsi-pci,id=hw-${DISK_ID},bus=pcie.0,addr=${DISK_ADDRESS} \
-drive file=${DISK_FILE},if=none,id=drive-${DISK_ID},format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \
-device scsi-hd,bus=hw-${DISK_ID}.0,channel=0,scsi-id=0,lun=0,drive=drive-${DISK_ID},id=${DISK_ID},rotation_rate=${DISK_ROTATION},bootindex=${DISK_INDEX}"
return 0 return 0
} }
DISK1_FILE="${STORAGE}/data.img" resizeDisk() {
local DISK_FILE=$1
local DISK_SPACE=$2
local DISK_DESC=$3
local DISK_FMT=$4
local CUR_SIZE DATA_SIZE DIR SPACE
if [[ ! -f "${DISK1_FILE}" ]] && [[ -f "${STORAGE}/data${DISK_SIZE}.img" ]]; then CUR_SIZE=$(getSize "$DISK_FILE")
# Fallback for legacy installs DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
mv "${STORAGE}/data${DISK_SIZE}.img" "${DISK1_FILE}" local REQ=$((DATA_SIZE-CUR_SIZE))
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace
DIR=$(dirname "$DISK_FILE")
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( REQ > SPACE )); then
error "Not enough free space to resize $DISK_DESC to $DISK_SPACE in $DIR, it has only $SPACE_GB GB available.."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
fi
fi
local GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
info "Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
local FAIL="Could not resize $DISK_FMT file of $DISK_DESC ($DISK_FILE) from ${GB}G to $DISK_SPACE .."
case "${DISK_FMT,,}" in
raw)
if [[ "$ALLOCATE" == [Nn]* ]]; then
# Resize file by changing its length
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
error "$FAIL" && exit 75
fi
else
# Resize file by allocating more space
if ! fallocate -l "$DISK_SPACE" "$DISK_FILE"; then
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
error "$FAIL" && exit 75
fi
fi
fi
;;
qcow2)
if ! qemu-img resize -f "$DISK_FMT" "--$DISK_ALLOC" "$DISK_FILE" "$DISK_SPACE" ; then
error "$FAIL" && exit 72
fi
;;
esac
return 0
}
convertDisk() {
local SOURCE_FILE=$1
local SOURCE_FMT=$2
local DST_FILE=$3
local DST_FMT=$4
local DISK_BASE=$5
local DISK_DESC=$6
local CONV_FLAGS="-p"
local DISK_OPTS="$DISK_ALLOC"
local TMP_FILE="$DISK_BASE.tmp"
local DIR CUR_SIZE SPACE
[ -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
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace
DIR=$(dirname "$TMP_FILE")
CUR_SIZE=$(getSize "$SOURCE_FILE")
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( CUR_SIZE > SPACE )); then
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $SPACE_GB GB available..."
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
case "$DST_FMT" in
qcow2)
if [[ "$ALLOCATE" == [Nn]* ]]; then
CONV_FLAGS="$CONV_FLAGS -c"
fi
[ -n "$DISK_FLAGS" ] && DISK_OPTS="$DISK_OPTS,$DISK_FLAGS"
;;
esac
rm -f "$TMP_FILE"
# shellcheck disable=SC2086
if ! qemu-img convert -f "$SOURCE_FMT" $CONV_FLAGS -o "$DISK_OPTS" -O "$DST_FMT" -- "$SOURCE_FILE" "$TMP_FILE"; then
rm -f "$TMP_FILE"
error "Failed to convert $DISK_DESC to $DST_FMT format in $DIR, is there enough space available?" && exit 79
fi
rm -f "$SOURCE_FILE"
mv "$TMP_FILE" "$DST_FILE"
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
return 0
}
addDisk () {
local DISK_ID=$1
local DISK_BASE=$2
local DISK_EXT=$3
local DISK_DESC=$4
local DISK_SPACE=$5
local DISK_INDEX=$6
local DISK_ADDRESS=$7
local DISK_FMT=$8
local DISK_FILE="$DISK_BASE.$DISK_EXT"
local DIR FS FA DATA_SIZE PREV_FMT PREV_EXT CUR_SIZE
DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0
FS=$(stat -f -c %T "$DIR")
if [[ "$FS" == "overlay"* ]]; then
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
fi
if [[ "$FS" == "btrfs"* ]]; then
if [ -f "$DISK_FILE" ] ; then
FA=$(lsattr "$DISK_FILE")
[[ "$FA" == *"C"* ]] && FA=$(lsattr -d "$DIR")
else
FA=$(lsattr -d "$DIR")
fi
if [[ "$FA" != *"C"* ]]; then
info "Warning: the filesystem of $DIR is BTRFS, and COW (copy on write) is not disabled for that folder!"
info "This will negatively affect write performance, please empty the folder and disable COW (chattr +C <path>)."
fi
fi
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G"
DISK_SPACE=$(echo "$DISK_SPACE" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
if (( DATA_SIZE < 6442450944 )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi
if ! [ -f "$DISK_FILE" ] ; then
if [[ "${DISK_FMT,,}" != "raw" ]]; then
PREV_FMT="raw"
else
PREV_FMT="qcow2"
fi
PREV_EXT="$(fmt2ext "$PREV_FMT")"
if [ -f "$DISK_BASE.$PREV_EXT" ] ; then
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" || exit $?
fi
fi
if [ -f "$DISK_FILE" ]; then
CUR_SIZE=$(getSize "$DISK_FILE")
if (( DATA_SIZE > CUR_SIZE )); then
resizeDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $?
fi
else
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $?
fi
DISK_OPTS="$DISK_OPTS \
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
-drive file=$DISK_FILE,if=none,id=drive-$DISK_ID,format=$DISK_FMT,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
return 0
}
DISK_EXT="$(fmt2ext "$DISK_FMT")" || exit $?
if [[ "$ALLOCATE" == [Nn]* ]]; then
DISK_ALLOC="preallocation=off"
else
DISK_ALLOC="preallocation=falloc"
fi fi
DISK2_FILE="/storage2/data2.img" DISK1_FILE="$STORAGE/data"
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
if [ ! -f "${DISK2_FILE}" ]; then DISK2_FILE="/storage2/data2"
if [ ! -f "$DISK2_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage2/data.img" FALLBACK="/storage2/data.img"
if [[ -f "${DISK1_FILE}" ]] && [[ -f "${FALLBACK}" ]]; then if [[ -f "$DISK1_FILE.img" ]] && [[ -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "${FALLBACK}") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "${DISK1_FILE}") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then
mv "${FALLBACK}" "${DISK2_FILE}" mv "$FALLBACK" "$DISK2_FILE.img"
fi fi
fi fi
fi fi
DISK3_FILE="/storage3/data3.img" DISK3_FILE="/storage3/data3"
if [ ! -f "$DISK3_FILE.img" ]; then
if [ ! -f "${DISK3_FILE}" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage3/data.img" FALLBACK="/storage3/data.img"
if [[ -f "${DISK1_FILE}" ]] && [[ -f "${FALLBACK}" ]]; then if [[ -f "$DISK1_FILE.img" ]] && [[ -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "${FALLBACK}") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "${DISK1_FILE}") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then
mv "${FALLBACK}" "${DISK3_FILE}" mv "$FALLBACK" "$DISK3_FILE.img"
fi fi
fi fi
fi fi
DISK4_FILE="/storage4/data4.img" DISK4_FILE="/storage4/data4"
DISK5_FILE="/storage5/data5.img" DISK5_FILE="/storage5/data5"
DISK6_FILE="/storage6/data6.img" DISK6_FILE="/storage6/data6"
: ${DISK2_SIZE:=''} : ${DISK2_SIZE:=''}
: ${DISK3_SIZE:=''} : ${DISK3_SIZE:=''}
@@ -171,12 +376,12 @@ DISK6_FILE="/storage6/data6.img"
: ${DISK5_SIZE:=''} : ${DISK5_SIZE:=''}
: ${DISK6_SIZE:=''} : ${DISK6_SIZE:=''}
addDisk "userdata" "${DISK1_FILE}" "disk" "${DISK_SIZE}" "3" "0xc" addDisk "userdata" "$DISK1_FILE" "$DISK_EXT" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" || exit $?
addDisk "userdata2" "${DISK2_FILE}" "disk2" "${DISK2_SIZE}" "4" "0xd" addDisk "userdata2" "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" || exit $?
addDisk "userdata3" "${DISK3_FILE}" "disk3" "${DISK3_SIZE}" "5" "0xe" addDisk "userdata3" "$DISK3_FILE" "$DISK_EXT" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" || exit $?
addDisk "userdata4" "${DISK4_FILE}" "disk4" "${DISK4_SIZE}" "9" "0x7" addDisk "userdata4" "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "9" "0x7" "$DISK_FMT" || exit $?
addDisk "userdata5" "${DISK5_FILE}" "disk5" "${DISK5_SIZE}" "10" "0x8" addDisk "userdata5" "$DISK5_FILE" "$DISK_EXT" "disk5" "$DISK5_SIZE" "10" "0x8" "$DISK_FMT" || exit $?
addDisk "userdata6" "${DISK6_FILE}" "disk6" "${DISK6_SIZE}" "11" "0x9" addDisk "userdata6" "$DISK6_FILE" "$DISK_EXT" "disk6" "$DISK6_SIZE" "11" "0x9" "$DISK_FMT" || exit $?
addDevice () { addDevice () {
@@ -185,13 +390,13 @@ addDevice () {
local DISK_INDEX=$3 local DISK_INDEX=$3
local DISK_ADDRESS=$4 local DISK_ADDRESS=$4
[ -z "${DISK_DEV}" ] && return 0 [ -z "$DISK_DEV" ] && return 0
[ ! -b "${DISK_DEV}" ] && error "Device ${DISK_DEV} cannot be found! Please add it to the 'devices' section of your compose file." && exit 55 [ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
DISK_OPTS="${DISK_OPTS} \ DISK_OPTS="$DISK_OPTS \
-device virtio-scsi-pci,id=hw-${DISK_ID},bus=pcie.0,addr=${DISK_ADDRESS} \ -device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
-drive file=${DISK_DEV},if=none,id=drive-${DISK_ID},format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -drive file=$DISK_DEV,if=none,id=drive-$DISK_ID,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-${DISK_ID}.0,channel=0,scsi-id=0,lun=0,drive=drive-${DISK_ID},id=${DISK_ID},rotation_rate=${DISK_ROTATION},bootindex=${DISK_INDEX}" -device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
return 0 return 0
} }
@@ -203,11 +408,11 @@ addDevice () {
: ${DEVICE5:=''} : ${DEVICE5:=''}
: ${DEVICE6:=''} : ${DEVICE6:=''}
addDevice "userdata7" "${DEVICE}" "6" "0xf" addDevice "userdata7" "$DEVICE" "6" "0xf" || exit $?
addDevice "userdata8" "${DEVICE2}" "7" "0x5" addDevice "userdata8" "$DEVICE2" "7" "0x5" || exit $?
addDevice "userdata9" "${DEVICE3}" "8" "0x6" addDevice "userdata9" "$DEVICE3" "8" "0x6" || exit $?
addDevice "userdata4" "${DEVICE4}" "9" "0x7" addDevice "userdata4" "$DEVICE4" "9" "0x7" || exit $?
addDevice "userdata5" "${DEVICE5}" "10" "0x8" addDevice "userdata5" "$DEVICE5" "10" "0x8" || exit $?
addDevice "userdata6" "${DEVICE6}" "11" "0x9" addDevice "userdata6" "$DEVICE6" "11" "0x9" || exit $?
return 0 return 0

View File

@@ -11,18 +11,24 @@ cd /run
. disk.sh # Initialize disks . disk.sh # Initialize disks
. network.sh # Initialize network . network.sh # Initialize network
. gpu.sh # Initialize graphics . gpu.sh # Initialize graphics
. cpu.sh # Initialize processor
. serial.sh # Initialize serialport . serial.sh # Initialize serialport
. power.sh # Configure shutdown . power.sh # Configure shutdown
. config.sh # Configure arguments . config.sh # Configure arguments
trap - ERR trap - ERR
if [[ "$CONSOLE" == [Yy]* ]]; then
exec qemu-system-x86_64 -pidfile "$QEMU_PID" ${ARGS:+ $ARGS}
exit $?
fi
set -m set -m
( (
[[ "${DEBUG}" == [Yy1]* ]] && info "$VERS" && set -x [[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
qemu-system-x86_64 ${ARGS:+ $ARGS} & echo $! > "${QEMU_PID}" qemu-system-x86_64 ${ARGS:+ $ARGS} & echo $! > "$QEMU_PID"
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
) )
set +m set +m
tail --pid "$(cat "${QEMU_PID}")" --follow /dev/null & wait $! tail --pid "$(cat "$QEMU_PID")" --follow /dev/null & wait $!

View File

@@ -1,7 +1,7 @@
#!/bin/bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
if [[ "${GPU}" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
return 0 return 0
fi fi
@@ -18,28 +18,7 @@ fi
chmod 666 /dev/dri/card0 chmod 666 /dev/dri/card0
chmod 666 /dev/dri/renderD128 chmod 666 /dev/dri/renderD128
if ! apt-mark showinstall | grep -q "xserver-xorg-video-intel"; then addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addPackage "qemu-system-modules-opengl" "OpenGL module"
info "Installing Intel GPU drivers..."
export DEBCONF_NOWARNINGS="yes"
export DEBIAN_FRONTEND="noninteractive"
apt-get -qq update
apt-get -qq --no-install-recommends -y install xserver-xorg-video-intel > /dev/null
fi
if ! apt-mark showinstall | grep -q "qemu-system-modules-opengl"; then
info "Installing OpenGL module..."
export DEBCONF_NOWARNINGS="yes"
export DEBIAN_FRONTEND="noninteractive"
apt-get -qq update
apt-get -qq --no-install-recommends -y install qemu-system-modules-opengl > /dev/null
fi
return 0 return 0

View File

@@ -5,7 +5,7 @@ set -Eeuo pipefail
: ${DEV:='Y'} # Controls whether device nodes are created. : ${DEV:='Y'} # Controls whether device nodes are created.
if [ -f "$STORAGE"/dsm.ver ]; then if [ -f "$STORAGE"/dsm.ver ]; then
BASE=$(cat "${STORAGE}/dsm.ver") BASE=$(cat "$STORAGE/dsm.ver")
else else
# Fallback for old installs # Fallback for old installs
BASE="DSM_VirtualDSM_42962" BASE="DSM_VirtualDSM_42962"
@@ -14,24 +14,81 @@ fi
[ -n "$URL" ] && BASE=$(basename "$URL" .pat) [ -n "$URL" ] && BASE=$(basename "$URL" .pat)
if [[ -f "$STORAGE/$BASE.boot.img" ]] && [[ -f "$STORAGE/$BASE.system.img" ]]; then if [[ -f "$STORAGE/$BASE.boot.img" ]] && [[ -f "$STORAGE/$BASE.system.img" ]]; then
# Previous installation found return 0 # Previous installation found
return 0
fi fi
# Display wait message # Display wait message
/run/server.sh 5000 install & /run/server.sh 5000 install &
# Download the required files from the Synology website DL=""
DL="https://global.synologydownload.com/download/DSM" DL_CHINA="https://cndl.synology.cn/download/DSM"
DL_GLOBAL="https://global.synologydownload.com/download/DSM"
if [ -z "$URL" ]; then [[ "${URL,,}" == *"cndl.synology"* ]] && DL="$DL_CHINA"
[[ "${URL,,}" == *"global.synology"* ]] && DL="$DL_GLOBAL"
if [ "$ARCH" == "amd64" ]; then if [ -z "$DL" ]; then
URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat" [ -z "$COUNTRY" ] && setCountry
else [ -z "$COUNTRY" ] && info "Warning: could not detect country to select mirror!"
URL="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat" [[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL"
fi
[ -z "$URL" ] && URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
BASE=$(basename "$URL" .pat)
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$STORAGE"/"$BASE".pat
fi
rm -f "$STORAGE"/"$BASE".agent
rm -f "$STORAGE"/"$BASE".boot.img
rm -f "$STORAGE"/"$BASE".system.img
[[ "$DEBUG" == [Yy1]* ]] && set -x
# Check filesystem
MIN_ROOT=471859200
MIN_SPACE=6442450944
FS=$(stat -f -c %T "$STORAGE")
if [[ "$FS" == "overlay"* ]]; then
info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
fi
if [[ "$FS" == "btrfs"* ]]; then
FA=$(lsattr -d "$STORAGE")
if [[ "$FA" != *"C"* ]]; then
info "Warning: the filesystem of $STORAGE is BTRFS, and COW (copy on write) is not disabled for that folder!"
info "This will negatively affect write performance, please empty the folder and disable COW (chattr +C <path>)."
fi fi
fi
if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \
"$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then
TMP="$STORAGE/tmp"
else
TMP="/tmp/dsm"
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
if (( MIN_SPACE > SPACE )); then
TMP="$STORAGE/tmp"
info "Warning: the $FS filesystem of $STORAGE does not support UNIX permissions.."
fi
fi
rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace
SPACE=$(df --output=avail -B 1 / | tail -n 1)
(( MIN_ROOT > SPACE )) && error "Not enough free space in container root, need at least 450 MB available." && exit 96
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB GB available but need at least 6 GB." && exit 95
if [[ "$TMP" != "$STORAGE/tmp" ]]; then
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB GB available but need at least 6 GB." && exit 94
fi fi
# Check if output is to interactive TTY # Check if output is to interactive TTY
@@ -41,38 +98,11 @@ else
PROGRESS="--progress=dot:giga" PROGRESS="--progress=dot:giga"
fi fi
BASE=$(basename "$URL" .pat) # Download the required files from the Synology website
rm -f "$STORAGE"/"$BASE".pat
rm -f "$STORAGE"/"$BASE".agent
rm -f "$STORAGE"/"$BASE".boot.img
rm -f "$STORAGE"/"$BASE".system.img
MIN_SPACE=6442450944
FS=$(stat -f -c %T "$STORAGE")
if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \
"$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then
TMP="$STORAGE/tmp"
else
TMP="/tmp/dsm"
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
(( MIN_SPACE > SPACE )) && TMP="$STORAGE/tmp"
fi
rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp"
rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation, need at least 6 GB." && exit 95
[[ "${DEBUG}" == [Yy1]* ]] && set -x
RDC="$STORAGE/dsm.rd" RDC="$STORAGE/dsm.rd"
if [ ! -f "${RDC}" ]; then if [ ! -f "$RDC" ]; then
info "Install: Downloading installer..." info "Install: Downloading installer..."
@@ -81,7 +111,7 @@ if [ ! -f "${RDC}" ]; then
VERIFY="b4215a4b213ff5154db0488f92c87864" VERIFY="b4215a4b213ff5154db0488f92c87864"
LOC="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat" LOC="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
{ curl -r "$POS" -sfk -o "$RD" "$LOC"; rc=$?; } || : { curl -r "$POS" -sfk -S -o "$RD" "$LOC"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60 (( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
SUM=$(md5sum "$RD" | cut -f 1 -d " ") SUM=$(md5sum "$RD" | cut -f 1 -d " ")
@@ -95,7 +125,7 @@ if [ ! -f "${RDC}" ]; then
{ wget "$LOC" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || : { wget "$LOC" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60 (( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
tar --extract --file="$PAT" --directory="$(dirname "${RD}")"/. "$(basename "${RD}")" tar --extract --file="$PAT" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
rm "$PAT" rm "$PAT"
fi fi
@@ -104,20 +134,21 @@ if [ ! -f "${RDC}" ]; then
fi fi
if [ -f "${RDC}" ]; then if [ -f "$RDC" ]; then
{ xz -dc <"$RDC" >"$TMP/rd" 2>/dev/null; rc=$?; } || : { xz -dc <"$RDC" >"$TMP/rd" 2>/dev/null; rc=$?; } || :
(( rc != 1 )) && error "Failed to unxz $RDC, reason $rc" && exit 91 (( rc != 1 )) && error "Failed to unxz $RDC, reason $rc" && exit 91
if [[ "${DEV}" == [Nn]* ]]; then if [[ "$DEV" == [Nn]* ]]; then
# Exclude dev/ from cpio extract # Exclude dev/ from cpio extract
{ (cd "$TMP" && cpio -it < "$TMP/rd" | grep -Ev 'dev/' | while read -r entry; do cpio -idm "$entry" < "$TMP/rd" 2>/dev/null; done); rc=$?; } || : { (cd "$TMP" && cpio -it < "$TMP/rd" | grep -Ev 'dev/' | while read -r entry; do cpio -idm "$entry" < "$TMP/rd" 2>/dev/null; done); rc=$?; } || :
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92
else else
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || : { (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc"
(( rc != 0 )) && error "If the container runs unprivileged, please set DEV=N to exclude device nodes." && exit 92
fi fi
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92
mkdir -p /run/extract mkdir -p /run/extract
for file in $TMP/usr/lib/libcurl.so.4 \ for file in $TMP/usr/lib/libcurl.so.4 \
$TMP/usr/lib/libmbedcrypto.so.5 \ $TMP/usr/lib/libmbedcrypto.so.5 \
@@ -149,9 +180,17 @@ info "Install: Downloading $(basename "$URL")..."
PAT="/$BASE.pat" PAT="/$BASE.pat"
rm -f "$PAT" rm -f "$PAT"
{ wget "$URL" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || : if [[ "$URL" == "file://"* ]]; then
cp "${URL:7}" "$PAT"
else
{ wget "$URL" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $URL, reason: $rc" && exit 69
fi
(( rc != 0 )) && error "Failed to download $URL, reason: $rc" && exit 69
[ ! -f "$PAT" ] && error "Failed to download $URL" && exit 69 [ ! -f "$PAT" ] && error "Failed to download $URL" && exit 69
SIZE=$(stat -c%s "$PAT") SIZE=$(stat -c%s "$PAT")
@@ -167,18 +206,6 @@ if { tar tf "$PAT"; } >/dev/null 2>&1; then
else else
if [ "$ARCH" != "amd64" ]; then
info "Install: Installing QEMU..."
export DEBCONF_NOWARNINGS="yes"
export DEBIAN_FRONTEND="noninteractive"
apt-get -qq update
apt-get -qq --no-install-recommends -y install qemu-user > /dev/null
fi
info "Install: Extracting downloaded image..." info "Install: Extracting downloaded image..."
export LD_LIBRARY_PATH="/run/extract" export LD_LIBRARY_PATH="/run/extract"
@@ -213,26 +240,27 @@ SYSTEM_SIZE=4954537983
# Check free diskspace # Check free diskspace
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1) SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
(( SYSTEM_SIZE > SPACE )) && error "Not enough free space to create a 4 GB system disk." && exit 87 SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
(( SYSTEM_SIZE > SPACE )) && error "Not enough free space to create a 4 GB system disk, have only $SPACE_GB GB available." && exit 97
if ! fallocate -l "${SYSTEM_SIZE}" "${SYSTEM}"; then if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then
if ! truncate -s "${SYSTEM_SIZE}" "${SYSTEM}"; then if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then
rm -f "${SYSTEM}" && error "Could not allocate a file for the system disk." && exit 88 rm -f "$SYSTEM" && error "Could not allocate a file for the system disk." && exit 98
fi fi
fi fi
# Check if file exists # Check if file exists
[ ! -f "${SYSTEM}" ] && error "System disk does not exist ($SYSTEM)" && exit 89 [ ! -f "$SYSTEM" ] && error "System disk does not exist ($SYSTEM)" && exit 99
# Check the filesize # Check the filesize
SIZE=$(stat -c%s "${SYSTEM}") SIZE=$(stat -c%s "$SYSTEM")
[[ SIZE -ne SYSTEM_SIZE ]] && rm -f "${SYSTEM}" && error "System disk has the wrong size: ${SIZE}" && exit 90 [[ SIZE -ne SYSTEM_SIZE ]] && rm -f "$SYSTEM" && error "System disk has the wrong size: $SIZE" && exit 90
PART="$TMP/partition.fdisk" PART="$TMP/partition.fdisk"
{ echo "label: dos" { echo "label: dos"
echo "label-id: 0x6f9ee2e9" echo "label-id: 0x6f9ee2e9"
echo "device: ${SYSTEM}" echo "device: $SYSTEM"
echo "unit: sectors" echo "unit: sectors"
echo "sector-size: 512" echo "sector-size: 512"
echo "" echo ""
@@ -249,7 +277,7 @@ rm -rf "$MOUNT" && mkdir -p "$MOUNT"
mv "$HDA.tgz" "$HDA.txz" mv "$HDA.tgz" "$HDA.txz"
if [[ "${DEV}" == [Nn]* ]]; then if [[ "$DEV" == [Nn]* ]]; then
# Exclude dev/ from tar extract # Exclude dev/ from tar extract
tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/" tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/"
else else
@@ -274,19 +302,18 @@ rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE"/dsm.ver echo "$BASE" > "$STORAGE"/dsm.ver
if [[ "$TMP" != "$STORAGE/tmp" ]]; then if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
# Check free diskspace rm -f "$PAT"
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) else
(( MIN_SPACE > SPACE )) && error "Not enough free space in storage folder, need at least 6 GB." && exit 94 mv -f "$PAT" "$STORAGE"/"$BASE".pat
fi fi
mv -f "$PAT" "$STORAGE"/"$BASE".pat
mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img
mv -f "$SYSTEM" "$STORAGE"/"$BASE".system.img mv -f "$SYSTEM" "$STORAGE"/"$BASE".system.img
rm -rf "$TMP" rm -rf "$TMP"
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
[[ "${DEBUG}" == [Yy1]* ]] && echo [[ "$DEBUG" == [Yy1]* ]] && echo
return 0 return 0

View File

@@ -11,7 +11,6 @@ set -Eeuo pipefail
: ${VM_NET_MAC:="$MAC"} : ${VM_NET_MAC:="$MAC"}
: ${VM_NET_HOST:='VirtualDSM'} : ${VM_NET_HOST:='VirtualDSM'}
: ${DNS_SERVERS:=''}
: ${DNSMASQ_OPTS:=''} : ${DNSMASQ_OPTS:=''}
: ${DNSMASQ:='/usr/sbin/dnsmasq'} : ${DNSMASQ:='/usr/sbin/dnsmasq'}
: ${DNSMASQ_CONF_DIR:='/etc/dnsmasq.d'} : ${DNSMASQ_CONF_DIR:='/etc/dnsmasq.d'}
@@ -24,37 +23,38 @@ configureDHCP() {
# Create a macvtap network for the VM guest # Create a macvtap network for the VM guest
{ ip link add link "${VM_NET_DEV}" name "${VM_NET_TAP}" address "${VM_NET_MAC}" type macvtap mode bridge ; rc=$?; } || : { ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
error "Cannot create macvtap interface. Please make sure the network type is 'macvlan' and not 'ipvlan'," error "Cannot create macvtap interface. Please make sure the network type is 'macvlan' and not 'ipvlan',"
error "and that the NET_ADMIN capability has been added to the container config: --cap-add NET_ADMIN" && exit 16 error "and that the NET_ADMIN capability has been added to the container config: --cap-add NET_ADMIN" && exit 16
fi fi
while ! ip link set "${VM_NET_TAP}" up; do while ! ip link set "$VM_NET_TAP" up; do
info "Waiting for address to become available..." info "Waiting for address to become available..."
sleep 2 sleep 2
done done
TAP_NR=$(</sys/class/net/"${VM_NET_TAP}"/ifindex) local TAP_NR TAP_PATH MAJOR MINOR
TAP_NR=$(</sys/class/net/"$VM_NET_TAP"/ifindex)
TAP_PATH="/dev/tap${TAP_NR}" TAP_PATH="/dev/tap${TAP_NR}"
# Create dev file (there is no udev in container: need to be done manually) # Create dev file (there is no udev in container: need to be done manually)
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"${VM_NET_TAP}"/tap*/dev) IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev)
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/${VM_NET_TAP}" && exit 18 (( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && exit 18
[[ ! -e "${TAP_PATH}" ]] && [[ -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "${TAP_PATH}" [[ ! -e "$TAP_PATH" ]] && [[ -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
if [[ ! -e "${TAP_PATH}" ]]; then if [[ ! -e "$TAP_PATH" ]]; then
{ mknod "${TAP_PATH}" c "$MAJOR" "$MINOR" ; rc=$?; } || : { mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
(( rc != 0 )) && error "Cannot mknod: ${TAP_PATH} ($rc)" && exit 20 (( rc != 0 )) && error "Cannot mknod: $TAP_PATH ($rc)" && exit 20
fi fi
{ exec 30>>"$TAP_PATH"; rc=$?; } 2>/dev/null || : { exec 30>>"$TAP_PATH"; rc=$?; } 2>/dev/null || :
if (( rc != 0 )); then if (( rc != 0 )); then
error "Cannot create TAP interface ($rc). Please add the following docker settings to your " error "Cannot create TAP interface ($rc). Please add the following docker settings to your "
error "container: --device-cgroup-rule='c ${MAJOR}:* rwm' --device=/dev/vhost-net" && exit 21 error "container: --device-cgroup-rule='c $MAJOR:* rwm' --device=/dev/vhost-net" && exit 21
fi fi
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || : { exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
@@ -75,42 +75,17 @@ configureDNS () {
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0" DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0"
# Create lease file for faster resolve # Create lease file for faster resolve
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:${VM_NET_MAC}" > /var/lib/misc/dnsmasq.leases echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
chmod 644 /var/lib/misc/dnsmasq.leases chmod 644 /var/lib/misc/dnsmasq.leases
# Build DNS options from container /etc/resolv.conf # Set DNS server and gateway
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1 --dhcp-option=option:router,${VM_NET_IP%.*}.1"
if [[ "${DEBUG}" == [Yy1]* ]]; then
echo "/etc/resolv.conf:" && echo && cat /etc/resolv.conf && echo
fi
mapfile -t nameservers < <( { grep '^nameserver' /etc/resolv.conf || true; } | sed 's/\t/ /g' | sed 's/nameserver //' | sed 's/ //g')
searchdomains=$( { grep '^search' /etc/resolv.conf || true; } | sed 's/\t/ /g' | sed 's/search //' | sed 's/#.*//' | sed 's/\s*$//g' | sed 's/ /,/g')
domainname=$(echo "$searchdomains" | awk -F"," '{print $1}')
for nameserver in "${nameservers[@]}"; do
nameserver=$(echo "$nameserver" | sed 's/#.*//' )
if ! [[ "$nameserver" =~ .*:.* ]]; then
[[ -z "$DNS_SERVERS" ]] && DNS_SERVERS="$nameserver" || DNS_SERVERS="$DNS_SERVERS,$nameserver"
fi
done
[[ -z "$DNS_SERVERS" ]] && DNS_SERVERS="1.1.1.1"
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:dns-server,$DNS_SERVERS --dhcp-option=option:router,${VM_NET_IP%.*}.1"
if [ -n "$searchdomains" ] && [ "$searchdomains" != "." ]; then
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:domain-search,$searchdomains --dhcp-option=option:domain-name,$domainname"
else
[[ -z $(hostname -d) ]] || DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:domain-name,$(hostname -d)"
fi
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "${DEBUG}" == [Yy1]* ]] && set -x [[ "$DEBUG" == [Yy1]* ]] && set -x
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
[[ "${DEBUG}" == [Yy1]* ]] && echo [[ "$DEBUG" == [Yy1]* ]] && echo
return 0 return 0
} }
@@ -120,7 +95,7 @@ configureNAT () {
# Create a bridge with a static IP for the VM guest # Create a bridge with a static IP for the VM guest
VM_NET_IP='20.20.20.21' VM_NET_IP='20.20.20.21'
[[ "${DEBUG}" == [Yy1]* ]] && set -x [[ "$DEBUG" == [Yy1]* ]] && set -x
{ ip link add dev dockerbridge type bridge ; rc=$?; } || : { ip link add dev dockerbridge type bridge ; rc=$?; } || :
@@ -137,21 +112,19 @@ configureNAT () {
done done
# QEMU Works with taps, set tap to the bridge created # QEMU Works with taps, set tap to the bridge created
ip tuntap add dev "${VM_NET_TAP}" mode tap ip tuntap add dev "$VM_NET_TAP" mode tap
while ! ip link set "${VM_NET_TAP}" up promisc on; do while ! ip link set "$VM_NET_TAP" up promisc on; do
info "Waiting for tap to become available..." info "Waiting for tap to become available..."
sleep 2 sleep 2
done done
ip link set dev "${VM_NET_TAP}" master dockerbridge ip link set dev "$VM_NET_TAP" master dockerbridge
# Add internet connection to the VM # Add internet connection to the VM
IP=$(ip address show dev "${VM_NET_DEV}" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/) iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp -j DNAT --to "$VM_NET_IP"
iptables -t nat -A POSTROUTING -o "${VM_NET_DEV}" -j MASQUERADE iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"
iptables -t nat -A PREROUTING -i "${VM_NET_DEV}" -d "${IP}" -p tcp -j DNAT --to $VM_NET_IP
iptables -t nat -A PREROUTING -i "${VM_NET_DEV}" -d "${IP}" -p udp -j DNAT --to $VM_NET_IP
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"
@@ -159,7 +132,7 @@ configureNAT () {
fi fi
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
[[ "${DEBUG}" == [Yy1]* ]] && echo [[ "$DEBUG" == [Yy1]* ]] && echo
# Check port forwarding flag # Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
@@ -169,7 +142,7 @@ configureNAT () {
fi fi
fi fi
NET_OPTS="-netdev tap,ifname=${VM_NET_TAP},script=no,downscript=no,id=hostnet0" NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0"
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || : { exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
(( rc == 0 )) && NET_OPTS="$NET_OPTS,vhost=on,vhostfd=40" (( rc == 0 )) && NET_OPTS="$NET_OPTS,vhost=on,vhostfd=40"
@@ -181,15 +154,19 @@ configureNAT () {
closeNetwork () { closeNetwork () {
if [[ "${DHCP}" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
ip link set "${VM_NET_TAP}" down || true { pkill -f server.sh || true; } 2>/dev/null
ip link delete "${VM_NET_TAP}" || true
ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true
else else
ip link set "${VM_NET_TAP}" down promisc off || true { pkill -f dnsmasq || true; } 2>/dev/null
ip link delete "${VM_NET_TAP}" || true
ip link set "$VM_NET_TAP" down promisc off || true
ip link delete "$VM_NET_TAP" || true
ip link set dockerbridge down || true ip link set dockerbridge down || true
ip link delete dockerbridge || true ip link delete dockerbridge || true
@@ -210,7 +187,7 @@ if [ ! -c /dev/net/tun ]; then
chmod 666 /dev/net/tun chmod 666 /dev/net/tun
fi fi
[ ! -c /dev/net/tun ] && error "TUN network interface not available..." && exit 85 [ ! -c /dev/net/tun ] && error "TUN network interface not available..." && exit 25
# 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
@@ -223,21 +200,19 @@ update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
VM_NET_MAC="${VM_NET_MAC//-/:}" VM_NET_MAC="${VM_NET_MAC//-/:}"
GATEWAY=$(ip r | grep default | awk '{print $3}') GATEWAY=$(ip r | grep default | awk '{print $3}')
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
if [[ "${DEBUG}" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY" && echo
IP=$(ip address show dev "${VM_NET_DEV}" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
info "Container IP is ${IP} with gateway ${GATEWAY}" && echo
fi fi
if [[ "${DHCP}" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
if [[ "$GATEWAY" == "172."* ]]; then if [[ "$GATEWAY" == "172."* ]]; then
if [[ "${DEBUG}" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
info "Warning: Are you sure the container is on a macvlan network?" info "Warning: Are you sure the container is on a macvlan network?"
else else
error "You can only enable DHCP while the container is on a macvlan network!" && exit 86 error "You can only enable DHCP while the container is on a macvlan network!" && exit 26
fi fi
fi fi
@@ -254,6 +229,6 @@ else
fi fi
NET_OPTS="${NET_OPTS} -device virtio-net-pci,romfile=,netdev=hostnet0,mac=${VM_NET_MAC},id=net0" NET_OPTS="$NET_OPTS -device virtio-net-pci,romfile=,netdev=hostnet0,mac=$VM_NET_MAC,id=net0"
return 0 return 0

View File

@@ -4,13 +4,12 @@ set -Eeuo pipefail
# Configure QEMU for graceful shutdown # Configure QEMU for graceful shutdown
QEMU_PORT=7100 QEMU_PORT=7100
QEMU_TIMEOUT=50 QEMU_TIMEOUT=55
QEMU_PID="/run/qemu.pid"
QEMU_COUNT="/run/qemu.count"
QEMU_PID=/run/qemu.pid rm -f "$QEMU_PID"
QEMU_COUNT=/run/qemu.count rm -f "$QEMU_COUNT"
rm -f "${QEMU_PID}"
rm -f "${QEMU_COUNT}"
_trap(){ _trap(){
func="$1" ; shift func="$1" ; shift
@@ -22,53 +21,68 @@ _trap(){
_graceful_shutdown() { _graceful_shutdown() {
set +e set +e
local cnt response
[ ! -f "${QEMU_PID}" ] && exit 130 [ ! -f "$QEMU_PID" ] && exit 130
[ -f "${QEMU_COUNT}" ] && return [ -f "$QEMU_COUNT" ] && return
echo && info "Received $1 signal, shutting down..." echo 0 > "$QEMU_COUNT"
echo 0 > "${QEMU_COUNT}" echo && info "Received $1 signal, sending shutdown command..."
# Don't send the powerdown signal because vDSM ignores ACPI signals # Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null # echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
# Send shutdown command to guest agent via serial port # Send shutdown command to guest agent via serial port
RESPONSE=$(curl -s -m 5 -S http://127.0.0.1:2210/read?command=6 2>&1) url="http://127.0.0.1:2210/read?command=6&timeout=50"
response=$(curl -sk -m 52 -S "$url" 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then if [[ "$response" =~ "\"success\"" ]]; then
echo && error "Could not send shutdown command to the guest ($RESPONSE)" echo && info "Virtual DSM is now ready to shutdown..."
kill -15 "$(cat "${QEMU_PID}")" else
response="${response#*message\"\: \"}"
echo && error "Failed to send shutdown command: ${response%%\"*}"
kill -15 "$(cat "$QEMU_PID")"
pkill -f qemu-system-x86_64 || true pkill -f qemu-system-x86_64 || true
fi fi
while [ "$(cat ${QEMU_COUNT})" -lt "${QEMU_TIMEOUT}" ]; do while [ "$(cat $QEMU_COUNT)" -lt "$QEMU_TIMEOUT" ]; do
# Increase the counter # Increase the counter
echo $(($(cat ${QEMU_COUNT})+1)) > ${QEMU_COUNT} echo $(($(cat $QEMU_COUNT)+1)) > "$QEMU_COUNT"
# Try to connect to qemu # Try to connect to qemu
if echo 'info version'| nc -q 1 -w 1 localhost "${QEMU_PORT}" >/dev/null 2>&1 ; then if echo 'info version'| nc -q 1 -w 1 localhost "$QEMU_PORT" >/dev/null 2>&1 ; then
sleep 1 sleep 1
CNT="$(cat ${QEMU_COUNT})/${QEMU_TIMEOUT}" cnt="$(cat $QEMU_COUNT)/$QEMU_TIMEOUT"
[[ "${DEBUG}" == [Yy1]* ]] && info "Shutting down, waiting... (${CNT})" [[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt)"
fi fi
done done
if [ "$(cat $QEMU_COUNT)" -ge "$QEMU_TIMEOUT" ]; then
echo && error "Shutdown timeout reached, forcefully quitting.."
fi
echo && echo " Quitting..." echo && echo " Quitting..."
echo 'quit' | nc -q 1 -w 1 localhost "${QEMU_PORT}" >/dev/null 2>&1 || true echo 'quit' | nc -q 1 -w 1 localhost "$QEMU_PORT" >/dev/null 2>&1 || true
{ pkill -f print.sh || true; } 2>/dev/null
{ pkill -f host.bin || true; } 2>/dev/null
closeNetwork closeNetwork
sleep 1
return return
} }
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT _trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
MON_OPTS="-monitor telnet:localhost:${QEMU_PORT},server,nowait,nodelay" MON_OPTS="-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"

View File

@@ -1,66 +1,79 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
info () { echo -e >&2 "\E[1;34m\E[1;36m $1\E[0m" ; } info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n" >&2; }
error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; } error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
file="/run/dsm.url" file="/run/dsm.url"
shutdown="/run/qemu.count"
url="http://127.0.0.1:2210/read?command=10"
resp_err="Guest returned an invalid response:"
jq_err="Failed to parse response from guest: jq error"
while [ ! -f "$file" ] while [ ! -f "$file" ]
do do
# Check if not shutting down
[ -f "$shutdown" ] && exit 1
sleep 3 sleep 3
[ -f "$file" ] && continue
[ -f "$shutdown" ] && exit 1
[ -f "$file" ] && break
# Retrieve IP from guest VM # Retrieve IP from guest VM
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
set +e [ -f "$shutdown" ] && exit 1
RESPONSE=$(curl -s -m 16 -S http://127.0.0.1:2210/read?command=10 2>&1) (( rc != 0 )) && error "Failed to connect to guest: curl error $rc" && continue
set -e
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then { result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
error "Failed to connect to guest: $RESPONSE" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$result" == "null" ]] && error "$resp_err $json" && continue
if [[ "$result" != "success" ]] ; then
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
error "Guest replied $result: $msg" && continue
fi fi
# Retrieve the HTTP port number { port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || :
if [[ ! "${RESPONSE}" =~ "\"http_port\"" ]] ; then (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
error "Failed to parse response from guest: $RESPONSE" && continue [[ "$port" == "null" ]] && error "$resp_err $json" && continue
fi [ -z "$port" ] && continue
rest=${RESPONSE#*http_port} { ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || :
rest=${rest#*:} (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
rest=${rest%%,*} [[ "$ip" == "null" ]] && error "$resp_err $json" && continue
PORT=${rest%%\"*} [ -z "$ip" ] && continue
[ -z "${PORT}" ] && continue echo "$ip:$port" > $file
# Retrieve the IP address
if [[ ! "${RESPONSE}" =~ "eth0" ]] ; then
error "Failed to parse response from guest: $RESPONSE" && continue
fi
rest=${RESPONSE#*eth0}
rest=${rest#*ip}
rest=${rest#*:}
rest=${rest#*\"}
IP=${rest%%\"*}
[ -z "${IP}" ] && continue
echo "${IP}:${PORT}" > $file
done done
LOCATION=$(cat "$file") [ -f "$shutdown" ] && exit 1
location=$(cat "$file")
if [[ "$location" != "20.20"* ]]; then
msg="http://$location"
if [[ "$LOCATION" == "20.20"* ]]; then
MSG="port ${LOCATION##*:}"
else else
MSG="http://${LOCATION}"
ip=$(ip address show dev eth0 | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
port="${location##*:}"
if [[ "$ip" == "172."* ]]; then
msg="port $port"
else
msg="http://$ip:$port"
fi
fi fi
echo "" >&2 echo "" >&2
info "--------------------------------------------------------" info "-----------------------------------------------------------"
info " You can now login to DSM at ${MSG}" info " You can now login to DSM at $msg"
info "--------------------------------------------------------" info "-----------------------------------------------------------"
echo "" >&2 echo "" >&2

View File

@@ -1,22 +1,26 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
info () { echo -e "\E[1;34m \E[1;36m$1\E[0m" ; } info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n"; }
error () { echo -e >&2 "\E[1;31m ERROR: $1\E[0m" ; } error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
trap 'error "Status $? while: ${BASH_COMMAND} (line $LINENO/$BASH_LINENO)"' ERR
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11 [ ! -f "/run/entry.sh" ] && error "Script must run inside Docker 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
# Docker environment variables # Docker environment variables
: ${GPU:='N'} # Enable GPU passthrough : ${GPU:='N'} # Disable GPU passthrough
: ${DEBUG:='N'} # Enable debugging mode : ${KVM:='Y'} # Enable KVM acceleration
: ${DEBUG:='N'} # Disable debugging mode
: ${COUNTRY:=''} # Country code for mirror
: ${CONSOLE:='N'} # Disable console mode
: ${ALLOCATE:='Y'} # Preallocate diskspace : ${ALLOCATE:='Y'} # Preallocate diskspace
: ${ARGUMENTS:=''} # Extra QEMU parameters : ${ARGUMENTS:=''} # Extra QEMU parameters
: ${CPU_CORES:='1'} # Amount of CPU cores : ${CPU_CORES:='1'} # Amount of CPU cores
: ${RAM_SIZE:='1G'} # Maximum RAM amount
: ${DISK_SIZE:='16G'} # Initial data disk size : ${DISK_SIZE:='16G'} # Initial data disk size
: ${RAM_SIZE:='512M'} # Maximum RAM amount
# Helper variables # Helper variables
@@ -28,7 +32,7 @@ VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
# Check folder # Check folder
STORAGE="/storage" STORAGE="/storage"
[ ! -d "$STORAGE" ] && error "Storage folder (${STORAGE}) not found!" && exit 13 [ ! -d "$STORAGE" ] && error "Storage folder ($STORAGE) not found!" && exit 13
# Cleanup files # Cleanup files
@@ -39,6 +43,65 @@ rm -f /run/qemu.count
# Cleanup dirs # Cleanup dirs
rm -rf /tmp/dsm rm -rf /tmp/dsm
rm -f /tmp/server.*
rm -rf "$STORAGE/tmp" rm -rf "$STORAGE/tmp"
# Helper functions
getCountry () {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry () {
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0
}
addPackage () {
local pkg=$1
local desc=$2
if apt-mark showinstall | grep -qx "$pkg"; then
return 0
fi
info "Installing $desc..."
export DEBCONF_NOWARNINGS="yes"
export DEBIAN_FRONTEND="noninteractive"
[ -z "$COUNTRY" ] && setCountry
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
apt-get -qq update
apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
return 0 return 0

View File

@@ -1,39 +1,24 @@
#!/bin/bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: ${HOST_CPU:=''}
: ${HOST_MAC:=''} : ${HOST_MAC:=''}
: ${HOST_DEBUG:=''} : ${HOST_DEBUG:=''}
: ${HOST_SERIAL:=''} : ${HOST_SERIAL:=''}
: ${HOST_MODEL:=''} : ${HOST_MODEL:=''}
: ${GUEST_SERIAL:=''} : ${GUEST_SERIAL:=''}
if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
if [ -n "$HOST_CPU" ]; then
HOST_CPU="$HOST_CPU,,"
else
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="QEMU, Virtual CPU, X86_64"
else
HOST_CPU="QEMU, Virtual CPU, $ARCH"
fi
fi
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")
[ -n "$HOST_MAC" ] && HOST_ARGS+=("-mac=${HOST_MAC}") [ -n "$HOST_MAC" ] && HOST_ARGS+=("-mac=$HOST_MAC")
[ -n "$HOST_MODEL" ] && HOST_ARGS+=("-model=${HOST_MODEL}") [ -n "$HOST_MODEL" ] && HOST_ARGS+=("-model=$HOST_MODEL")
[ -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 [[ "$HOST_DEBUG" == [Yy1]* ]]; then
set -x set -x
./host.bin "${HOST_ARGS[@]}" & ./host.bin "${HOST_ARGS[@]}" &
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
@@ -45,18 +30,18 @@ fi
cnt=0 cnt=0
sleep 0.2 sleep 0.2
while ! nc -z -w1 127.0.0.1 2210 > /dev/null 2>&1; do while ! nc -z -w2 127.0.0.1 2210 > /dev/null 2>&1; do
sleep 0.1 sleep 0.1
cnt=$((cnt + 1)) cnt=$((cnt + 1))
(( cnt > 20 )) && error "Failed to connect to qemu-host.." && exit 58 (( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
done done
cnt=0 cnt=0
while ! nc -z -w1 127.0.0.1 12345 > /dev/null 2>&1; do while ! nc -z -w2 127.0.0.1 12345 > /dev/null 2>&1; do
sleep 0.1 sleep 0.1
cnt=$((cnt + 1)) cnt=$((cnt + 1))
(( cnt > 20 )) && error "Failed to connect to qemu-host.." && exit 59 (( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
done done
# Configure serial ports # Configure serial ports

View File

@@ -14,9 +14,9 @@ trap 'stop' EXIT SIGINT SIGTERM SIGHUP
html() html()
{ {
local h="<!DOCTYPE html><HTML><HEAD><TITLE>VirtualDSM</TITLE>" local h="<!DOCTYPE html><HTML><HEAD><TITLE>VirtualDSM</TITLE>"
h="${h} <STYLE>body { color: white; background-color: #125bdb; font-family: Verdana," h="$h<STYLE>body { color: white; background-color: #125bdb; font-family: Verdana,"
h="${h} Arial,sans-serif; } a, a:hover, a:active, a:visited { color: white; }</STYLE></HEAD>" h="$h Arial,sans-serif; } a, a:hover, a:active, a:visited { color: white; }</STYLE></HEAD>"
h="${h}<BODY><BR><BR><H1><CENTER>$1</CENTER></H1></BODY></HTML>" h="$h<BODY><BR><BR><H1><CENTER>$1</CENTER></H1></BODY></HTML>"
echo "$h" echo "$h"
} }
@@ -33,8 +33,8 @@ if [[ "$2" != "/"* ]]; then
HTML=$(html "$BODY") HTML=$(html "$BODY")
printf '%b' "HTTP/1.1 200 OK\nContent-Length: ${#HTML}\nConnection: close\n\n$HTML" > "$TMP_FILE" printf '%b' "HTTP/1.1 200 OK\nContent-Length: ${#HTML}\nConnection: close\n\n$HTML" > "$TMP_FILE"
socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"cat ${TMP_FILE}" 2> /dev/null & socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null &
socat TCP4-LISTEN:"${1:-5000}",reuseaddr,fork,crlf SYSTEM:"cat ${TMP_FILE}" 2> /dev/null & wait $! socat TCP4-LISTEN:"${1:-5000}",reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null & wait $!
exit exit
@@ -46,15 +46,15 @@ if [[ "$2" != "/run/ip.sh" ]]; then
else else
BODY="The location of DSM is <a href='http://\${LOCATION}'>http://\${LOCATION}</a><script>" BODY="The location of DSM is <a href='http://\$LOCATION'>http://\$LOCATION</a><script>"
BODY="${BODY}setTimeout(function(){ window.location.assign('http://\${LOCATION}'); }, 3000);</script>" BODY="$BODY setTimeout(function(){ window.location.assign('http://\$LOCATION'); }, 3000);</script>"
WAIT="Please wait while discovering IP...<script>setTimeout(() => { document.location.reload(); }, 4999);</script>" WAIT="Please wait while discovering IP...<script>setTimeout(() => { document.location.reload(); }, 4999);</script>"
HTML=$(html "xxx") HTML=$(html "xxx")
{ echo "#!/bin/bash" { echo "#!/bin/bash"
echo "[ -f \"/run/dsm.url\" ] && LOCATION=\$(cat \"/run/dsm.url\")" echo "[ -f \"/run/dsm.url\" ] && LOCATION=\$(cat \"/run/dsm.url\")"
echo "HTML=\"$HTML\"; [ -z \"\${LOCATION}\" ] && BODY=\"$WAIT\" || BODY=\"$BODY\"; HTML=\${HTML/xxx/\$BODY}" echo "HTML=\"$HTML\"; [ -z \"\$LOCATION\" ] && BODY=\"$WAIT\" || BODY=\"$BODY\"; HTML=\${HTML/xxx/\$BODY}"
echo "printf '%b' \"HTTP/1.1 200 OK\\nContent-Length: \${#HTML}\\nConnection: close\\n\\n\$HTML\"" echo "printf '%b' \"HTTP/1.1 200 OK\\nContent-Length: \${#HTML}\\nConnection: close\\n\\n\$HTML\""
} > "$TMP_FILE" } > "$TMP_FILE"