Compare commits

...

38 Commits
v4.42 ... v5.02

Author SHA1 Message Date
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
18 changed files with 368 additions and 357 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

@@ -79,16 +79,14 @@ jobs:
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }} 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,11 +9,13 @@ 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 \
&& apt-get --no-install-recommends -y install \
jq \ jq \
tini \ tini \
curl \ curl \
@@ -32,6 +34,7 @@ RUN apt-get update && apt-get -y upgrade && \
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/*

View File

@@ -20,4 +20,4 @@ services:
volumes: volumes:
- /opt/dsm:/storage - /opt/dsm:/storage
restart: on-failure restart: on-failure
stop_grace_period: 1m stop_grace_period: 2m

View File

@@ -41,7 +41,7 @@ services:
volumes: volumes:
- /opt/dsm:/storage - /opt/dsm:/storage
restart: on-failure restart: on-failure
stop_grace_period: 1m stop_grace_period: 2m
``` ```
Via `docker run` Via `docker run`
@@ -89,7 +89,7 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
* ### How do I create a growable 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 allocates 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 variable:
```yaml ```yaml
environment: environment:

View File

@@ -1,47 +1,16 @@
#!/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
# Retrieve IP from guest VM for Docker healthcheck
{ json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || :
(( 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
{ port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
[[ "$port" == "null" ]] && echo "Guest has not set a portnumber yet.." && exit 1
[ -z "${port}" ] && echo "Guest has not set a portnumber yet.." && exit 1
{ ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
[[ "$ip" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1
[ -z "${ip}" ] && echo "Guest has not received an IP yet.." && exit 1
echo "${ip}:${port}" > $file
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 echo "Failed to reach page at http://$location" && exit 1
echo "Failed to reach http://${location}"
exit 1
fi fi
echo "Healthcheck OK" echo "Healthcheck OK"

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

@@ -17,11 +17,11 @@ SYSTEM="$STORAGE/$BASE.system.img"
DISK_OPTS="\ DISK_OPTS="\
-device virtio-scsi-pci,id=hw-synoboot,bus=pcie.0,addr=0xa \ -device virtio-scsi-pci,id=hw-synoboot,bus=pcie.0,addr=0xa \
-drive file=${BOOT},if=none,id=drive-synoboot,format=raw,cache=${DISK_CACHE},aio=${DISK_IO},discard=${DISK_DISCARD},detect-zeroes=on \ -drive file=$BOOT,if=none,id=drive-synoboot,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-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 \ -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 \
-device virtio-scsi-pci,id=hw-synosys,bus=pcie.0,addr=0xb \ -device virtio-scsi-pci,id=hw-synosys,bus=pcie.0,addr=0xb \
-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"
fmt2ext() { fmt2ext() {
local DISK_FMT=$1 local DISK_FMT=$1
@@ -34,7 +34,7 @@ fmt2ext() {
echo "img" echo "img"
;; ;;
*) *)
error "Unrecognized disk format: ${DISK_FMT}" && exit 88 error "Unrecognized disk format: $DISK_FMT" && exit 78
;; ;;
esac esac
} }
@@ -50,161 +50,151 @@ ext2fmt() {
echo "raw" echo "raw"
;; ;;
*) *)
error "Unrecognized file extension: .${DISK_EXT}" && exit 90 error "Unrecognized file extension: .$DISK_EXT" && exit 78
;; ;;
esac esac
} }
getSize() { getSize() {
local DISK_FILE=$1 local DISK_FILE=$1
local DISK_EXT local DISK_EXT DISK_FMT
local DISK_FMT
DISK_EXT="$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')" DISK_EXT="$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')"
DISK_FMT="$(ext2fmt "${DISK_EXT}")" DISK_FMT="$(ext2fmt "$DISK_EXT")"
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
stat -c%s "${DISK_FILE}" stat -c%s "$DISK_FILE"
;; ;;
qcow2) qcow2)
qemu-img info "${DISK_FILE}" -f "${DISK_FMT}" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/' qemu-img info "$DISK_FILE" -f "$DISK_FMT" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/'
;; ;;
*) *)
error "Unrecognized disk format: ${DISK_FMT}" && exit 88 error "Unrecognized disk format: $DISK_FMT" && exit 78
;; ;;
esac esac
} }
resizeDisk() { resizeDisk() {
local GB
local REQ
local SPACE
local SPACE_GB
local DISK_FILE=$1 local DISK_FILE=$1
local CUR_SIZE=$2 local CUR_SIZE=$2
local DATA_SIZE=$3 local DATA_SIZE=$3
local DISK_SPACE=$4 local DISK_SPACE=$4
local DISK_DESC=$5 local DISK_DESC=$5
local DISK_FMT=$6 local DISK_FMT=$6
local GB REQ FAIL SPACE SPACE_GB
GB=$(( (CUR_SIZE + 1073741823)/1073741824 )) GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
info "Resizing ${DISK_DESC} from ${GB}G to ${DISK_SPACE} .." info "Resizing $DISK_DESC from ${GB}G to $DISK_SPACE .."
FAIL="Could not resize $DISK_FMT file of $DISK_DESC ($DISK_FILE) from ${GB}G to $DISK_SPACE .."
REQ=$((DATA_SIZE-CUR_SIZE))
(( REQ < 1 )) && error "Shrinking disks is not supported!" && exit 71
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
if [[ "${ALLOCATE}" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
# Resize file by changing its length # Resize file by changing its length
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 "$FAIL" && exit 75
fi fi
else else
REQ=$((DATA_SIZE-CUR_SIZE))
# 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 )) 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} in ${DIR}, it has only ${SPACE_GB} GB available.." 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 ${DISK_DESC^^}_SIZE or switch to a growable disk with DISK_FMT=qcow2." && exit 84 error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting DISK_FMT to \"qcow2\"." && exit 74
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 "$FAIL" && exit 75
fi fi
fi fi
fi fi
;; ;;
qcow2) qcow2)
if ! qemu-img resize -f "${DISK_FMT}" "${DISK_FILE}" "${DISK_SPACE}" ; then 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 error "$FAIL" && exit 72
fi fi
;; ;;
esac esac
} }
convertDisk() { convertDisk() {
local CONV_FLAGS="-p"
local CONV_FLAGS=""
local SOURCE_FILE=$1 local SOURCE_FILE=$1
local SOURCE_FMT=$2 local SOURCE_FMT=$2
local DST_FILE=$3 local DST_FILE=$3
local DST_FMT=$4 local DST_FMT=$4
case "${DST_FMT}" in case "$DST_FMT" in
qcow2) qcow2)
CONV_FLAGS="${CONV_FLAGS} -c" CONV_FLAGS="$CONV_FLAGS -c"
;; ;;
esac esac
# shellcheck disable=SC2086 # shellcheck disable=SC2086
qemu-img convert ${CONV_FLAGS} -f "${SOURCE_FMT}" -O "${DST_FMT}" -- "${SOURCE_FILE}" "${DST_FILE}" qemu-img convert $CONV_FLAGS -f "$SOURCE_FMT" -O "$DST_FMT" -- "$SOURCE_FILE" "$DST_FILE"
} }
createDisk() { createDisk() {
local GB
local SPACE
local SPACE_GB
local DISK_FILE=$1 local DISK_FILE=$1
local DISK_SPACE=$2 local DISK_SPACE=$2
local DISK_DESC=$3 local DISK_DESC=$3
local DISK_FMT=$4 local DISK_FMT=$4
local GB FAIL SPACE SPACE_GB
FAIL="Could not create a $DISK_SPACE $DISK_FMT file for $DISK_DESC ($DISK_FILE)"
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
if [[ "${ALLOCATE}" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
# 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 ${DISK_SPACE} ${DISK_FMT} file for ${DISK_DESC} (${DISK_FILE})" && exit 87 error "$FAIL" && exit 77
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 )) 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} in ${DIR}, it has only ${SPACE_GB} GB available.." error "Not enough free space to create a $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 error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting DISK_FMT to \"qcow2\"." && exit 76
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 ${DISK_SPACE} ${DISK_FMT} file for ${DISK_DESC} (${DISK_FILE})" && exit 87 error "$FAIL" && exit 77
fi fi
fi fi
fi fi
;; ;;
qcow2) qcow2)
if ! qemu-img create -f "$DISK_FMT" -- "${DISK_FILE}" "${DISK_SPACE}" ; then 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 rm -f "$DISK_FILE"
error "$FAIL" && exit 70
fi fi
;; ;;
esac esac
} }
addDisk () { addDisk () {
local FS
local DIR
local CUR_SIZE
local DATA_SIZE
local DISK_FILE
local DISK_ID=$1 local DISK_ID=$1
local DISK_BASE=$2 local DISK_BASE=$2
local DISK_EXT=$3 local DISK_EXT=$3
@@ -213,104 +203,107 @@ addDisk () {
local DISK_INDEX=$6 local DISK_INDEX=$6
local DISK_ADDRESS=$7 local DISK_ADDRESS=$7
local DISK_FMT=$8 local DISK_FMT=$8
local FS DIR CUR_SIZE DATA_SIZE DISK_FILE
DISK_FILE="${DISK_BASE}.${DISK_EXT}" DISK_FILE="$DISK_BASE.$DISK_EXT"
DIR=$(dirname "$DISK_FILE")
DIR=$(dirname "${DISK_FILE}") [ ! -d "$DIR" ] && return 0
[ ! -d "${DIR}" ] && return 0
FS=$(stat -f -c %T "$DIR") FS=$(stat -f -c %T "$DIR")
if [[ "$FS" == "overlay"* ]]; then if [[ "$FS" == "overlay"* ]]; then
info "Warning: the filesystem of ${DIR} is OverlayFS, this usually means it was binded to an invalid path!" info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
if ! [ -f "${DISK_FILE}" ] ; then [ -z "$DISK_SPACE" ] && DISK_SPACE="16G"
local PREV_EXT DISK_SPACE=$(echo "$DISK_SPACE" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
local PREV_FMT DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
local PREV_FILE
if (( DATA_SIZE < 6442450944 )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi
if ! [ -f "$DISK_FILE" ] ; then
local PREV_EXT PREV_FMT PREV_FILE TMP_FILE
if [[ "${DISK_FMT,,}" != "raw" ]]; then if [[ "${DISK_FMT,,}" != "raw" ]]; then
PREV_FMT="raw" PREV_FMT="raw"
else else
PREV_FMT="qcow2" PREV_FMT="qcow2"
fi fi
PREV_EXT="$(fmt2ext "${PREV_FMT}")" PREV_EXT="$(fmt2ext "$PREV_FMT")"
PREV_FILE="${DISK_BASE}.${PREV_EXT}" PREV_FILE="$DISK_BASE.$PREV_EXT"
if [ -f "${PREV_FILE}" ] ; then if [ -f "$PREV_FILE" ] ; then
info "Disk format change detected for ${DISK_DESC} (${PREV_FMT} to ${DISK_FMT}), converting ${PREV_FILE} ..."
info "Detected that ${DISK_DESC^^}_FMT changed from \"$PREV_FMT\" to \"$DISK_FMT\"."
if ! convertDisk "${PREV_FILE}" "${PREV_FMT}" "${DISK_FILE}" "${DISK_FMT}" ; then info "Starting conversion of $DISK_DESC to this new format, please wait until completed..."
info "Disk conversion failed, creating new disk image as fallback."
rm -f "${DISK_FILE}" TMP_FILE="$DISK_BASE.tmp"
else rm -f "$TMP_FILE"
info "Disk conversion completed succesfully, removing ${PREV_FILE} ..."
rm -f "${PREV_FILE}" if ! convertDisk "$PREV_FILE" "$PREV_FMT" "$TMP_FILE" "$DISK_FMT" ; then
rm -f "$TMP_FILE"
error "Failed to convert $DISK_DESC to $DISK_FMT format." && exit 79
fi fi
mv "$TMP_FILE" "$DISK_FILE"
rm -f "$PREV_FILE"
info "Conversion of $DISK_DESC completed succesfully!"
fi fi
fi fi
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G" if [ -f "$DISK_FILE" ]; then
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 CUR_SIZE=$(getSize "$DISK_FILE")
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83
fi
if [ -f "${DISK_FILE}" ]; then
CUR_SIZE=$(getSize "${DISK_FILE}")
if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then if [ "$DATA_SIZE" -gt "$CUR_SIZE" ]; then
resizeDisk "${DISK_FILE}" "${CUR_SIZE}" "${DATA_SIZE}" "${DISK_SPACE}" "${DISK_DESC}" "${DISK_FMT}" || exit $? resizeDisk "$DISK_FILE" "$CUR_SIZE" "$DATA_SIZE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $?
fi fi
else else
createDisk "${DISK_FILE}" "${DISK_SPACE}" "${DISK_DESC}" "${DISK_FMT}" || exit $? 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=${DISK_FMT},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
} }
DISK_EXT="$(fmt2ext "${DISK_FMT}")" || exit $? DISK_EXT="$(fmt2ext "$DISK_FMT")" || exit $?
DISK1_FILE="${STORAGE}/data" DISK1_FILE="$STORAGE/data"
if [[ ! -f "${DISK1_FILE}.img" ]] && [[ -f "${STORAGE}/data${DISK_SIZE}.img" ]]; then if [[ ! -f "$DISK1_FILE.img" ]] && [[ -f "$STORAGE/data${DISK_SIZE}.img" ]]; then
# Fallback for legacy installs # Fallback for legacy installs
mv "${STORAGE}/data${DISK_SIZE}.img" "${DISK1_FILE}.img" mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi fi
DISK2_FILE="/storage2/data2" DISK2_FILE="/storage2/data2"
if [ ! -f "${DISK2_FILE}.img" ]; then if [ ! -f "$DISK2_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage2/data.img" FALLBACK="/storage2/data.img"
if [[ -f "${DISK1_FILE}.img" ]] && [[ -f "${FALLBACK}" ]]; then if [[ -f "$DISK1_FILE.img" ]] && [[ -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "${FALLBACK}") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "${DISK1_FILE}.img") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then
mv "${FALLBACK}" "${DISK2_FILE}.img" mv "$FALLBACK" "$DISK2_FILE.img"
fi fi
fi fi
fi fi
DISK3_FILE="/storage3/data3" DISK3_FILE="/storage3/data3"
if [ ! -f "${DISK3_FILE}.img" ]; then if [ ! -f "$DISK3_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage3/data.img" FALLBACK="/storage3/data.img"
if [[ -f "${DISK1_FILE}.img" ]] && [[ -f "${FALLBACK}" ]]; then if [[ -f "$DISK1_FILE.img" ]] && [[ -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "${FALLBACK}") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "${DISK1_FILE}.img") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then
mv "${FALLBACK}" "${DISK3_FILE}.img" mv "$FALLBACK" "$DISK3_FILE.img"
fi fi
fi fi
fi fi
@@ -325,12 +318,12 @@ DISK6_FILE="/storage6/data6"
: ${DISK5_SIZE:=''} : ${DISK5_SIZE:=''}
: ${DISK6_SIZE:=''} : ${DISK6_SIZE:=''}
addDisk "userdata" "${DISK1_FILE}" "${DISK_EXT}" "disk" "${DISK_SIZE}" "3" "0xc" "${DISK_FMT}" addDisk "userdata" "$DISK1_FILE" "$DISK_EXT" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" || exit $?
addDisk "userdata2" "${DISK2_FILE}" "${DISK_EXT}" "disk2" "${DISK2_SIZE}" "4" "0xd" "${DISK_FMT}" addDisk "userdata2" "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" || exit $?
addDisk "userdata3" "${DISK3_FILE}" "${DISK_EXT}" "disk3" "${DISK3_SIZE}" "5" "0xe" "${DISK_FMT}" addDisk "userdata3" "$DISK3_FILE" "$DISK_EXT" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" || exit $?
addDisk "userdata4" "${DISK4_FILE}" "${DISK_EXT}" "disk4" "${DISK4_SIZE}" "9" "0x7" "${DISK_FMT}" addDisk "userdata4" "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "9" "0x7" "$DISK_FMT" || exit $?
addDisk "userdata5" "${DISK5_FILE}" "${DISK_EXT}" "disk5" "${DISK5_SIZE}" "10" "0x8" "${DISK_FMT}" addDisk "userdata5" "$DISK5_FILE" "$DISK_EXT" "disk5" "$DISK5_SIZE" "10" "0x8" "$DISK_FMT" || exit $?
addDisk "userdata6" "${DISK6_FILE}" "${DISK_EXT}" "disk6" "${DISK6_SIZE}" "11" "0x9" "${DISK_FMT}" addDisk "userdata6" "$DISK6_FILE" "$DISK_EXT" "disk6" "$DISK6_SIZE" "11" "0x9" "$DISK_FMT" || exit $?
addDevice () { addDevice () {
@@ -339,13 +332,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,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
} }
@@ -357,11 +350,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,23 +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 if [[ "$CONSOLE" == [Yy]* ]]; then
exec qemu-system-x86_64 -pidfile "${QEMU_PID}" ${ARGS:+ $ARGS} exec qemu-system-x86_64 -pidfile "$QEMU_PID" ${ARGS:+ $ARGS}
exit $? exit $?
fi 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

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"
@@ -29,27 +29,15 @@ DL_GLOBAL="https://global.synologydownload.com/download/DSM"
if [ -z "$DL" ]; then if [ -z "$DL" ]; then
[ -z "$COUNTRY" ] && setCountry [ -z "$COUNTRY" ] && setCountry
[ -z "$COUNTRY" ] && info "Warning: could not detect country to select mirror!"
[[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL" [[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL"
fi fi
if [ -z "$URL" ]; then [ -z "$URL" ] && URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
if [ "$ARCH" == "amd64" ]; then
URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
else
URL="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
fi
fi
# Check if output is to interactive TTY
if [ -t 1 ]; then
PROGRESS="--progress=bar:noscroll"
else
PROGRESS="--progress=dot:giga"
fi
BASE=$(basename "$URL" .pat) BASE=$(basename "$URL" .pat)
if [[ "$URL" != "file://${STORAGE}/${BASE}.pat" ]]; then if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$STORAGE"/"$BASE".pat rm -f "$STORAGE"/"$BASE".pat
fi fi
@@ -57,7 +45,7 @@ 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 [[ "$DEBUG" == [Yy1]* ]] && set -x
# Check filesystem # Check filesystem
MIN_ROOT=471859200 MIN_ROOT=471859200
@@ -65,7 +53,7 @@ MIN_SPACE=6442450944
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
if [[ "$FS" == "overlay"* ]]; then if [[ "$FS" == "overlay"* ]]; then
info "Warning: the filesystem of ${STORAGE} is OverlayFS, this usually means it was binded to an invalid path!" info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \ if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \
@@ -76,7 +64,7 @@ else
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1) SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
if (( MIN_SPACE > SPACE )); then if (( MIN_SPACE > SPACE )); then
TMP="$STORAGE/tmp" TMP="$STORAGE/tmp"
info "Warning: the ${FS} filesystem of ${STORAGE} does not support UNIX permissions.." info "Warning: the $FS filesystem of $STORAGE does not support UNIX permissions.."
fi fi
fi fi
@@ -88,19 +76,26 @@ SPACE=$(df --output=avail -B 1 / | tail -n 1)
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1) SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) 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 (( 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 if [[ "$TMP" != "$STORAGE/tmp" ]]; then
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1) SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) SPACE_GB=$(( (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 (( 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
# Check if output is to interactive TTY
if [ -t 1 ]; then
PROGRESS="--progress=bar:noscroll"
else
PROGRESS="--progress=dot:giga"
fi fi
# Download the required files from the Synology website # Download the required files from the Synology website
RDC="$STORAGE/dsm.rd" RDC="$STORAGE/dsm.rd"
if [ ! -f "${RDC}" ]; then if [ ! -f "$RDC" ]; then
info "Install: Downloading installer..." info "Install: Downloading installer..."
@@ -123,7 +118,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
@@ -132,20 +127,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 \
@@ -203,8 +199,6 @@ if { tar tf "$PAT"; } >/dev/null 2>&1; then
else else
[ "$ARCH" != "amd64" ] && addPackage "qemu-user" "QEMU"
info "Install: Extracting downloaded image..." info "Install: Extracting downloaded image..."
export LD_LIBRARY_PATH="/run/extract" export LD_LIBRARY_PATH="/run/extract"
@@ -240,26 +234,26 @@ 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)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) 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 (( 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 ""
@@ -276,7 +270,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
@@ -301,7 +295,7 @@ rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE"/dsm.ver echo "$BASE" > "$STORAGE"/dsm.ver
if [[ "$URL" == "file://${STORAGE}/${BASE}.pat" ]]; then if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$PAT" rm -f "$PAT"
else else
mv -f "$PAT" "$STORAGE"/"$BASE".pat mv -f "$PAT" "$STORAGE"/"$BASE".pat
@@ -313,6 +307,6 @@ 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

@@ -23,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 || :
@@ -74,17 +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
# Set DNS server and gateway # 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" DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1 --dhcp-option=option:router,${VM_NET_IP%.*}.1"
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
} }
@@ -94,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=$?; } || :
@@ -111,19 +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
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"
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"
@@ -131,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
@@ -141,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"
@@ -153,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
@@ -182,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
@@ -195,19 +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/) 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 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
@@ -224,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

@@ -8,8 +8,8 @@ QEMU_TIMEOUT=50
QEMU_PID=/run/qemu.pid QEMU_PID=/run/qemu.pid
QEMU_COUNT=/run/qemu.count QEMU_COUNT=/run/qemu.count
rm -f "${QEMU_PID}" rm -f "$QEMU_PID"
rm -f "${QEMU_COUNT}" rm -f "$QEMU_COUNT"
_trap(){ _trap(){
func="$1" ; shift func="$1" ; shift
@@ -21,53 +21,64 @@ _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 0 > "${QEMU_COUNT}" echo 0 > "$QEMU_COUNT"
echo && info "Received $1 signal, shutting down..." 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 -sk -m 30 -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 "Failed to send shutdown command ( ${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
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

@@ -5,46 +5,60 @@ info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n" >&2; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; } error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
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
# Healthcheck may have intervened
[ -f "$file" ] && break
# Retrieve IP from guest VM # Retrieve IP from guest VM
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
{ json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || : [ -f "$shutdown" ] && exit 1
(( rc != 0 )) && error "Failed to connect to guest: curl error $rc" && continue (( rc != 0 )) && error "Failed to connect to guest: curl error $rc" && continue
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || : { result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && error "Failed to parse response from guest: jq error $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$result" == "null" ]] && error "Guest returned invalid response: $json" && continue [[ "$result" == "null" ]] && error "$resp_err $json" && continue
if [[ "$result" != "success" ]] ; then if [[ "$result" != "success" ]] ; then
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || : { msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
error "Guest replied ${result}: $msg" && continue error "Guest replied $result: $msg" && continue
fi fi
{ port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || : { port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || :
(( rc != 0 )) && error "Failed to parse response from guest: jq error $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$port" == "null" ]] && error "Guest returned invalid response: $json" && continue [[ "$port" == "null" ]] && error "$resp_err $json" && continue
[ -z "${port}" ] && continue [ -z "$port" ] && continue
{ ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || : { ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || :
(( rc != 0 )) && error "Failed to parse response from guest: jq error $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$ip" == "null" ]] && error "Guest returned invalid response: $json" && continue [[ "$ip" == "null" ]] && error "$resp_err $json" && continue
[ -z "${ip}" ] && continue [ -z "$ip" ] && continue
echo "${ip}:${port}" > $file echo "$ip:$port" > $file
done done
[ -f "$shutdown" ] && exit 1
location=$(cat "$file") location=$(cat "$file")
if [[ "$location" != "20.20"* ]]; then if [[ "$location" != "20.20"* ]]; then
msg="http://${location}" msg="http://$location"
else else
@@ -52,15 +66,15 @@ else
port="${location##*:}" port="${location##*:}"
if [[ "$ip" == "172."* ]]; then if [[ "$ip" == "172."* ]]; then
msg="port ${port}" msg="port $port"
else else
msg="http://${ip}:${port}" msg="http://$ip:$port"
fi 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

@@ -4,17 +4,18 @@ set -Eeuo pipefail
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n"; } info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n"; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; } error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
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 : ${COUNTRY:=''} # Country code for mirror
: ${CONSOLE:='N'} # Start in console mode : ${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
@@ -31,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
@@ -48,11 +49,9 @@ rm -rf "$STORAGE/tmp"
getCountry () { getCountry () {
local rc
local json
local result
local url=$1 local url=$1
local query=$2 local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || : { json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0 (( rc != 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

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"