mirror of
https://github.com/vdsm/virtual-dsm.git
synced 2026-08-29 12:17:07 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9a2a6966b | ||
|
|
498248f43e | ||
|
|
8963228b1c | ||
|
|
827d6d9e10 | ||
|
|
1c577abef4 | ||
|
|
20fd1a975c | ||
|
|
770774680c | ||
|
|
0c75aa81fe | ||
|
|
36ec57ac15 | ||
|
|
9c488c52b9 | ||
|
|
2ba756260f | ||
|
|
b306012743 | ||
|
|
61b3ee7e01 | ||
|
|
245036f844 | ||
|
|
68cadfe035 | ||
|
|
96102832b1 | ||
|
|
211a56bf73 | ||
|
|
506fb7b114 | ||
|
|
bcc3393b7f | ||
|
|
45c0f91d6e | ||
|
|
a1910546a6 | ||
|
|
637ed0e3a6 | ||
|
|
954a6076ad | ||
|
|
415aaa3038 | ||
|
|
c87e45719b | ||
|
|
102945ab48 | ||
|
|
1c3d490303 | ||
|
|
73fa70f52f | ||
|
|
7b095702fc | ||
|
|
f82b2769d3 | ||
|
|
cafe619028 | ||
|
|
e94a0c4a52 | ||
|
|
d6a353df0e | ||
|
|
050812e996 | ||
|
|
75280e9aa9 | ||
|
|
8dcc8ccee1 | ||
|
|
ba866e9e75 | ||
|
|
06e7a8edb9 | ||
|
|
495c67734a | ||
|
|
1557735331 |
+57
-41
@@ -12,47 +12,63 @@ ARG DEBCONF_NOWARNINGS="yes"
|
|||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
||||||
|
|
||||||
RUN set -eu && \
|
RUN <<EOF
|
||||||
apt-get update && \
|
set -eu
|
||||||
apt-get --no-install-recommends -y install \
|
|
||||||
jq \
|
apt-get update
|
||||||
tini \
|
apt-get --no-install-recommends -y install \
|
||||||
curl \
|
jq \
|
||||||
wget \
|
tini \
|
||||||
fdisk \
|
curl \
|
||||||
unzip \
|
wget \
|
||||||
nginx \
|
fdisk \
|
||||||
procps \
|
unzip \
|
||||||
ethtool \
|
nginx \
|
||||||
python3 \
|
procps \
|
||||||
python3-pip \
|
ipcalc \
|
||||||
python3-msgpack \
|
ethtool \
|
||||||
python3-pysodium \
|
python3 \
|
||||||
xz-utils \
|
python3-pip \
|
||||||
iptables \
|
python3-msgpack \
|
||||||
iproute2 \
|
python3-pysodium \
|
||||||
dnsmasq \
|
xz-utils \
|
||||||
fakeroot \
|
iptables \
|
||||||
apt-utils \
|
iproute2 \
|
||||||
net-tools \
|
dnsmasq \
|
||||||
e2fsprogs \
|
fakeroot \
|
||||||
qemu-utils \
|
apt-utils \
|
||||||
websocketd \
|
net-tools \
|
||||||
iputils-ping \
|
e2fsprogs \
|
||||||
inotify-tools \
|
qemu-utils \
|
||||||
ca-certificates \
|
websocketd \
|
||||||
netcat-openbsd \
|
iputils-ping \
|
||||||
qemu-system-x86 && \
|
inotify-tools \
|
||||||
wget "https://github.com/qemus/passt/releases/download/v${VERSION_PASST}/passt_${VERSION_PASST}_${TARGETARCH}.deb" -O /tmp/passt.deb -q --timeout=10 && \
|
ca-certificates \
|
||||||
dpkg -i /tmp/passt.deb && \
|
netcat-openbsd \
|
||||||
apt-get clean && \
|
qemu-system-x86
|
||||||
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
|
|
||||||
mkdir -p /etc/qemu && \
|
# Install Passt package
|
||||||
echo "allow br0" > /etc/qemu/bridge.conf && \
|
wget "https://github.com/qemus/passt/releases/download/v${VERSION_PASST}/passt_${VERSION_PASST}_${TARGETARCH}.deb" -O /tmp/passt.deb -q --timeout=10
|
||||||
unlink /etc/nginx/sites-enabled/default && \
|
dpkg -i /tmp/passt.deb
|
||||||
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \
|
|
||||||
echo "$VERSION_ARG" > /etc/version && \
|
apt-get clean
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
|
||||||
|
# Install Python dependencies
|
||||||
|
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct
|
||||||
|
|
||||||
|
# Configure QEMU
|
||||||
|
mkdir -p /etc/qemu
|
||||||
|
echo "allow br0" > /etc/qemu/bridge.conf
|
||||||
|
|
||||||
|
# Configure nginx
|
||||||
|
unlink /etc/nginx/sites-enabled/default
|
||||||
|
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf
|
||||||
|
|
||||||
|
# Set version file
|
||||||
|
echo "$VERSION_ARG" > /etc/version
|
||||||
|
|
||||||
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
EOF
|
||||||
|
|
||||||
COPY --chmod=755 ./src /run/
|
COPY --chmod=755 ./src /run/
|
||||||
COPY --chmod=755 ./web /var/www/
|
COPY --chmod=755 ./web /var/www/
|
||||||
|
|||||||
@@ -16,9 +16,13 @@ Virtual DSM in a Docker container.
|
|||||||
|
|
||||||
## Features ✨
|
## Features ✨
|
||||||
|
|
||||||
- Multiple disks
|
- Runs Virtual DSM inside a Docker container
|
||||||
- KVM acceleration
|
- Automatic download of the installation files
|
||||||
- Automatic download
|
- Web-based access to the DSM desktop
|
||||||
|
- Near-native performance with KVM acceleration
|
||||||
|
- Customizable CPU, memory, and storage allocation
|
||||||
|
- Supports multiple disks and physical disk passthrough
|
||||||
|
- Supports bridge, user-mode, macvlan, and macvtap networking
|
||||||
|
|
||||||
## Usage 🐳
|
## Usage 🐳
|
||||||
|
|
||||||
@@ -79,7 +83,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
|
|||||||
|
|
||||||
- Wait until DSM finishes its installation
|
- Wait until DSM finishes its installation
|
||||||
|
|
||||||
- Choose an username and password, and you will be taken to the desktop.
|
- Choose a username and password, and you will be taken to the desktop.
|
||||||
|
|
||||||
Enjoy your brand new NAS, and don't forget to star this repo!
|
Enjoy your brand new NAS, and don't forget to star this repo!
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -1,12 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
cd /run
|
||||||
|
. utils.sh # Load functions
|
||||||
|
|
||||||
: "${DHCP:="N"}"
|
: "${DHCP:="N"}"
|
||||||
: "${NETWORK:="Y"}"
|
: "${NETWORK:="Y"}"
|
||||||
|
|
||||||
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down..." && exit 1
|
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down..." && exit 1
|
||||||
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet..." && exit 0
|
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet..." && exit 0
|
||||||
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled." && exit 0
|
disabled "$NETWORK" && echo "Networking is disabled." && exit 0
|
||||||
|
|
||||||
file="/run/shm/dsm.url"
|
file="/run/shm/dsm.url"
|
||||||
address="/run/shm/qemu.ip"
|
address="/run/shm/qemu.ip"
|
||||||
@@ -18,7 +21,7 @@ location=$(<"$file")
|
|||||||
|
|
||||||
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
||||||
|
|
||||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
if enabled "$DHCP"; then
|
||||||
ip=$(<"$address")
|
ip=$(<"$address")
|
||||||
echo "Failed to reach DSM at http://$location"
|
echo "Failed to reach DSM at http://$location"
|
||||||
else
|
else
|
||||||
|
|||||||
+185
-134
@@ -8,7 +8,7 @@ set -Eeuo pipefail
|
|||||||
: "${DISK_TYPE:=""}" # Device type to be used, "sata", "nvme", "blk" or "scsi"
|
: "${DISK_TYPE:=""}" # Device type to be used, "sata", "nvme", "blk" or "scsi"
|
||||||
: "${DISK_FLAGS:=""}" # Specifies the options for use with the qcow2 disk format
|
: "${DISK_FLAGS:=""}" # Specifies the options for use with the qcow2 disk format
|
||||||
: "${DISK_CACHE:="none"}" # Caching mode, can be set to 'writeback' for better performance
|
: "${DISK_CACHE:="none"}" # Caching mode, can be set to 'writeback' for better performance
|
||||||
: "${DISK_DISCARD:="on"}" # Controls whether unmap (TRIM) commands are passed to the host.
|
: "${DISK_DISCARD:="unmap"}" # Controls whether unmap (TRIM) commands are passed to the host.
|
||||||
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
|
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
|
||||||
|
|
||||||
# Sanitize all variables
|
# Sanitize all variables
|
||||||
@@ -38,15 +38,9 @@ fmt2ext() {
|
|||||||
local DISK_FMT="$1"
|
local DISK_FMT="$1"
|
||||||
|
|
||||||
case "${DISK_FMT,,}" in
|
case "${DISK_FMT,,}" in
|
||||||
qcow2)
|
qcow2) echo "qcow2" ;;
|
||||||
echo "qcow2"
|
raw) echo "img" ;;
|
||||||
;;
|
*) error "Unrecognized disk format: $DISK_FMT" && exit 78 ;;
|
||||||
raw)
|
|
||||||
echo "img"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
error "Unrecognized disk format: $DISK_FMT" && exit 78
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,21 +48,16 @@ ext2fmt() {
|
|||||||
local DISK_EXT="$1"
|
local DISK_EXT="$1"
|
||||||
|
|
||||||
case "${DISK_EXT,,}" in
|
case "${DISK_EXT,,}" in
|
||||||
qcow2)
|
qcow2) echo "qcow2" ;;
|
||||||
echo "qcow2"
|
img) echo "raw" ;;
|
||||||
;;
|
*) error "Unrecognized file extension: .$DISK_EXT" && exit 78 ;;
|
||||||
img)
|
|
||||||
echo "raw"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
error "Unrecognized file extension: .$DISK_EXT" && exit 78
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
getSize() {
|
getSize() {
|
||||||
|
|
||||||
local DISK_FILE="$1"
|
local DISK_FILE="$1"
|
||||||
local DISK_EXT DISK_FMT
|
local DISK_EXT DISK_FMT size
|
||||||
|
|
||||||
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
||||||
DISK_FMT=$(ext2fmt "$DISK_EXT")
|
DISK_FMT=$(ext2fmt "$DISK_EXT")
|
||||||
@@ -77,11 +66,19 @@ getSize() {
|
|||||||
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/'
|
size=$(qemu-img info --output=json -f "$DISK_FMT" "$DISK_FILE" | jq -r '."virtual-size" // empty')
|
||||||
|
if [[ ! "$size" =~ ^[0-9]+$ ]]; then
|
||||||
|
error "Failed to determine virtual size of $DISK_FILE"
|
||||||
|
exit 78
|
||||||
|
fi
|
||||||
|
echo "$size"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
error "Unrecognized disk format: $DISK_FMT" && exit 78
|
error "Unrecognized disk format: $DISK_FMT"
|
||||||
|
exit 78
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -106,6 +103,82 @@ supportsDirect() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allocateRaw() {
|
||||||
|
|
||||||
|
local DISK_FILE="$1"
|
||||||
|
local DATA_SIZE="$2"
|
||||||
|
|
||||||
|
if disabled "$ALLOCATE"; then
|
||||||
|
truncate -s "$DATA_SIZE" "$DISK_FILE"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null && return 0
|
||||||
|
fallocate -l -x "$DATA_SIZE" "$DISK_FILE" && return 0
|
||||||
|
truncate -s "$DATA_SIZE" "$DISK_FILE" || return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
getDiskOptions() {
|
||||||
|
|
||||||
|
local FS="$1"
|
||||||
|
local DISK_FMT="$2"
|
||||||
|
local DISK_PARAM="$DISK_ALLOC"
|
||||||
|
|
||||||
|
isCow "$FS" && DISK_PARAM+=",nocow=on"
|
||||||
|
|
||||||
|
if [[ "${DISK_FMT,,}" != "raw" ]]; then
|
||||||
|
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$DISK_PARAM"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeSize() {
|
||||||
|
|
||||||
|
local DISK_SPACE="$1"
|
||||||
|
local DISK_DESC="$2"
|
||||||
|
local DIR="$3"
|
||||||
|
local SPACE FREE GB DATA_SIZE
|
||||||
|
|
||||||
|
if [[ "${DISK_SPACE,,}" == "max" || "${DISK_SPACE,,}" == "half" ]]; then
|
||||||
|
|
||||||
|
local SPARE=1073741824
|
||||||
|
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||||
|
|
||||||
|
if [[ "${DISK_SPACE,,}" == "max" ]]; then
|
||||||
|
FREE=$(( FREE - SPARE ))
|
||||||
|
else
|
||||||
|
FREE=$(( FREE / 2 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( FREE < SPARE )) && FREE="$SPARE"
|
||||||
|
GB=$(( FREE / 1073741825 ))
|
||||||
|
DISK_SPACE="${GB}G"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
SPACE="${DISK_SPACE// /}"
|
||||||
|
[ -z "$SPACE" ] && SPACE="256G"
|
||||||
|
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
||||||
|
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||||
|
|
||||||
|
if ! numfmt --from=iec "$SPACE" &>/dev/null; then
|
||||||
|
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
|
||||||
|
fi
|
||||||
|
|
||||||
|
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
||||||
|
|
||||||
|
if (( DATA_SIZE < 6442450944 )); then
|
||||||
|
error "Please increase the ${DISK_DESC^^}_SIZE variable to at least 6 GB." && exit 73
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$SPACE"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
createDisk() {
|
createDisk() {
|
||||||
|
|
||||||
local DISK_FILE="$1"
|
local DISK_FILE="$1"
|
||||||
@@ -148,33 +221,15 @@ createDisk() {
|
|||||||
{ chattr +C "$DISK_FILE"; } || :
|
{ chattr +C "$DISK_FILE"; } || :
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if disabled "$ALLOCATE"; then
|
if ! allocateRaw "$DISK_FILE" "$DATA_SIZE"; then
|
||||||
|
rm -f "$DISK_FILE"
|
||||||
# Create an empty file
|
error "$FAIL" && exit 77
|
||||||
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
|
||||||
rm -f "$DISK_FILE"
|
|
||||||
error "$FAIL" && exit 77
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
# Create an empty file
|
|
||||||
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
|
|
||||||
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
|
|
||||||
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
|
||||||
rm -f "$DISK_FILE"
|
|
||||||
error "$FAIL" && exit 77
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
qcow2)
|
qcow2)
|
||||||
|
|
||||||
local DISK_PARAM="$DISK_ALLOC"
|
local DISK_PARAM
|
||||||
isCow "$FS" && DISK_PARAM+=",nocow=on"
|
DISK_PARAM=$(getDiskOptions "$FS" "$DISK_FMT")
|
||||||
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
|
|
||||||
|
|
||||||
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
|
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
|
||||||
rm -f "$DISK_FILE"
|
rm -f "$DISK_FILE"
|
||||||
@@ -202,7 +257,7 @@ resizeDisk() {
|
|||||||
local FS="$5"
|
local FS="$5"
|
||||||
local CUR_SIZE DATA_SIZE DIR SPACE GB
|
local CUR_SIZE DATA_SIZE DIR SPACE GB
|
||||||
|
|
||||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
CUR_SIZE=$(getSize "$DISK_FILE") || exit 71
|
||||||
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||||
local REQ=$(( DATA_SIZE - CUR_SIZE ))
|
local REQ=$(( DATA_SIZE - CUR_SIZE ))
|
||||||
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
|
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
|
||||||
@@ -230,24 +285,8 @@ resizeDisk() {
|
|||||||
case "${DISK_FMT,,}" in
|
case "${DISK_FMT,,}" in
|
||||||
raw)
|
raw)
|
||||||
|
|
||||||
if disabled "$ALLOCATE"; then
|
if ! allocateRaw "$DISK_FILE" "$DATA_SIZE"; then
|
||||||
|
error "$FAIL" && exit 75
|
||||||
# Resize file by changing its length
|
|
||||||
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
|
||||||
error "$FAIL" && exit 75
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
# Resize file by allocating more space
|
|
||||||
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
|
|
||||||
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
|
|
||||||
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
|
||||||
error "$FAIL" && exit 75
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
qcow2)
|
qcow2)
|
||||||
@@ -286,7 +325,7 @@ convertDisk() {
|
|||||||
local CUR_SIZE SPACE GB
|
local CUR_SIZE SPACE GB
|
||||||
|
|
||||||
# Check free diskspace
|
# Check free diskspace
|
||||||
CUR_SIZE=$(getSize "$SOURCE_FILE")
|
CUR_SIZE=$(getSize "$SOURCE_FILE") || exit 79
|
||||||
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||||
|
|
||||||
if (( CUR_SIZE > SPACE )); then
|
if (( CUR_SIZE > SPACE )); then
|
||||||
@@ -302,14 +341,13 @@ convertDisk() {
|
|||||||
info "$msg, please wait until completed..."
|
info "$msg, please wait until completed..."
|
||||||
|
|
||||||
local CONV_FLAGS="-p"
|
local CONV_FLAGS="-p"
|
||||||
local DISK_PARAM="$DISK_ALLOC"
|
local DISK_PARAM
|
||||||
isCow "$FS" && DISK_PARAM+=",nocow=on"
|
DISK_PARAM=$(getDiskOptions "$FS" "$DST_FMT")
|
||||||
|
|
||||||
if [[ "$DST_FMT" != "raw" ]]; then
|
if [[ "$DST_FMT" != "raw" ]]; then
|
||||||
if disabled "$ALLOCATE"; then
|
if disabled "$ALLOCATE"; then
|
||||||
CONV_FLAGS+=" -c"
|
CONV_FLAGS+=" -c"
|
||||||
fi
|
fi
|
||||||
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
@@ -330,8 +368,17 @@ convertDisk() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "$SOURCE_FILE"
|
if ! rm -f "$SOURCE_FILE"; then
|
||||||
mv "$TMP_FILE" "$DST_FILE"
|
rm -f "$TMP_FILE"
|
||||||
|
error "Failed to remove old $DISK_DESC image $SOURCE_FILE."
|
||||||
|
exit 79
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! mv "$TMP_FILE" "$DST_FILE"; then
|
||||||
|
rm -f "$TMP_FILE"
|
||||||
|
error "Failed to move converted $DISK_DESC image to $DST_FILE."
|
||||||
|
exit 79
|
||||||
|
fi
|
||||||
|
|
||||||
if isCow "$FS"; then
|
if isCow "$FS"; then
|
||||||
FA=$(lsattr "$DST_FILE")
|
FA=$(lsattr "$DST_FILE")
|
||||||
@@ -460,7 +507,7 @@ addDisk () {
|
|||||||
local DISK_FMT="$7"
|
local DISK_FMT="$7"
|
||||||
local DISK_IO="$8"
|
local DISK_IO="$8"
|
||||||
local DISK_CACHE="$9"
|
local DISK_CACHE="$9"
|
||||||
local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE LEFT FREE USED
|
local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE LEFT FREE USED
|
||||||
|
|
||||||
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
||||||
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
||||||
@@ -468,38 +515,9 @@ addDisk () {
|
|||||||
DIR=$(dirname "$DISK_FILE")
|
DIR=$(dirname "$DISK_FILE")
|
||||||
[ ! -d "$DIR" ] && return 0
|
[ ! -d "$DIR" ] && return 0
|
||||||
|
|
||||||
if [[ "${DISK_SPACE,,}" == "max" || "${DISK_SPACE,,}" == "half" ]]; then
|
SPACE=$(normalizeSize "$DISK_SPACE" "$DISK_DESC" "$DIR")
|
||||||
|
|
||||||
local SPARE=1073741824
|
|
||||||
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
|
||||||
|
|
||||||
if [[ "${DISK_SPACE,,}" == "max" ]]; then
|
|
||||||
FREE=$(( FREE - SPARE ))
|
|
||||||
else
|
|
||||||
FREE=$(( FREE / 2 ))
|
|
||||||
fi
|
|
||||||
|
|
||||||
(( FREE < SPARE )) && FREE="$SPARE"
|
|
||||||
GB=$(( FREE / 1073741825 ))
|
|
||||||
DISK_SPACE="${GB}G"
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
SPACE="${DISK_SPACE// /}"
|
|
||||||
[ -z "$SPACE" ] && SPACE="256G"
|
|
||||||
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
|
||||||
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
|
||||||
|
|
||||||
if ! numfmt --from=iec "$SPACE" &>/dev/null; then
|
|
||||||
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
|
|
||||||
fi
|
|
||||||
|
|
||||||
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
||||||
|
|
||||||
if (( DATA_SIZE < 6442450944 )); then
|
|
||||||
error "Please increase the ${DISK_DESC^^}_SIZE variable to at least 6 GB." && exit 73
|
|
||||||
fi
|
|
||||||
|
|
||||||
FS=$(stat -f -c %T "$DIR")
|
FS=$(stat -f -c %T "$DIR")
|
||||||
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
|
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
|
||||||
|
|
||||||
@@ -526,7 +544,7 @@ addDisk () {
|
|||||||
|
|
||||||
if [ -s "$DISK_FILE" ]; then
|
if [ -s "$DISK_FILE" ]; then
|
||||||
|
|
||||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
CUR_SIZE=$(getSize "$DISK_FILE") || exit 71
|
||||||
|
|
||||||
if (( DATA_SIZE > CUR_SIZE )); then
|
if (( DATA_SIZE > CUR_SIZE )); then
|
||||||
|
|
||||||
@@ -551,13 +569,15 @@ addDisk () {
|
|||||||
|
|
||||||
if [ -f "$DISK_FILE" ] && disabled "$ALLOCATE"; then
|
if [ -f "$DISK_FILE" ] && disabled "$ALLOCATE"; then
|
||||||
|
|
||||||
CUR_SIZE=$(getSize "$DISK_FILE")
|
CUR_SIZE=$(getSize "$DISK_FILE") || exit 73
|
||||||
USED=$(du -sB 1 "$DISK_FILE" | cut -f1)
|
USED=$(du -sB 1 "$DISK_FILE" | cut -f1)
|
||||||
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||||
LEFT=$(( CUR_SIZE - USED - FREE ))
|
LEFT=$(( CUR_SIZE - USED - FREE ))
|
||||||
|
(( LEFT < 0 )) && LEFT=0
|
||||||
|
|
||||||
if (( LEFT > 0 )); then
|
if (( LEFT > 0 )); then
|
||||||
|
|
||||||
|
local GB
|
||||||
GB=$(formatBytes "$FREE")
|
GB=$(formatBytes "$FREE")
|
||||||
LEFT=$(formatBytes "$LEFT")
|
LEFT=$(formatBytes "$LEFT")
|
||||||
CUR_SIZE=$(formatBytes "$CUR_SIZE")
|
CUR_SIZE=$(formatBytes "$CUR_SIZE")
|
||||||
@@ -603,18 +623,22 @@ addDevice () {
|
|||||||
# Only detect and apply sector sizes for partitions, not whole disks
|
# Only detect and apply sector sizes for partitions, not whole disks
|
||||||
# Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk
|
# Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk
|
||||||
if [[ "$dev_type" == "part" ]]; then
|
if [[ "$dev_type" == "part" ]]; then
|
||||||
local result logical physical
|
local result=""
|
||||||
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
|
local logical=""
|
||||||
logical="${result%% *}"
|
local physical=""
|
||||||
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
|
|
||||||
physical="${physical%% *}"
|
|
||||||
|
|
||||||
if [ -n "$physical" ]; then
|
result=$(fdisk -l "$DISK_DEV" 2>/dev/null | grep -m 1 -o "(logical/physical): .*" | cut -c 21- || true)
|
||||||
if [[ "$physical" != "512" ]]; then
|
|
||||||
sectors=",logical_block_size=$logical,physical_block_size=$physical"
|
if [ -n "$result" ]; then
|
||||||
fi
|
logical="${result%% *}"
|
||||||
else
|
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3- || true)
|
||||||
|
physical="${physical%% *}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$logical" ] || [ -z "$physical" ]; then
|
||||||
warn "Failed to determine the sector size for $DISK_DEV"
|
warn "Failed to determine the sector size for $DISK_DEV"
|
||||||
|
elif [[ "$physical" != "512" ]]; then
|
||||||
|
sectors=",logical_block_size=$logical,physical_block_size=$physical"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -634,11 +658,45 @@ if ! enabled "$DISK_DISABLE"; then
|
|||||||
enabled "$DEBUG" && echo "$msg"
|
enabled "$DEBUG" && echo "$msg"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${DISK_IO,,}" == "native" && "${DISK_CACHE,,}" != "none" && "${DISK_CACHE,,}" != "directsync" ]]; then
|
||||||
|
warn "DISK_IO=native requires direct I/O caching, using DISK_IO=threads with DISK_CACHE=$DISK_CACHE."
|
||||||
|
DISK_IO="threads"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${DISK_DISCARD,,}" in
|
||||||
|
"y" | "yes" | "true" | "1" | "on" | "unmap" )
|
||||||
|
DISK_DISCARD="unmap" ;;
|
||||||
|
|
||||||
|
"n" | "no" | "false" | "0" | "off" | "ignore" )
|
||||||
|
DISK_DISCARD="ignore" ;;
|
||||||
|
|
||||||
|
* )
|
||||||
|
warn "Invalid DISK_DISCARD value '$DISK_DISCARD', using 'unmap'."
|
||||||
|
DISK_DISCARD="unmap" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ ! "$DISK_ROTATION" =~ ^[0-9]+$ ]]; then
|
||||||
|
warn "Invalid DISK_ROTATION value '$DISK_ROTATION', using 1."
|
||||||
|
DISK_ROTATION="1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DISK_FMT="${DISK_FMT,,}"
|
||||||
|
|
||||||
|
case "$DISK_FMT" in
|
||||||
|
"raw" | "qcow2" ) ;;
|
||||||
|
* ) error "Invalid DISK_FMT specified, value \"$DISK_FMT\" is not recognized!" && exit 78 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
case "${DISK_TYPE,,}" in
|
case "${DISK_TYPE,,}" in
|
||||||
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "virtio-blk" | "virtio-scsi" | "auto" | "none" ) ;;
|
||||||
* ) error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is not recognized!" && exit 80 ;;
|
* ) error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is not recognized!" && exit 80 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
if [[ "$DISK_FLAGS" =~ [[:space:]] ]]; then
|
||||||
|
error "Invalid DISK_FLAGS value '$DISK_FLAGS', spaces are not allowed."
|
||||||
|
exit 78
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -z "$ALLOCATE" ]; then
|
if [ -z "$ALLOCATE" ]; then
|
||||||
ALLOCATE="N"
|
ALLOCATE="N"
|
||||||
fi
|
fi
|
||||||
@@ -684,29 +742,22 @@ DISK4_FILE="/storage4/${DISK_NAME}4"
|
|||||||
[ -z "$DEVICE3" ] && [ -b "/dev/disk3" ] && DEVICE3="/dev/disk3"
|
[ -z "$DEVICE3" ] && [ -b "/dev/disk3" ] && DEVICE3="/dev/disk3"
|
||||||
[ -z "$DEVICE4" ] && [ -b "/dev/disk4" ] && DEVICE4="/dev/disk4"
|
[ -z "$DEVICE4" ] && [ -b "/dev/disk4" ] && DEVICE4="/dev/disk4"
|
||||||
|
|
||||||
if [ -n "$DEVICE" ]; then
|
DISK_FILES=( "$DISK1_FILE" "$DISK2_FILE" "$DISK3_FILE" "$DISK4_FILE" )
|
||||||
addDevice "$DEVICE" "$DISK_TYPE" "3" "0xc" || exit $?
|
DISK_DESCS=( "disk" "disk2" "disk3" "disk4" )
|
||||||
else
|
DISK_SIZES=( "$DISK_SIZE" "$DISK2_SIZE" "$DISK3_SIZE" "$DISK4_SIZE" )
|
||||||
addDisk "$DISK1_FILE" "$DISK_TYPE" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
DISK_DEVICES=( "$DEVICE" "$DEVICE2" "$DEVICE3" "$DEVICE4" )
|
||||||
fi
|
DISK_INDEXES=( "3" "4" "5" "6" )
|
||||||
|
DISK_ADDRESSES=( "0xc" "0xd" "0xe" "0xf" )
|
||||||
|
|
||||||
if [ -n "$DEVICE2" ]; then
|
for i in "${!DISK_FILES[@]}"; do
|
||||||
addDevice "$DEVICE2" "$DISK_TYPE" "4" "0xd" || exit $?
|
|
||||||
else
|
|
||||||
addDisk "$DISK2_FILE" "$DISK_TYPE" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$DEVICE3" ]; then
|
if [ -n "${DISK_DEVICES[i]}" ]; then
|
||||||
addDevice "$DEVICE3" "$DISK_TYPE" "5" "0xe" || exit $?
|
addDevice "${DISK_DEVICES[i]}" "$DISK_TYPE" "${DISK_INDEXES[i]}" "${DISK_ADDRESSES[i]}" || exit $?
|
||||||
else
|
else
|
||||||
addDisk "$DISK3_FILE" "$DISK_TYPE" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
addDisk "${DISK_FILES[i]}" "$DISK_TYPE" "${DISK_DESCS[i]}" "${DISK_SIZES[i]}" "${DISK_INDEXES[i]}" "${DISK_ADDRESSES[i]}" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$DEVICE4" ]; then
|
done
|
||||||
addDevice "$DEVICE4" "$DISK_TYPE" "6" "0xf" || exit $?
|
|
||||||
else
|
|
||||||
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
finishDisks
|
finishDisks
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -205,11 +205,12 @@ fi
|
|||||||
MSG="Preparing system partition..."
|
MSG="Preparing system partition..."
|
||||||
info "Install: $MSG" && html "$MSG"
|
info "Install: $MSG" && html "$MSG"
|
||||||
|
|
||||||
BOOT=$(find "$TMP" -name "*.bin.zip")
|
BOOT=$(find "$TMP" -name "*.bin.zip" -print -quit)
|
||||||
[ ! -s "$BOOT" ] && error "The PAT file contains no boot image." && exit 67
|
[ -z "$BOOT" ] && error "The PAT file contains no boot image." && exit 67
|
||||||
|
[ ! -s "$BOOT" ] && error "The PAT boot image archive is empty." && exit 67
|
||||||
|
|
||||||
BOOT=$(echo "$BOOT" | head -c -5)
|
unzip -q -o "$BOOT" -d "$TMP"
|
||||||
unzip -q -o "$BOOT".zip -d "$TMP"
|
BOOT="${BOOT%.zip}"
|
||||||
|
|
||||||
SYSTEM="$STORAGE/$BASE.system.img"
|
SYSTEM="$STORAGE/$BASE.system.img"
|
||||||
rm -f "$SYSTEM"
|
rm -f "$SYSTEM"
|
||||||
@@ -281,8 +282,9 @@ mv "$HDA.tgz" "$HDA.txz"
|
|||||||
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
|
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
|
||||||
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
|
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
|
||||||
|
|
||||||
|
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb"
|
||||||
|
|
||||||
if [ -s "$IDB.txz" ]; then
|
if [ -s "$IDB.txz" ]; then
|
||||||
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb"
|
|
||||||
mkdir -p "$INDEX_DB"
|
mkdir -p "$INDEX_DB"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
+45
-12
@@ -7,7 +7,13 @@ enabled "$DEBUG" && echo "$msg"
|
|||||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
||||||
|
|
||||||
if ! disabled "$RAM_CHECK" && [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
|
checkConfiguredMemory() {
|
||||||
|
|
||||||
|
local wanted msg
|
||||||
|
|
||||||
|
if disabled "$RAM_CHECK" || [[ "${RAM_SIZE,,}" == "max" || "${RAM_SIZE,,}" == "half" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||||
|
|
||||||
@@ -30,9 +36,16 @@ if ! disabled "$RAM_CHECK" && [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" !=
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
if [[ "${RAM_SIZE,,}" == "half" ]]; then
|
configureHalfMemory() {
|
||||||
|
|
||||||
|
local wanted
|
||||||
|
|
||||||
|
if [[ "${RAM_SIZE,,}" != "half" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if (( (RAM_AVAIL / 2) > RAM_SPARE )); then
|
if (( (RAM_AVAIL / 2) > RAM_SPARE )); then
|
||||||
wanted=$(( (RAM_AVAIL / 2) / 1048577 ))
|
wanted=$(( (RAM_AVAIL / 2) / 1048577 ))
|
||||||
@@ -42,9 +55,16 @@ if [[ "${RAM_SIZE,,}" == "half" ]]; then
|
|||||||
RAM_SIZE="max"
|
RAM_SIZE="max"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
if [[ "${RAM_SIZE,,}" == "max" ]]; then
|
configureMaxMemory() {
|
||||||
|
|
||||||
|
local wanted
|
||||||
|
|
||||||
|
if [[ "${RAM_SIZE,,}" != "max" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if (( RAM_AVAIL < (RAM_SPARE * 2) )); then
|
if (( RAM_AVAIL < (RAM_SPARE * 2) )); then
|
||||||
|
|
||||||
@@ -65,14 +85,27 @@ if [[ "${RAM_SIZE,,}" == "max" ]]; then
|
|||||||
|
|
||||||
info "Allocated $wanted MB of RAM for the virtual machine."
|
info "Allocated $wanted MB of RAM for the virtual machine."
|
||||||
|
|
||||||
fi
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
checkMinimumMemory() {
|
||||||
|
|
||||||
if [ "$wanted" -lt "$RAM_MINIMUM" ]; then
|
local wanted
|
||||||
wanted=$(( wanted / 1048577 ))
|
|
||||||
error "Not enough memory available, there is only $wanted MB left!"
|
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||||
exit 16
|
|
||||||
fi
|
if [ "$wanted" -lt "$RAM_MINIMUM" ]; then
|
||||||
|
wanted=$(( wanted / 1048577 ))
|
||||||
|
error "Not enough memory available, there is only $wanted MB left!"
|
||||||
|
exit 16
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkConfiguredMemory
|
||||||
|
configureHalfMemory
|
||||||
|
configureMaxMemory
|
||||||
|
checkMinimumMemory
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+1006
-382
File diff suppressed because it is too large
Load Diff
+131
-64
@@ -15,6 +15,7 @@ API_HOST="127.0.0.1:$COM_PORT"
|
|||||||
QEMU_END="$QEMU_DIR/qemu.end"
|
QEMU_END="$QEMU_DIR/qemu.end"
|
||||||
|
|
||||||
_trap() {
|
_trap() {
|
||||||
|
|
||||||
local func="$1"; shift
|
local func="$1"; shift
|
||||||
local sig
|
local sig
|
||||||
TRAP_PID=$BASHPID
|
TRAP_PID=$BASHPID
|
||||||
@@ -22,88 +23,103 @@ _trap() {
|
|||||||
for sig; do
|
for sig; do
|
||||||
trap "$func $sig" "$sig"
|
trap "$func $sig" "$sig"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
app() {
|
signalCode() {
|
||||||
echo "$APP" && return 0
|
|
||||||
|
local sig="$1"
|
||||||
|
|
||||||
|
case "$sig" in
|
||||||
|
SIGHUP) echo 129 ;;
|
||||||
|
SIGINT) echo 130 ;;
|
||||||
|
SIGQUIT) echo 131 ;;
|
||||||
|
SIGABRT) echo 134 ;;
|
||||||
|
SIGTERM) echo 143 ;;
|
||||||
|
*) echo 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
finish() {
|
displayReason() {
|
||||||
|
|
||||||
local i=0
|
local reason="$1"
|
||||||
|
|
||||||
|
case "$reason" in
|
||||||
|
129 ) echo "SIGHUP" ;;
|
||||||
|
130 ) echo "SIGINT" ;;
|
||||||
|
131 ) echo "SIGQUIT" ;;
|
||||||
|
134 ) echo "SIGABRT" ;;
|
||||||
|
143 ) echo "SIGTERM" ;;
|
||||||
|
* ) echo "$reason" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readQemuPid() {
|
||||||
|
|
||||||
|
local -n _pid="$1"
|
||||||
|
|
||||||
|
if [ ! -s "$QEMU_PID" ] || ! read -r _pid <"$QEMU_PID"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
forceKillQemu() {
|
||||||
|
|
||||||
|
local reason="$1"
|
||||||
local pid=""
|
local pid=""
|
||||||
local reason=$1
|
local display
|
||||||
|
|
||||||
|
! readQemuPid pid && return 0
|
||||||
|
! isAlive "$pid" && return 0
|
||||||
|
|
||||||
|
display=$(displayReason "$reason")
|
||||||
|
error "Forcefully terminating $(app), reason: $display..."
|
||||||
|
{ disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupHelpers() {
|
||||||
|
|
||||||
local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \
|
local pids=( "${HOST_PID:-}" "${WSD_PID:-}" \
|
||||||
"${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" )
|
"${WEB_PID:-}" "${PASST_PID:-}" "${DNSMASQ_PID:-}" )
|
||||||
|
|
||||||
touch "$QEMU_END"
|
|
||||||
|
|
||||||
if [ -s "$QEMU_PID" ]; then
|
|
||||||
if read -r pid <"$QEMU_PID"; then
|
|
||||||
if [ -n "$pid" ] && isAlive "$pid"; then
|
|
||||||
local display="$reason"
|
|
||||||
case "$reason" in
|
|
||||||
129 ) display="SIGHUP" ;;
|
|
||||||
130 ) display="SIGINT" ;;
|
|
||||||
131 ) display="SIGQUIT" ;;
|
|
||||||
134 ) display="SIGABRT" ;;
|
|
||||||
143 ) display="SIGTERM" ;;
|
|
||||||
esac
|
|
||||||
error "Forcefully terminating $(app), reason: $display..."
|
|
||||||
{ disown "$pid" || :; kill -9 -- "$pid" || :; } 2>/dev/null
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
mKill "${pids[@]}"
|
mKill "${pids[@]}"
|
||||||
fKill "print.sh"
|
fKill "print.sh"
|
||||||
|
|
||||||
closeNetwork
|
closeNetwork
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
|
||||||
|
local reason=$1
|
||||||
|
touch "$QEMU_END"
|
||||||
|
|
||||||
|
forceKillQemu "$reason"
|
||||||
|
cleanupHelpers
|
||||||
|
|
||||||
if ! waitPidFile "$QEMU_PID" 10; then
|
if ! waitPidFile "$QEMU_PID" 10; then
|
||||||
warn "Timed out while waiting for $(app) to exit!"
|
warn "Timed out while waiting for $(app) to exit!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(( reason != 1 )) && echo && echo "❯ Shutdown completed!"
|
(( reason != 1 )) && echo && echo "❯ Shutdown completed!"
|
||||||
|
|
||||||
exit "$reason"
|
exit "$reason"
|
||||||
}
|
}
|
||||||
|
|
||||||
graceful_shutdown() {
|
sendGuestShutdown() {
|
||||||
|
|
||||||
local sig="$1"
|
local pid="$1"
|
||||||
local pid=""
|
local response
|
||||||
local code=0
|
local url
|
||||||
local start url response elapsed
|
|
||||||
|
|
||||||
[[ $BASHPID != "$TRAP_PID" ]] && return
|
|
||||||
|
|
||||||
case "$sig" in
|
|
||||||
SIGHUP) code=129 ;;
|
|
||||||
SIGINT) code=130 ;;
|
|
||||||
SIGQUIT) code=131 ;;
|
|
||||||
SIGABRT) code=134 ;;
|
|
||||||
SIGTERM) code=143 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -f "$QEMU_END" ]; then
|
|
||||||
echo && info "Received $1 signal while already shutting down..."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
set +e
|
|
||||||
start=$SECONDS
|
|
||||||
touch "$QEMU_END"
|
|
||||||
echo && info "Received $1 signal, sending shutdown command..."
|
|
||||||
|
|
||||||
if [ ! -s "$QEMU_PID" ] || ! read -r pid <"$QEMU_PID"; then
|
|
||||||
warn "QEMU PID file ($QEMU_PID) does not exist?"
|
|
||||||
finish "$code"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$pid" ] || ! isAlive "$pid"; then
|
|
||||||
warn "QEMU process with PID $pid does not exist?"
|
|
||||||
finish "$code"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Don't send the powerdown signal because vDSM ignores ACPI signals
|
# Don't send the powerdown signal because vDSM ignores ACPI signals
|
||||||
# nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || :
|
# nc -q 1 -w 1 -U "$QEMU_DIR/monitor.sock" &> /dev/null <<<'system_powerdown' || :
|
||||||
@@ -121,16 +137,22 @@ graceful_shutdown() {
|
|||||||
|
|
||||||
response="${response#*message\"\: \"}"
|
response="${response#*message\"\: \"}"
|
||||||
[ -z "$response" ] && response="second signal"
|
[ -z "$response" ] && response="second signal"
|
||||||
|
|
||||||
echo && error "Forcefully terminating because of: ${response%%\"*}"
|
echo && error "Forcefully terminating because of: ${response%%\"*}"
|
||||||
kill -15 -- "$pid" 2>/dev/null || :
|
kill -15 -- "$pid" 2>/dev/null || :
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local name
|
return 0
|
||||||
name="$(app)"
|
}
|
||||||
|
|
||||||
|
normalizeTimeout() {
|
||||||
|
|
||||||
local term_grace=3 # seconds before loop ends to send SIGTERM
|
local term_grace=3 # seconds before loop ends to send SIGTERM
|
||||||
local cleanup_grace=3 # seconds reserved after the loop for cleanup
|
local cleanup_grace=3 # seconds reserved after the loop for cleanup
|
||||||
|
local elapsed
|
||||||
|
local timeout_left
|
||||||
|
local min
|
||||||
|
|
||||||
TIMEOUT=$(strip "$TIMEOUT")
|
TIMEOUT=$(strip "$TIMEOUT")
|
||||||
if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]]; then
|
if [[ ! "$TIMEOUT" =~ ^[0-9]+$ ]]; then
|
||||||
@@ -145,8 +167,6 @@ graceful_shutdown() {
|
|||||||
cleanup_grace=4
|
cleanup_grace=4
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local cnt=0 sigterm_at=0 min wait_until elapsed timeout_left
|
|
||||||
|
|
||||||
elapsed=$((SECONDS - start))
|
elapsed=$((SECONDS - start))
|
||||||
timeout_left=$((TIMEOUT - elapsed))
|
timeout_left=$((TIMEOUT - elapsed))
|
||||||
|
|
||||||
@@ -156,10 +176,20 @@ graceful_shutdown() {
|
|||||||
wait_until=$((timeout_left - cleanup_grace))
|
wait_until=$((timeout_left - cleanup_grace))
|
||||||
sigterm_at=$((wait_until - term_grace))
|
sigterm_at=$((wait_until - term_grace))
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForShutdown() {
|
||||||
|
|
||||||
|
local cnt=0
|
||||||
|
local pid="$1"
|
||||||
|
local name="$APP"
|
||||||
|
local slp
|
||||||
|
|
||||||
while (( cnt <= wait_until )); do
|
while (( cnt <= wait_until )); do
|
||||||
|
|
||||||
sleep 1 &
|
sleep 1 &
|
||||||
local slp=$!
|
slp=$!
|
||||||
|
|
||||||
# Stop waiting if the process has exited
|
# Stop waiting if the process has exited
|
||||||
! isAlive "$pid" && break
|
! isAlive "$pid" && break
|
||||||
@@ -179,6 +209,43 @@ graceful_shutdown() {
|
|||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
graceful_shutdown() {
|
||||||
|
|
||||||
|
local sig="$1"
|
||||||
|
local pid=""
|
||||||
|
local code=0
|
||||||
|
|
||||||
|
[[ $BASHPID != "$TRAP_PID" ]] && return
|
||||||
|
|
||||||
|
code=$(signalCode "$sig")
|
||||||
|
|
||||||
|
if [ -f "$QEMU_END" ]; then
|
||||||
|
echo && info "Received $1 signal while already shutting down..."
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
set +e
|
||||||
|
start=$SECONDS
|
||||||
|
touch "$QEMU_END"
|
||||||
|
echo && info "Received $1 signal, sending shutdown command..."
|
||||||
|
|
||||||
|
if ! readQemuPid pid; then
|
||||||
|
warn "QEMU PID file ($QEMU_PID) does not exist?"
|
||||||
|
finish "$code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! isAlive "$pid"; then
|
||||||
|
warn "QEMU process with PID $pid does not exist?"
|
||||||
|
finish "$code"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sendGuestShutdown "$pid"
|
||||||
|
normalizeTimeout
|
||||||
|
waitForShutdown "$pid"
|
||||||
|
|
||||||
finish "$code"
|
finish "$code"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+146
-47
@@ -1,16 +1,20 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# shellcheck disable=SC2329
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
: "${DHCP:="N"}"
|
: "${DHCP:="N"}"
|
||||||
: "${NETWORK:="Y"}"
|
: "${NETWORK:="Y"}"
|
||||||
|
|
||||||
[[ "$NETWORK" == [Nn]* ]] && exit 0
|
cd /run
|
||||||
|
. utils.sh # Load functions
|
||||||
|
|
||||||
info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "$1" "\E[0m\n" >&2; }
|
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; }
|
||||||
|
|
||||||
|
disabled "$NETWORK" && exit 0
|
||||||
|
|
||||||
file="/run/shm/dsm.url"
|
file="/run/shm/dsm.url"
|
||||||
info="/run/shm/msg.html"
|
msgs="/run/shm/msg.html"
|
||||||
driver="/run/shm/qemu.nic"
|
driver="/run/shm/qemu.nic"
|
||||||
page="/run/shm/index.html"
|
page="/run/shm/index.html"
|
||||||
address="/run/shm/qemu.ip"
|
address="/run/shm/qemu.ip"
|
||||||
@@ -22,68 +26,144 @@ resp_err="Guest returned an invalid response:"
|
|||||||
curl_err="Failed to connect to guest: curl error"
|
curl_err="Failed to connect to guest: curl error"
|
||||||
jq_err="Failed to parse response from guest: jq error"
|
jq_err="Failed to parse response from guest: jq error"
|
||||||
|
|
||||||
while [ ! -s "$file" ]
|
exitIfShuttingDown() {
|
||||||
do
|
|
||||||
|
|
||||||
# Check if not shutting down
|
|
||||||
[ -f "$shutdown" ] && exit 1
|
|
||||||
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
[ -f "$shutdown" ] && exit 1
|
[ -f "$shutdown" ] && exit 1
|
||||||
[ -s "$file" ] && break
|
|
||||||
|
|
||||||
# Retrieve network info from guest VM
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
queryGuest() {
|
||||||
|
|
||||||
|
local rc
|
||||||
|
|
||||||
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
|
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
|
||||||
|
|
||||||
[ -f "$shutdown" ] && exit 1
|
exitIfShuttingDown
|
||||||
(( rc != 0 )) && error "$curl_err $rc" && continue
|
|
||||||
|
|
||||||
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
|
if (( rc != 0 )); then
|
||||||
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
error "$curl_err $rc"
|
||||||
[[ "$result" == "null" ]] && error "$resp_err $json" && continue
|
return 1
|
||||||
|
|
||||||
if [[ "$result" != "success" ]] ; then
|
|
||||||
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
|
|
||||||
error "Guest replied $result: $msg" && continue
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
{ port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || :
|
return 0
|
||||||
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
}
|
||||||
[[ "$port" == "null" ]] && error "$resp_err $json" && continue
|
|
||||||
[ -z "$port" ] && continue
|
|
||||||
|
|
||||||
{ ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || :
|
readJsonField() {
|
||||||
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
|
||||||
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue
|
|
||||||
|
|
||||||
[ -z "$ip" ] && continue
|
local query="$1"
|
||||||
echo "$ip:$port" > $file
|
local result
|
||||||
|
local rc
|
||||||
|
|
||||||
done
|
{ result=$(jq -r "$query" <<< "$json"); rc=$?; } || :
|
||||||
|
|
||||||
[ -f "$shutdown" ] && exit 1
|
if (( rc != 0 )); then
|
||||||
|
error "$jq_err $rc ( $json )"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
location=$(<"$file")
|
if [[ "$result" == "null" ]]; then
|
||||||
|
error "$resp_err $json"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
printf '%s\n' "$result"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readGuestStatus() {
|
||||||
|
|
||||||
|
local result msg rc
|
||||||
|
|
||||||
|
result=$(readJsonField '.status') || return 1
|
||||||
|
|
||||||
|
if [[ "$result" != "success" ]]; then
|
||||||
|
{ msg=$(jq -r '.message // empty' <<< "$json"); rc=$?; } || :
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
error "$jq_err $rc ( $json )"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
error "Guest replied $result: $msg"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readGuestPort() {
|
||||||
|
|
||||||
|
port=$(readJsonField '.data.data.dsm_setting.data.http_port') || return 1
|
||||||
|
[ -z "$port" ] && return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
readGuestIp() {
|
||||||
|
|
||||||
|
ip=$(readJsonField '.data.data.ip.data[] | select(.name=="eth0" and has("ip")) | .ip | select(test("^[0-9]+\\."))') || return 1
|
||||||
|
[ -z "$ip" ] && return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDsmLocation() {
|
||||||
|
|
||||||
|
echo "$ip:$port" > "$file"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pollGuestLocation() {
|
||||||
|
|
||||||
|
while [ ! -s "$file" ]; do
|
||||||
|
|
||||||
|
# Check if not shutting down
|
||||||
|
exitIfShuttingDown
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
exitIfShuttingDown
|
||||||
|
[ -s "$file" ] && break
|
||||||
|
|
||||||
|
# Retrieve network info from guest VM
|
||||||
|
queryGuest || continue
|
||||||
|
readGuestStatus || continue
|
||||||
|
readGuestPort || continue
|
||||||
|
readGuestIp || continue
|
||||||
|
writeDsmLocation
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDhcpPage() {
|
||||||
|
|
||||||
|
local title body script html
|
||||||
|
|
||||||
msg="http://$location"
|
msg="http://$location"
|
||||||
title="<title>Virtual DSM</title>"
|
title="<title>Virtual DSM</title>"
|
||||||
body="The location of DSM is <a href='http://$location'>http://$location</a>"
|
body="The location of DSM is <a href='http://$location'>http://$location</a>"
|
||||||
script="<script>setTimeout(function(){ window.location.assign('http://$location'); }, 3000);</script>"
|
script="<script>setTimeout(function(){ window.location.assign('http://$location'); }, 3000);</script>"
|
||||||
|
|
||||||
HTML=$(<"$template")
|
html=$(<"$template")
|
||||||
HTML="${HTML/\[1\]/$title}"
|
html="${html/\[1\]/$title}"
|
||||||
HTML="${HTML/\[2\]/$script}"
|
html="${html/\[2\]/$script}"
|
||||||
HTML="${HTML/\[3\]/$body}"
|
html="${html/\[3\]/$body}"
|
||||||
HTML="${HTML/\[4\]/}"
|
html="${html/\[4\]/}"
|
||||||
HTML="${HTML/\[5\]/}"
|
html="${html/\[5\]/}"
|
||||||
|
|
||||||
echo "$HTML" > "$page"
|
echo "$html" > "$page"
|
||||||
echo "$body" > "$info"
|
echo "$body" > "$msgs"
|
||||||
|
|
||||||
else
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
buildStaticMessage() {
|
||||||
|
|
||||||
|
local nic ip port
|
||||||
|
|
||||||
nic=$(<"$driver")
|
nic=$(<"$driver")
|
||||||
ip=$(<"$address")
|
ip=$(<"$address")
|
||||||
@@ -95,12 +175,31 @@ else
|
|||||||
msg="http://$ip:$port"
|
msg="http://$ip:$port"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
printLoginMessage() {
|
||||||
|
|
||||||
|
echo "" >&2
|
||||||
|
info "-----------------------------------------------------------"
|
||||||
|
info " You can now login to DSM at $msg"
|
||||||
|
info "-----------------------------------------------------------"
|
||||||
|
echo "" >&2
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pollGuestLocation
|
||||||
|
exitIfShuttingDown
|
||||||
|
|
||||||
|
location=$(<"$file")
|
||||||
|
|
||||||
|
if enabled "$DHCP"; then
|
||||||
|
writeDhcpPage
|
||||||
|
else
|
||||||
|
buildStaticMessage
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "" >&2
|
printLoginMessage
|
||||||
info "-----------------------------------------------------------"
|
|
||||||
info " You can now login to DSM at $msg"
|
|
||||||
info "-----------------------------------------------------------"
|
|
||||||
echo "" >&2
|
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
+143
-69
@@ -11,15 +11,27 @@ HOST_CPU=$(strip "$HOST_CPU")
|
|||||||
CPU_FLAGS=$(strip "$CPU_FLAGS")
|
CPU_FLAGS=$(strip "$CPU_FLAGS")
|
||||||
CPU_MODEL=$(strip "$CPU_MODEL")
|
CPU_MODEL=$(strip "$CPU_MODEL")
|
||||||
|
|
||||||
CLOCKSOURCE="tsc"
|
selectClocksource() {
|
||||||
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
|
|
||||||
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
CLOCKSOURCE="tsc"
|
||||||
|
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
|
||||||
|
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkClocksource() {
|
||||||
|
|
||||||
|
local result
|
||||||
|
|
||||||
|
if [ ! -f "$CLOCK" ]; then
|
||||||
|
warn "file \"$CLOCK\" cannot be found?"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "$CLOCK" ]; then
|
|
||||||
warn "file \"$CLOCK\" cannot be found?"
|
|
||||||
else
|
|
||||||
result=$(<"$CLOCK")
|
result=$(<"$CLOCK")
|
||||||
result="${result//[![:print:]]/}"
|
result="${result//[![:print:]]/}"
|
||||||
|
|
||||||
case "${result,,}" in
|
case "${result,,}" in
|
||||||
"${CLOCKSOURCE,,}" ) ;;
|
"${CLOCKSOURCE,,}" ) ;;
|
||||||
"kvm-clock" ) info "Nested KVM virtualization detected.." ;;
|
"kvm-clock" ) info "Nested KVM virtualization detected.." ;;
|
||||||
@@ -27,29 +39,38 @@ else
|
|||||||
"hpet" ) warn "unsupported clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
"hpet" ) warn "unsupported clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
||||||
*) warn "unexpected clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
*) warn "unexpected clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
||||||
esac
|
esac
|
||||||
fi
|
|
||||||
|
|
||||||
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
if ! disabled "$KVM"; then
|
checkSse42() {
|
||||||
|
|
||||||
CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor"
|
|
||||||
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
|
|
||||||
|
|
||||||
if ! grep -qw "sse4_2" <<< "$flags"; then
|
if ! grep -qw "sse4_2" <<< "$flags"; then
|
||||||
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
||||||
! enabled "$DEBUG" && exit 88
|
! enabled "$DEBUG" && exit 88
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureKvmCpuModel() {
|
||||||
|
|
||||||
|
CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor"
|
||||||
|
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
|
||||||
|
|
||||||
if [ -z "$CPU_MODEL" ]; then
|
if [ -z "$CPU_MODEL" ]; then
|
||||||
CPU_MODEL="host"
|
CPU_MODEL="host"
|
||||||
CPU_FEATURES+=",migratable=no"
|
CPU_FEATURES+=",migratable=no"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
appendKvmInvtscFeature() {
|
||||||
|
|
||||||
if grep -qw "svm" <<< "$flags"; then
|
if grep -qw "svm" <<< "$flags"; then
|
||||||
|
|
||||||
# AMD processor
|
# AMD processor
|
||||||
|
|
||||||
if grep -qw "tsc_scale" <<< "$flags"; then
|
if grep -qw "tsc_scale" <<< "$flags"; then
|
||||||
CPU_FEATURES+=",+invtsc"
|
CPU_FEATURES+=",+invtsc"
|
||||||
fi
|
fi
|
||||||
@@ -57,7 +78,7 @@ if ! disabled "$KVM"; then
|
|||||||
else
|
else
|
||||||
|
|
||||||
# Intel processor
|
# Intel processor
|
||||||
|
local vmx
|
||||||
vmx=$(sed -ne '/^vmx flags/s/^.*: //p' /proc/cpuinfo)
|
vmx=$(sed -ne '/^vmx flags/s/^.*: //p' /proc/cpuinfo)
|
||||||
|
|
||||||
if grep -qw "tsc_scaling" <<< "$vmx"; then
|
if grep -qw "tsc_scaling" <<< "$vmx"; then
|
||||||
@@ -66,7 +87,35 @@ if ! disabled "$KVM"; then
|
|||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
else
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureKvm() {
|
||||||
|
|
||||||
|
configureKvmCpuModel
|
||||||
|
checkSse42
|
||||||
|
appendKvmInvtscFeature
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureTcgCpuModel() {
|
||||||
|
|
||||||
|
if [ -n "$CPU_MODEL" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ARCH" == "amd64" ]]; then
|
||||||
|
CPU_MODEL="max"
|
||||||
|
CPU_FEATURES+=",migratable=no"
|
||||||
|
else
|
||||||
|
CPU_MODEL="qemu64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureTcg() {
|
||||||
|
|
||||||
KVM_OPTS=""
|
KVM_OPTS=""
|
||||||
CPU_FEATURES="l3-cache=on,+hypervisor"
|
CPU_FEATURES="l3-cache=on,+hypervisor"
|
||||||
@@ -75,70 +124,95 @@ else
|
|||||||
KVM_OPTS=" -accel tcg,thread=multi"
|
KVM_OPTS=" -accel tcg,thread=multi"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$CPU_MODEL" ]; then
|
configureTcgCpuModel
|
||||||
if [[ "$ARCH" == "amd64" ]]; then
|
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
|
||||||
CPU_MODEL="max"
|
|
||||||
CPU_FEATURES+=",migratable=no"
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
extractHostCpuArgument() {
|
||||||
|
|
||||||
|
local args prefix suffix param
|
||||||
|
|
||||||
|
if [[ "$ARGUMENTS" == *"-cpu host,"* ]]; then
|
||||||
|
|
||||||
|
args="${ARGUMENTS} "
|
||||||
|
prefix="${args/-cpu host,*/}"
|
||||||
|
suffix="${args/*-cpu host,/}"
|
||||||
|
param="${suffix%% *}"
|
||||||
|
suffix="${suffix#* }"
|
||||||
|
args="${prefix}${suffix}"
|
||||||
|
ARGUMENTS="${args::-1}"
|
||||||
|
|
||||||
|
if [ -z "$CPU_FLAGS" ]; then
|
||||||
|
CPU_FLAGS="$param"
|
||||||
else
|
else
|
||||||
CPU_MODEL="qemu64"
|
CPU_FLAGS+=",$param"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if [[ "$ARGUMENTS" == *"-cpu host"* ]]; then
|
||||||
|
ARGUMENTS="${ARGUMENTS//-cpu host/}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
composeCpuFlags() {
|
||||||
|
|
||||||
|
if [ -z "$CPU_FLAGS" ]; then
|
||||||
|
if [ -z "$CPU_FEATURES" ]; then
|
||||||
|
CPU_FLAGS="$CPU_MODEL"
|
||||||
|
else
|
||||||
|
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if [ -z "$CPU_FEATURES" ]; then
|
||||||
|
CPU_FLAGS="$CPU_MODEL,$CPU_FLAGS"
|
||||||
|
else
|
||||||
|
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES,$CPU_FLAGS"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
fi
|
configureHostCpuName() {
|
||||||
|
|
||||||
if [[ "$ARGUMENTS" == *"-cpu host,"* ]]; then
|
if [ -z "$HOST_CPU" ]; then
|
||||||
|
[[ "${CPU,,}" != "unknown" ]] && HOST_CPU="$CPU"
|
||||||
args="${ARGUMENTS} "
|
|
||||||
prefix="${args/-cpu host,*/}"
|
|
||||||
suffix="${args/*-cpu host,/}"
|
|
||||||
param="${suffix%% *}"
|
|
||||||
suffix="${suffix#* }"
|
|
||||||
args="${prefix}${suffix}"
|
|
||||||
ARGUMENTS="${args::-1}"
|
|
||||||
|
|
||||||
if [ -z "$CPU_FLAGS" ]; then
|
|
||||||
CPU_FLAGS="$param"
|
|
||||||
else
|
|
||||||
CPU_FLAGS+=",$param"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$HOST_CPU" ]; then
|
||||||
|
HOST_CPU="${HOST_CPU%%,*},,"
|
||||||
|
else
|
||||||
|
HOST_CPU="QEMU, Virtual CPU,"
|
||||||
|
if [ "$ARCH" == "amd64" ]; then
|
||||||
|
HOST_CPU+=" X86_64"
|
||||||
|
else
|
||||||
|
HOST_CPU+=" $ARCH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
selectClocksource
|
||||||
|
checkClocksource
|
||||||
|
|
||||||
|
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
||||||
|
|
||||||
|
if ! disabled "$KVM"; then
|
||||||
|
configureKvm
|
||||||
else
|
else
|
||||||
|
configureTcg
|
||||||
if [[ "$ARGUMENTS" == *"-cpu host"* ]]; then
|
|
||||||
ARGUMENTS="${ARGUMENTS//-cpu host/}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$CPU_FLAGS" ]; then
|
extractHostCpuArgument
|
||||||
if [ -z "$CPU_FEATURES" ]; then
|
composeCpuFlags
|
||||||
CPU_FLAGS="$CPU_MODEL"
|
configureHostCpuName
|
||||||
else
|
|
||||||
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if [ -z "$CPU_FEATURES" ]; then
|
|
||||||
CPU_FLAGS="$CPU_MODEL,$CPU_FLAGS"
|
|
||||||
else
|
|
||||||
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES,$CPU_FLAGS"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$HOST_CPU" ]; then
|
|
||||||
[[ "${CPU,,}" != "unknown" ]] && HOST_CPU="$CPU"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$HOST_CPU" ]; then
|
|
||||||
HOST_CPU="${HOST_CPU%%,*},,"
|
|
||||||
else
|
|
||||||
HOST_CPU="QEMU, Virtual CPU,"
|
|
||||||
if [ "$ARCH" == "amd64" ]; then
|
|
||||||
HOST_CPU+=" X86_64"
|
|
||||||
else
|
|
||||||
HOST_CPU+=" $ARCH"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+204
-155
@@ -21,6 +21,203 @@ enabled "${TRACE:-}" && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DE
|
|||||||
: "${DISK_SIZE:="16G"}" # Initial data disk size
|
: "${DISK_SIZE:="16G"}" # Initial data disk size
|
||||||
: "${STORAGE:="/storage"}" # Storage folder location
|
: "${STORAGE:="/storage"}" # Storage folder location
|
||||||
|
|
||||||
|
detectEngine() {
|
||||||
|
|
||||||
|
if [ -f "/run/.containerenv" ]; then
|
||||||
|
ENGINE="${container:-}"
|
||||||
|
if [[ "${ENGINE,,}" == *"podman"* ]]; then
|
||||||
|
ROOTLESS="Y"
|
||||||
|
ENGINE="Podman"
|
||||||
|
else
|
||||||
|
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPrivileged() {
|
||||||
|
|
||||||
|
local cap_bnd
|
||||||
|
local last_cap
|
||||||
|
local max_cap
|
||||||
|
|
||||||
|
# Get the capability bounding set
|
||||||
|
cap_bnd=$(grep '^CapBnd:' /proc/$$/status | awk '{print $2}')
|
||||||
|
cap_bnd=$(printf "%d" "0x${cap_bnd}")
|
||||||
|
|
||||||
|
# Get the last capability number
|
||||||
|
last_cap=$(cat /proc/sys/kernel/cap_last_cap)
|
||||||
|
|
||||||
|
# Calculate the maximum capability value
|
||||||
|
max_cap=$(((1 << (last_cap + 1)) - 1))
|
||||||
|
|
||||||
|
if [ "$cap_bnd" -eq "$max_cap" ]; then
|
||||||
|
ROOTLESS="N"
|
||||||
|
PRIVILEGED="Y"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeCpuCores() {
|
||||||
|
|
||||||
|
CPU_CORES=$(strip "$CPU_CORES")
|
||||||
|
[ -z "$CPU_CORES" ] && CPU_CORES=2
|
||||||
|
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
|
||||||
|
[[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 ))
|
||||||
|
[ -z "${CPU_CORES##*[!0-9]*}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
|
||||||
|
|
||||||
|
[ "$CPU_CORES" -lt "1" ] && CPU_CORES=1
|
||||||
|
if [ "$CPU_CORES" -gt "$CORES" ]; then
|
||||||
|
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available (${CORES}) and will be limited."
|
||||||
|
CPU_CORES="$CORES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStorage() {
|
||||||
|
|
||||||
|
# Check system
|
||||||
|
|
||||||
|
QEMU_DIR="/run/shm"
|
||||||
|
|
||||||
|
if [ ! -d "/dev/shm" ]; then
|
||||||
|
error "Directory /dev/shm not found!" && exit 14
|
||||||
|
else
|
||||||
|
[ ! -d "$QEMU_DIR" ] && ln -s /dev/shm "$QEMU_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
QEMU_PID="$QEMU_DIR/qemu.pid"
|
||||||
|
|
||||||
|
# Check folder
|
||||||
|
|
||||||
|
if [[ "${STORAGE,,}" != "/storage" ]]; then
|
||||||
|
mkdir -p "$STORAGE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$STORAGE" ]; then
|
||||||
|
error "Storage folder ($STORAGE) not found!" && exit 13
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -w "$STORAGE" ]; then
|
||||||
|
msg="Storage folder ($STORAGE) is not writeable!"
|
||||||
|
msg+=" If SELinux is active, you need to add the \":Z\" flag to the bind mount."
|
||||||
|
error "$msg" && exit 13
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFilesystem() {
|
||||||
|
|
||||||
|
# Check filesystem
|
||||||
|
FS=$(stat -f -c %T "$STORAGE")
|
||||||
|
|
||||||
|
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
||||||
|
DISK_IO="threads"
|
||||||
|
DISK_CACHE="writeback"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeRamSize() {
|
||||||
|
|
||||||
|
# Read memory
|
||||||
|
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||||
|
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
||||||
|
|
||||||
|
RAM_SPARE=500000000
|
||||||
|
RAM_MINIMUM=136314880
|
||||||
|
|
||||||
|
RAM_SIZE=$(strip "$RAM_SIZE")
|
||||||
|
RAM_SIZE="${RAM_SIZE// /}"
|
||||||
|
[ -z "$RAM_SIZE" ] && RAM_SIZE="2G"
|
||||||
|
|
||||||
|
if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
|
||||||
|
|
||||||
|
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
|
||||||
|
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
|
||||||
|
fi
|
||||||
|
|
||||||
|
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||||
|
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
|
||||||
|
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
||||||
|
[ "$wanted" -lt "$RAM_MINIMUM" ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkKvm() {
|
||||||
|
|
||||||
|
# Check KVM support
|
||||||
|
|
||||||
|
if [[ "${PLATFORM,,}" == "x64" ]]; then
|
||||||
|
TARGET="amd64"
|
||||||
|
else
|
||||||
|
TARGET="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if disabled "$KVM"; then
|
||||||
|
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
|
||||||
|
else
|
||||||
|
if [[ "${ARCH,,}" != "$TARGET" ]]; then
|
||||||
|
KVM="N"
|
||||||
|
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ${PLATFORM^^} instructions, so the machine will run about 10 times slower."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! disabled "$KVM"; then
|
||||||
|
|
||||||
|
KVM_ERR=""
|
||||||
|
|
||||||
|
if [ ! -e /dev/kvm ]; then
|
||||||
|
KVM_ERR="(/dev/kvm is missing)"
|
||||||
|
else
|
||||||
|
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
||||||
|
KVM_ERR="(/dev/kvm is unwriteable)"
|
||||||
|
else
|
||||||
|
if [[ "${PLATFORM,,}" == "x64" ]]; then
|
||||||
|
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
||||||
|
if ! grep -qw "vmx\|svm" <<< "$flags"; then
|
||||||
|
KVM_ERR="(not enabled in BIOS)"
|
||||||
|
fi
|
||||||
|
if ! grep -qw "sse4_2" <<< "$flags"; then
|
||||||
|
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
||||||
|
! enabled "$DEBUG" && exit 88
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$KVM_ERR" ]; then
|
||||||
|
KVM="N"
|
||||||
|
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
||||||
|
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
|
||||||
|
else
|
||||||
|
kernel=$(uname -a)
|
||||||
|
case "${kernel,,}" in
|
||||||
|
*"microsoft"* )
|
||||||
|
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
|
||||||
|
*"synology"* )
|
||||||
|
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
|
||||||
|
*)
|
||||||
|
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
|
||||||
|
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
|
||||||
|
esac
|
||||||
|
! enabled "$DEBUG" && exit 88
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# Sanitize variables
|
# Sanitize variables
|
||||||
TZ=$(strip "$TZ")
|
TZ=$(strip "$TZ")
|
||||||
STORAGE=$(strip "$STORAGE")
|
STORAGE=$(strip "$STORAGE")
|
||||||
@@ -34,33 +231,12 @@ ENGINE="Docker"
|
|||||||
PROCESS="${APP,,}"
|
PROCESS="${APP,,}"
|
||||||
PROCESS="${PROCESS// /-}"
|
PROCESS="${PROCESS// /-}"
|
||||||
|
|
||||||
if [ -f "/run/.containerenv" ]; then
|
detectEngine
|
||||||
ENGINE="${container:-}"
|
|
||||||
if [[ "${ENGINE,,}" == *"podman"* ]]; then
|
|
||||||
ROOTLESS="Y"
|
|
||||||
ENGINE="Podman"
|
|
||||||
else
|
|
||||||
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "❯ Starting $APP for $ENGINE v$(</etc/version)..."
|
echo "❯ Starting $APP for $ENGINE v$(</etc/version)..."
|
||||||
echo "❯ For support visit $SUPPORT"
|
echo "❯ For support visit $SUPPORT"
|
||||||
|
|
||||||
# Get the capability bounding set
|
checkPrivileged
|
||||||
CAP_BND=$(grep '^CapBnd:' /proc/$$/status | awk '{print $2}')
|
|
||||||
CAP_BND=$(printf "%d" "0x${CAP_BND}")
|
|
||||||
|
|
||||||
# Get the last capability number
|
|
||||||
LAST_CAP=$(cat /proc/sys/kernel/cap_last_cap)
|
|
||||||
|
|
||||||
# Calculate the maximum capability value
|
|
||||||
MAX_CAP=$(((1 << (LAST_CAP + 1)) - 1))
|
|
||||||
|
|
||||||
if [ "${CAP_BND}" -eq "${MAX_CAP}" ]; then
|
|
||||||
ROOTLESS="N"
|
|
||||||
PRIVILEGED="Y"
|
|
||||||
fi
|
|
||||||
|
|
||||||
INFO="/run/shm/msg.html"
|
INFO="/run/shm/msg.html"
|
||||||
PAGE="/run/shm/index.html"
|
PAGE="/run/shm/index.html"
|
||||||
@@ -71,7 +247,6 @@ FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
|||||||
SOCKETS=1
|
SOCKETS=1
|
||||||
CPU=$(cpu)
|
CPU=$(cpu)
|
||||||
SYS=$(uname -r)
|
SYS=$(uname -r)
|
||||||
HOST=$(hostname -s)
|
|
||||||
KERNEL=$(echo "$SYS" | cut -b 1)
|
KERNEL=$(echo "$SYS" | cut -b 1)
|
||||||
MINOR=$(echo "$SYS" | cut -d '.' -f2)
|
MINOR=$(echo "$SYS" | cut -d '.' -f2)
|
||||||
ARCH=$(dpkg --print-architecture)
|
ARCH=$(dpkg --print-architecture)
|
||||||
@@ -83,77 +258,10 @@ if grep -qi "socket(s)" <<< "$(lscpu)"; then
|
|||||||
[ "$SOCKETS" -lt "1" ] && SOCKETS=1
|
[ "$SOCKETS" -lt "1" ] && SOCKETS=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CPU_CORES=$(strip "$CPU_CORES")
|
normalizeCpuCores
|
||||||
[ -z "$CPU_CORES" ] && CPU_CORES=2
|
checkStorage
|
||||||
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
|
checkFilesystem
|
||||||
[[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 ))
|
normalizeRamSize
|
||||||
[ -z "${CPU_CORES##*[!0-9]*}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
|
|
||||||
|
|
||||||
[ "$CPU_CORES" -lt "1" ] && CPU_CORES=1
|
|
||||||
if [ "$CPU_CORES" -gt "$CORES" ]; then
|
|
||||||
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available (${CORES}) and will be limited."
|
|
||||||
CPU_CORES="$CORES"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check system
|
|
||||||
|
|
||||||
QEMU_DIR="/run/shm"
|
|
||||||
|
|
||||||
if [ ! -d "/dev/shm" ]; then
|
|
||||||
error "Directory /dev/shm not found!" && exit 14
|
|
||||||
else
|
|
||||||
[ ! -d "$QEMU_DIR" ] && ln -s /dev/shm "$QEMU_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
QEMU_PID="$QEMU_DIR/qemu.pid"
|
|
||||||
|
|
||||||
# Check folder
|
|
||||||
|
|
||||||
if [[ "${STORAGE,,}" != "/storage" ]]; then
|
|
||||||
mkdir -p "$STORAGE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d "$STORAGE" ]; then
|
|
||||||
error "Storage folder ($STORAGE) not found!" && exit 13
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -w "$STORAGE" ]; then
|
|
||||||
msg="Storage folder ($STORAGE) is not writeable!"
|
|
||||||
msg+=" If SELinux is active, you need to add the \":Z\" flag to the bind mount."
|
|
||||||
error "$msg" && exit 13
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check filesystem
|
|
||||||
FS=$(stat -f -c %T "$STORAGE")
|
|
||||||
|
|
||||||
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
|
||||||
DISK_IO="threads"
|
|
||||||
DISK_CACHE="writeback"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read memory
|
|
||||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
|
||||||
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
|
||||||
|
|
||||||
RAM_SPARE=500000000
|
|
||||||
RAM_MINIMUM=136314880
|
|
||||||
|
|
||||||
RAM_SIZE=$(strip "$RAM_SIZE")
|
|
||||||
RAM_SIZE="${RAM_SIZE// /}"
|
|
||||||
[ -z "$RAM_SIZE" ] && RAM_SIZE="2G"
|
|
||||||
|
|
||||||
if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
|
|
||||||
|
|
||||||
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
|
|
||||||
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
|
|
||||||
fi
|
|
||||||
|
|
||||||
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
|
||||||
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
|
|
||||||
wanted=$(numfmt --from=iec "$RAM_SIZE")
|
|
||||||
[ "$wanted" -lt "$RAM_MINIMUM" ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print system info
|
# Print system info
|
||||||
SYS="${SYS/-generic/}"
|
SYS="${SYS/-generic/}"
|
||||||
@@ -168,66 +276,7 @@ TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
|
|||||||
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
||||||
echo
|
echo
|
||||||
|
|
||||||
# Check KVM support
|
checkKvm
|
||||||
|
|
||||||
if [[ "${PLATFORM,,}" == "x64" ]]; then
|
|
||||||
TARGET="amd64"
|
|
||||||
else
|
|
||||||
TARGET="arm64"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if disabled "$KVM"; then
|
|
||||||
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
|
|
||||||
else
|
|
||||||
if [[ "${ARCH,,}" != "$TARGET" ]]; then
|
|
||||||
KVM="N"
|
|
||||||
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ${PLATFORM^^} instructions, so the machine will run about 10 times slower."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! disabled "$KVM"; then
|
|
||||||
|
|
||||||
KVM_ERR=""
|
|
||||||
|
|
||||||
if [ ! -e /dev/kvm ]; then
|
|
||||||
KVM_ERR="(/dev/kvm is missing)"
|
|
||||||
else
|
|
||||||
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
|
||||||
KVM_ERR="(/dev/kvm is unwriteable)"
|
|
||||||
else
|
|
||||||
if [[ "${PLATFORM,,}" == "x64" ]]; then
|
|
||||||
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
|
||||||
if ! grep -qw "vmx\|svm" <<< "$flags"; then
|
|
||||||
KVM_ERR="(not enabled in BIOS)"
|
|
||||||
fi
|
|
||||||
if ! grep -qw "sse4_2" <<< "$flags"; then
|
|
||||||
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
|
|
||||||
! enabled "$DEBUG" && exit 88
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$KVM_ERR" ]; then
|
|
||||||
KVM="N"
|
|
||||||
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
|
||||||
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
|
|
||||||
else
|
|
||||||
kernel=$(uname -a)
|
|
||||||
case "${kernel,,}" in
|
|
||||||
*"microsoft"* )
|
|
||||||
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
|
|
||||||
*"synology"* )
|
|
||||||
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
|
|
||||||
*)
|
|
||||||
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
|
|
||||||
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
|
|
||||||
esac
|
|
||||||
! enabled "$DEBUG" && exit 88
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Cleanup files
|
# Cleanup files
|
||||||
rm -f "$QEMU_DIR"/dsm.url
|
rm -f "$QEMU_DIR"/dsm.url
|
||||||
|
|||||||
+66
-35
@@ -15,7 +15,12 @@ HOST_MODEL=$(strip "$HOST_MODEL")
|
|||||||
HOST_SERIAL=$(strip "$HOST_SERIAL")
|
HOST_SERIAL=$(strip "$HOST_SERIAL")
|
||||||
GUEST_SERIAL=$(strip "$GUEST_SERIAL")
|
GUEST_SERIAL=$(strip "$GUEST_SERIAL")
|
||||||
|
|
||||||
if [ -n "$HOST_MAC" ]; then
|
validateHostMac() {
|
||||||
|
local m
|
||||||
|
|
||||||
|
if [ -z "$HOST_MAC" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
HOST_MAC="${HOST_MAC//-/:}"
|
HOST_MAC="${HOST_MAC//-/:}"
|
||||||
|
|
||||||
@@ -28,51 +33,77 @@ if [ -n "$HOST_MAC" ]; then
|
|||||||
error "Invalid HOST_MAC address: '$HOST_MAC', should be 12 or 17 digits long!" && exit 28
|
error "Invalid HOST_MAC address: '$HOST_MAC', should be 12 or 17 digits long!" && exit 28
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
HOST_PID="$QEMU_DIR/host.pid"
|
buildHostArguments() {
|
||||||
|
|
||||||
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 enabled "$HOST_DEBUG"; then
|
return 0
|
||||||
set -x
|
}
|
||||||
./host.bin "${HOST_ARGS[@]}" &
|
|
||||||
{ set +x; } 2>/dev/null
|
|
||||||
echo "$!" > "$HOST_PID"
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
./host.bin "${HOST_ARGS[@]}" >/dev/null &
|
|
||||||
echo "$!" > "$HOST_PID"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cnt=0
|
startHostBinary() {
|
||||||
sleep 0.2
|
|
||||||
|
|
||||||
while ! nc -z -w2 127.0.0.1 "$COM_PORT" > /dev/null 2>&1; do
|
if enabled "$HOST_DEBUG"; then
|
||||||
sleep 0.1
|
set -x
|
||||||
cnt=$((cnt + 1))
|
./host.bin "${HOST_ARGS[@]}" &
|
||||||
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
|
{ set +x; } 2>/dev/null
|
||||||
done
|
echo "$!" > "$HOST_PID"
|
||||||
|
echo
|
||||||
|
else
|
||||||
|
./host.bin "${HOST_ARGS[@]}" >/dev/null &
|
||||||
|
echo "$!" > "$HOST_PID"
|
||||||
|
fi
|
||||||
|
|
||||||
cnt=0
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
while ! nc -z -w2 127.0.0.1 "$CHR_PORT" > /dev/null 2>&1; do
|
waitForPort() {
|
||||||
sleep 0.1
|
|
||||||
cnt=$((cnt + 1))
|
|
||||||
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
|
|
||||||
done
|
|
||||||
|
|
||||||
# Configure serial ports
|
local port="$1"
|
||||||
SERIAL_OPTS="-serial mon:stdio \
|
local exit_code="$2"
|
||||||
|
local cnt=0
|
||||||
|
|
||||||
|
while ! nc -z -w2 127.0.0.1 "$port" > /dev/null 2>&1; do
|
||||||
|
sleep 0.1
|
||||||
|
cnt=$((cnt + 1))
|
||||||
|
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit "$exit_code"
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureSerialPorts() {
|
||||||
|
|
||||||
|
# Configure serial ports
|
||||||
|
SERIAL_OPTS="-serial mon:stdio \
|
||||||
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
|
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
|
||||||
-chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \
|
-chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \
|
||||||
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
|
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
validateHostMac
|
||||||
|
|
||||||
|
HOST_PID="$QEMU_DIR/host.pid"
|
||||||
|
|
||||||
|
buildHostArguments
|
||||||
|
startHostBinary
|
||||||
|
|
||||||
|
sleep 0.2
|
||||||
|
|
||||||
|
waitForPort "$COM_PORT" 58
|
||||||
|
waitForPort "$CHR_PORT" 59
|
||||||
|
|
||||||
|
configureSerialPorts
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
+40
-11
@@ -15,33 +15,62 @@ WSD_PORT=$(strip "$WSD_PORT")
|
|||||||
WEB_PID="/run/nginx.pid"
|
WEB_PID="/run/nginx.pid"
|
||||||
WSD_PID="$QEMU_DIR/websocketd.pid"
|
WSD_PID="$QEMU_DIR/websocketd.pid"
|
||||||
|
|
||||||
cp -r /var/www/* "$QEMU_DIR"
|
prepareWebFiles() {
|
||||||
rm -f "$WSD_PID" "$WEB_PID"
|
cp -r /var/www/* "$QEMU_DIR"
|
||||||
|
rm -f "$WSD_PID" "$WEB_PID"
|
||||||
|
|
||||||
html "Starting $APP for $ENGINE..."
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
if ! disabled "${WEB:-}"; then
|
configureWebPorts() {
|
||||||
|
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
|
||||||
|
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureIpv6Listen() {
|
||||||
|
|
||||||
|
# shellcheck disable=SC2143
|
||||||
|
if [ -f /proc/net/if_inet6 ] && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)" != "1" ]] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
||||||
|
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureWebServer() {
|
||||||
|
|
||||||
mkdir -p /etc/nginx/sites-enabled
|
mkdir -p /etc/nginx/sites-enabled
|
||||||
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
|
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
|
||||||
|
|
||||||
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
|
configureWebPorts
|
||||||
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
|
configureIpv6Listen
|
||||||
|
|
||||||
# shellcheck disable=SC2143
|
return 0
|
||||||
if [ -f /proc/net/if_inet6 ] && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null)" != "1" ]] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
}
|
||||||
|
|
||||||
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
startWebServer() {
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start webserver
|
# Start webserver
|
||||||
nginx -e stderr
|
nginx -e stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
startWebsocketServer() {
|
||||||
|
|
||||||
# Start websocket server
|
# Start websocket server
|
||||||
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
|
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
|
||||||
echo "$!" > "$WSD_PID"
|
echo "$!" > "$WSD_PID"
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareWebFiles
|
||||||
|
|
||||||
|
html "Starting $APP for $ENGINE..."
|
||||||
|
|
||||||
|
if ! disabled "${WEB:-}"; then
|
||||||
|
configureWebServer
|
||||||
|
startWebServer
|
||||||
|
startWebsocketServer
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
Reference in New Issue
Block a user