Compare commits

..

10 Commits
v4.42 ... v4.44

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

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,57 @@
#!/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.count" ] && echo "QEMU is shutting down.." && exit 1
file="/run/dsm.url" file="/run/dsm.url"
active="/run/qemu.pid"
shutdown="/run/qemu.count"
url="http://127.0.0.1:2210/read?command=10"
active_msg="QEMU not running yet.."
shutdown_msg="QEMU is shutting down.."
resp_err="Guest returned an invalid response:"
jq_err="Failed to parse response from guest: jq error"
[ ! -f "$active" ] && echo "$active_msg" && exit 0
[ -f "$shutdown" ] && echo "$shutdown_msg" && exit 1
if [ ! -f "$file" ]; then if [ ! -f "$file" ]; then
# Retrieve IP from guest VM for Docker healthcheck # Retrieve IP from guest VM for Docker healthcheck
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
{ json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || : [ -f "$shutdown" ] && echo "$shutdown_msg" && exit 1
(( rc != 0 )) && echo "Failed to connect to guest: curl error $rc" && exit 1 (( rc != 0 )) && echo "Failed to connect to guest: curl error $rc" && exit 1
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || : { result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1 (( rc != 0 )) && echo "$jq_err $rc ( $json )" && exit 1
[[ "$result" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1 [[ "$result" == "null" ]] && echo "$resp_err $json" && exit 1
if [[ "$result" != "success" ]] ; then if [[ "$result" != "success" ]] ; then
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || : { msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
echo "Guest replied ${result}: $msg" && exit 1 echo "Guest replied $result: $msg" && exit 1
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 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1 (( rc != 0 )) && echo "$jq_err $rc ( $json )" && exit 1
[[ "$port" == "null" ]] && echo "Guest has not set a portnumber yet.." && exit 1 [[ "$port" == "null" ]] && echo "$resp_err $json" && exit 1
[ -z "${port}" ] && 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=$?; } || : { 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 (( rc != 0 )) && echo "$jq_err $rc ( $json )" && exit 1
[[ "$ip" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1 [[ "$ip" == "null" ]] && echo "$resp_err $json" && exit 1
[ -z "${ip}" ] && echo "Guest has not received an IP yet.." && exit 1 [ -z "$ip" ] && echo "Guest has not received an IP yet.." && exit 1
echo "${ip}:${port}" > $file echo "$ip:$port" > $file
fi fi
[ -f "$shutdown" ] && echo "$shutdown_msg" && exit 1
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 [ -f "$shutdown" ] && echo "$shutdown_msg" && exit 1
echo "Failed to reach http://${location}" echo "Failed to reach page at http://$location" && exit 1
exit 1
fi fi
echo "Healthcheck OK" echo "Healthcheck OK"

View File

@@ -12,29 +12,29 @@ else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)" [ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi fi
if [ -n "${KVM_ERR}" ]; then if [ -n "$KVM_ERR" ]; then
if [ "$ARCH" == "amd64" ]; then if [ "$ARCH" == "amd64" ]; then
error "KVM acceleration not detected ${KVM_ERR}, see the FAQ about this." error "KVM acceleration not detected $KVM_ERR, see the FAQ about this."
[[ "${DEBUG}" != [Yy1]* ]] && exit 88 [[ "$DEBUG" != [Yy1]* ]] && exit 88
fi fi
else else
KVM_OPTS=",accel=kvm -enable-kvm -cpu host" KVM_OPTS=",accel=kvm -enable-kvm -cpu host"
fi 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="-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

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 88
;; ;;
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 88
;; ;;
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 88
;; ;;
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 84
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 85
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 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 "$FAIL" && exit 85
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 85
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 87
fi fi
else else
# Check free diskspace # Check free diskspace
SPACE=$(df --output=avail -B 1 "${DIR}" | tail -n 1) SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) 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 86
fi fi
# Create an empty file # Create an empty file
if ! fallocate -l "${DISK_SPACE}" "${DISK_FILE}"; then if ! fallocate -l "$DISK_SPACE" "$DISK_FILE"; then
if ! truncate -s "${DISK_SPACE}" "${DISK_FILE}"; then if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
rm -f "${DISK_FILE}" rm -f "$DISK_FILE"
error "Could not create a ${DISK_SPACE} ${DISK_FMT} file for ${DISK_DESC} (${DISK_FILE})" && exit 87 error "$FAIL" && exit 87
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 89
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 83
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} ..."
if ! convertDisk "${PREV_FILE}" "${PREV_FMT}" "${DISK_FILE}" "${DISK_FMT}" ; then info "Detected that ${DISK_DESC^^}_FMT changed from \"$PREV_FMT\" to \"$DISK_FMT\"."
info "Disk conversion failed, creating new disk image as fallback." info "Starting conversion of $DISK_DESC to this new format, please wait until completed..."
rm -f "${DISK_FILE}"
else TMP_FILE="$DISK_BASE.tmp"
info "Disk conversion completed succesfully, removing ${PREV_FILE} ..." rm -f "$TMP_FILE"
rm -f "${PREV_FILE}"
fi if ! convertDisk "$PREV_FILE" "$PREV_FMT" "$TMP_FILE" "$DISK_FMT" ; then
fi rm -f "$TMP_FILE"
error "Failed to convert $DISK_DESC to $DISK_FMT format." && exit 89
fi fi
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G" mv "$TMP_FILE" "$DISK_FILE"
DISK_SPACE=$(echo "${DISK_SPACE}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') rm -f "$PREV_FILE"
DATA_SIZE=$(numfmt --from=iec "${DISK_SPACE}") info "Conversion of $DISK_DESC completed succesfully!"
fi
if (( DATA_SIZE < 6442450944 )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83
fi fi
if [ -f "${DISK_FILE}" ]; then if [ -f "$DISK_FILE" ]; then
CUR_SIZE=$(getSize "${DISK_FILE}") 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

@@ -17,17 +17,17 @@ cd /run
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 #!/bin/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,6 +29,7 @@ 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
@@ -40,16 +41,9 @@ if [ -z "$URL" ]; then
fi fi
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 +51,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 +59,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 +70,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 +82,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 +124,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,12 +133,12 @@ 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=$?; } || :
else else
@@ -240,26 +241,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 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
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 88
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 89
# 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 +277,7 @@ rm -rf "$MOUNT" && mkdir -p "$MOUNT"
mv "$HDA.tgz" "$HDA.txz" mv "$HDA.tgz" "$HDA.txz"
if [[ "${DEV}" == [Nn]* ]]; then if [[ "$DEV" == [Nn]* ]]; then
# Exclude dev/ from tar extract # Exclude dev/ from tar extract
tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/" tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/"
else else
@@ -301,7 +302,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 +314,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
@@ -195,16 +200,16 @@ 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 86
@@ -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,7 +4,7 @@ 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
@@ -31,7 +31,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 +48,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

@@ -25,15 +25,15 @@ else
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"