Compare commits

..

21 Commits
v4.34 ... v4.42

Author SHA1 Message Date
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
14 changed files with 456 additions and 269 deletions

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,10 +74,10 @@ 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} VCS_REF=${GITHUB_SHA::8}
VERSION_ARG=${{ steps.meta.outputs.version }} VERSION_ARG=${{ steps.meta.outputs.version }}

View File

@@ -14,7 +14,8 @@ 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 \
tini \ jq \
tini \
curl \ curl \
cpio \ cpio \
wget \ wget \
@@ -27,6 +28,7 @@ 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 \
@@ -45,9 +47,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

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
@@ -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.
@@ -87,25 +87,25 @@ 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 disk space is reserved in advance. To create a growable disk that only allocates space that is actually used, add the following environment variable:
```yaml ```yaml
environment: environment:
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. This can also be used to convert any existing disks to the ```qcow2``` format.
* ### 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?
@@ -172,7 +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 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

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

@@ -4,6 +4,7 @@ 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_FMT:='raw'} # Disk file format, 'raw' by default for best performance
: ${DISK_CACHE:='none'} # Caching mode, can be set to 'writeback' for better performance : ${DISK_CACHE:='none'} # Caching mode, can be set to 'writeback' for better performance
: ${DISK_DISCARD:='on'} # Controls whether unmap (TRIM) commands are passed to the host. : ${DISK_DISCARD:='on'} # Controls whether unmap (TRIM) commands are passed to the host.
: ${DISK_ROTATION:='1'} # Rotation rate, set to 1 for SSD storage and increase for HDD : ${DISK_ROTATION:='1'} # Rotation rate, set to 1 for SSD storage and increase for HDD
@@ -22,41 +23,77 @@ DISK_OPTS="\
-drive file=${SYSTEM},if=none,id=drive-synosys,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -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" -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
case "${DISK_FMT,,}" in
qcow2)
echo "qcow2"
;;
raw)
echo "img"
;;
*)
error "Unrecognized disk format: ${DISK_FMT}" && exit 88
;;
esac
}
ext2fmt() {
local DISK_EXT=$1
case "${DISK_EXT,,}" in
qcow2)
echo "qcow2"
;;
img)
echo "raw"
;;
*)
error "Unrecognized file extension: .${DISK_EXT}" && exit 90
;;
esac
}
getSize() {
local DISK_FILE=$1
local DISK_EXT
local 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 88
;;
esac
}
resizeDisk() {
local GB local GB
local DIR
local REQ local REQ
local SPACE local SPACE
local CUR_SIZE local SPACE_GB
local DATA_SIZE local DISK_FILE=$1
local DISK_ID=$1 local CUR_SIZE=$2
local DISK_FILE=$2 local DATA_SIZE=$3
local DISK_DESC=$3
local DISK_SPACE=$4 local DISK_SPACE=$4
local DISK_INDEX=$5 local DISK_DESC=$5
local DISK_ADDRESS=$6 local DISK_FMT=$6
DIR=$(dirname "${DISK_FILE}") GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
[ ! -d "${DIR}" ] && return 0 info "Resizing ${DISK_DESC} from ${GB}G to ${DISK_SPACE} .."
[ -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 83
fi
if [ -f "${DISK_FILE}" ]; then
CUR_SIZE=$(stat -c%s "${DISK_FILE}")
if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then
GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
info "Resizing ${DISK_DESC} from ${GB}G to ${DISK_SPACE} .."
case "${DISK_FMT,,}" in
raw)
if [[ "${ALLOCATE}" == [Nn]* ]]; then if [[ "${ALLOCATE}" == [Nn]* ]]; then
# Resize file by changing its length # Resize file by changing its length
@@ -70,100 +107,217 @@ 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 switch to a growable disk with DISK_FMT=qcow2." && 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
fi fi
;;
qcow2)
if ! qemu-img resize -f "${DISK_FMT}" "${DISK_FILE}" "${DISK_SPACE}" ; then
error "Could not resize ${DISK_DESC} file (${DISK_FILE}) to ${DISK_SPACE}" && exit 85
fi
;;
esac
}
convertDisk() {
local CONV_FLAGS=""
local SOURCE_FILE=$1
local SOURCE_FMT=$2
local DST_FILE=$3
local DST_FMT=$4
case "${DST_FMT}" in
qcow2)
CONV_FLAGS="${CONV_FLAGS} -c"
;;
esac
# shellcheck disable=SC2086
qemu-img convert ${CONV_FLAGS} -f "${SOURCE_FMT}" -O "${DST_FMT}" -- "${SOURCE_FILE}" "${DST_FILE}"
}
createDisk() {
local GB
local SPACE
local SPACE_GB
local DISK_FILE=$1
local DISK_SPACE=$2
local DISK_DESC=$3
local DISK_FMT=$4
case "${DISK_FMT,,}" in
raw)
if [[ "${ALLOCATE}" == [Nn]* ]]; then
# Create an empty file
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then
rm -f "${DISK_FILE}"
error "Could not create a ${DISK_SPACE} ${DISK_FMT} file for ${DISK_DESC} (${DISK_FILE})" && exit 87
fi
else
# Check free diskspace
SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( DATA_SIZE > SPACE )); then
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 ${DISK_DESC^^}_SIZE or switch to a growable disk with DISK_FMT=qcow2." && 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 ${DISK_SPACE} ${DISK_FMT} file for ${DISK_DESC} (${DISK_FILE})" && exit 87
fi
fi
fi
;;
qcow2)
if ! qemu-img create -f "$DISK_FMT" -- "${DISK_FILE}" "${DISK_SPACE}" ; then
error "Could not create a ${DISK_SPACE} ${DISK_FMT} file for ${DISK_DESC} (${DISK_FILE})" && exit 89
fi
;;
esac
}
addDisk () {
local FS
local DIR
local CUR_SIZE
local DATA_SIZE
local DISK_FILE
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
DISK_FILE="${DISK_BASE}.${DISK_EXT}"
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 ! [ -f "${DISK_FILE}" ] ; then
local PREV_EXT
local PREV_FMT
local PREV_FILE
if [[ "${DISK_FMT,,}" != "raw" ]]; then
PREV_FMT="raw"
else
PREV_FMT="qcow2"
fi
PREV_EXT="$(fmt2ext "${PREV_FMT}")"
PREV_FILE="${DISK_BASE}.${PREV_EXT}"
if [ -f "${PREV_FILE}" ] ; then
info "Disk format change detected for ${DISK_DESC} (${PREV_FMT} to ${DISK_FMT}), converting ${PREV_FILE} ..."
if ! convertDisk "${PREV_FILE}" "${PREV_FMT}" "${DISK_FILE}" "${DISK_FMT}" ; then
info "Disk conversion failed, creating new disk image as fallback."
rm -f "${DISK_FILE}"
else
info "Disk conversion completed succesfully, removing ${PREV_FILE} ..."
rm -f "${PREV_FILE}"
fi
fi fi
fi fi
if [ ! -f "${DISK_FILE}" ]; then [ -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 [[ "${ALLOCATE}" == [Nn]* ]]; then if (( DATA_SIZE < 6442450944 )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83
fi
# Create an empty file if [ -f "${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})" && exit 87
fi
else CUR_SIZE=$(getSize "${DISK_FILE}")
# 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
if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then
resizeDisk "${DISK_FILE}" "${CUR_SIZE}" "${DATA_SIZE}" "${DISK_SPACE}" "${DISK_DESC}" "${DISK_FMT}" || exit $?
fi fi
else
createDisk "${DISK_FILE}" "${DISK_SPACE}" "${DISK_DESC}" "${DISK_FMT}" || exit $?
fi fi
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},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 \ -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}" -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" DISK_EXT="$(fmt2ext "${DISK_FMT}")" || exit $?
if [[ ! -f "${DISK1_FILE}" ]] && [[ -f "${STORAGE}/data${DISK_SIZE}.img" ]]; then DISK1_FILE="${STORAGE}/data"
if [[ ! -f "${DISK1_FILE}.img" ]] && [[ -f "${STORAGE}/data${DISK_SIZE}.img" ]]; then
# Fallback for legacy installs # Fallback for legacy installs
mv "${STORAGE}/data${DISK_SIZE}.img" "${DISK1_FILE}" mv "${STORAGE}/data${DISK_SIZE}.img" "${DISK1_FILE}.img"
fi fi
DISK2_FILE="/storage2/data2.img" DISK2_FILE="/storage2/data2"
if [ ! -f "${DISK2_FILE}.img" ]; then
if [ ! -f "${DISK2_FILE}" ]; 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 +325,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}"
addDisk "userdata2" "${DISK2_FILE}" "disk2" "${DISK2_SIZE}" "4" "0xd" addDisk "userdata2" "${DISK2_FILE}" "${DISK_EXT}" "disk2" "${DISK2_SIZE}" "4" "0xd" "${DISK_FMT}"
addDisk "userdata3" "${DISK3_FILE}" "disk3" "${DISK3_SIZE}" "5" "0xe" addDisk "userdata3" "${DISK3_FILE}" "${DISK_EXT}" "disk3" "${DISK3_SIZE}" "5" "0xe" "${DISK_FMT}"
addDisk "userdata4" "${DISK4_FILE}" "disk4" "${DISK4_SIZE}" "9" "0x7" addDisk "userdata4" "${DISK4_FILE}" "${DISK_EXT}" "disk4" "${DISK4_SIZE}" "9" "0x7" "${DISK_FMT}"
addDisk "userdata5" "${DISK5_FILE}" "disk5" "${DISK5_SIZE}" "10" "0x8" addDisk "userdata5" "${DISK5_FILE}" "${DISK_EXT}" "disk5" "${DISK5_SIZE}" "10" "0x8" "${DISK_FMT}"
addDisk "userdata6" "${DISK6_FILE}" "disk6" "${DISK6_SIZE}" "11" "0x9" addDisk "userdata6" "${DISK6_FILE}" "${DISK_EXT}" "disk6" "${DISK6_SIZE}" "11" "0x9" "${DISK_FMT}"
addDevice () { addDevice () {

View File

@@ -17,6 +17,11 @@ cd /run
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

@@ -14,24 +14,30 @@ 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"
[[ "${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
@@ -43,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"
@@ -81,7 +109,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 " ")
@@ -149,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")
@@ -167,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..."
@@ -213,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
@@ -274,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 -sk -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
@@ -41,4 +44,64 @@ rm -f /run/qemu.count
rm -rf /tmp/dsm rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp" 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