Compare commits

...

25 Commits
v5.07 ... v5.11

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

View File

@@ -44,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 80 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

@@ -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,12 +1,12 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: ${VM_NET_DEV:='eth0'} [ -f "/run/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
[ ! -f "/run/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1
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")
@@ -19,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 "$VM_NET_DEV" | 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

@@ -14,6 +14,8 @@ if [[ "$GPU" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then
DEF_OPTS="$DEF_OPTS -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1" DEF_OPTS="$DEF_OPTS -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1"
fi fi
[[ "$CONSOLE" != [Yy]* ]] && DEF_OPTS="$DEF_OPTS -daemonize -D $QEMU_LOG"
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $EXTRA_OPTS $ARGUMENTS" ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $EXTRA_OPTS $ARGUMENTS"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ') ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')

View File

@@ -7,10 +7,11 @@ set -Eeuo pipefail
: ${CPU_MODEL:='host'} : ${CPU_MODEL:='host'}
: ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'} : ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'}
KVM_ERR="" [ "$ARCH" != "amd64" ] && KVM="N"
KVM_OPTS=""
if [[ "$ARCH" == "amd64" && "$KVM" != [Nn]* ]]; then if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then if ! grep -q -e vmx -e svm /proc/cpuinfo; then
@@ -21,25 +22,35 @@ if [[ "$ARCH" == "amd64" && "$KVM" != [Nn]* ]]; then
fi fi
if [ -n "$KVM_ERR" ]; then if [ -n "$KVM_ERR" ]; then
KVM="N"
error "KVM acceleration not detected $KVM_ERR, this will cause a major loss of performance." 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)." error "See the FAQ on how to enable it, or continue without KVM by setting KVM=N (not recommended)."
[[ "$DEBUG" != [Yy1]* ]] && exit 88 [[ "$DEBUG" != [Yy1]* ]] && exit 88
[[ "$CPU_MODEL" == "host"* ]] && CPU_MODEL="max,$CPU_FEATURES"
else
KVM_OPTS=",accel=kvm -enable-kvm"
fi fi
if [ -n "$KVM_OPTS" ]; then fi
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." if [[ "$KVM" != [Nn]* ]]; then
error "Disable KVM by setting KVM=N to emulate a compatible CPU, at the cost of performance."
[[ "$DEBUG" != [Yy1]* ]] && exit 89 KVM_OPTS=",accel=kvm -enable-kvm"
fi
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 else
[[ "$CPU_MODEL" == "host"* ]] && CPU_MODEL="max,$CPU_FEATURES" 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 fi

View File

@@ -123,10 +123,6 @@ createDisk() {
error "$FAIL" && exit 77 error "$FAIL" && exit 77
fi fi
{ chattr +C "$DISK_FILE"; } || : { chattr +C "$DISK_FILE"; } || :
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 fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
@@ -159,17 +155,16 @@ createDisk() {
rm -f "$DISK_FILE" rm -f "$DISK_FILE"
error "$FAIL" && exit 70 error "$FAIL" && exit 70
fi fi
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
;; ;;
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
} }
@@ -328,7 +323,7 @@ checkFS () {
if [ -f "$DISK_FILE" ]; then if [ -f "$DISK_FILE" ]; then
FA=$(lsattr "$DISK_FILE") FA=$(lsattr "$DISK_FILE")
if [[ "$FA" != *"C"* ]]; then if [[ "$FA" != *"C"* ]]; then
info "Warning: COW (copy on write) is not disabled for the $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!" info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
fi fi
fi fi
fi fi

View File

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

@@ -8,16 +8,17 @@ fi
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri [ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then if [ ! -c /dev/dri/card0 ]; then
mknod /dev/dri/card0 c 226 0 if mknod /dev/dri/card0 c 226 0; then
chmod 666 /dev/dri/card0
fi
fi fi
if [ ! -c /dev/dri/renderD128 ]; then if [ ! -c /dev/dri/renderD128 ]; then
mknod /dev/dri/renderD128 c 226 128 if mknod /dev/dri/renderD128 c 226 128; then
chmod 666 /dev/dri/renderD128
fi
fi fi
chmod 666 /dev/dri/card0
chmod 666 /dev/dri/renderD128
addPackage "xserver-xorg-video-intel" "Intel GPU drivers" addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addPackage "qemu-system-modules-opengl" "OpenGL module" addPackage "qemu-system-modules-opengl" "OpenGL module"

View File

@@ -205,12 +205,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
@@ -271,15 +266,18 @@ PART="$TMP/partition.fdisk"
sfdisk -q "$SYSTEM" < "$PART" sfdisk -q "$SYSTEM" < "$PART"
info "Install: Extracting system partition..."
LABEL="1.44.1-42218"
OFFSET="1048576" # 2048 * 512
NUMBLOCKS="622560" # (4980480 * 512) / 4096
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 [[ "$ROOT" != [Nn]* ]]; then if [[ "$ROOT" != [Nn]* ]]; then
@@ -299,6 +297,10 @@ if [ -f "$IDB.txz" ]; then
tar xpfJ "$IDB.txz" --absolute-names -C "$INDEX_DB" tar xpfJ "$IDB.txz" --absolute-names -C "$INDEX_DB"
fi fi
LABEL="1.44.1-42218"
OFFSET="1048576" # 2048 * 512
NUMBLOCKS="622560" # (4980480 * 512) / 4096
if [[ "$ROOT" != [Nn]* ]]; then if [[ "$ROOT" != [Nn]* ]]; then
info "Install: Installing system partition..." info "Install: Installing system partition..."

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,6 +141,9 @@ 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"
@@ -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,50 +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
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY on interface $VM_NET_DEV" && echo
fi
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
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
fi
fi
if [ ! -c /dev/net/tun ]; then
error "Please add the following docker settings to your container: --device=/dev/net/tun" && exit 25
fi
# Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then if mknod /dev/vhost-net c 10 238; then
chmod 660 /dev/vhost-net chmod 660 /dev/vhost-net
fi 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
info "Container IP is $IP with gateway $GATEWAY" && echo
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#*charserial0}"
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,37 @@ _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 if [[ "$CONSOLE" != [Yy]* ]]; then
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
fi
MON_OPTS="-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay" MON_OPTS="\
-pidfile $QEMU_PID \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"

View File

@@ -2,16 +2,17 @@
set -Eeuo pipefail set -Eeuo pipefail
: ${DHCP:='N'} : ${DHCP:='N'}
: ${VM_NET_DEV:='eth0'}
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; }
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" ]
@@ -29,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
@@ -68,7 +69,7 @@ if [[ "$location" != "20.20"* ]]; then
else else
ip=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/) ip="$(cat "$address")"
port="${location##*:}" port="${location##*:}"
if [[ "$ip" == "172."* ]]; then if [[ "$ip" == "172."* ]]; then

View File

@@ -38,8 +38,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
@@ -49,8 +51,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
@@ -69,7 +104,7 @@ getCountry () {
return 0 return 0
} }
setCountry () { setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN" [[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN" [[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
@@ -86,8 +121,7 @@ setCountry () {
return 0 return 0
} }
addPackage () { addPackage() {
local pkg=$1 local pkg=$1
local desc=$2 local desc=$2

View File

@@ -47,10 +47,10 @@ done
# Configure serial ports # Configure serial ports
SERIAL_OPTS="\ SERIAL_OPTS="\
-serial mon:stdio \ -serial none \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev pty,id=charserial0 \ -chardev pty,id=charserial0 \
-device isa-serial,chardev=charserial0,id=serial0 \ -device isa-serial,chardev=charserial0,id=serial0 \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-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"