Compare commits

..

52 Commits
v4.31 ... v4.41

Author SHA1 Message Date
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
Kroese
f32d8cbefc feat: Read version from image
feat: Read version from image
2023-11-29 18:13:37 +01:00
Kroese
3e985502c2 docs: Networking 2023-11-29 18:11:19 +01:00
Kroese
ad3132e8c2 docs: Networking 2023-11-29 18:09:02 +01:00
Kroese
830ace0e47 feat: Read version from image 2023-11-29 18:07:41 +01:00
Kroese
1c36893729 build: Set version 2023-11-29 18:05:47 +01:00
Kroese
a87aaab6f7 build: Docker metadata 2023-11-29 18:03:34 +01:00
Kroese
21699b8960 build: Bash
build: Bash
2023-11-29 08:52:15 +01:00
Kroese
87ee25d404 build: Bash 2023-11-29 08:51:52 +01:00
Kroese
c1714f9e6b build: External labels
* build: External labels
2023-11-29 08:26:41 +01:00
Kroese
754765b766 docs: Readme
docs: Readme
2023-11-28 11:51:06 +01:00
Kroese
419f0cf571 docs: Readme 2023-11-28 11:50:47 +01:00
Kroese
55d9ac521f build: Push to mirror
build: Push to mirror
2023-11-26 04:06:16 +01:00
Kroese
3406b3b471 build: Push to mirror 2023-11-26 04:05:58 +01:00
Kroese
f067ad2458 build: Concurrency
build: Concurrency
2023-11-24 04:09:16 +01:00
Kroese
7eafd0a969 build: Concurrency 2023-11-24 04:08:53 +01:00
Kroese
116f30bc0a fix: Cleanup dirs
fix: Cleanup dirs
2023-11-24 01:41:19 +01:00
Kroese
04aa20e836 fix: Cleanup dirs 2023-11-24 01:37:39 +01:00
Kroese
3bf4cc861b docs: Readme
docs: Readme
2023-11-23 21:21:14 +01:00
Kroese
3c6620a3f9 docs: Readme 2023-11-23 21:20:29 +01:00
Kroese
ab0ea5a1d8 docs: Readme
docs: Readme
2023-11-22 19:17:24 +01:00
Kroese
f894ad2686 docs: Readme 2023-11-22 19:16:08 +01:00
Kroese
570340d4e5 fix: Healthcheck start period
fix: Healthcheck start period
2023-11-20 11:46:38 +01:00
Kroese
7dbe706282 fix: Healthcheck start period 2023-11-20 11:45:28 +01:00
Kroese
0c9559f695 fix: Variables
fix: Variables
2023-11-19 20:16:10 +01:00
Kroese
3113e2b64e fix: Variables 2023-11-19 19:56:11 +01:00
Kroese
f0ce992a27 feat: Control device nodes #382
feat: Control device nodes #382
2023-11-17 15:16:36 +01:00
databreach
6334cfc8bc Control device nodes
Add control over whether device nodes are created.
2023-11-17 14:39:53 +01:00
Kroese
451a569617 fix: Remove unused vars
fix: Remove unused vars
2023-11-16 23:28:53 +01:00
Kroese
44d82d6544 fix: Remove unused vars 2023-11-16 23:28:12 +01:00
Kroese
618ec66401 fix: Remove size check
fix: Remove size check
2023-11-16 23:02:31 +01:00
Kroese
d24ae86c12 fix: Remove size check 2023-11-16 23:00:21 +01:00
Kroese
32db74e50d style: Tabs
style: Tabs
2023-11-16 21:59:03 +01:00
Kroese
503c89f08c style: Tabs 2023-11-16 21:57:43 +01:00
14 changed files with 333 additions and 321 deletions

View File

@@ -13,6 +13,10 @@ on:
- '.github/**' - '.github/**'
- '.github/workflows/**' - '.github/workflows/**'
concurrency:
group: build
cancel-in-progress: false
jobs: jobs:
shellcheck: shellcheck:
name: Check name: Check
@@ -22,37 +26,34 @@ jobs:
needs: shellcheck needs: shellcheck
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions: permissions:
actions: write
packages: write packages: write
contents: read contents: read
steps: steps:
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- -
name: Checkout name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- -
name: Prepare Docker build name: Docker metadata
id: prepare id: meta
run: | uses: docker/metadata-action@v5
with:
PLATFORMS="linux/amd64,linux/arm64" context: git
VERSION="${{ vars.MAJOR }}.${{ vars.MINOR }}" images: |
${{ secrets.DOCKERHUB_REPO }}
TAGS=() ghcr.io/${{ github.repository }}
TAGS=("${{ github.repository }}:latest") tags: |
TAGS+=("${{ github.repository }}:${VERSION}") type=raw,value=latest,priority=100
#TAGS+=("${{ secrets.DOCKERHUB_MIRROR }}:latest") type=raw,value=${{ vars.MAJOR }}.${{ vars.MINOR }}
#TAGS+=("${{ secrets.DOCKERHUB_MIRROR }}:${VERSION}") labels: |
TAGS+=("ghcr.io/${{ github.repository }}:latest") org.opencontainers.image.title=${{ vars.NAME }}
TAGS+=("ghcr.io/${{ github.repository }}:${VERSION}") env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index
echo "tags=${TAGS[@]}" >> $GITHUB_OUTPUT -
echo "version=${VERSION}" >> $GITHUB_OUTPUT name: Set up Docker Buildx
echo "docker_platforms=${PLATFORMS}" >> $GITHUB_OUTPUT uses: docker/setup-buildx-action@v3
echo "build_date=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
- -
name: Login into Docker Hub name: Login into Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
@@ -68,34 +69,26 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- -
name: Build Docker image name: Build Docker image
run: | uses: docker/build-push-action@v5
with:
TAGS=(${{ steps.prepare.outputs.tags }}) context: .
push: true
echo "Build date: ${{ steps.prepare.outputs.build_date }}" provenance: false
echo "Docker platform: ${{ steps.prepare.outputs.docker_platforms }}" platforms: linux/amd64,linux/arm64,linux/arm
echo "Tags: ${{ steps.prepare.outputs.tags }}" tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
docker buildx build --platform ${{ steps.prepare.outputs.docker_platforms }} \ annotations: ${{ steps.meta.outputs.annotations }}
--output "type=image,push=true" \ build-args: |
--progress=plain \ VCS_REF=${GITHUB_SHA::8}
--build-arg "BUILD_ARG=${GITHUB_RUN_ID}" \ VERSION_ARG=${{ steps.meta.outputs.version }}
--build-arg "VERSION_ARG=${{ steps.prepare.outputs.version }}" \
--build-arg "DATE_ARG=${{ steps.prepare.outputs.build_date }}" \
--build-arg "VCS_REF=${GITHUB_SHA::8}" \
$(printf "%s" "${TAGS[@]/#/ --tag }" ) .
-
name: Clear Docker credentials
run: |
rm -f ${HOME}/.docker/config.json
- -
name: Create a release name: Create a release
uses: action-pack/github-release@v2 uses: action-pack/github-release@v2
env: env:
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with: with:
tag: "v${{ steps.prepare.outputs.version }}" tag: "v${{ steps.meta.outputs.version }}"
title: "v${{ steps.prepare.outputs.version }}" title: "v${{ steps.meta.outputs.version }}"
- -
name: Increment version variable name: Increment version variable
uses: action-pack/bump@v2 uses: action-pack/bump@v2

View File

@@ -14,6 +14,7 @@ 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 \ apt-get --no-install-recommends -y install \
jq \
tini \ tini \
curl \ curl \
cpio \ cpio \
@@ -33,7 +34,7 @@ RUN apt-get update && apt-get -y upgrade && \
&& apt-get clean \ && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY src/*.sh /run/ COPY ./src /run/
COPY --from=builder /qemu-host.bin /run/host.bin COPY --from=builder /qemu-host.bin /run/host.bin
RUN chmod +x /run/*.sh && chmod +x /run/*.bin RUN chmod +x /run/*.sh && chmod +x /run/*.bin
@@ -45,24 +46,13 @@ 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 DATE_ARG=""
ARG BUILD_ARG=0
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
ENV VERSION=$VERSION_ARG RUN echo "$VERSION_ARG" > /run/version
LABEL org.opencontainers.image.licenses="MIT" HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh
LABEL org.opencontainers.image.title="Virtual DSM"
LABEL org.opencontainers.image.created=${DATE_ARG}
LABEL org.opencontainers.image.revision=${BUILD_ARG}
LABEL org.opencontainers.image.version=${VERSION_ARG}
LABEL org.opencontainers.image.source="https://github.com/vdsm/virtual-dsm/"
LABEL org.opencontainers.image.url="https://hub.docker.com/r/vdsm/virtual-dsm/"
LABEL org.opencontainers.image.description="Virtual DSM in a docker container"
HEALTHCHECK --interval=60s --retries=2 CMD /run/check.sh
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"] ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]

View File

@@ -4,11 +4,12 @@ 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/vhost-net - /dev/vhost-net
device_cgroup_rules: device_cgroup_rules:
- 'c *:* rwm' - 'c *:* rwm'

View File

@@ -10,11 +10,12 @@
[![Pulls]][hub_url] [![Pulls]][hub_url]
</div></h1> </div></h1>
Virtual DSM in a docker container. Virtual DSM in a docker container.
## Features ## Features
- Multi-platform - Multiple disks
- KVM acceleration - KVM acceleration
- GPU passthrough - GPU passthrough
- Upgrades supported - Upgrades supported
@@ -33,7 +34,6 @@ services:
DISK_SIZE: "16G" DISK_SIZE: "16G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/vhost-net
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
ports: ports:
@@ -58,7 +58,7 @@ 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.
@@ -74,6 +74,19 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
Replace the example path `/home/user/data` with the desired storage folder. Replace the example path `/home/user/data` with the desired storage folder.
* ### How do I add multiple disks?
To create additional disks, modify your compose file like this:
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- /home/example:/storage2
- /mnt/data/example:/storage3
```
* ### How do I change the space reserved by the virtual disk? * ### How do I change the space reserved by the virtual 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 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:
@@ -85,40 +98,14 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
Keep in mind that this will not affect any of your existing disks, it only applies to newly created disks. Keep in mind that this will not affect any of your existing disks, it only applies to newly created disks.
* ### How do I add multiple disks?
To mount extra volumes, modify your compose file like this:
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- /home/example2:/storage2
- /home/example3:/storage3
```
Additionally, it's also possible to passthrough raw disk devices like this:
```yaml
environment:
DEVICE2: "/dev/vdc1"
DEVICE3: "/dev/vdc2"
devices:
- /dev/vdc1
- /dev/vdc2
```
Please beware that any pre-existing partitions and data on those devices will be wiped.
* ### 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?
@@ -185,18 +172,7 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
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 need DHCP, it's still recommended to enable this feature as it prevents NAT issues and increases performance by using a `macvtap` interface.
* ### How do I install a specific version of vDSM? * ### How do I passthrough the GPU?
By default, version 7.2.1 will be installed, but if you prefer an older version, you can add its download URL to your compose file as follows:
```yaml
environment:
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
```
With this method, it is even possible to switch between different versions while keeping all your file data intact.
* ### How do I passthrough my 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:
@@ -209,6 +185,17 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
This can be used to enable the facial recognition function in Synology Photos for example. This can be used to enable the facial recognition function in Synology Photos for example.
* ### How do I install a specific version of vDSM?
By default, version 7.2.1 will be installed, but if you prefer an older version, you can add its download URL to your compose file as follows:
```yaml
environment:
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
```
With this method, it is even possible to switch between different versions while keeping all your file data intact.
* ### What are the differences compared to the standard DSM? * ### What are the differences compared to the standard DSM?
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses. There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.

View File

@@ -9,53 +9,40 @@ file="/run/dsm.url"
if [ ! -f "$file" ]; then if [ ! -f "$file" ]; then
# Retrieve IP from guest VM for Docker healthcheck # 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 { json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || :
echo "Failed to connect to guest: $RESPONSE" && exit 1 (( rc != 0 )) && echo "Failed to connect to guest: curl error $rc" && exit 1
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
[[ "$result" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1
if [[ "$result" != "success" ]] ; then
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
echo "Guest replied ${result}: $msg" && exit 1
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 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
echo "Failed to parse response from guest: $RESPONSE" && exit 1 [[ "$port" == "null" ]] && echo "Guest has not set a portnumber yet.." && exit 1
fi [ -z "${port}" ] && echo "Guest has not set a portnumber yet.." && exit 1
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 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
rest=${rest%%,*} [[ "$ip" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1
PORT=${rest%%\"*} [ -z "${ip}" ] && echo "Guest has not received an IP yet.." && exit 1
[ -z "${PORT}" ] && echo "Guest has not set a portnumber yet.." && exit 1 echo "${ip}:${port}" > $file
# 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
fi fi
LOCATION=$(cat "$file") location=$(cat "$file")
if ! curl -m 20 -ILfSs "http://${LOCATION}/" > /dev/null; then if ! curl -m 20 -ILfSs "http://${location}/" > /dev/null; then
rm -f $file rm -f $file
echo "Failed to reach http://${LOCATION}" echo "Failed to reach http://${location}"
exit 1 exit 1
fi fi
if [[ "$LOCATION" == "20.20"* ]]; then echo "Healthcheck OK"
echo "Healthcheck OK"
else
echo "Healthcheck OK ( ${LOCATION%:*} )"
fi
exit 0 exit 0

View File

@@ -24,12 +24,11 @@ DISK_OPTS="\
addDisk () { addDisk () {
local FS
local GB local GB
local DIR local DIR
local REQ local REQ
local SIZE
local SPACE local SPACE
local MIN_SIZE
local CUR_SIZE local CUR_SIZE
local DATA_SIZE local DATA_SIZE
local DISK_ID=$1 local DISK_ID=$1
@@ -42,12 +41,17 @@ addDisk () {
DIR=$(dirname "${DISK_FILE}") DIR=$(dirname "${DISK_FILE}")
[ ! -d "${DIR}" ] && return 0 [ ! -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
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G" [ -z "$DISK_SPACE" ] && DISK_SPACE="16G"
DISK_SPACE=$(echo "${DISK_SPACE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') 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}") DATA_SIZE=$(numfmt --from=iec "${DISK_SPACE}")
MIN_SIZE=6442450944 if (( DATA_SIZE < 6442450944 )); then
if (( DATA_SIZE < MIN_SIZE )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83 error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83
fi fi
@@ -73,16 +77,17 @@ addDisk () {
# Check free diskspace # Check free diskspace
SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1) SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( REQ > SPACE )); then if (( REQ > SPACE )); then
error "Not enough free space to resize ${DISK_DESC} to ${DISK_SPACE} .." error "Not enough free space to resize ${DISK_DESC} to ${DISK_SPACE} in ${DIR}, it has only ${SPACE_GB} GB available.."
error "Specify a smaller size or disable preallocation with ALLOCATE=N." && exit 84 error "Specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation with ALLOCATE=N." && exit 84
fi fi
# Resize file by allocating more space # Resize file by allocating more space
if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then
if ! truncate -s "${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 error "Could not resize ${DISK_DESC} file (${DISK_FILE}) to ${DISK_SPACE}" && exit 85
fi fi
fi fi
@@ -97,41 +102,29 @@ addDisk () {
# Create an empty file # Create an empty file
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
rm -f "${DISK_FILE}" rm -f "${DISK_FILE}"
error "Could not create a file for ${DISK_DESC} (${DISK_FILE})" && exit 87 error "Could not create a ${DISK_SPACE} file for ${DISK_DESC} (${DISK_FILE})" && exit 87
fi fi
else else
# Check free diskspace # Check free diskspace
SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1) SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( DATA_SIZE > SPACE )); then if (( DATA_SIZE > SPACE )); then
error "Not enough free space to create ${DISK_DESC} of ${DISK_SPACE} .." error "Not enough free space to create ${DISK_DESC} of ${DISK_SPACE} in ${DIR}, it has only ${SPACE_GB} GB available.."
error "Specify a smaller size or disable preallocation with ALLOCATE=N." && exit 86 error "Specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation with ALLOCATE=N." && exit 86
fi fi
# Create an empty file # Create an empty file
if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
rm -f "${DISK_FILE}" rm -f "${DISK_FILE}"
error "Could not create a file for ${DISK_DESC} (${DISK_FILE}) of ${DISK_SPACE} .." && exit 87 error "Could not create a ${DISK_SPACE} file for ${DISK_DESC} (${DISK_FILE})" && exit 87
fi fi
fi fi
fi fi
# Check if file exists
if [ ! -f "${DISK_FILE}" ]; then
error "File for ${DISK_DESC} ($DISK_FILE) does not exist!" && exit 88
fi
fi
# Check the filesize
SIZE=$(stat -c%s "${DISK_FILE}")
if [[ SIZE -ne DATA_SIZE ]]; then
error "File for ${DISK_DESC} (${DISK_FILE}) has the wrong size: ${SIZE} bytes" && exit 89
fi fi
DISK_OPTS="${DISK_OPTS} \ DISK_OPTS="${DISK_OPTS} \

View File

@@ -1,22 +1,27 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
echo " Starting Virtual DSM for Docker v${VERSION}..." echo " Starting Virtual DSM for Docker v$(</run/version)..."
echo " For support visit https://github.com/vdsm/virtual-dsm/" echo " For support visit https://github.com/vdsm/virtual-dsm/"
cd /run cd /run
. reset.sh # Initialize system . reset.sh # Initialize system
. install.sh # Run installation . install.sh # Run installation
. disk.sh # Initialize disks . disk.sh # Initialize disks
. network.sh # Initialize network . network.sh # Initialize network
. gpu.sh # Initialize graphics . gpu.sh # Initialize graphics
. 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

View File

@@ -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

@@ -1,6 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: ${URL:=''} # URL of the PAT file to be downloaded.
: ${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
@@ -8,28 +11,33 @@ else
BASE="DSM_VirtualDSM_42962" BASE="DSM_VirtualDSM_42962"
fi fi
: ${URL:=''}
[ -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"
[[ "${URL,,}" == *"cndl.synology"* ]] && DL="$DL_CHINA"
[[ "${URL,,}" == *"global.synology"* ]] && DL="$DL_GLOBAL"
if [ -z "$DL" ]; then
[ -z "$COUNTRY" ] && setCountry
[[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL"
fi
if [ -z "$URL" ]; then if [ -z "$URL" ]; then
if [ "$ARCH" == "amd64" ]; then if [ "$ARCH" == "amd64" ]; then
URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat" URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
else else
URL="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat" URL="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
fi fi
fi fi
# Check if output is to interactive TTY # Check if output is to interactive TTY
@@ -41,32 +49,54 @@ fi
BASE=$(basename "$URL" .pat) BASE=$(basename "$URL" .pat)
rm -f "$STORAGE"/"$BASE".pat if [[ "$URL" != "file://${STORAGE}/${BASE}.pat" ]]; then
rm -f "$STORAGE"/"$BASE".pat
fi
rm -f "$STORAGE"/"$BASE".agent rm -f "$STORAGE"/"$BASE".agent
rm -f "$STORAGE"/"$BASE".boot.img rm -f "$STORAGE"/"$BASE".boot.img
rm -f "$STORAGE"/"$BASE".system.img rm -f "$STORAGE"/"$BASE".system.img
[[ "${DEBUG}" == [Yy1]* ]] && set -x
# Check filesystem
MIN_ROOT=471859200
MIN_SPACE=6442450944 MIN_SPACE=6442450944
FS=$(stat -f -c %T "$STORAGE") 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" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \ if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \
"$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then "$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then
TMP="$STORAGE/tmp" TMP="$STORAGE/tmp"
else else
TMP="/tmp/dsm" TMP="/tmp/dsm"
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1) SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
(( MIN_SPACE > SPACE )) && TMP="$STORAGE/tmp" if (( MIN_SPACE > SPACE )); then
TMP="$STORAGE/tmp"
info "Warning: the ${FS} filesystem of ${STORAGE} does not support UNIX permissions.."
fi
fi fi
rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp"
rm -rf "$TMP" && mkdir -p "$TMP" rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace # Check free diskspace
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1) SPACE=$(df --output=avail -B 1 / | tail -n 1)
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation, need at least 6 GB." && exit 95 (( MIN_ROOT > SPACE )) && error "Not enough free space in container root, need at least 450 MB available." && exit 96
[[ "${DEBUG}" == [Yy1]* ]] && set -x 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
# Download the required files from the Synology website
RDC="$STORAGE/dsm.rd" RDC="$STORAGE/dsm.rd"
@@ -107,8 +137,14 @@ 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
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || : if [[ "${DEV}" == [Nn]* ]]; then
(( rc != 0 )) && error "Failed to cpio $RDC, reason $rc" && exit 92 # 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=$?; } || :
else
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
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 \
@@ -141,9 +177,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")
@@ -159,17 +203,7 @@ if { tar tf "$PAT"; } >/dev/null 2>&1; then
else else
if [ "$ARCH" != "amd64" ]; then [ "$ARCH" != "amd64" ] && addPackage "qemu-user" "QEMU"
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..."
@@ -205,7 +239,8 @@ 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 87
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
@@ -240,7 +275,13 @@ MOUNT="$TMP/system"
rm -rf "$MOUNT" && mkdir -p "$MOUNT" rm -rf "$MOUNT" && mkdir -p "$MOUNT"
mv "$HDA.tgz" "$HDA.txz" mv "$HDA.tgz" "$HDA.txz"
tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/"
if [[ "${DEV}" == [Nn]* ]]; then
# Exclude dev/ from tar extract
tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/"
else
tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/"
fi
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/" [ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"* rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
@@ -260,13 +301,12 @@ 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

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'}
@@ -78,33 +77,8 @@ configureDNS () {
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
@@ -147,8 +121,6 @@ configureNAT () {
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 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 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 iptables -t nat -A PREROUTING -i "${VM_NET_DEV}" -d "${IP}" -p udp -j DNAT --to $VM_NET_IP
@@ -223,12 +195,10 @@ 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
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 info "Container IP is ${IP} with gateway ${GATEWAY}" && echo
fi fi
if [[ "${DHCP}" == [Yy1]* ]]; then if [[ "${DHCP}" == [Yy1]* ]]; then

View File

@@ -5,7 +5,6 @@ set -Eeuo pipefail
QEMU_PORT=7100 QEMU_PORT=7100
QEMU_TIMEOUT=50 QEMU_TIMEOUT=50
QEMU_PID=/run/qemu.pid QEMU_PID=/run/qemu.pid
QEMU_COUNT=/run/qemu.count QEMU_COUNT=/run/qemu.count
@@ -26,18 +25,18 @@ _graceful_shutdown() {
[ ! -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, shutting down..."
# 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) RESPONSE=$(curl -s -m 30 -S http://127.0.0.1:2210/read?command=6 2>&1)
if [[ ! "${RESPONSE}" =~ "\"success\"" ]] ; then if [[ ! "${RESPONSE}" =~ "\"success\"" ]]; then
echo && error "Could not send shutdown command to the guest ($RESPONSE)" echo && error "Failed to send shutdown command ( ${RESPONSE} )."
kill -15 "$(cat "${QEMU_PID}")" kill -15 "$(cat "${QEMU_PID}")"
pkill -f qemu-system-x86_64 || true pkill -f qemu-system-x86_64 || true

View File

@@ -1,8 +1,8 @@
#!/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"
@@ -14,53 +14,53 @@ do
# Retrieve IP from guest VM # Retrieve IP from guest VM
set +e { json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || :
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 "Failed to parse response from guest: jq error $rc ( $json )" && continue
[[ "$result" == "null" ]] && error "Guest returned invalid response: $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 "Failed to parse response from guest: jq error $rc ( $json )" && continue
error "Failed to parse response from guest: $RESPONSE" && continue [[ "$port" == "null" ]] && error "Guest returned invalid response: $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 "Failed to parse response from guest: jq error $rc ( $json )" && continue
rest=${rest%%,*} [[ "$ip" == "null" ]] && error "Guest returned invalid response: $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") 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,8 +1,9 @@
#!/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
@@ -12,11 +13,13 @@ trap 'error "Status $? while: ${BASH_COMMAND} (line $LINENO/$BASH_LINENO)"' ERR
: ${GPU:='N'} # Enable GPU passthrough : ${GPU:='N'} # Enable GPU passthrough
: ${DEBUG:='N'} # Enable debugging mode : ${DEBUG:='N'} # Enable debugging mode
: ${COUNTRY:=''} # Country code for mirror
: ${CONSOLE:='N'} # Start in 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
@@ -25,15 +28,80 @@ MINOR=$(uname -r | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture) ARCH=$(dpkg --print-architecture)
VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1) VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
# Check folder
STORAGE="/storage"
[ ! -d "$STORAGE" ] && error "Storage folder (${STORAGE}) not found!" && exit 13
# Cleanup files # Cleanup files
rm -f /run/dsm.url rm -f /run/dsm.url
rm -f /run/qemu.pid rm -f /run/qemu.pid
rm -f /run/qemu.count rm -f /run/qemu.count
# Check folder # Cleanup dirs
STORAGE="/storage" rm -rf /tmp/dsm
[ ! -d "$STORAGE" ] && error "Storage folder (${STORAGE}) not found!" && exit 13 rm -rf "$STORAGE/tmp"
# Helper functions
getCountry () {
local rc
local json
local result
local url=$1
local query=$2
{ 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

@@ -45,18 +45,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