Compare commits

...

53 Commits
v5.05 ... v5.14

Author SHA1 Message Date
Kroese
b9ae73e322 build: Remove apt-get upgrade (#543) 2024-01-06 03:17:50 +01:00
Kroese
c28dbb6b9b feat: Add FUSE warning (#542)
* feat: Add FUSE warning
2024-01-05 13:24:54 +01:00
Kroese
dcc26b67a6 fix: Diskspace check (#541)
* fix: Diskspace check
2024-01-05 02:27:15 +01:00
Kroese
2627d320f3 fix: Remove VNC support (#540)
* fix: Remove VNC support
2024-01-05 00:02:49 +01:00
Kroese
fd4bc28835 fix: Console mode (#538) 2024-01-04 23:24:34 +01:00
Kroese
63e4d588a2 docs: Readme (#535) 2024-01-04 05:33:30 +01:00
Kroese
feb1680909 build: Update location (#534) 2024-01-04 05:31:34 +01:00
IP2Location
9d21489b8e Added api.ip2location.io (#533) 2024-01-04 04:11:20 +01:00
Kroese
e70ed1900f fix: Port forwarding (#530) 2024-01-03 14:02:16 +01:00
Kroese
d65b5a089a fix: KVM check (#529) 2024-01-03 13:33:38 +01:00
Kroese
84643647cc fix: Compose (#527) 2023-12-31 15:11:20 +01:00
Kroese
57f487db6d build: Remove agent
* build: Remove agent
2023-12-31 01:56:03 +01:00
Kroese
0862cad2ce fix: Port forwarding (#525)
* fix: Port forwarding
2023-12-30 00:01:12 +01:00
Kroese
5ae4f59315 fix: QEMU monitor (#524) 2023-12-29 19:05:51 +01:00
Kroese
3fc3005ba5 fix: Processor (#523)
* fix: Processor
2023-12-29 18:27:51 +01:00
Kroese
20f48edd00 fix: Serial port (#522)
* fix: Serial port
2023-12-29 18:15:39 +01:00
Kroese
b854aad830 fix: Use single IO thread (#521) 2023-12-29 17:34:23 +01:00
Kroese
6cbe03f656 fix: Port forwarding (#520) 2023-12-29 17:20:18 +01:00
Kroese
63cac9a75e fix: Display config (#519)
* fix: Display config
2023-12-29 16:56:49 +01:00
Kroese
08616f1057 build: Update Dockerfile (#518) 2023-12-29 00:24:36 +01:00
Kroese
e6193b1020 fix: Port forwarding (#517) 2023-12-28 21:30:28 +01:00
Kroese
f28b9903f3 fix: iptables for NAT (#516) 2023-12-28 21:20:38 +01:00
Kroese
7bf2d119ea feat: Validate mac address (#515) 2023-12-28 20:54:33 +01:00
Kroese
527bded1b2 feat: Detect default interface
* feat: Detect default interface
2023-12-28 20:25:04 +01:00
Kroese
1208c53ebb feat: Check network interface (#513) 2023-12-28 18:26:56 +01:00
Kroese
973efa2d27 feat: Show daemon log
* feat: Show daemon log
2023-12-28 17:58:07 +01:00
Kroese
d09588b915 fix: Refactor
* fix: Refactor
2023-12-28 16:08:12 +01:00
Kroese
19aa313753 fix: Shellcheck (#509) 2023-12-28 08:35:51 +01:00
Kroese
9db12cd25f fix: Device detection
* fix: Device detection
2023-12-28 08:31:32 +01:00
Kroese
69e785e6ee fix: Shutdown message
* fix: Shutdown message
2023-12-28 05:26:53 +01:00
Kroese
159fce6839 fix: Detect device
* fix: Detect device
2023-12-28 05:04:40 +01:00
Kroese
08e4084458 feat: Daemonize QEMU
* feat: Daemonize QEMU
2023-12-28 03:42:06 +01:00
Kroese
06f210846c fix: KVM flag (#504) 2023-12-27 16:28:24 +01:00
Kroese
74629e4b55 fix: Installation (#501) 2023-12-27 04:07:45 +01:00
Kroese
6e8af6e52f fix: Host CPU (#500) 2023-12-27 03:23:39 +01:00
Kroese
38611a7af2 fix: Host CPU (#499) 2023-12-27 03:18:03 +01:00
Kroese
f089acc01a fix: CPU features (#496) 2023-12-25 05:58:14 +01:00
Kroese
5a7ecb48d6 fix: Error messages (#495) 2023-12-25 05:03:00 +01:00
Kroese
5b3880aa5e fix: Error messages (#494) 2023-12-25 04:48:18 +01:00
Kroese
4653aafbee docs: Readme (#493) 2023-12-25 04:21:50 +01:00
Kroese
281f2992ff fix: Close file descriptors
* fix: Close file descriptors
2023-12-25 04:04:01 +01:00
Kroese
4bdcf8bfe1 fix: Skip mknod errors (#491) 2023-12-24 14:39:37 +01:00
Kroese
62acaa95bf fix: Check attribute (#490) 2023-12-24 14:27:29 +01:00
Kroese
369bff339d fix: Update Dockerfile (#489) 2023-12-24 02:54:57 +01:00
Kroese
5332d387f4 fix: Set file attribute (#488)
* fix: Set file attribute
2023-12-24 02:41:44 +01:00
Kroese
f0d08ef263 fix: Remove flag (#487)
* fix: Remove flag
2023-12-23 22:58:45 +01:00
Kroese
e4334f9499 fix: Ignore mknod errors (#486) 2023-12-23 22:28:25 +01:00
Kroese
627ec56262 feat: LINUX_IMMUTABLE flag (#485)
* feat: LINUX_IMMUTABLE flag
2023-12-23 21:26:23 +01:00
Kroese
251cf8121e feat: Add LINUX_IMMUTABLE flag (#484)
To change directory attributes on COW filesystems
2023-12-23 20:35:06 +01:00
Kroese
87fad1b0e9 feat: Set directory attributes (#483)
* feat: Set directory attributes
2023-12-23 20:01:27 +01:00
Kroese
698516ac8c feat: Detect country from timezone (#482) 2023-12-23 18:46:14 +01:00
databreach
dae5d75674 feat: Improve support for unprivileged hosts (including LXC) (#479)
* * Add fakeroot to extract the dsm system without elevated permissions
* Remove obsolete docker variable "DEV" used to exclude extraction of device nodes

* feat: Detect unprivileged container

* fix: Use fakeroot for mke2fs

---------

Co-authored-by: Kroese <kroese@users.noreply.github.com>
2023-12-23 18:04:43 +01:00
Kroese
95facffa9b fix: Use specified LAN adaptor (#481)
* fix: Use specified LAN adaptor
2023-12-23 15:23:36 +01:00
20 changed files with 572 additions and 568 deletions

View File

@@ -11,4 +11,4 @@ jobs:
- name: Run ShellCheck - name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
env: env:
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 -e SC2317 -e SC2028 -e SC2153 -e SC2004 SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028

View File

@@ -2,7 +2,7 @@ FROM qemux/qemu-host as builder
# FROM golang as builder # FROM golang as builder
# WORKDIR / # WORKDIR /
# RUN git clone https://github.com/qemu-tools/qemu-host.git # RUN git clone https://github.com/qemus/qemu-host.git
# WORKDIR /qemu-host/src # WORKDIR /qemu-host/src
# RUN go mod download # RUN go mod download
# RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host.bin . # RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host.bin .
@@ -13,8 +13,8 @@ ARG TARGETPLATFORM
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND noninteractive ARG DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get -y upgrade \ RUN if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi \
&& if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi \ && apt-get update \
&& apt-get --no-install-recommends -y install \ && apt-get --no-install-recommends -y install \
jq \ jq \
tini \ tini \
@@ -29,6 +29,7 @@ RUN apt-get update && apt-get -y upgrade \
iptables \ iptables \
iproute2 \ iproute2 \
dnsmasq \ dnsmasq \
fakeroot \
net-tools \ net-tools \
qemu-utils \ qemu-utils \
ca-certificates \ ca-certificates \
@@ -43,12 +44,7 @@ COPY --from=builder /qemu-host.bin /run/host.bin
RUN chmod +x /run/*.sh && chmod +x /run/*.bin RUN chmod +x /run/*.sh && chmod +x /run/*.bin
VOLUME /storage VOLUME /storage
EXPOSE 22 139 445 5000
EXPOSE 22
EXPOSE 80
EXPOSE 139
EXPOSE 445
EXPOSE 5000
ENV RAM_SIZE "1G" ENV RAM_SIZE "1G"
ENV DISK_SIZE "16G" ENV DISK_SIZE "16G"

View File

@@ -1,140 +0,0 @@
#!/usr/bin/env bash
set -u
VERSION="9"
HEADER="VirtualDSM Agent"
# Functions
error () { echo -e "\E[1;31m ERROR: $1\E[0m" ; }
info () { echo -e "\E[1;34m\E[1;36m $1\E[0m" ; }
finish() {
echo " $HEADER: Shutting down.."
exit
}
function checkNMI {
local nmi
nmi=$(cat /proc/interrupts | grep NMI | sed 's/[^1-9]*//g')
if [ "$nmi" != "" ]; then
info "Received shutdown request through NMI.."
/usr/syno/sbin/synoshutdown -s > /dev/null
finish
fi
}
function downloadUpdate {
TMP="/tmp/agent.sh"
rm -f "${TMP}"
# Auto update the agent
URL="https://raw.githubusercontent.com/vdsm/virtual-dsm/master/agent/agent.sh"
remote_size=$(curl -sIk -m 4 "${URL}" | grep -i "content-length:" | tr -d " \t" | cut -d ':' -f 2)
remote_size=${remote_size//$'\r'}
[[ "$remote_size" == "" || "$remote_size" == "0" ]] && return
remote_size=$(($remote_size+0))
((remote_size<100)) && return
SCRIPT=$(readlink -f "${BASH_SOURCE[0]}")
local_size=$(stat -c%s "$SCRIPT")
local_size=$(($local_size+0))
[[ remote_size -eq local_size ]] && return
if ! curl -sfk -m 10 -o "${TMP}" "${URL}"; then
error "$HEADER: curl error ($?)" && return
fi
if [ ! -f "${TMP}" ]; then
error "$HEADER: update error, file not found.." && return
fi
line=$(head -1 "${TMP}")
if [[ "$line" != "#!/usr/bin/env bash" ]]; then
error "$HEADER: update error, invalid header: $line" && return
fi
if cmp --silent -- "${TMP}" "${SCRIPT}"; then
error "$HEADER: update file is already equal? (${local_size} / ${remote_size})" && return
fi
mv -f "${TMP}" "${SCRIPT}"
chmod 755 "${SCRIPT}"
info "$HEADER: succesfully installed update..."
}
function installPackages {
for filename in /usr/local/packages/*.spk; do
if [ -f "$filename" ]; then
BASE=$(basename "$filename" .spk)
BASE="${BASE%%-*}"
[[ $BASE == "ActiveInsight" ]] && continue
info "Installing package ${BASE}.."
/usr/syno/bin/synopkg install "$filename" > /dev/null
/usr/syno/bin/synopkg start "$BASE" > /dev/null &
rm "$filename"
fi
done
}
trap finish SIGINT SIGTERM
ts=$(date +%s%N)
echo ""
echo " Started $HEADER v$VERSION..."
checkNMI
# Install packages
first_run=false
for filename in /usr/local/packages/*.spk; do
if [ -f "$filename" ]; then
first_run=true
fi
done
if [ "$first_run" = true ]; then
installPackages
else
downloadUpdate
fi
# Wait for NMI interrupt as a shutdown signal
while true; do
checkNMI
sleep 2 & wait $!
done

View File

@@ -1,87 +0,0 @@
#!/bin/bash
PIDFILE="/var/run/agent.pid"
SCRIPT="/usr/local/bin/agent.sh"
error () { echo -e "\E[1;31m ERROR: $1\E[0m" ; }
info () { echo -e "\E[1;34m\E[1;36m $1\E[0m" ; }
status() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")"; then
echo 'Service running'
return 1
fi
return 0
}
start() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")"; then
echo 'Service already running'
return 1
fi
echo 'Starting agent service...'
chmod 666 /dev/ttyS0
if [ ! -f "$SCRIPT" ]; then
URL="https://raw.githubusercontent.com/vdsm/virtual-dsm/master/agent/agent.sh"
if ! curl -sfk -m 10 -o "${SCRIPT}" "${URL}"; then
error 'Failed to download agent script.' > /dev/ttyS0
rm -f "${SCRIPT}"
return 1
else
info 'Agent script was missing?' > /dev/ttyS0
fi
chmod 755 "${SCRIPT}"
fi
echo "-" > /var/lock/subsys/agent.sh
"$SCRIPT" &> /dev/ttyS0 & echo $! > "$PIDFILE"
return 0
}
stop() {
if [ ! -f "$PIDFILE" ] || ! kill -0 "$(cat "$PIDFILE")"; then
echo 'Service not running'
return 1
fi
echo 'Stopping agent service...'
chmod 666 /dev/ttyS0
info 'Stopping agent service...' > /dev/ttyS0
kill -15 "$(cat "$PIDFILE")" && rm -f "$PIDFILE"
rm -f /var/lock/subsys/agent.sh
echo 'Service stopped'
return 0
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac

View File

@@ -9,8 +9,6 @@ services:
CPU_CORES: "1" CPU_CORES: "1"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun
- /dev/vhost-net
device_cgroup_rules: device_cgroup_rules:
- 'c *:* rwm' - 'c *:* rwm'
cap_add: cap_add:

View File

@@ -122,7 +122,7 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
sudo kvm-ok sudo kvm-ok
``` ```
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check your BIOS settings. If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check the virtualization settings in the BIOS.
* ### How do I assign an individual IP address to the container? * ### How do I assign an individual IP address to the container?
@@ -169,8 +169,6 @@ docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-ti
```yaml ```yaml
environment: environment:
DHCP: "Y" DHCP: "Y"
devices:
- /dev/vhost-net
device_cgroup_rules: device_cgroup_rules:
- 'c *:* rwm' - 'c *:* rwm'
``` ```

View File

@@ -1,10 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0 [ -f "/run/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1 [ ! -f "/run/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
file="/run/dsm.url" file="/run/dsm.url"
address="/run/qemu.ip"
[ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1 [ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
location=$(cat "$file") location=$(cat "$file")
@@ -17,7 +19,7 @@ if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
echo "Failed to reach DSM at port $port" echo "Failed to reach DSM at port $port"
else else
echo "Failed to reach DSM at http://$location" echo "Failed to reach DSM at http://$location"
ip=$(ip address show dev eth0 | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/) ip="$(cat "$address")"
fi fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1 echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1

View File

@@ -1,20 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
DEF_OPTS="-nographic -nodefaults -boot strict=on -display none" DEF_OPTS="-nodefaults -boot strict=on"
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="-cpu $CPU_MODEL -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1" CPU_OPTS="-cpu $CPU_MODEL -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}" MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4" DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom" DEV_OPTS="$DEV_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" DEV_OPTS="$DEV_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
if [[ "$GPU" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $DEV_OPTS $ARGUMENTS"
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"
fi
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

@@ -1,61 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: ${HOST_CPU:=''}
: ${CPU_MODEL:='host'}
: ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'}
KVM_ERR=""
KVM_OPTS=""
if [[ "$ARCH" == "amd64" && "$KVM" != [Nn]* ]]; then
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi
if [ -n "$KVM_ERR" ]; then
error "KVM acceleration not detected $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ on how to enable it, or skip this error by setting KVM=N (not recommended)."
[[ "$DEBUG" != [Yy1]* ]] && exit 88
[[ "$CPU_MODEL" == "host"* ]] && CPU_MODEL="max,$CPU_FEATURES"
else
KVM_OPTS=",accel=kvm -enable-kvm"
fi
if [ -n "$KVM_OPTS" ]; then
if ! grep -qE '^flags.* (sse4_2)' /proc/cpuinfo; then
error "Your host CPU does not have the SSE4.2 instruction set that Virtual DSM requires to boot."
error "Disable KVM by setting KVM=N to emulate a compatible CPU, at the cost of performance."
[[ "$DEBUG" != [Yy1]* ]] && exit 89
fi
fi
else
[[ "$CPU_MODEL" == "host"* ]] && CPU_MODEL="max,$CPU_FEATURES"
fi
if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
if [ -n "$HOST_CPU" ]; then
HOST_CPU="${HOST_CPU%%,*},,"
else
HOST_CPU="QEMU, Virtual CPU,"
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="$HOST_CPU X86_64"
else
HOST_CPU="$HOST_CPU $ARCH"
fi
fi
return 0

View File

@@ -5,10 +5,10 @@ set -Eeuo pipefail
: ${DISK_IO:='native'} # I/O Mode, can be set to 'native', 'threads' or 'io_turing' : ${DISK_IO:='native'} # I/O Mode, can be set to 'native', 'threads' or 'io_turing'
: ${DISK_FMT:='raw'} # Disk file format, 'raw' by default for best performance : ${DISK_FMT:='raw'} # Disk file format, 'raw' by default for best performance
: ${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:='on'} # Controls whether unmap (TRIM) commands are passed to the host.
: ${DISK_ROTATION:='1'} # Rotation rate, set to 1 for SSD storage and increase for HDD : ${DISK_ROTATION:='1'} # Rotation rate, set to 1 for SSD storage and increase for HDD
: ${DISK_FLAGS:=''} # Specifies the options for use with the qcow2 disk format
BOOT="$STORAGE/$BASE.boot.img" BOOT="$STORAGE/$BASE.boot.img"
SYSTEM="$STORAGE/$BASE.system.img" SYSTEM="$STORAGE/$BASE.system.img"
@@ -17,11 +17,11 @@ SYSTEM="$STORAGE/$BASE.system.img"
[ ! -f "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82 [ ! -f "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
DISK_OPTS="\ DISK_OPTS="\
-object iothread,id=io1 -object iothread,id=io2 \ -object iothread,id=io2 \
-device virtio-scsi-pci,id=hw-synoboot,iothread=io1,bus=pcie.0,addr=0xa \ -device virtio-scsi-pci,id=hw-synoboot,iothread=io2,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,iothread=io1,bus=pcie.0,addr=0xb \ -device virtio-scsi-pci,id=hw-synosys,iothread=io2,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"
@@ -77,15 +77,28 @@ getSize() {
esac esac
} }
isCow() {
local FS=$1
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "zfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
return 0
fi
return 1
}
createDisk() { createDisk() {
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 DATA_SIZE DIR SPACE local FS=$5
local DATA_SIZE DIR SPACE FA
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
rm -f "$DISK_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace # Check free diskspace
@@ -104,6 +117,14 @@ createDisk() {
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
raw) raw)
if isCow "$FS"; then
if ! touch "$DISK_FILE"; then
error "$FAIL" && exit 77
fi
{ chattr +C "$DISK_FILE"; } || :
fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
# Create an empty file # Create an empty file
@@ -125,15 +146,25 @@ createDisk() {
fi fi
;; ;;
qcow2) qcow2)
local DISK_OPTS="$DISK_ALLOC"
[ -n "$DISK_FLAGS" ] && DISK_OPTS="$DISK_OPTS,$DISK_FLAGS" local DISK_PARAM="$DISK_ALLOC"
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_OPTS" -- "$DISK_FILE" "$DATA_SIZE" ; then isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
[ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
rm -f "$DISK_FILE" rm -f "$DISK_FILE"
error "$FAIL" && exit 70 error "$FAIL" && exit 70
fi fi
;; ;;
esac esac
if isCow "$FS"; then
FA=$(lsattr "$DISK_FILE")
if [[ "$FA" != *"C"* ]]; then
error "Failed to disable COW for $DISK_DESC image $DISK_FILE on ${FS^^} filesystem (returned $FA)"
fi
fi
return 0 return 0
} }
@@ -142,6 +173,7 @@ resizeDisk() {
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 FS=$5
local CUR_SIZE DATA_SIZE DIR SPACE local CUR_SIZE DATA_SIZE DIR SPACE
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
@@ -168,6 +200,7 @@ resizeDisk() {
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
@@ -187,9 +220,11 @@ resizeDisk() {
fi fi
;; ;;
qcow2) qcow2)
if ! qemu-img resize -f "$DISK_FMT" "--$DISK_ALLOC" "$DISK_FILE" "$DATA_SIZE" ; then if ! qemu-img resize -f "$DISK_FMT" "--$DISK_ALLOC" "$DISK_FILE" "$DATA_SIZE" ; then
error "$FAIL" && exit 72 error "$FAIL" && exit 72
fi fi
;; ;;
esac esac
@@ -203,16 +238,18 @@ convertDisk() {
local DST_FMT=$4 local DST_FMT=$4
local DISK_BASE=$5 local DISK_BASE=$5
local DISK_DESC=$6 local DISK_DESC=$6
local CONV_FLAGS="-p" local FS=$7
local DISK_OPTS="$DISK_ALLOC"
local TMP_FILE="$DISK_BASE.tmp"
local DIR CUR_SIZE SPACE
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79 [ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79 [ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
local TMP_FILE="$DISK_BASE.tmp"
rm -f "$TMP_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
local DIR CUR_SIZE SPACE
# Check free diskspace # Check free diskspace
DIR=$(dirname "$TMP_FILE") DIR=$(dirname "$TMP_FILE")
CUR_SIZE=$(getSize "$SOURCE_FILE") CUR_SIZE=$(getSize "$SOURCE_FILE")
@@ -227,26 +264,29 @@ convertDisk() {
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..." info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
local CONV_FLAGS="-p"
local DISK_PARAM="$DISK_ALLOC"
isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
if [[ "$DST_FMT" != "raw" ]]; then if [[ "$DST_FMT" != "raw" ]]; then
if [[ "$ALLOCATE" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
CONV_FLAGS="$CONV_FLAGS -c" CONV_FLAGS="$CONV_FLAGS -c"
fi fi
[ -n "$DISK_FLAGS" ] && DISK_OPTS="$DISK_OPTS,$DISK_FLAGS" [ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
fi fi
rm -f "$TMP_FILE"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if ! qemu-img convert -f "$SOURCE_FMT" $CONV_FLAGS -o "$DISK_OPTS" -O "$DST_FMT" -- "$SOURCE_FILE" "$TMP_FILE"; then if ! qemu-img convert -f "$SOURCE_FMT" $CONV_FLAGS -o "$DISK_PARAM" -O "$DST_FMT" -- "$SOURCE_FILE" "$TMP_FILE"; then
rm -f "$TMP_FILE" rm -f "$TMP_FILE"
error "Failed to convert $DISK_TYPE $DISK_DESC image to $DST_FMT format in $DIR, is there enough space available?" && exit 79 error "Failed to convert $DISK_TYPE $DISK_DESC image to $DST_FMT format in $DIR, is there enough space available?" && exit 79
fi fi
if [[ "$DST_FMT" == "raw" ]]; then if [[ "$DST_FMT" == "raw" ]]; then
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
# Work around qemu-img bug
CUR_SIZE=$(stat -c%s "$TMP_FILE") CUR_SIZE=$(stat -c%s "$TMP_FILE")
if ! fallocate -l "$CUR_SIZE" "$TMP_FILE"; then if ! fallocate -l "$CUR_SIZE" "$TMP_FILE"; then
info "Failed to allocate $CUR_SIZE bytes for $TMP_FILE" error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
fi fi
fi fi
fi fi
@@ -254,44 +294,41 @@ convertDisk() {
rm -f "$SOURCE_FILE" rm -f "$SOURCE_FILE"
mv "$TMP_FILE" "$DST_FILE" mv "$TMP_FILE" "$DST_FILE"
if isCow "$FS"; then
FA=$(lsattr "$DST_FILE")
if [[ "$FA" != *"C"* ]]; then
error "Failed to disable COW for $DISK_DESC image $DST_FILE on ${FS^^} filesystem (returned $FA)"
fi
fi
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!" info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
return 0 return 0
} }
checkFS () { checkFS () {
local DISK_FILE=$1 local FS=$1
local DIR FS FA local DISK_FILE=$2
local DISK_DESC=$3
local DIR FA
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
FS=$(stat -f -c %T "$DIR") if [[ "${FS,,}" == "overlay"* ]]; then
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 [[ "$FS" == "xfs" || "$FS" == "zfs" || "$FS" == "btrfs" || "$FS" == "bcachefs" ]]; then if [[ "${FS,,}" == "fuse"* ]]; then
local FLAG="nocow" info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
if [[ "$DISK_FLAGS" != *"$FLAG="* ]]; then fi
if [ -z "$DISK_FLAGS" ]; then
DISK_FLAGS="$FLAG=on"
else
DISK_FLAGS="$DISK_FLAGS,$FLAG=on"
fi
fi
if [ -f "$DISK_FILE" ] ; then if isCow "$FS"; then
if [ -f "$DISK_FILE" ]; then
FA=$(lsattr "$DISK_FILE") FA=$(lsattr "$DISK_FILE")
[[ "$FA" == *"C"* ]] && FA=$(lsattr -d "$DIR") if [[ "$FA" != *"C"* ]]; then
else info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
FA=$(lsattr -d "$DIR") fi
fi
if [[ "$FA" != *"C"* ]]; then
info "Warning: the filesystem of $DIR is ${FS^^}, and COW (copy on write) is not disabled for that folder!"
info "This will negatively affect performance, please empty the folder and disable COW (chattr +C <path>)."
fi fi
fi fi
@@ -308,7 +345,7 @@ addDisk () {
local DISK_ADDRESS=$7 local DISK_ADDRESS=$7
local DISK_FMT=$8 local DISK_FMT=$8
local DISK_FILE="$DISK_BASE.$DISK_EXT" local DISK_FILE="$DISK_BASE.$DISK_EXT"
local DIR DATA_SIZE PREV_FMT PREV_EXT CUR_SIZE local DIR DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
@@ -325,7 +362,8 @@ addDisk () {
fi fi
fi fi
checkFS "$DISK_FILE" || exit $? FS=$(stat -f -c %T "$DIR")
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
if ! [ -f "$DISK_FILE" ] ; then if ! [ -f "$DISK_FILE" ] ; then
@@ -337,7 +375,7 @@ addDisk () {
PREV_EXT="$(fmt2ext "$PREV_FMT")" PREV_EXT="$(fmt2ext "$PREV_FMT")"
if [ -f "$DISK_BASE.$PREV_EXT" ] ; then if [ -f "$DISK_BASE.$PREV_EXT" ] ; then
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" || exit $? convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
fi fi
fi fi
@@ -346,12 +384,12 @@ addDisk () {
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
if (( DATA_SIZE > CUR_SIZE )); then if (( DATA_SIZE > CUR_SIZE )); then
resizeDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $? resizeDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi fi
else else
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $? createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi fi

35
src/display.sh Normal file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: ${GPU:='N'} # GPU passthrough
: ${DISPLAY:='none'} # Display type
if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
DISPLAY_OPTS="-display $DISPLAY -vga none"
return 0
fi
DISPLAY_OPTS="-display egl-headless,rendernode=/dev/dri/renderD128 -vga virtio"
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then
if mknod /dev/dri/card0 c 226 0; then
chmod 666 /dev/dri/card0
fi
fi
if [ ! -c /dev/dri/renderD128 ]; then
if mknod /dev/dri/renderD128 c 226 128; then
chmod 666 /dev/dri/renderD128
fi
fi
addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addPackage "qemu-system-modules-opengl" "OpenGL module"
return 0

View File

@@ -9,9 +9,9 @@ cd /run
. reset.sh # Initialize system . reset.sh # Initialize system
. install.sh # Run installation . install.sh # Run installation
. disk.sh # Initialize disks . disk.sh # Initialize disks
. display.sh # Initialize graphics
. network.sh # Initialize network . network.sh # Initialize network
. gpu.sh # Initialize graphics . proc.sh # Initialize processor
. cpu.sh # Initialize processor
. serial.sh # Initialize serialport . serial.sh # Initialize serialport
. power.sh # Configure shutdown . power.sh # Configure shutdown
. config.sh # Configure arguments . config.sh # Configure arguments
@@ -19,16 +19,14 @@ 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 ${ARGS:+ $ARGS}
exit $?
fi fi
set -m [[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
( msg=$(qemu-system-x86_64 ${ARGS:+ $ARGS})
[[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
qemu-system-x86_64 ${ARGS:+ $ARGS} & echo $! > "$QEMU_PID"
{ set +x; } 2>/dev/null
)
set +m
tail --pid "$(cat "$QEMU_PID")" --follow /dev/null & wait $! { set +x; } 2>/dev/null && terminal "$msg"
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || true
sleep 1 && finish 0

View File

@@ -1,24 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
return 0
fi
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then
mknod /dev/dri/card0 c 226 0
fi
if [ ! -c /dev/dri/renderD128 ]; then
mknod /dev/dri/renderD128 c 226 128
fi
chmod 666 /dev/dri/card0
chmod 666 /dev/dri/renderD128
addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addPackage "qemu-system-modules-opengl" "OpenGL module"
return 0

View File

@@ -2,9 +2,8 @@
set -Eeuo pipefail set -Eeuo pipefail
: ${URL:=''} # URL of the PAT file to be downloaded. : ${URL:=''} # URL of the PAT file to be downloaded.
: ${DEV:='Y'} # Controls whether device nodes are created.
if [ -f "$STORAGE"/dsm.ver ]; then if [ -f "$STORAGE/dsm.ver" ]; then
BASE=$(cat "$STORAGE/dsm.ver") BASE=$(cat "$STORAGE/dsm.ver")
else else
# Fallback for old installs # Fallback for old installs
@@ -38,59 +37,50 @@ 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
rm -f "$STORAGE"/"$BASE".agent rm -f "$STORAGE/$BASE.agent"
rm -f "$STORAGE"/"$BASE".boot.img rm -f "$STORAGE/$BASE.boot.img"
rm -f "$STORAGE"/"$BASE".system.img rm -f "$STORAGE/$BASE.system.img"
[[ "$DEBUG" == [Yy1]* ]] && set -x [[ "$DEBUG" == [Yy1]* ]] && set -x
# Check filesystem # Check filesystem
MIN_ROOT=471859200
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" == "xfs" || "$FS" == "zfs" || "$FS" == "btrfs" || "$FS" == "bcachefs" ]]; then
FA=$(lsattr -d "$STORAGE") if [[ "${FS,,}" == "fuse"* ]]; then
if [[ "$FA" != *"C"* ]]; then info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
info "Warning: the filesystem of $STORAGE is ${FS^^}, and COW (copy on write) is not disabled for that folder!"
info "This will negatively affect performance, please empty the folder and disable COW (chattr +C <path>)."
fi
fi fi
if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \ if [[ "${FS,,}" != "fat"* && "${FS,,}" != "vfat"* && "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "msdos"* ]]; then
"$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then
TMP="$STORAGE/tmp" TMP="$STORAGE/tmp"
else else
TMP="/tmp/dsm" TMP="/tmp/dsm"
TMP_SPACE=2147483648
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 SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
DEV="N" if (( TMP_SPACE > SPACE )); then
TMP="$STORAGE/tmp" error "Not enough free space inside the container, have $SPACE_MB MB available but need at least 2 GB." && exit 93
info "Warning: the $FS filesystem of $STORAGE does not support UNIX permissions.."
fi fi
fi fi
rm -rf "$TMP" && mkdir -p "$TMP" rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace # Check free diskspace
ROOT_SPACE=536870912
SPACE=$(df --output=avail -B 1 / | tail -n 1) SPACE=$(df --output=avail -B 1 / | tail -n 1)
(( MIN_ROOT > SPACE )) && error "Not enough free space in container root, need at least 450 MB available." && exit 96 SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
(( ROOT_SPACE > SPACE )) && error "Not enough free space inside the container, have $SPACE_MB MB available but need at least 500 MB." && exit 96
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1) MIN_SPACE=8589934592
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 95 (( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB GB available but need at least 8 GB." && exit 94
if [[ "$TMP" != "$STORAGE/tmp" ]]; then
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB GB available but need at least 6 GB." && exit 94
fi
# Check if output is to interactive TTY # Check if output is to interactive TTY
if [ -t 1 ]; then if [ -t 1 ]; then
@@ -101,6 +91,7 @@ fi
# Download the required files from the Synology website # Download the required files from the Synology website
ROOT="Y"
RDC="$STORAGE/dsm.rd" RDC="$STORAGE/dsm.rd"
if [ ! -f "$RDC" ]; then if [ ! -f "$RDC" ]; then
@@ -140,14 +131,12 @@ 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 { (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
# 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=$?; } || : if (( rc != 0 )); then
ROOT="N"
{ (cd "$TMP" && fakeroot cpio -idmu <"$TMP/rd" 2>/dev/null); rc=$?; } || :
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92 (( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92
else
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc"
(( rc != 0 )) && error "If the container runs unprivileged, please set DEV=N to exclude device nodes." && exit 92
fi fi
mkdir -p /run/extract mkdir -p /run/extract
@@ -222,12 +211,7 @@ else
fi fi
HDA="$TMP/hda1" info "Install: Preparing system partition..."
IDB="$TMP/indexdb"
PKG="$TMP/packages"
HDP="$TMP/synohdpack_img"
[ ! -f "$HDA.tgz" ] && error "The PAT file contains no OS image." && exit 64
BOOT=$(find "$TMP" -name "*.bin.zip") BOOT=$(find "$TMP" -name "*.bin.zip")
[ ! -f "$BOOT" ] && error "The PAT file contains no boot image." && exit 67 [ ! -f "$BOOT" ] && error "The PAT file contains no boot image." && exit 67
@@ -235,26 +219,36 @@ BOOT=$(find "$TMP" -name "*.bin.zip")
BOOT=$(echo "$BOOT" | head -c -5) BOOT=$(echo "$BOOT" | head -c -5)
unzip -q -o "$BOOT".zip -d "$TMP" unzip -q -o "$BOOT".zip -d "$TMP"
SYSTEM="$TMP/sys.img" SYSTEM="$STORAGE/$BASE.system.img"
SYSTEM_SIZE=4954537983 rm -f "$SYSTEM"
# Check free diskspace # Check free diskspace
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1) SYSTEM_SIZE=4954537983
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 )) SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
(( SYSTEM_SIZE > SPACE )) && error "Not enough free space to create a 4 GB system disk, have only $SPACE_GB GB available." && exit 97 SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then if (( SYSTEM_SIZE > SPACE )); then
if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then error "Not enough free space in $STORAGE to create a 5 GB system disk, have only $SPACE_MB MB available." && exit 97
rm -f "$SYSTEM" && error "Could not allocate a file for the system disk." && exit 98 fi
if ! touch "$SYSTEM"; then
error "Could not create file $SYSTEM for the system disk." && exit 98
fi
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "zfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
{ chattr +C "$SYSTEM"; } || :
FA=$(lsattr "$SYSTEM")
if [[ "$FA" != *"C"* ]]; then
error "Failed to disable COW for system image $SYSTEM on ${FS^^} filesystem (returned $FA)"
fi fi
fi fi
# Check if file exists if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then
[ ! -f "$SYSTEM" ] && error "System disk does not exist ($SYSTEM)" && exit 99 if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then
rm -f "$SYSTEM"
# Check the filesize error "Could not allocate file $SYSTEM for the system disk." && exit 98
SIZE=$(stat -c%s "$SYSTEM") fi
[[ SIZE -ne SYSTEM_SIZE ]] && rm -f "$SYSTEM" && error "System disk has the wrong size: $SIZE" && exit 90 fi
PART="$TMP/partition.fdisk" PART="$TMP/partition.fdisk"
@@ -270,46 +264,67 @@ PART="$TMP/partition.fdisk"
sfdisk -q "$SYSTEM" < "$PART" sfdisk -q "$SYSTEM" < "$PART"
info "Install: Extracting system partition..."
MOUNT="$TMP/system" MOUNT="$TMP/system"
rm -rf "$MOUNT" && mkdir -p "$MOUNT" rm -rf "$MOUNT" && mkdir -p "$MOUNT"
info "Install: Extracting system partition..."
HDA="$TMP/hda1"
IDB="$TMP/indexdb"
PKG="$TMP/packages"
HDP="$TMP/synohdpack_img"
[ ! -f "$HDA.tgz" ] && error "The PAT file contains no OS image." && exit 64
mv "$HDA.tgz" "$HDA.txz" mv "$HDA.tgz" "$HDA.txz"
if [[ "$DEV" == [Nn]* ]]; then if [[ "$ROOT" != [Nn]* ]]; then
# Exclude dev/ from tar extract
tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/"
else
tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/" tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/"
fi fi
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/" [ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"* rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
[ -f "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/" [ -f "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/"
[ -f "$IDB.txz" ] && tar xpfJ "$IDB.txz" --absolute-names -C "$MOUNT/usr/syno/synoman/indexdb/"
info "Install: Installing system partition..." if [ -f "$IDB.txz" ]; then
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb/"
mkdir -p "$INDEX_DB"
tar xpfJ "$IDB.txz" --absolute-names -C "$INDEX_DB"
fi
LABEL="1.44.1-42218" LABEL="1.44.1-42218"
OFFSET="1048576" # 2048 * 512 OFFSET="1048576" # 2048 * 512
NUMBLOCKS="622560" # (4980480 * 512) / 4096 NUMBLOCKS="622560" # (4980480 * 512) / 4096
mke2fs -q -t ext4 -b 4096 -d "$MOUNT/" -L "$LABEL" -F -E "offset=$OFFSET" "$SYSTEM" "$NUMBLOCKS" if [[ "$ROOT" != [Nn]* ]]; then
info "Install: Installing system partition..."
mke2fs -q -t ext4 -b 4096 -d "$MOUNT/" -L "$LABEL" -F -E "offset=$OFFSET" "$SYSTEM" "$NUMBLOCKS"
else
fakeroot -- bash -c "set -Eeu;\
tar xpfJ $HDA.txz --absolute-names --skip-old-files -C $MOUNT/;\
printf '%b%s%b' '\E[1;34m \E[1;36m' 'Install: Installing system partition...' '\E[0m\n';\
mke2fs -q -t ext4 -b 4096 -d $MOUNT/ -L $LABEL -F -E offset=$OFFSET $SYSTEM $NUMBLOCKS"
fi
rm -rf "$MOUNT" 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"
fi fi
mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
mv -f "$SYSTEM" "$STORAGE"/"$BASE".system.img
rm -rf "$TMP" rm -rf "$TMP"

View File

@@ -6,8 +6,8 @@ set -Eeuo pipefail
: ${DHCP:='N'} : ${DHCP:='N'}
: ${MAC:='02:11:32:AA:BB:CC'} : ${MAC:='02:11:32:AA:BB:CC'}
: ${VM_NET_DEV:=''}
: ${VM_NET_TAP:='dsm'} : ${VM_NET_TAP:='dsm'}
: ${VM_NET_DEV:='eth0'}
: ${VM_NET_MAC:="$MAC"} : ${VM_NET_MAC:="$MAC"}
: ${VM_NET_HOST:='VirtualDSM'} : ${VM_NET_HOST:='VirtualDSM'}
@@ -15,6 +15,8 @@ set -Eeuo pipefail
: ${DNSMASQ:='/usr/sbin/dnsmasq'} : ${DNSMASQ:='/usr/sbin/dnsmasq'}
: ${DNSMASQ_CONF_DIR:='/etc/dnsmasq.d'} : ${DNSMASQ_CONF_DIR:='/etc/dnsmasq.d'}
ADD_ERR="Please add the following setting to your container:"
# ###################################### # ######################################
# Functions # Functions
# ###################################### # ######################################
@@ -27,7 +29,7 @@ configureDHCP() {
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: --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
@@ -53,15 +55,13 @@ configureDHCP() {
{ 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). $ADD_ERR --device-cgroup-rule='c *:* rwm'" && 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 || :
if (( rc != 0 )); then if (( rc != 0 )); then
error "VHOST can not be found ($rc). Please add the following " error "VHOST can not be found ($rc). $ADD_ERR --device=/dev/vhost-net" && exit 22
error "docker setting to your container: --device=/dev/vhost-net" && exit 22
fi fi
NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30" NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30"
@@ -69,7 +69,7 @@ configureDHCP() {
return 0 return 0
} }
configureDNS () { configureDNS() {
# dnsmasq configuration: # dnsmasq configuration:
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"
@@ -90,7 +90,27 @@ configureDNS () {
return 0 return 0
} }
configureNAT () { configureNAT() {
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
fi
fi
if [ ! -c /dev/net/tun ]; then
error "TUN device missing. $ADD_ERR --cap-add NET_ADMIN" && exit 25
fi
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 ; rc=$?; } || :
if (( rc != 0 )); then
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
fi
fi
# Create a bridge with a static IP for the VM guest # Create a bridge with a static IP for the VM guest
@@ -100,8 +120,7 @@ configureNAT () {
{ ip link add dev dockerbridge type bridge ; rc=$?; } || : { ip link add dev dockerbridge type bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
error "Capability NET_ADMIN has not been set most likely. Please add the " error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && exit 23
error "following docker setting to your container: --cap-add NET_ADMIN" && exit 23
fi fi
ip address add ${VM_NET_IP%.*}.1/24 broadcast ${VM_NET_IP%.*}.255 dev dockerbridge ip address add ${VM_NET_IP%.*}.1/24 broadcast ${VM_NET_IP%.*}.255 dev dockerbridge
@@ -122,8 +141,11 @@ configureNAT () {
ip link set dev "$VM_NET_TAP" master dockerbridge ip link set dev "$VM_NET_TAP" master dockerbridge
# Add internet connection to the VM # Add internet connection to the VM
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
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
@@ -134,14 +156,6 @@ configureNAT () {
{ set +x; } 2>/dev/null { set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo [[ "$DEBUG" == [Yy1]* ]] && echo
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 ; rc=$?; } || :
if (( rc != 0 )); then
error "Please add the following docker setting to your container: --sysctl net.ipv4.ip_forward=1" && exit 24
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 || :
@@ -152,18 +166,21 @@ configureNAT () {
return 0 return 0
} }
closeNetwork () { closeNetwork() {
exec 30<&- || true
exec 40<&- || true
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
{ pkill -f server.sh || true; } 2>/dev/null fKill "server.sh"
ip link set "$VM_NET_TAP" down || true ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true ip link delete "$VM_NET_TAP" || true
else else
{ pkill -f dnsmasq || true; } 2>/dev/null fKill "dnsmasq"
ip link set "$VM_NET_TAP" down promisc off || true ip link set "$VM_NET_TAP" down promisc off || true
ip link delete "$VM_NET_TAP" || true ip link delete "$VM_NET_TAP" || true
@@ -172,46 +189,62 @@ closeNetwork () {
ip link delete dockerbridge || true ip link delete dockerbridge || true
fi fi
return 0
}
getInfo() {
if [ -z "$VM_NET_DEV" ]; then
# Automaticly detect the default network interface
VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
fi
if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then
error "Network interface '$VM_NET_DEV' does not exist inside the container!"
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 27
fi
VM_NET_MAC="${VM_NET_MAC//-/:}"
if [[ ${#VM_NET_MAC} == 12 ]]; then
m="$VM_NET_MAC"
VM_NET_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
fi
if [[ ${#VM_NET_MAC} != 17 ]]; then
error "Invalid mac address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi
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/)
echo "$IP" > /run/qemu.ip
return 0
} }
# ###################################### # ######################################
# Configure Network # Configure Network
# ###################################### # ######################################
{ pkill -f server.sh || true; } 2>/dev/null fKill "server.sh"
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
mknod /dev/net/tun c 10 200
chmod 666 /dev/net/tun
fi
[ ! -c /dev/net/tun ] && error "TUN network interface not available..." && exit 25
# Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then if [ ! -c /dev/vhost-net ]; then
mknod /dev/vhost-net c 10 238 if mknod /dev/vhost-net c 10 238; then
chmod 660 /dev/vhost-net chmod 660 /dev/vhost-net
fi
fi fi
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null getInfo
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
VM_NET_MAC="${VM_NET_MAC//-/:}"
GATEWAY=$(ip r | grep default | awk '{print $3}')
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
if [[ "$DEBUG" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY" && echo info "Container IP is $IP with gateway $GATEWAY on interface $VM_NET_DEV" && 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?"
else
error "You can only enable DHCP while the container is on a macvlan network!" && exit 26 error "You can only enable DHCP while the container is on a macvlan network!" && exit 26
fi fi
fi fi

View File

@@ -3,38 +3,125 @@ set -Eeuo pipefail
# Configure QEMU for graceful shutdown # Configure QEMU for graceful shutdown
API_CMD=6
API_TIMEOUT=50
API_HOST="127.0.0.1:2210"
QEMU_TERM=""
QEMU_PORT=7100 QEMU_PORT=7100
QEMU_TIMEOUT=55 QEMU_TIMEOUT=50
QEMU_PID="/run/qemu.pid" QEMU_PID="/run/qemu.pid"
QEMU_COUNT="/run/qemu.count" QEMU_LOG="/run/qemu.log"
QEMU_END="/run/qemu.end"
if [[ "$KVM" == [Nn]* ]]; then
API_TIMEOUT=$(( API_TIMEOUT*2 ))
QEMU_TIMEOUT=$(( QEMU_TIMEOUT*2 ))
fi
rm -f "$QEMU_PID" rm -f "$QEMU_PID"
rm -f "$QEMU_COUNT" rm -f "$QEMU_LOG"
rm -f "$QEMU_END"
touch "$QEMU_LOG"
_trap(){ _trap() {
func="$1" ; shift func="$1" ; shift
for sig ; do for sig ; do
trap "$func $sig" "$sig" trap "$func $sig" "$sig"
done
}
finish() {
local pid
local reason=$1
if [ -f "$QEMU_PID" ]; then
pid="$(cat "$QEMU_PID")"
echo && error "Forcefully terminating QEMU process, reason: $reason..."
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 1
# Workaround for zombie pid
[ ! -f "$QEMU_PID" ] && break
done done
fi
fKill "print.sh"
fKill "host.bin"
closeNetwork
sleep 1
echo && echo " Shutdown completed!"
exit "$reason"
}
terminal() {
local msg=$1
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
echo "$msg"
fi
local dev="${msg#*/dev/p}"
dev="/dev/p${dev%% *}"
if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev="${dev#*serial0}"
dev="${dev#*pty:}"
dev="${dev%%$'\n'*}"
dev="${dev%%$'\r'*}"
fi
if [ ! -c "$dev" ]; then
error "Device '$dev' not found!"
finish 34 && return 34
fi
QEMU_TERM="$dev"
return 0
} }
_graceful_shutdown() { _graceful_shutdown() {
local cnt=0
local code=$?
local pid url response
set +e set +e
local cnt response
[ ! -f "$QEMU_PID" ] && exit 130 if [ -f "$QEMU_END" ]; then
[ -f "$QEMU_COUNT" ] && return echo && info "Received $1 signal while already shutting down..."
return
fi
echo 0 > "$QEMU_COUNT" touch "$QEMU_END"
echo && info "Received $1 signal, sending shutdown command..." echo && info "Received $1 signal, sending shutdown command..."
if [ ! -f "$QEMU_PID" ]; then
echo && error "QEMU PID file does not exist?"
finish "$code" && return "$code"
fi
pid="$(cat "$QEMU_PID")"
if ! isAlive "$pid"; then
echo && error "QEMU process does not exist?"
finish "$code" && return "$code"
fi
# 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
url="http://127.0.0.1:2210/read?command=6&timeout=50" url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
response=$(curl -sk -m 52 -S "$url" 2>&1) response=$(curl -sk -m "$(( API_TIMEOUT+2 ))" -S "$url" 2>&1)
if [[ "$response" =~ "\"success\"" ]]; then if [[ "$response" =~ "\"success\"" ]]; then
@@ -43,45 +130,43 @@ _graceful_shutdown() {
else else
response="${response#*message\"\: \"}" response="${response#*message\"\: \"}"
echo && error "Failed to send shutdown command: ${response%%\"*}" [ -z "$response" ] && response="second signal"
echo && error "Forcefully terminating because of: ${response%%\"*}"
kill -15 "$(cat "$QEMU_PID")" { kill -15 "$pid" || true; } 2>/dev/null
pkill -f qemu-system-x86_64 || true
fi fi
while [ "$(cat $QEMU_COUNT)" -lt "$QEMU_TIMEOUT" ]; do while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
# Try to connect to qemu ! isAlive "$pid" && break
if ! echo 'info version'| nc -q 1 -w 1 localhost "$QEMU_PORT" >/dev/null 2>&1 ; then
break
fi
# Increase the counter sleep 1
cnt=$(($(cat $QEMU_COUNT)+1)) cnt=$((cnt+1))
echo $cnt > "$QEMU_COUNT"
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)" [[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
# Workaround for zombie pid
[ ! -f "$QEMU_PID" ] && break
done done
if [ "$(cat $QEMU_COUNT)" -ge "$QEMU_TIMEOUT" ]; then if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
echo && error "Shutdown timeout reached, forcefully quitting.." echo && error "Shutdown timeout reached, aborting..."
else
echo && echo " Quitting..."
fi fi
echo 'quit' | nc -q 1 -w 1 localhost "$QEMU_PORT" >/dev/null 2>&1 || true finish "$code" && return "$code"
{ pkill -f print.sh || true; } 2>/dev/null
{ pkill -f host.bin || true; } 2>/dev/null
closeNetwork
sleep 1
return
} }
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT MON_OPTS="\
-pidfile $QEMU_PID \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
MON_OPTS="-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay" if [[ "$CONSOLE" != [Yy]* ]]; then
MON_OPTS="$MON_OPTS -daemonize -D $QEMU_LOG"
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
fi
return 0

View File

@@ -7,10 +7,12 @@ 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" address="/run/qemu.ip"
shutdown="/run/qemu.end"
url="http://127.0.0.1:2210/read?command=10" url="http://127.0.0.1:2210/read?command=10"
resp_err="Guest returned an invalid response:" resp_err="Guest returned an invalid response:"
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 [ ! -f "$file" ] while [ ! -f "$file" ]
@@ -28,7 +30,7 @@ do
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || : { json=$(curl -m 20 -sk "$url"); rc=$?; } || :
[ -f "$shutdown" ] && exit 1 [ -f "$shutdown" ] && exit 1
(( rc != 0 )) && error "Failed to connect to guest: curl error $rc" && continue (( rc != 0 )) && error "$curl_err $rc" && continue
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || : { result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
@@ -67,7 +69,7 @@ if [[ "$location" != "20.20"* ]]; then
else else
ip=$(ip address show dev eth0 | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/) ip="$(cat "$address")"
port="${location##*:}" port="${location##*:}"
if [[ "$ip" == "172."* ]]; then if [[ "$ip" == "172."* ]]; then

77
src/proc.sh Normal file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: ${KVM:='Y'}
: ${HOST_CPU:=''}
: ${CPU_MODEL:='host'}
: ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'}
[ "$ARCH" != "amd64" ] && KVM="N"
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(device file missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(no write access)"
else
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
error "KVM acceleration not detected $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ on how to enable it, or continue without KVM by setting KVM=N (not recommended)."
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_OPTS=",accel=kvm -enable-kvm"
if ! grep -qE '^flags.* (sse4_2)' /proc/cpuinfo; then
error "Your host CPU does not have the SSE4.2 instruction set that Virtual DSM requires to boot."
error "Disable KVM by setting KVM=N to emulate a compatible CPU, at the cost of performance."
[[ "$DEBUG" != [Yy1]* ]] && exit 89
fi
else
KVM_OPTS=""
if [[ "$CPU_MODEL" == "host"* ]]; then
if [[ "$ARCH" == "amd64" ]]; then
CPU_MODEL="max,$CPU_FEATURES"
else
CPU_MODEL="qemu64,$CPU_FEATURES"
fi
fi
fi
if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
if [ -n "$HOST_CPU" ]; then
HOST_CPU="${HOST_CPU%%,*},,"
else
HOST_CPU="QEMU, Virtual CPU,"
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="$HOST_CPU X86_64"
else
HOST_CPU="$HOST_CPU $ARCH"
fi
fi
return 0

View File

@@ -11,8 +11,7 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
# Docker environment variables # Docker environment variables
: ${GPU:='N'} # Disable GPU passthrough : ${TZ:=''} # System local timezone
: ${KVM:='Y'} # Enable KVM acceleration
: ${DEBUG:='N'} # Disable debugging mode : ${DEBUG:='N'} # Disable debugging mode
: ${COUNTRY:=''} # Country code for mirror : ${COUNTRY:=''} # Country code for mirror
: ${CONSOLE:='N'} # Disable console mode : ${CONSOLE:='N'} # Disable console mode
@@ -37,8 +36,10 @@ STORAGE="/storage"
# Cleanup files # Cleanup files
rm -f /run/dsm.url rm -f /run/dsm.url
rm -f /run/qemu.ip
rm -f /run/qemu.log
rm -f /run/qemu.pid rm -f /run/qemu.pid
rm -f /run/qemu.count rm -f /run/qemu.end
# Cleanup dirs # Cleanup dirs
@@ -48,8 +49,41 @@ rm -rf "$STORAGE/tmp"
# Helper functions # Helper functions
getCountry () { isAlive() {
local pid=$1
if kill -0 "$pid" 2>/dev/null; then
return 0
fi
return 1
}
pKill() {
local pid=$1
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 0.1
done
return 0
}
fKill() {
local name=$1
{ pkill -f "$name" || true; } 2>/dev/null
while pgrep -f -l "$name" >/dev/null; do
sleep 0.1
done
return 0
}
getCountry() {
local url=$1 local url=$1
local query=$2 local query=$2
local rc json result local rc json result
@@ -68,18 +102,25 @@ getCountry () {
return 0 return 0
} }
setCountry () { setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code" [ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso" [ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country" [ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc" [ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0 return 0
} }
addPackage () { addPackage() {
local pkg=$1 local pkg=$1
local desc=$2 local desc=$2

View File

@@ -46,11 +46,14 @@ done
# Configure serial ports # Configure serial ports
SERIAL_OPTS="\ if [[ "$CONSOLE" != [Yy]* ]]; then
-serial mon:stdio \ SERIAL_OPTS="-serial pty"
else
SERIAL_OPTS="-serial mon:stdio"
fi
SERIAL_OPTS="$SERIAL_OPTS \
-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 pty,id=charserial0 \
-device isa-serial,chardev=charserial0,id=serial0 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \ -chardev socket,id=charchannel0,host=127.0.0.1,port=12345,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"