mirror of
https://github.com/vdsm/virtual-dsm.git
synced 2025-11-07 02:23:42 +08:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78594098cc | ||
|
|
3c31bc91e4 | ||
|
|
72141bab7a | ||
|
|
bc52463aa4 | ||
|
|
9fa68908a9 | ||
|
|
740dbec1b1 | ||
|
|
440d203730 | ||
|
|
1a83c67e2c | ||
|
|
34a707a2a5 | ||
|
|
cabb2cdfc9 | ||
|
|
dc52ccf172 | ||
|
|
bdd7fec3c3 | ||
|
|
bd8b03d089 | ||
|
|
a10588b0ce | ||
|
|
3503b86e12 | ||
|
|
9e124980cd | ||
|
|
675aa5e122 | ||
|
|
2a62d4e938 | ||
|
|
9cbb51cc86 | ||
|
|
7790f81d15 | ||
|
|
fdbff4879b | ||
|
|
739e679a66 | ||
|
|
0dea507d85 | ||
|
|
4f524c47d8 | ||
|
|
0c74201eb4 | ||
|
|
1e13258bc9 | ||
|
|
2c7cea042f | ||
|
|
fc92b66ff4 | ||
|
|
89ae24a2ac | ||
|
|
bd4a23b287 | ||
|
|
1b8c4d9f08 | ||
|
|
a1187decb5 | ||
|
|
b9d7aa182d | ||
|
|
b908c1118d | ||
|
|
fab776764f | ||
|
|
f3e17e399d | ||
|
|
3706ca873b | ||
|
|
10c565f32b | ||
|
|
d237e5b9e6 | ||
|
|
3c7e1ce12f | ||
|
|
f422f64325 | ||
|
|
df0656b345 | ||
|
|
1fc9c56c8f | ||
|
|
893a013ae9 | ||
|
|
6d3812d1d0 | ||
|
|
6422aec780 | ||
|
|
fcd7b8a825 | ||
|
|
575da1f574 | ||
|
|
3a507f5bf6 | ||
|
|
a3d6e3740c | ||
|
|
53e0330e21 | ||
|
|
f935c1e28a | ||
|
|
6a0c708224 | ||
|
|
a407d2d94d | ||
|
|
53605bd6e8 | ||
|
|
944faaa927 | ||
|
|
fb7cfc09de | ||
|
|
b9ae73e322 | ||
|
|
c28dbb6b9b | ||
|
|
dcc26b67a6 | ||
|
|
2627d320f3 | ||
|
|
fd4bc28835 | ||
|
|
63e4d588a2 | ||
|
|
feb1680909 | ||
|
|
9d21489b8e | ||
|
|
e70ed1900f | ||
|
|
d65b5a089a | ||
|
|
84643647cc | ||
|
|
57f487db6d | ||
|
|
0862cad2ce | ||
|
|
5ae4f59315 | ||
|
|
3fc3005ba5 | ||
|
|
20f48edd00 | ||
|
|
b854aad830 | ||
|
|
6cbe03f656 | ||
|
|
63cac9a75e | ||
|
|
08616f1057 | ||
|
|
e6193b1020 | ||
|
|
f28b9903f3 | ||
|
|
7bf2d119ea | ||
|
|
527bded1b2 | ||
|
|
1208c53ebb | ||
|
|
973efa2d27 | ||
|
|
d09588b915 | ||
|
|
19aa313753 | ||
|
|
9db12cd25f | ||
|
|
69e785e6ee | ||
|
|
159fce6839 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -8,6 +8,10 @@ on:
|
||||
paths-ignore:
|
||||
- '**/*.md'
|
||||
- '**/*.yml'
|
||||
- '**/*.js'
|
||||
- '**/*.css'
|
||||
- '**/*.html'
|
||||
- 'web/**'
|
||||
- '.gitignore'
|
||||
- '.dockerignore'
|
||||
- '.github/**'
|
||||
|
||||
18
.github/workflows/check.yml
vendored
18
.github/workflows/check.yml
vendored
@@ -7,8 +7,18 @@ jobs:
|
||||
name: shellcheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run ShellCheck
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
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
|
||||
env:
|
||||
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
|
||||
-
|
||||
name: Lint Dockerfile
|
||||
uses: hadolint/hadolint-action@v3.1.0
|
||||
with:
|
||||
dockerfile: Dockerfile
|
||||
ignore: DL3008,DL3003,DL3006
|
||||
failure-threshold: warning
|
||||
|
||||
2
.github/workflows/hub.yml
vendored
2
.github/workflows/hub.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
-
|
||||
name: Docker Hub Description
|
||||
uses: peter-evans/dockerhub-description@v3
|
||||
uses: peter-evans/dockerhub-description@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
@@ -3,6 +3,7 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**/*.sh'
|
||||
- 'Dockerfile'
|
||||
- '.github/workflows/test.yml'
|
||||
- '.github/workflows/check.yml'
|
||||
|
||||
|
||||
28
Dockerfile
28
Dockerfile
@@ -2,7 +2,7 @@ FROM qemux/qemu-host as builder
|
||||
|
||||
# FROM golang as builder
|
||||
# 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
|
||||
# RUN go mod download
|
||||
# RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host.bin .
|
||||
@@ -10,11 +10,12 @@ FROM qemux/qemu-host as builder
|
||||
FROM debian:trixie-slim
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
ARG DEBCONF_NOWARNINGS="yes"
|
||||
ARG DEBIAN_FRONTEND noninteractive
|
||||
ARG DEBCONF_NOWARNINGS "yes"
|
||||
ARG DEBIAN_FRONTEND "noninteractive"
|
||||
ARG DEBCONF_NONINTERACTIVE_SEEN "true"
|
||||
|
||||
RUN apt-get update && apt-get -y upgrade \
|
||||
&& if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi \
|
||||
RUN if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi \
|
||||
&& apt-get update \
|
||||
&& apt-get --no-install-recommends -y install \
|
||||
jq \
|
||||
tini \
|
||||
@@ -23,11 +24,12 @@ RUN apt-get update && apt-get -y upgrade \
|
||||
wget \
|
||||
fdisk \
|
||||
unzip \
|
||||
socat \
|
||||
nginx \
|
||||
procps \
|
||||
xz-utils \
|
||||
iptables \
|
||||
iproute2 \
|
||||
apt-utils \
|
||||
dnsmasq \
|
||||
fakeroot \
|
||||
net-tools \
|
||||
@@ -37,25 +39,25 @@ RUN apt-get update && apt-get -y upgrade \
|
||||
qemu-system-x86 \
|
||||
"$extra" \
|
||||
&& apt-get clean \
|
||||
&& unlink /etc/nginx/sites-enabled/default \
|
||||
&& sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf \
|
||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY ./src /run/
|
||||
COPY ./web /var/www/
|
||||
COPY --from=builder /qemu-host.bin /run/host.bin
|
||||
|
||||
RUN chmod +x /run/*.sh && chmod +x /run/*.bin
|
||||
RUN mv /var/www/nginx.conf /etc/nginx/sites-enabled/web.conf
|
||||
|
||||
VOLUME /storage
|
||||
|
||||
EXPOSE 22
|
||||
EXPOSE 80
|
||||
EXPOSE 139
|
||||
EXPOSE 445
|
||||
EXPOSE 5000
|
||||
EXPOSE 22 139 445 5000
|
||||
|
||||
ENV RAM_SIZE "1G"
|
||||
ENV DISK_SIZE "16G"
|
||||
ENV CPU_CORES "1"
|
||||
|
||||
ARG VERSION_ARG="0.0"
|
||||
ARG VERSION_ARG "0.0"
|
||||
RUN echo "$VERSION_ARG" > /run/version
|
||||
|
||||
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh
|
||||
|
||||
140
agent/agent.sh
140
agent/agent.sh
@@ -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
|
||||
@@ -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
|
||||
@@ -9,8 +9,6 @@ services:
|
||||
CPU_CORES: "1"
|
||||
devices:
|
||||
- /dev/kvm
|
||||
- /dev/net/tun
|
||||
- /dev/vhost-net
|
||||
device_cgroup_rules:
|
||||
- 'c *:* rwm'
|
||||
cap_add:
|
||||
|
||||
243
readme.md
243
readme.md
@@ -1,4 +1,4 @@
|
||||
<h1 align="center">Virtual DSM for Docker<br />
|
||||
<h1 align="center">Virtual DSM<br />
|
||||
<div align="center">
|
||||
<img src="https://github.com/vdsm/virtual-dsm/raw/master/.github/screen.jpg" title="Screenshot" style="max-width:100%;" width="432" />
|
||||
</div>
|
||||
@@ -17,7 +17,6 @@ Virtual DSM in a docker container.
|
||||
|
||||
- Multiple disks
|
||||
- KVM acceleration
|
||||
- GPU passthrough
|
||||
- Upgrades supported
|
||||
|
||||
## Usage
|
||||
@@ -29,7 +28,7 @@ version: "3"
|
||||
services:
|
||||
dsm:
|
||||
container_name: dsm
|
||||
image: vdsm/virtual-dsm:latest
|
||||
image: vdsm/virtual-dsm
|
||||
environment:
|
||||
DISK_SIZE: "16G"
|
||||
devices:
|
||||
@@ -47,167 +46,193 @@ services:
|
||||
Via `docker run`
|
||||
|
||||
```bash
|
||||
docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 60 vdsm/virtual-dsm:latest
|
||||
docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 120 vdsm/virtual-dsm
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
* ### How do I change the size of the virtual disk?
|
||||
* ### How do I use it?
|
||||
|
||||
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
|
||||
Very simple! These are the steps:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
DISK_SIZE: "128G"
|
||||
```
|
||||
- Start the container and connect to [port 5000](http://localhost:5000) using your web browser.
|
||||
|
||||
This can also be used to resize the existing disk to a larger capacity without any data loss.
|
||||
- Wait until DSM is ready, choose an username and password, and you will be taken to the desktop.
|
||||
|
||||
* ### How do I change the location of the virtual disk?
|
||||
Enjoy your brand new machine, and don't forget to star this repo!
|
||||
|
||||
To change the location of the virtual disk, include the following bind mount in your compose file:
|
||||
* ### How do I change the size of the disk?
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /var/dsm:/storage
|
||||
```
|
||||
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
|
||||
|
||||
Replace the example path `/var/dsm` with the desired storage folder.
|
||||
```yaml
|
||||
environment:
|
||||
DISK_SIZE: "128G"
|
||||
```
|
||||
|
||||
* ### How do I add multiple disks?
|
||||
This can also be used to resize the existing disk to a larger capacity without any data loss.
|
||||
|
||||
To create additional disks, modify your compose file like this:
|
||||
* ### How do I change the storage location?
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
DISK2_SIZE: "32G"
|
||||
DISK3_SIZE: "64G"
|
||||
volumes:
|
||||
- /home/example:/storage2
|
||||
- /mnt/data/example:/storage3
|
||||
```
|
||||
To change the storage location, include the following bind mount in your compose file:
|
||||
|
||||
* ### How do I create a growable disk?
|
||||
```yaml
|
||||
volumes:
|
||||
- /var/dsm:/storage
|
||||
```
|
||||
|
||||
By default, the entire capacity of the disk is reserved in advance.
|
||||
Replace the example path `/var/dsm` with the desired storage folder.
|
||||
|
||||
To create a growable disk that only allocates space that is actually used, add the following environment variables:
|
||||
* ### How do I create a growable disk?
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
ALLOCATE: "N"
|
||||
DISK_FMT: "qcow2"
|
||||
```
|
||||
By default, the entire capacity of the disk is reserved in advance.
|
||||
|
||||
Please note that this may reduce the write performance of the disk.
|
||||
To create a growable disk that only allocates space that is actually used, add the following environment variable:
|
||||
|
||||
* ### How do I increase the amount of CPU or RAM?
|
||||
```yaml
|
||||
environment:
|
||||
DISK_FMT: "qcow2"
|
||||
```
|
||||
|
||||
By default, a single core and 1 GB of RAM are allocated to the container.
|
||||
Please note that this may reduce the write performance of the disk.
|
||||
|
||||
To increase this, add the following environment variables:
|
||||
* ### How do I add multiple disks?
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
RAM_SIZE: "4G"
|
||||
CPU_CORES: "4"
|
||||
```
|
||||
To create additional disks, modify your compose file like this:
|
||||
|
||||
* ### How do I verify if my system supports KVM?
|
||||
```yaml
|
||||
environment:
|
||||
DISK2_SIZE: "32G"
|
||||
DISK3_SIZE: "64G"
|
||||
volumes:
|
||||
- /home/example:/storage2
|
||||
- /mnt/data/example:/storage3
|
||||
```
|
||||
|
||||
To verify if your system supports KVM, run the following commands:
|
||||
* ### How do I pass-through a disk?
|
||||
|
||||
```bash
|
||||
sudo apt install cpu-checker
|
||||
sudo kvm-ok
|
||||
```
|
||||
It is possible to pass-through disk devices directly by adding them to your compose file in this way:
|
||||
|
||||
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check your BIOS settings.
|
||||
```yaml
|
||||
environment:
|
||||
DEVICE2: "/dev/sda"
|
||||
DEVICE3: "/dev/sdb"
|
||||
devices:
|
||||
- /dev/sda
|
||||
- /dev/sdb
|
||||
```
|
||||
|
||||
* ### How do I assign an individual IP address to the container?
|
||||
Please note that the device needs to be totally empty (without any partition table) otherwise DSM does not always format it into a volume.
|
||||
|
||||
By default, the container uses bridge networking, which shares the IP address with the host.
|
||||
Do NOT use this feature with the goal of sharing files from the host, they will all be lost without warning when DSM creates the volume.
|
||||
|
||||
If you want to assign an individual IP address to the container, you can create a macvlan network as follows:
|
||||
* ### How do I increase the amount of CPU or RAM?
|
||||
|
||||
```bash
|
||||
docker network create -d macvlan \
|
||||
--subnet=192.168.0.0/24 \
|
||||
--gateway=192.168.0.1 \
|
||||
--ip-range=192.168.0.100/28 \
|
||||
-o parent=eth0 vdsm
|
||||
```
|
||||
By default, a single CPU core and 1 GB of RAM are allocated to the container.
|
||||
|
||||
Be sure to modify these values to match your local subnet.
|
||||
To increase this, add the following environment variables:
|
||||
|
||||
Once you have created the network, change your compose file to look as follows:
|
||||
```yaml
|
||||
environment:
|
||||
RAM_SIZE: "4G"
|
||||
CPU_CORES: "4"
|
||||
```
|
||||
|
||||
```yaml
|
||||
services:
|
||||
dsm:
|
||||
container_name: dsm
|
||||
..<snip>..
|
||||
networks:
|
||||
vdsm:
|
||||
ipv4_address: 192.168.0.100
|
||||
* ### How do I verify if my system supports KVM?
|
||||
|
||||
networks:
|
||||
vdsm:
|
||||
external: true
|
||||
```
|
||||
To verify if your system supports KVM, run the following commands:
|
||||
|
||||
An added benefit of this approach is that you won't have to perform any port mapping anymore since all ports will be exposed by default.
|
||||
```bash
|
||||
sudo apt install cpu-checker
|
||||
sudo kvm-ok
|
||||
```
|
||||
|
||||
Please note that this IP address won't be accessible from the Docker host due to the design of macvlan, which doesn't permit communication between the two. If this is a concern, you need to create a [second macvlan](https://blog.oddbit.com/post/2018-03-12-using-docker-macvlan-networks/#host-access) as a workaround.
|
||||
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check the virtualization settings in the BIOS.
|
||||
|
||||
* ### How can the container acquire an IP address from my router?
|
||||
* ### How do I assign an individual IP address to the container?
|
||||
|
||||
After configuring the container for macvlan (see above), it is possible for DSM to become part of your home network by requesting an IP from your router, just like your other devices.
|
||||
By default, the container uses bridge networking, which shares the IP address with the host.
|
||||
|
||||
To enable this feature, add the following lines to your compose file:
|
||||
If you want to assign an individual IP address to the container, you can create a macvlan network as follows:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
DHCP: "Y"
|
||||
device_cgroup_rules:
|
||||
- 'c *:* rwm'
|
||||
```
|
||||
```bash
|
||||
docker network create -d macvlan \
|
||||
--subnet=192.168.0.0/24 \
|
||||
--gateway=192.168.0.1 \
|
||||
--ip-range=192.168.0.100/28 \
|
||||
-o parent=eth0 vdsm
|
||||
```
|
||||
|
||||
Please note that even if you don't want DHCP, it's still recommended to enable this feature as it prevents NAT issues and increases performance by using a `macvtap` interface. In that case just set a static IP from the DSM control panel after you enabled this mode.
|
||||
Be sure to modify these values to match your local subnet.
|
||||
|
||||
* ### How do I passthrough the GPU?
|
||||
Once you have created the network, change your compose file to look as follows:
|
||||
|
||||
To passthrough your Intel GPU, add the following lines to your compose file:
|
||||
```yaml
|
||||
services:
|
||||
dsm:
|
||||
container_name: dsm
|
||||
..<snip>..
|
||||
networks:
|
||||
vdsm:
|
||||
ipv4_address: 192.168.0.100
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
GPU: "Y"
|
||||
devices:
|
||||
- /dev/dri
|
||||
```
|
||||
networks:
|
||||
vdsm:
|
||||
external: true
|
||||
```
|
||||
|
||||
This can be used to enable the facial recognition function in Synology Photos for example.
|
||||
An added benefit of this approach is that you won't have to perform any port mapping anymore, since all ports will be exposed by default.
|
||||
|
||||
* ### How do I install a specific version of vDSM?
|
||||
Please note that this IP address won't be accessible from the Docker host due to the design of macvlan, which doesn't permit communication between the two. If this is a concern, you need to create a [second macvlan](https://blog.oddbit.com/post/2018-03-12-using-docker-macvlan-networks/#host-access) as a workaround.
|
||||
|
||||
By default, version 7.2.1 will be installed, but if you prefer an older version, you can add its download URL to your compose file as follows:
|
||||
* ### How can DSM acquire an IP address from my router?
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
|
||||
```
|
||||
After configuring the container for macvlan (see above), it is possible for DSM to become part of your home network by requesting an IP from your router, just like your other devices.
|
||||
|
||||
With this method, it is even possible to switch between different versions while keeping all your file data intact.
|
||||
To enable this feature, add the following lines to your compose file:
|
||||
|
||||
* ### What are the differences compared to the standard DSM?
|
||||
```yaml
|
||||
environment:
|
||||
DHCP: "Y"
|
||||
device_cgroup_rules:
|
||||
- 'c *:* rwm'
|
||||
```
|
||||
|
||||
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
|
||||
Please note that even if you don't want DHCP, it's still recommended to enable this feature, as it prevents NAT issues and increases performance by using a `macvtap` interface. In that case, just set a static IP from the DSM control panel after you enabled this mode.
|
||||
|
||||
* ### Is this project legal?
|
||||
* ### How do I pass-through the GPU?
|
||||
|
||||
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project would be considered legal.
|
||||
To pass-through your Intel GPU, add the following lines to your compose file:
|
||||
|
||||
However, by installing Synology's Virtual DSM, you must accept their end-user license agreement, which does not permit installation on non-Synology hardware. So only run this project on an official Synology NAS, as any other use will be a violation of their terms and conditions.
|
||||
```yaml
|
||||
environment:
|
||||
GPU: "Y"
|
||||
devices:
|
||||
- /dev/dri
|
||||
```
|
||||
|
||||
This can be used to enable the facial recognition function in Synology Photos for example.
|
||||
|
||||
* ### How do I install a specific version of vDSM?
|
||||
|
||||
By default, version 7.2 will be installed, but if you prefer an older version, you can add its download URL to your compose file as follows:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
|
||||
```
|
||||
|
||||
With this method, it is even possible to switch between different versions while keeping all your file data intact.
|
||||
|
||||
* ### What are the differences compared to the standard DSM?
|
||||
|
||||
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
|
||||
|
||||
* ### Is this project legal?
|
||||
|
||||
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project would be considered legal.
|
||||
|
||||
However, by installing Synology's Virtual DSM, you must accept their end-user license agreement, which does not permit installation on non-Synology hardware. So only run this project on an official Synology NAS, as any other use will be a violation of their terms and conditions.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
||||
12
src/check.sh
12
src/check.sh
@@ -1,15 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: ${VM_NET_DEV:='eth0'}
|
||||
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
|
||||
[ ! -f "/run/shm/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
|
||||
|
||||
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1
|
||||
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
|
||||
file="/run/shm/dsm.url"
|
||||
address="/run/shm/qemu.ip"
|
||||
|
||||
file="/run/dsm.url"
|
||||
[ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
|
||||
|
||||
location=$(cat "$file")
|
||||
location=$(<"$file")
|
||||
|
||||
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
||||
|
||||
@@ -19,7 +19,7 @@ if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
|
||||
echo "Failed to reach DSM at port $port"
|
||||
else
|
||||
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=$(<"$address")
|
||||
fi
|
||||
|
||||
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1
|
||||
|
||||
@@ -1,20 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
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')
|
||||
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}"
|
||||
EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
|
||||
EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
|
||||
EXTRA_OPTS="$EXTRA_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
|
||||
CPU_OPTS="-cpu $CPU_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
|
||||
MAC_OPTS="-machine type=q35,usb=off,vmport=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
|
||||
DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
|
||||
DEV_OPTS="$DEV_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
|
||||
DEV_OPTS="$DEV_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
|
||||
|
||||
if [[ "$GPU" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then
|
||||
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="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $DEV_OPTS $ARGUMENTS"
|
||||
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
|
||||
|
||||
return 0
|
||||
|
||||
140
src/disk.sh
140
src/disk.sh
@@ -3,12 +3,12 @@ set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: ${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_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_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
|
||||
: "${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_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_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
|
||||
|
||||
BOOT="$STORAGE/$BASE.boot.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
|
||||
|
||||
DISK_OPTS="\
|
||||
-object iothread,id=io1 -object iothread,id=io2 \
|
||||
-device virtio-scsi-pci,id=hw-synoboot,iothread=io1,bus=pcie.0,addr=0xa \
|
||||
-object iothread,id=io2 \
|
||||
-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 \
|
||||
-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 \
|
||||
-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"
|
||||
|
||||
@@ -61,8 +61,8 @@ getSize() {
|
||||
local DISK_FILE=$1
|
||||
local DISK_EXT DISK_FMT
|
||||
|
||||
DISK_EXT="$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')"
|
||||
DISK_FMT="$(ext2fmt "$DISK_EXT")"
|
||||
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
||||
DISK_FMT=$(ext2fmt "$DISK_EXT")
|
||||
|
||||
case "${DISK_FMT,,}" in
|
||||
raw)
|
||||
@@ -80,7 +80,7 @@ getSize() {
|
||||
isCow() {
|
||||
local FS=$1
|
||||
|
||||
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "zfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
|
||||
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -112,7 +112,9 @@ createDisk() {
|
||||
fi
|
||||
fi
|
||||
|
||||
info "Creating a $DISK_TYPE $DISK_DESC image in $DISK_FMT format with a size of $DISK_SPACE..."
|
||||
html "Creating a $DISK_DESC image..."
|
||||
info "Creating a $DISK_SPACE $DISK_TYPE $DISK_DESC image in $DISK_FMT format..."
|
||||
|
||||
local FAIL="Could not create a $DISK_TYPE $DISK_FMT $DISK_DESC image of $DISK_SPACE ($DISK_FILE)"
|
||||
|
||||
case "${DISK_FMT,,}" in
|
||||
@@ -195,7 +197,9 @@ resizeDisk() {
|
||||
fi
|
||||
|
||||
local GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
|
||||
info "Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
|
||||
MSG="Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
|
||||
info "$MSG" && html "$MSG"
|
||||
|
||||
local FAIL="Could not resize the $DISK_TYPE $DISK_FMT $DISK_DESC image from ${GB}G to $DISK_SPACE ($DISK_FILE)"
|
||||
|
||||
case "${DISK_FMT,,}" in
|
||||
@@ -262,6 +266,7 @@ convertDisk() {
|
||||
fi
|
||||
fi
|
||||
|
||||
html "Converting $DISK_DESC to $DST_FMT..."
|
||||
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
|
||||
|
||||
local CONV_FLAGS="-p"
|
||||
@@ -301,6 +306,7 @@ convertDisk() {
|
||||
fi
|
||||
fi
|
||||
|
||||
html "Conversion of $DISK_DESC completed..."
|
||||
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
|
||||
|
||||
return 0
|
||||
@@ -319,6 +325,10 @@ checkFS () {
|
||||
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" == "fuse"* ]]; then
|
||||
info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
|
||||
fi
|
||||
|
||||
if isCow "$FS"; then
|
||||
if [ -f "$DISK_FILE" ]; then
|
||||
FA=$(lsattr "$DISK_FILE")
|
||||
@@ -368,7 +378,7 @@ addDisk () {
|
||||
else
|
||||
PREV_FMT="qcow2"
|
||||
fi
|
||||
PREV_EXT="$(fmt2ext "$PREV_FMT")"
|
||||
PREV_EXT=$(fmt2ext "$PREV_FMT")
|
||||
|
||||
if [ -f "$DISK_BASE.$PREV_EXT" ] ; then
|
||||
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
|
||||
@@ -397,7 +407,36 @@ addDisk () {
|
||||
return 0
|
||||
}
|
||||
|
||||
DISK_EXT="$(fmt2ext "$DISK_FMT")" || exit $?
|
||||
addDevice () {
|
||||
|
||||
local DISK_ID=$1
|
||||
local DISK_DEV=$2
|
||||
local DISK_DESC=$3
|
||||
local DISK_INDEX=$4
|
||||
local DISK_ADDRESS=$5
|
||||
|
||||
[ -z "$DISK_DEV" ] && return 0
|
||||
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
|
||||
|
||||
DISK_OPTS="$DISK_OPTS \
|
||||
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
|
||||
-drive file=$DISK_DEV,if=none,id=drive-$DISK_ID,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
|
||||
-device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
html "Initializing disks..."
|
||||
|
||||
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
||||
|
||||
if [ -z "$ALLOCATE" ]; then
|
||||
if [[ "${DISK_FMT,,}" == "raw" ]]; then
|
||||
ALLOCATE="Y"
|
||||
else
|
||||
ALLOCATE="N"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||
DISK_TYPE="growable"
|
||||
@@ -440,52 +479,39 @@ if [ ! -f "$DISK3_FILE.img" ]; then
|
||||
fi
|
||||
|
||||
DISK4_FILE="/storage4/data4"
|
||||
DISK5_FILE="/storage5/data5"
|
||||
DISK6_FILE="/storage6/data6"
|
||||
|
||||
: ${DISK2_SIZE:=''}
|
||||
: ${DISK3_SIZE:=''}
|
||||
: ${DISK4_SIZE:=''}
|
||||
: ${DISK5_SIZE:=''}
|
||||
: ${DISK6_SIZE:=''}
|
||||
: "${DISK2_SIZE:=""}"
|
||||
: "${DISK3_SIZE:=""}"
|
||||
: "${DISK4_SIZE:=""}"
|
||||
|
||||
addDisk "userdata" "$DISK1_FILE" "$DISK_EXT" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" || exit $?
|
||||
addDisk "userdata2" "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" || exit $?
|
||||
addDisk "userdata3" "$DISK3_FILE" "$DISK_EXT" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" || exit $?
|
||||
addDisk "userdata4" "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "9" "0x7" "$DISK_FMT" || exit $?
|
||||
addDisk "userdata5" "$DISK5_FILE" "$DISK_EXT" "disk5" "$DISK5_SIZE" "10" "0x8" "$DISK_FMT" || exit $?
|
||||
addDisk "userdata6" "$DISK6_FILE" "$DISK_EXT" "disk6" "$DISK6_SIZE" "11" "0x9" "$DISK_FMT" || exit $?
|
||||
: "${DEVICE:=""}" # Docker variables to passthrough a block device, like /dev/vdc1.
|
||||
: "${DEVICE2:=""}"
|
||||
: "${DEVICE3:=""}"
|
||||
: "${DEVICE4:=""}"
|
||||
|
||||
addDevice () {
|
||||
if [ -n "$DEVICE" ]; then
|
||||
addDevice "userdata" "$DEVICE" "device" "3" "0xc" || exit $?
|
||||
else
|
||||
addDisk "userdata" "$DISK1_FILE" "$DISK_EXT" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" || exit $?
|
||||
fi
|
||||
|
||||
local DISK_ID=$1
|
||||
local DISK_DEV=$2
|
||||
local DISK_INDEX=$3
|
||||
local DISK_ADDRESS=$4
|
||||
if [ -n "$DEVICE2" ]; then
|
||||
addDevice "userdata2" "$DEVICE2" "device2" "4" "0xd" || exit $?
|
||||
else
|
||||
addDisk "userdata2" "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" || exit $?
|
||||
fi
|
||||
|
||||
[ -z "$DISK_DEV" ] && return 0
|
||||
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
|
||||
if [ -n "$DEVICE3" ]; then
|
||||
addDevice "userdata3" "$DEVICE3" "device3" "5" "0xe" || exit $?
|
||||
else
|
||||
addDisk "userdata3" "$DISK3_FILE" "$DISK_EXT" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" || exit $?
|
||||
fi
|
||||
|
||||
DISK_OPTS="$DISK_OPTS \
|
||||
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
|
||||
-drive file=$DISK_DEV,if=none,id=drive-$DISK_ID,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
|
||||
-device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
: ${DEVICE:=''} # Docker variable to passthrough a block device, like /dev/vdc1.
|
||||
: ${DEVICE2:=''}
|
||||
: ${DEVICE3:=''}
|
||||
: ${DEVICE4:=''}
|
||||
: ${DEVICE5:=''}
|
||||
: ${DEVICE6:=''}
|
||||
|
||||
addDevice "userdata7" "$DEVICE" "6" "0xf" || exit $?
|
||||
addDevice "userdata8" "$DEVICE2" "7" "0x5" || exit $?
|
||||
addDevice "userdata9" "$DEVICE3" "8" "0x6" || exit $?
|
||||
addDevice "userdata4" "$DEVICE4" "9" "0x7" || exit $?
|
||||
addDevice "userdata5" "$DEVICE5" "10" "0x8" || exit $?
|
||||
addDevice "userdata6" "$DEVICE6" "11" "0x9" || exit $?
|
||||
if [ -n "$DEVICE4" ]; then
|
||||
addDevice "userdata4" "$DEVICE4" "device4" "6" "0xf" || exit $?
|
||||
else
|
||||
addDisk "userdata4" "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" || exit $?
|
||||
fi
|
||||
|
||||
html "Initialized disks successfully..."
|
||||
return 0
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: "${GPU:="N"}" # GPU passthrough
|
||||
: "${VGA:="virtio"}" # VGA adaptor
|
||||
: "${DISPLAY:="none"}" # Display type
|
||||
|
||||
if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
|
||||
|
||||
[[ "${DISPLAY,,}" == "none" ]] && VGA="none"
|
||||
DISPLAY_OPTS="-display $DISPLAY -vga $VGA"
|
||||
return 0
|
||||
|
||||
fi
|
||||
|
||||
DISPLAY_OPTS="-display egl-headless,rendernode=/dev/dri/renderD128"
|
||||
DISPLAY_OPTS="$DISPLAY_OPTS -vga $VGA"
|
||||
|
||||
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
|
||||
|
||||
if [ ! -c /dev/dri/card0 ]; then
|
||||
26
src/entry.sh
26
src/entry.sh
@@ -1,33 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
echo "❯ Starting Virtual DSM for Docker v$(</run/version)..."
|
||||
echo "❯ For support visit https://github.com/vdsm/virtual-dsm/"
|
||||
APP="Virtual DSM"
|
||||
SUPPORT="https://github.com/vdsm/virtual-dsm"
|
||||
|
||||
cd /run
|
||||
|
||||
. reset.sh # Initialize system
|
||||
. install.sh # Run installation
|
||||
. disk.sh # Initialize disks
|
||||
. display.sh # Initialize graphics
|
||||
. network.sh # Initialize network
|
||||
. gpu.sh # Initialize graphics
|
||||
. cpu.sh # Initialize processor
|
||||
. proc.sh # Initialize processor
|
||||
. serial.sh # Initialize serialport
|
||||
. power.sh # Configure shutdown
|
||||
. config.sh # Configure arguments
|
||||
|
||||
trap - ERR
|
||||
|
||||
info "Booting $APP using $VERS..."
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo "Arguments: $ARGS" && echo
|
||||
|
||||
if [[ "$CONSOLE" == [Yy]* ]]; then
|
||||
exec qemu-system-x86_64 -pidfile "$QEMU_PID" ${ARGS:+ $ARGS}
|
||||
exit $?
|
||||
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
|
||||
fi
|
||||
|
||||
[[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
|
||||
qemu-system-x86_64 -daemonize -pidfile "$QEMU_PID" ${ARGS:+ $ARGS}
|
||||
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || :
|
||||
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
|
||||
|
||||
{ set +x; } 2>/dev/null
|
||||
cat /dev/pts/1 2>/dev/null & wait $! || true
|
||||
terminal
|
||||
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
|
||||
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
|
||||
|
||||
sleep 1
|
||||
finish 0
|
||||
sleep 1 && finish 0
|
||||
|
||||
141
src/install.sh
141
src/install.sh
@@ -1,23 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: ${URL:=''} # URL of the PAT file to be downloaded.
|
||||
: "${URL:=""}" # URL of the PAT file to be downloaded.
|
||||
|
||||
if [ -f "$STORAGE"/dsm.ver ]; then
|
||||
BASE=$(cat "$STORAGE/dsm.ver")
|
||||
if [ -f "$STORAGE/dsm.ver" ]; then
|
||||
BASE=$(<"$STORAGE/dsm.ver")
|
||||
else
|
||||
# Fallback for old installs
|
||||
BASE="DSM_VirtualDSM_42962"
|
||||
fi
|
||||
|
||||
[ -n "$URL" ] && BASE=$(basename "$URL" .pat)
|
||||
if [ -n "$URL" ]; then
|
||||
BASE=$(basename "$URL" .pat)
|
||||
if [ ! -f "$STORAGE/$BASE.system.img" ]; then
|
||||
BASE=$(basename "${URL%%\?*}" .pat)
|
||||
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
|
||||
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$STORAGE/$BASE.boot.img" ]] && [[ -f "$STORAGE/$BASE.system.img" ]]; then
|
||||
return 0 # Previous installation found
|
||||
fi
|
||||
|
||||
# Display wait message
|
||||
/run/server.sh 5000 install &
|
||||
html "Please wait while Virtual DSM is being installed..."
|
||||
|
||||
DL=""
|
||||
DL_CHINA="https://cndl.synology.cn/download/DSM"
|
||||
@@ -34,47 +40,55 @@ fi
|
||||
|
||||
[ -z "$URL" ] && URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
|
||||
|
||||
BASE=$(basename "$URL" .pat)
|
||||
BASE=$(basename "${URL%%\?*}" .pat)
|
||||
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
|
||||
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
||||
|
||||
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
|
||||
rm -f "$STORAGE"/"$BASE".pat
|
||||
rm -f "$STORAGE/$BASE.pat"
|
||||
fi
|
||||
|
||||
rm -f "$STORAGE"/"$BASE".agent
|
||||
rm -f "$STORAGE"/"$BASE".boot.img
|
||||
rm -f "$STORAGE"/"$BASE".system.img
|
||||
rm -f "$STORAGE/$BASE.agent"
|
||||
rm -f "$STORAGE/$BASE.boot.img"
|
||||
rm -f "$STORAGE/$BASE.system.img"
|
||||
|
||||
[[ "$DEBUG" == [Yy1]* ]] && set -x
|
||||
|
||||
# Check filesystem
|
||||
MIN_ROOT=471859200
|
||||
MIN_SPACE=6442450944
|
||||
FS=$(stat -f -c %T "$STORAGE")
|
||||
|
||||
if [[ "${FS,,}" == "overlay"* ]]; then
|
||||
info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" != "fat"* && "${FS,,}" != "vfat"* && "${FS,,}" != "exfat"* && \
|
||||
"${FS,,}" != "ntfs"* && "${FS,,}" != "fuse"* && "${FS,,}" != "msdos"* ]]; then
|
||||
if [[ "${FS,,}" == "fuse"* ]]; then
|
||||
info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
|
||||
fi
|
||||
|
||||
if [[ "${FS,,}" != "fat"* && "${FS,,}" != "vfat"* && "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "msdos"* ]]; then
|
||||
TMP="$STORAGE/tmp"
|
||||
else
|
||||
TMP="/tmp/dsm"
|
||||
TMP_SPACE=2147483648
|
||||
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
|
||||
if (( MIN_SPACE > SPACE )); then
|
||||
error "The ${FS^^} filesystem of $STORAGE does not support UNIX permissions, and no space left in container!" && exit 93
|
||||
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
|
||||
if (( TMP_SPACE > SPACE )); then
|
||||
error "Not enough free space inside the container, have $SPACE_MB MB available but need at least 2 GB." && exit 93
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$TMP" && mkdir -p "$TMP"
|
||||
|
||||
# Check free diskspace
|
||||
ROOT_SPACE=536870912
|
||||
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
|
||||
|
||||
MIN_SPACE=8589934592
|
||||
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
|
||||
(( 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
|
||||
|
||||
# Check if output is to interactive TTY
|
||||
if [ -t 1 ]; then
|
||||
@@ -90,14 +104,20 @@ RDC="$STORAGE/dsm.rd"
|
||||
|
||||
if [ ! -f "$RDC" ]; then
|
||||
|
||||
info "Install: Downloading installer..."
|
||||
MSG="Downloading installer..."
|
||||
PRG="Downloading installer ([P])..."
|
||||
info "Install: $MSG" && html "$MSG"
|
||||
|
||||
RD="$TMP/rd.gz"
|
||||
POS="65627648-71021835"
|
||||
VERIFY="b4215a4b213ff5154db0488f92c87864"
|
||||
LOC="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
|
||||
|
||||
rm -f "$RD"
|
||||
/run/progress.sh "$RD" "$PRG" &
|
||||
{ curl -r "$POS" -sfk -S -o "$RD" "$LOC"; rc=$?; } || :
|
||||
|
||||
fKill "progress.sh"
|
||||
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
|
||||
|
||||
SUM=$(md5sum "$RD" | cut -f 1 -d " ")
|
||||
@@ -108,8 +128,12 @@ if [ ! -f "$RDC" ]; then
|
||||
rm "$RD"
|
||||
rm -f "$PAT"
|
||||
|
||||
html "$MSG"
|
||||
/run/progress.sh "$PAT" "$PRG" &
|
||||
{ wget "$LOC" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
|
||||
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
|
||||
|
||||
fKill "progress.sh"
|
||||
(( rc != 0 )) && error "Failed to download $LOC , reason: $rc" && exit 60
|
||||
|
||||
tar --extract --file="$PAT" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
|
||||
rm "$PAT"
|
||||
@@ -133,7 +157,7 @@ if [ -f "$RDC" ]; then
|
||||
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92
|
||||
fi
|
||||
|
||||
mkdir -p /run/extract
|
||||
rm -rf /run/extract && mkdir -p /run/extract
|
||||
for file in $TMP/usr/lib/libcurl.so.4 \
|
||||
$TMP/usr/lib/libmbedcrypto.so.5 \
|
||||
$TMP/usr/lib/libmbedtls.so.13 \
|
||||
@@ -159,7 +183,11 @@ fi
|
||||
|
||||
rm -rf "$TMP" && mkdir -p "$TMP"
|
||||
|
||||
info "Install: Downloading $(basename "$URL")..."
|
||||
info "Install: Downloading $BASE.pat..."
|
||||
|
||||
MSG="Downloading DSM..."
|
||||
PRG="Downloading DSM ([P])..."
|
||||
html "$MSG"
|
||||
|
||||
PAT="/$BASE.pat"
|
||||
rm -f "$PAT"
|
||||
@@ -170,8 +198,12 @@ if [[ "$URL" == "file://"* ]]; then
|
||||
|
||||
else
|
||||
|
||||
/run/progress.sh "$PAT" "$PRG" &
|
||||
|
||||
{ wget "$URL" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
|
||||
(( rc != 0 )) && error "Failed to download $URL, reason: $rc" && exit 69
|
||||
|
||||
fKill "progress.sh"
|
||||
(( rc != 0 )) && error "Failed to download $URL , reason: $rc" && exit 69
|
||||
|
||||
fi
|
||||
|
||||
@@ -183,7 +215,8 @@ if ((SIZE<250000000)); then
|
||||
error "The specified PAT file is probably an update pack as it's too small." && exit 62
|
||||
fi
|
||||
|
||||
info "Install: Extracting downloaded image..."
|
||||
MSG="Extracting downloaded image..."
|
||||
info "Install: $MSG" && html "$MSG"
|
||||
|
||||
if { tar tf "$PAT"; } >/dev/null 2>&1; then
|
||||
|
||||
@@ -205,7 +238,10 @@ else
|
||||
|
||||
fi
|
||||
|
||||
info "Install: Preparing system partition..."
|
||||
rm -rf /run/extract
|
||||
|
||||
MSG="Preparing system partition..."
|
||||
info "Install: $MSG" && html "$MSG"
|
||||
|
||||
BOOT=$(find "$TMP" -name "*.bin.zip")
|
||||
[ ! -f "$BOOT" ] && error "The PAT file contains no boot image." && exit 67
|
||||
@@ -213,20 +249,23 @@ BOOT=$(find "$TMP" -name "*.bin.zip")
|
||||
BOOT=$(echo "$BOOT" | head -c -5)
|
||||
unzip -q -o "$BOOT".zip -d "$TMP"
|
||||
|
||||
SYSTEM="$TMP/sys.img"
|
||||
SYSTEM_SIZE=4954537983
|
||||
SYSTEM="$STORAGE/$BASE.system.img"
|
||||
rm -f "$SYSTEM"
|
||||
|
||||
# Check free diskspace
|
||||
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
|
||||
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
|
||||
(( SYSTEM_SIZE > SPACE )) && error "Not enough free space to create a 4 GB system disk, have only $SPACE_GB GB available." && exit 97
|
||||
SYSTEM_SIZE=4954537983
|
||||
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
|
||||
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
|
||||
|
||||
if (( SYSTEM_SIZE > SPACE )); then
|
||||
error "Not enough free space in $STORAGE to create a 5 GB system disk, have only $SPACE_MB MB available." && exit 97
|
||||
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
|
||||
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
|
||||
{ chattr +C "$SYSTEM"; } || :
|
||||
FA=$(lsattr "$SYSTEM")
|
||||
if [[ "$FA" != *"C"* ]]; then
|
||||
@@ -241,17 +280,6 @@ if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if file exists
|
||||
[ ! -f "$SYSTEM" ] && error "System disk does not exist ($SYSTEM)" && exit 99
|
||||
|
||||
# Check the filesize
|
||||
SIZE=$(stat -c%s "$SYSTEM")
|
||||
|
||||
if [[ SIZE -ne SYSTEM_SIZE ]]; then
|
||||
rm -f "$SYSTEM"
|
||||
error "System disk has the wrong size: $SIZE vs $SYSTEM_SIZE" && exit 90
|
||||
fi
|
||||
|
||||
PART="$TMP/partition.fdisk"
|
||||
|
||||
{ echo "label: dos"
|
||||
@@ -269,7 +297,8 @@ sfdisk -q "$SYSTEM" < "$PART"
|
||||
MOUNT="$TMP/system"
|
||||
rm -rf "$MOUNT" && mkdir -p "$MOUNT"
|
||||
|
||||
info "Install: Extracting system partition..."
|
||||
MSG="Extracting system partition..."
|
||||
info "Install: $MSG" && html "$MSG"
|
||||
|
||||
HDA="$TMP/hda1"
|
||||
IDB="$TMP/indexdb"
|
||||
@@ -277,15 +306,8 @@ 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"
|
||||
|
||||
if [[ "$ROOT" != [Nn]* ]]; then
|
||||
|
||||
tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/"
|
||||
|
||||
fi
|
||||
|
||||
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
|
||||
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
|
||||
|
||||
@@ -300,10 +322,13 @@ fi
|
||||
LABEL="1.44.1-42218"
|
||||
OFFSET="1048576" # 2048 * 512
|
||||
NUMBLOCKS="622560" # (4980480 * 512) / 4096
|
||||
MSG="Installing system partition..."
|
||||
|
||||
if [[ "$ROOT" != [Nn]* ]]; then
|
||||
|
||||
info "Install: Installing system partition..."
|
||||
tar xpfJ "$HDA.txz" --absolute-names --skip-old-files -C "$MOUNT/"
|
||||
|
||||
info "Install: $MSG" && html "$MSG"
|
||||
|
||||
mke2fs -q -t ext4 -b 4096 -d "$MOUNT/" -L "$LABEL" -F -E "offset=$OFFSET" "$SYSTEM" "$NUMBLOCKS"
|
||||
|
||||
@@ -311,27 +336,25 @@ 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';\
|
||||
printf '%b%s%b' '\E[1;34m❯ \E[1;36m' 'Install: $MSG' '\E[0m\n';\
|
||||
mke2fs -q -t ext4 -b 4096 -d $MOUNT/ -L $LABEL -F -E offset=$OFFSET $SYSTEM $NUMBLOCKS"
|
||||
|
||||
fi
|
||||
|
||||
rm -rf "$MOUNT"
|
||||
|
||||
echo "$BASE" > "$STORAGE"/dsm.ver
|
||||
echo "$BASE" > "$STORAGE/dsm.ver"
|
||||
|
||||
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
|
||||
rm -f "$PAT"
|
||||
else
|
||||
mv -f "$PAT" "$STORAGE"/"$BASE".pat
|
||||
mv -f "$PAT" "$STORAGE/$BASE.pat"
|
||||
fi
|
||||
|
||||
mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img
|
||||
mv -f "$SYSTEM" "$STORAGE"/"$BASE".system.img
|
||||
|
||||
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
|
||||
rm -rf "$TMP"
|
||||
|
||||
{ set +x; } 2>/dev/null
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo
|
||||
|
||||
html "Installation finished successfully..."
|
||||
return 0
|
||||
|
||||
157
src/network.sh
157
src/network.sh
@@ -3,17 +3,17 @@ set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: ${DHCP:='N'}
|
||||
: ${MAC:='02:11:32:AA:BB:CC'}
|
||||
: "${MAC:=""}"
|
||||
: "${DHCP:="N"}"
|
||||
|
||||
: ${VM_NET_TAP:='dsm'}
|
||||
: ${VM_NET_DEV:='eth0'}
|
||||
: ${VM_NET_MAC:="$MAC"}
|
||||
: ${VM_NET_HOST:='VirtualDSM'}
|
||||
: "${VM_NET_DEV:=""}"
|
||||
: "${VM_NET_TAP:="dsm"}"
|
||||
: "${VM_NET_MAC:="$MAC"}"
|
||||
: "${VM_NET_HOST:="VirtualDSM"}"
|
||||
|
||||
: ${DNSMASQ_OPTS:=''}
|
||||
: ${DNSMASQ:='/usr/sbin/dnsmasq'}
|
||||
: ${DNSMASQ_CONF_DIR:='/etc/dnsmasq.d'}
|
||||
: "${DNSMASQ_OPTS:=""}"
|
||||
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
|
||||
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
|
||||
|
||||
ADD_ERR="Please add the following setting to your container:"
|
||||
|
||||
@@ -33,7 +33,7 @@ configureDHCP() {
|
||||
fi
|
||||
|
||||
while ! ip link set "$VM_NET_TAP" up; do
|
||||
info "Waiting for address to become available..."
|
||||
info "Waiting for MAC address $VM_NET_MAC to become available..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
@@ -69,7 +69,7 @@ configureDHCP() {
|
||||
return 0
|
||||
}
|
||||
|
||||
configureDNS () {
|
||||
configureDNS() {
|
||||
|
||||
# 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"
|
||||
@@ -83,19 +83,40 @@ configureDNS () {
|
||||
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
||||
|
||||
[[ "$DEBUG" == [Yy1]* ]] && set -x
|
||||
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}
|
||||
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
|
||||
error "Failed to start dnsmasq, reason: $?" && exit 29
|
||||
fi
|
||||
{ set +x; } 2>/dev/null
|
||||
[[ "$DEBUG" == [Yy1]* ]] && echo
|
||||
|
||||
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
|
||||
|
||||
VM_NET_IP='20.20.20.21'
|
||||
[[ "$DEBUG" == [Yy1]* ]] && set -x
|
||||
|
||||
{ ip link add dev dockerbridge type bridge ; rc=$?; } || :
|
||||
|
||||
@@ -106,7 +127,7 @@ configureNAT () {
|
||||
ip address add ${VM_NET_IP%.*}.1/24 broadcast ${VM_NET_IP%.*}.255 dev dockerbridge
|
||||
|
||||
while ! ip link set dockerbridge up; do
|
||||
info "Waiting for address to become available..."
|
||||
info "Waiting for IP address to become available..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
@@ -114,15 +135,18 @@ configureNAT () {
|
||||
ip tuntap add dev "$VM_NET_TAP" mode tap
|
||||
|
||||
while ! ip link set "$VM_NET_TAP" up promisc on; do
|
||||
info "Waiting for tap to become available..."
|
||||
info "Waiting for TAP to become available..."
|
||||
sleep 2
|
||||
done
|
||||
|
||||
ip link set dev "$VM_NET_TAP" master dockerbridge
|
||||
|
||||
# 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 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"
|
||||
|
||||
if (( KERNEL > 4 )); then
|
||||
@@ -130,17 +154,6 @@ configureNAT () {
|
||||
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill || true
|
||||
fi
|
||||
|
||||
{ set +x; } 2>/dev/null
|
||||
[[ "$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 "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
|
||||
fi
|
||||
fi
|
||||
|
||||
NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0"
|
||||
|
||||
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
|
||||
@@ -151,21 +164,24 @@ configureNAT () {
|
||||
return 0
|
||||
}
|
||||
|
||||
closeNetwork () {
|
||||
closeNetwork() {
|
||||
|
||||
exec 30<&- || true
|
||||
exec 40<&- || true
|
||||
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
|
||||
fKill "server.sh"
|
||||
# Shutdown nginx
|
||||
nginx -s stop 2> /dev/null
|
||||
fWait "nginx"
|
||||
|
||||
ip link set "$VM_NET_TAP" down || true
|
||||
ip link delete "$VM_NET_TAP" || true
|
||||
|
||||
else
|
||||
|
||||
fKill "dnsmasq"
|
||||
local pid="/var/run/dnsmasq.pid"
|
||||
[ -f "$pid" ] && pKill "$(<"$pid")"
|
||||
|
||||
ip link set "$VM_NET_TAP" down promisc off || true
|
||||
ip link delete "$VM_NET_TAP" || true
|
||||
@@ -174,60 +190,83 @@ closeNetwork () {
|
||||
ip link delete dockerbridge || true
|
||||
|
||||
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
|
||||
|
||||
if [ -z "$VM_NET_MAC" ]; then
|
||||
# Generate MAC address based on Docker container ID in hostname
|
||||
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
|
||||
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/shm/qemu.ip
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ######################################
|
||||
# Configure Network
|
||||
# ######################################
|
||||
|
||||
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 "TUN device missing. $ADD_ERR --cap-add NET_ADMIN" && exit 25
|
||||
fi
|
||||
|
||||
# Create the necessary file structure for /dev/vhost-net
|
||||
if [ ! -c /dev/vhost-net ]; then
|
||||
if mknod /dev/vhost-net c 10 238; then
|
||||
chmod 660 /dev/vhost-net
|
||||
fi
|
||||
fi
|
||||
|
||||
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
|
||||
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/)
|
||||
getInfo
|
||||
html "Initializing network..."
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
info "Container IP is $IP with gateway $GATEWAY" && echo
|
||||
info "Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC"
|
||||
[ -f /etc/resolv.conf ] && grep '^nameserver*' /etc/resolv.conf
|
||||
echo
|
||||
fi
|
||||
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
|
||||
if [[ "$GATEWAY" == "172."* ]]; then
|
||||
if [[ "$DEBUG" != [Yy1]* ]]; then
|
||||
error "You can only enable DHCP while the container is on a macvlan network!" && exit 26
|
||||
fi
|
||||
if [[ "$GATEWAY" == "172."* ]] && [[ "$DEBUG" != [Yy1]* ]]; then
|
||||
error "You can only enable DHCP while the container is on a macvlan network!" && exit 26
|
||||
fi
|
||||
|
||||
# Configuration for DHCP IP
|
||||
configureDHCP
|
||||
|
||||
# Display IP on port 80 and 5000
|
||||
/run/server.sh 5000 /run/ip.sh &
|
||||
MSG="Booting DSM instance..."
|
||||
html "$MSG"
|
||||
|
||||
else
|
||||
|
||||
# Shutdown nginx
|
||||
nginx -s stop 2> /dev/null
|
||||
fWait "nginx"
|
||||
|
||||
# Configuration for static IP
|
||||
configureNAT
|
||||
|
||||
|
||||
97
src/power.sh
97
src/power.sh
@@ -7,18 +7,20 @@ API_CMD=6
|
||||
API_TIMEOUT=50
|
||||
API_HOST="127.0.0.1:2210"
|
||||
|
||||
QEMU_TERM=""
|
||||
QEMU_PORT=7100
|
||||
QEMU_TIMEOUT=50
|
||||
QEMU_PID="/run/qemu.pid"
|
||||
QEMU_COUNT="/run/qemu.count"
|
||||
QEMU_PID="/run/shm/qemu.pid"
|
||||
QEMU_LOG="/run/shm/qemu.log"
|
||||
QEMU_OUT="/run/shm/qemu.out"
|
||||
QEMU_END="/run/shm/qemu.end"
|
||||
|
||||
if [[ "$KVM" == [Nn]* ]]; then
|
||||
API_TIMEOUT=$(( API_TIMEOUT*2 ))
|
||||
QEMU_TIMEOUT=$(( QEMU_TIMEOUT*2 ))
|
||||
fi
|
||||
|
||||
rm -f "$QEMU_PID"
|
||||
rm -f "$QEMU_COUNT"
|
||||
touch "$QEMU_LOG"
|
||||
|
||||
_trap() {
|
||||
func="$1" ; shift
|
||||
@@ -34,8 +36,8 @@ finish() {
|
||||
|
||||
if [ -f "$QEMU_PID" ]; then
|
||||
|
||||
pid="$(cat "$QEMU_PID")"
|
||||
echo && error "Forcefully quitting QEMU process, reason: $reason..."
|
||||
pid=$(<"$QEMU_PID")
|
||||
echo && error "Forcefully terminating QEMU process, reason: $reason..."
|
||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||
|
||||
while isAlive "$pid"; do
|
||||
@@ -51,20 +53,62 @@ finish() {
|
||||
closeNetwork
|
||||
|
||||
sleep 1
|
||||
echo && info "Shutdown completed!"
|
||||
echo && echo "❯ Shutdown completed!"
|
||||
|
||||
exit "$reason"
|
||||
}
|
||||
|
||||
terminal() {
|
||||
|
||||
local dev=""
|
||||
|
||||
if [ -f "$QEMU_OUT" ]; then
|
||||
|
||||
local msg
|
||||
msg=$(<"$QEMU_OUT")
|
||||
|
||||
if [ -n "$msg" ]; then
|
||||
|
||||
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
|
||||
echo "$msg"
|
||||
fi
|
||||
|
||||
dev="${msg#*/dev/p}"
|
||||
dev="/dev/p${dev%% *}"
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
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() {
|
||||
|
||||
local code=$?
|
||||
local pid cnt response
|
||||
|
||||
[ -f "$QEMU_COUNT" ] && return
|
||||
echo 0 > "$QEMU_COUNT"
|
||||
local pid url response
|
||||
|
||||
set +e
|
||||
|
||||
if [ -f "$QEMU_END" ]; then
|
||||
echo && info "Received $1 signal while already shutting down..."
|
||||
return
|
||||
fi
|
||||
|
||||
touch "$QEMU_END"
|
||||
echo && info "Received $1 signal, sending shutdown command..."
|
||||
|
||||
if [ ! -f "$QEMU_PID" ]; then
|
||||
@@ -72,7 +116,7 @@ _graceful_shutdown() {
|
||||
finish "$code" && return "$code"
|
||||
fi
|
||||
|
||||
pid="$(cat "$QEMU_PID")"
|
||||
pid=$(<"$QEMU_PID")
|
||||
|
||||
if ! isAlive "$pid"; then
|
||||
echo && error "QEMU process does not exist?"
|
||||
@@ -94,20 +138,19 @@ _graceful_shutdown() {
|
||||
|
||||
response="${response#*message\"\: \"}"
|
||||
[ -z "$response" ] && response="second signal"
|
||||
echo && error "Forcefully quitting because of: ${response%%\"*}"
|
||||
echo && error "Forcefully terminating because of: ${response%%\"*}"
|
||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||
|
||||
fi
|
||||
|
||||
while [ "$(cat $QEMU_COUNT)" -lt "$QEMU_TIMEOUT" ]; do
|
||||
local cnt=0
|
||||
|
||||
while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
|
||||
|
||||
! isAlive "$pid" && break
|
||||
|
||||
sleep 1
|
||||
|
||||
# Increase the counter
|
||||
cnt=$(($(cat $QEMU_COUNT)+1))
|
||||
echo $cnt > "$QEMU_COUNT"
|
||||
cnt=$((cnt+1))
|
||||
|
||||
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
|
||||
|
||||
@@ -116,13 +159,23 @@ _graceful_shutdown() {
|
||||
|
||||
done
|
||||
|
||||
if [ "$(cat $QEMU_COUNT)" -ge "$QEMU_TIMEOUT" ]; then
|
||||
echo && error "Shutdown timeout reached!"
|
||||
if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
|
||||
echo && error "Shutdown timeout reached, aborting..."
|
||||
fi
|
||||
|
||||
finish "$code" && return "$code"
|
||||
}
|
||||
|
||||
_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
|
||||
|
||||
31
src/print.sh
31
src/print.sh
@@ -1,17 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
: ${DHCP:='N'}
|
||||
: ${VM_NET_DEV:='eth0'}
|
||||
: "${DHCP:="N"}"
|
||||
|
||||
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; }
|
||||
|
||||
file="/run/dsm.url"
|
||||
shutdown="/run/qemu.count"
|
||||
file="/run/shm/dsm.url"
|
||||
info="/run/shm/msg.html"
|
||||
page="/run/shm/index.html"
|
||||
address="/run/shm/qemu.ip"
|
||||
shutdown="/run/shm/qemu.end"
|
||||
template="/var/www/index.html"
|
||||
url="http://127.0.0.1:2210/read?command=10"
|
||||
|
||||
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"
|
||||
|
||||
while [ ! -f "$file" ]
|
||||
@@ -29,7 +33,7 @@ do
|
||||
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
|
||||
|
||||
[ -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=$?; } || :
|
||||
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
|
||||
@@ -60,15 +64,28 @@ done
|
||||
|
||||
[ -f "$shutdown" ] && exit 1
|
||||
|
||||
location=$(cat "$file")
|
||||
location=$(<"$file")
|
||||
|
||||
if [[ "$location" != "20.20"* ]]; then
|
||||
|
||||
msg="http://$location"
|
||||
title="<title>Virtual DSM</title>"
|
||||
body="The location of DSM is <a href='http://$location'>http://$location</a>"
|
||||
script="<script>setTimeout(function(){ window.location.assign('http://$location'); }, 3000);</script>"
|
||||
|
||||
HTML=$(<"$template")
|
||||
HTML="${HTML/\[1\]/$title}"
|
||||
HTML="${HTML/\[2\]/$script}"
|
||||
HTML="${HTML/\[3\]/$body}"
|
||||
HTML="${HTML/\[4\]/}"
|
||||
HTML="${HTML/\[5\]/}"
|
||||
|
||||
echo "$HTML" > "$page"
|
||||
echo "$body" > "$info"
|
||||
|
||||
else
|
||||
|
||||
ip=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
|
||||
ip=$(<"$address")
|
||||
port="${location##*:}"
|
||||
|
||||
if [[ "$ip" == "172."* ]]; then
|
||||
|
||||
@@ -3,9 +3,10 @@ set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: ${HOST_CPU:=''}
|
||||
: ${CPU_MODEL:='host'}
|
||||
: ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'}
|
||||
: "${KVM:="Y"}"
|
||||
: "${HOST_CPU:=""}"
|
||||
: "${CPU_FLAGS:=""}"
|
||||
: "${CPU_MODEL:="host"}"
|
||||
|
||||
[ "$ARCH" != "amd64" ] && KVM="N"
|
||||
|
||||
@@ -13,12 +14,16 @@ if [[ "$KVM" != [Nn]* ]]; then
|
||||
|
||||
KVM_ERR=""
|
||||
|
||||
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
|
||||
if [ ! -e /dev/kvm ]; then
|
||||
KVM_ERR="(device file missing)"
|
||||
else
|
||||
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
|
||||
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
|
||||
@@ -32,6 +37,7 @@ fi
|
||||
|
||||
if [[ "$KVM" != [Nn]* ]]; then
|
||||
|
||||
CPU_FEATURES="kvm=on"
|
||||
KVM_OPTS=",accel=kvm -enable-kvm"
|
||||
|
||||
if ! grep -qE '^flags.* (sse4_2)' /proc/cpuinfo; then
|
||||
@@ -43,15 +49,23 @@ if [[ "$KVM" != [Nn]* ]]; then
|
||||
else
|
||||
|
||||
KVM_OPTS=""
|
||||
CPU_FEATURES="+ssse3,+sse4.1,+sse4.2"
|
||||
|
||||
if [[ "${CPU_MODEL,,}" == "host"* ]]; then
|
||||
|
||||
if [[ "$CPU_MODEL" == "host"* ]]; then
|
||||
if [[ "$ARCH" == "amd64" ]]; then
|
||||
CPU_MODEL="max,$CPU_FEATURES"
|
||||
CPU_MODEL="max"
|
||||
else
|
||||
CPU_MODEL="qemu64,$CPU_FEATURES"
|
||||
CPU_MODEL="qemu64"
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$CPU_FLAGS" ]; then
|
||||
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES"
|
||||
else
|
||||
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES,$CPU_FLAGS"
|
||||
fi
|
||||
|
||||
if [ -z "$HOST_CPU" ]; then
|
||||
32
src/progress.sh
Normal file
32
src/progress.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
escape () {
|
||||
local s
|
||||
s=${1//&/\&}
|
||||
s=${s//</\<}
|
||||
s=${s//>/\>}
|
||||
s=${s//'"'/\"}
|
||||
printf -- %s "$s"
|
||||
return 0
|
||||
}
|
||||
|
||||
file="$1"
|
||||
body=$(escape "$2")
|
||||
info="/run/shm/msg.html"
|
||||
|
||||
if [[ "$body" == *"..." ]]; then
|
||||
body="<p class=\"loading\">${body/.../}</p>"
|
||||
fi
|
||||
|
||||
while true
|
||||
do
|
||||
if [ -f "$file" ]; then
|
||||
bytes=$(du -sb "$file" | cut -f1)
|
||||
if (( bytes > 1000 )); then
|
||||
size=$(echo "$bytes" | numfmt --to=iec --suffix=B | sed -r 's/([A-Z])/ \1/')
|
||||
echo "${body//(\[P\])/($size)}"> "$info"
|
||||
fi
|
||||
fi
|
||||
sleep 1 & wait $!
|
||||
done
|
||||
137
src/reset.sh
137
src/reset.sh
@@ -3,48 +3,64 @@ set -Eeuo pipefail
|
||||
|
||||
info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "$1" "\E[0m\n"; }
|
||||
error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: $1" "\E[0m\n" >&2; }
|
||||
warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: $1" "\E[0m\n" >&2; }
|
||||
|
||||
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
|
||||
|
||||
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
|
||||
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
|
||||
|
||||
echo "❯ Starting $APP for Docker v$(</run/version)..."
|
||||
echo "❯ For support visit $SUPPORT"
|
||||
echo
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: ${TZ:=''} # System local timezone
|
||||
: ${GPU:='N'} # Disable GPU passthrough
|
||||
: ${KVM:='Y'} # Enable KVM acceleration
|
||||
: ${DEBUG:='N'} # Disable debugging mode
|
||||
: ${COUNTRY:=''} # Country code for mirror
|
||||
: ${CONSOLE:='N'} # Disable console mode
|
||||
: ${ALLOCATE:='Y'} # Preallocate diskspace
|
||||
: ${ARGUMENTS:=''} # Extra QEMU parameters
|
||||
: ${CPU_CORES:='1'} # Amount of CPU cores
|
||||
: ${RAM_SIZE:='1G'} # Maximum RAM amount
|
||||
: ${DISK_SIZE:='16G'} # Initial data disk size
|
||||
: "${TZ:=""}" # System local timezone
|
||||
: "${DEBUG:="N"}" # Disable debugging mode
|
||||
: "${COUNTRY:=""}" # Country code for mirror
|
||||
: "${CONSOLE:="N"}" # Disable console mode
|
||||
: "${ALLOCATE:=""}" # Preallocate diskspace
|
||||
: "${ARGUMENTS:=""}" # Extra QEMU parameters
|
||||
: "${CPU_CORES:="1"}" # Amount of CPU cores
|
||||
: "${RAM_SIZE:="1G"}" # Maximum RAM amount
|
||||
: "${DISK_SIZE:="16G"}" # Initial data disk size
|
||||
|
||||
# Helper variables
|
||||
|
||||
STORAGE="/storage"
|
||||
INFO="/run/shm/msg.html"
|
||||
PAGE="/run/shm/index.html"
|
||||
TEMPLATE="/var/www/index.html"
|
||||
FOOTER1="$APP for Docker v$(</run/version)"
|
||||
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
||||
|
||||
HOST=$(hostname -s)
|
||||
KERNEL=$(uname -r | cut -b 1)
|
||||
MINOR=$(uname -r | cut -d '.' -f2)
|
||||
ARCH=$(dpkg --print-architecture)
|
||||
VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
|
||||
|
||||
# Check system
|
||||
|
||||
if [ ! -d "/dev/shm" ]; then
|
||||
error "Directory /dev/shm not found!" && exit 14
|
||||
else
|
||||
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
|
||||
fi
|
||||
|
||||
# Check folder
|
||||
|
||||
STORAGE="/storage"
|
||||
[ ! -d "$STORAGE" ] && error "Storage folder ($STORAGE) not found!" && exit 13
|
||||
if [ ! -d "$STORAGE" ]; then
|
||||
error "Storage folder ($STORAGE) not found!" && exit 13
|
||||
fi
|
||||
|
||||
# Cleanup files
|
||||
|
||||
rm -f /run/dsm.url
|
||||
rm -f /run/qemu.pid
|
||||
rm -f /run/qemu.count
|
||||
rm -f /run/shm/qemu.*
|
||||
rm -f /run/shm/dsm.url
|
||||
|
||||
# Cleanup dirs
|
||||
|
||||
rm -rf /tmp/dsm
|
||||
rm -f /tmp/server.*
|
||||
rm -rf "$STORAGE/tmp"
|
||||
|
||||
# Helper functions
|
||||
@@ -65,25 +81,74 @@ pKill() {
|
||||
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||
|
||||
while isAlive "$pid"; do
|
||||
sleep 0.1
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
fKill () {
|
||||
fWait() {
|
||||
local name=$1
|
||||
|
||||
while pgrep -f -l "$name" >/dev/null; do
|
||||
sleep 0.2
|
||||
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
|
||||
fWait "$name"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
getCountry () {
|
||||
escape () {
|
||||
local s
|
||||
s=${1//&/\&}
|
||||
s=${s//</\<}
|
||||
s=${s//>/\>}
|
||||
s=${s//'"'/\"}
|
||||
printf -- %s "$s"
|
||||
return 0
|
||||
}
|
||||
|
||||
html()
|
||||
{
|
||||
local title
|
||||
local body
|
||||
local script
|
||||
local footer
|
||||
|
||||
title=$(escape "$APP")
|
||||
title="<title>$title</title>"
|
||||
footer=$(escape "$FOOTER1")
|
||||
|
||||
body=$(escape "$1")
|
||||
if [[ "$body" == *"..." ]]; then
|
||||
body="<p class=\"loading\">${body/.../}</p>"
|
||||
fi
|
||||
|
||||
[ -n "${2:-}" ] && script="$2" || script=""
|
||||
|
||||
local HTML
|
||||
HTML=$(<"$TEMPLATE")
|
||||
HTML="${HTML/\[1\]/$title}"
|
||||
HTML="${HTML/\[2\]/$script}"
|
||||
HTML="${HTML/\[3\]/$body}"
|
||||
HTML="${HTML/\[4\]/$footer}"
|
||||
HTML="${HTML/\[5\]/$FOOTER2}"
|
||||
|
||||
echo "$HTML" > "$PAGE"
|
||||
echo "$body" > "$INFO"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
getCountry() {
|
||||
local url=$1
|
||||
local query=$2
|
||||
local rc json result
|
||||
@@ -102,7 +167,7 @@ getCountry () {
|
||||
return 0
|
||||
}
|
||||
|
||||
setCountry () {
|
||||
setCountry() {
|
||||
|
||||
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
|
||||
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
|
||||
@@ -113,13 +178,14 @@ setCountry () {
|
||||
|
||||
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
|
||||
[ -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://api.myip.com" ".cc"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
addPackage () {
|
||||
addPackage() {
|
||||
local pkg=$1
|
||||
local desc=$2
|
||||
|
||||
@@ -127,10 +193,8 @@ addPackage () {
|
||||
return 0
|
||||
fi
|
||||
|
||||
info "Installing $desc..."
|
||||
|
||||
export DEBCONF_NOWARNINGS="yes"
|
||||
export DEBIAN_FRONTEND="noninteractive"
|
||||
MSG="Installing $desc..."
|
||||
info "$MSG" && html "$MSG"
|
||||
|
||||
[ -z "$COUNTRY" ] && setCountry
|
||||
|
||||
@@ -138,10 +202,15 @@ addPackage () {
|
||||
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
|
||||
fi
|
||||
|
||||
apt-get -qq update
|
||||
apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -qq update
|
||||
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Start webserver
|
||||
cp -r /var/www/* /run/shm
|
||||
html "Starting $APP for Docker..."
|
||||
nginx -e stderr
|
||||
|
||||
return 0
|
||||
|
||||
@@ -3,11 +3,26 @@ set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: ${HOST_MAC:=''}
|
||||
: ${HOST_DEBUG:=''}
|
||||
: ${HOST_SERIAL:=''}
|
||||
: ${HOST_MODEL:=''}
|
||||
: ${GUEST_SERIAL:=''}
|
||||
: "${HOST_MAC:=""}"
|
||||
: "${HOST_DEBUG:=""}"
|
||||
: "${HOST_SERIAL:=""}"
|
||||
: "${HOST_MODEL:=""}"
|
||||
: "${GUEST_SERIAL:=""}"
|
||||
|
||||
if [ -n "$HOST_MAC" ]; then
|
||||
|
||||
HOST_MAC="${HOST_MAC//-/:}"
|
||||
|
||||
if [[ ${#HOST_MAC} == 12 ]]; then
|
||||
m="$HOST_MAC"
|
||||
HOST_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
|
||||
fi
|
||||
|
||||
if [[ ${#HOST_MAC} != 17 ]]; then
|
||||
error "Invalid HOST_MAC address: '$HOST_MAC', should be 12 or 17 digits long!" && exit 28
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
HOST_ARGS=()
|
||||
HOST_ARGS+=("-cpu=$CPU_CORES")
|
||||
@@ -46,9 +61,13 @@ done
|
||||
|
||||
# Configure serial ports
|
||||
|
||||
SERIAL_OPTS="\
|
||||
-chardev pty,id=charserial0 \
|
||||
-device isa-serial,chardev=charserial0,id=serial0 \
|
||||
if [[ "$CONSOLE" != [Yy]* ]]; then
|
||||
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 \
|
||||
-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"
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
TMP_FILE=$(mktemp -q /tmp/server.XXXXXX)
|
||||
|
||||
stop() {
|
||||
trap - SIGINT EXIT
|
||||
{ pkill -f socat || true; } 2>/dev/null
|
||||
[ -f "$TMP_FILE" ] && rm -f "$TMP_FILE"
|
||||
}
|
||||
|
||||
trap 'stop' EXIT SIGINT SIGTERM SIGHUP
|
||||
|
||||
html()
|
||||
{
|
||||
local h="<!DOCTYPE html><HTML><HEAD><TITLE>VirtualDSM</TITLE>"
|
||||
h="$h<STYLE>body { color: white; background-color: #125bdb; font-family: Verdana,"
|
||||
h="$h Arial,sans-serif; } a, a:hover, a:active, a:visited { color: white; }</STYLE></HEAD>"
|
||||
h="$h<BODY><BR><BR><H1><CENTER>$1</CENTER></H1></BODY></HTML>"
|
||||
|
||||
echo "$h"
|
||||
}
|
||||
|
||||
if [[ "$2" != "/"* ]]; then
|
||||
|
||||
BODY="$2"
|
||||
|
||||
if [[ "$BODY" == "install" ]]; then
|
||||
BODY="Please wait while Virtual DSM is being installed..."
|
||||
BODY="$BODY<script>setTimeout(() => { document.location.reload(); }, 9999);</script>"
|
||||
fi
|
||||
|
||||
HTML=$(html "$BODY")
|
||||
printf '%b' "HTTP/1.1 200 OK\nContent-Length: ${#HTML}\nConnection: close\n\n$HTML" > "$TMP_FILE"
|
||||
|
||||
socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null &
|
||||
socat TCP4-LISTEN:"${1:-5000}",reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null & wait $!
|
||||
|
||||
exit
|
||||
|
||||
fi
|
||||
|
||||
if [[ "$2" != "/run/ip.sh" ]]; then
|
||||
|
||||
cp "$2" "$TMP_FILE"
|
||||
|
||||
else
|
||||
|
||||
BODY="The location of DSM is <a href='http://\$LOCATION'>http://\$LOCATION</a><script>"
|
||||
BODY="$BODY setTimeout(function(){ window.location.assign('http://\$LOCATION'); }, 3000);</script>"
|
||||
WAIT="Please wait while discovering IP...<script>setTimeout(() => { document.location.reload(); }, 4999);</script>"
|
||||
|
||||
HTML=$(html "xxx")
|
||||
|
||||
{ echo "#!/bin/bash"
|
||||
echo "[ -f \"/run/dsm.url\" ] && LOCATION=\$(cat \"/run/dsm.url\")"
|
||||
echo "HTML=\"$HTML\"; [ -z \"\$LOCATION\" ] && BODY=\"$WAIT\" || BODY=\"$BODY\"; HTML=\${HTML/xxx/\$BODY}"
|
||||
echo "printf '%b' \"HTTP/1.1 200 OK\\nContent-Length: \${#HTML}\\nConnection: close\\n\\n\$HTML\""
|
||||
} > "$TMP_FILE"
|
||||
|
||||
fi
|
||||
|
||||
chmod +x "$TMP_FILE"
|
||||
|
||||
socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"$TMP_FILE" 2> /dev/null &
|
||||
socat TCP4-LISTEN:"${1:-5000}",reuseaddr,fork,crlf SYSTEM:"$TMP_FILE" 2> /dev/null & wait $!
|
||||
167
web/css/style.css
Normal file
167
web/css/style.css
Normal file
@@ -0,0 +1,167 @@
|
||||
body {
|
||||
color: white;
|
||||
background-color: #125bdb;
|
||||
font-smoothing: antialiased;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: Verdana, Geneva, sans-serif;
|
||||
}
|
||||
|
||||
#info {
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
#content {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
footer {
|
||||
width: 98%;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
color: #0c8aeb;
|
||||
text-shadow: 0 0 1px #0c8aeb;
|
||||
}
|
||||
|
||||
#empty {
|
||||
height: 40px;
|
||||
/* Same height as footer */
|
||||
}
|
||||
|
||||
a,
|
||||
a:hover,
|
||||
a:active,
|
||||
a:visited {
|
||||
color: white;
|
||||
}
|
||||
|
||||
footer a:link,
|
||||
footer a:visited,
|
||||
footer a:active {
|
||||
color: #0c8aeb;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #73e6ff;
|
||||
}
|
||||
|
||||
.loading:after {
|
||||
content: " .";
|
||||
animation: dots 1s steps(5, end) infinite;
|
||||
}
|
||||
|
||||
@keyframes dots {
|
||||
|
||||
0%,
|
||||
20% {
|
||||
color: rgba(0, 0, 0, 0);
|
||||
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
40% {
|
||||
color: white;
|
||||
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
60% {
|
||||
text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
80%,
|
||||
100% {
|
||||
text-shadow: 0.25em 0 0 white, 0.5em 0 0 white;
|
||||
}
|
||||
}
|
||||
|
||||
.spinner_LWk7 {
|
||||
animation: spinner_GWy6 1.2s linear infinite, spinner_BNNO 1.2s linear infinite
|
||||
}
|
||||
|
||||
.spinner_yOMU {
|
||||
animation: spinner_GWy6 1.2s linear infinite, spinner_pVqn 1.2s linear infinite;
|
||||
animation-delay: .15s
|
||||
}
|
||||
|
||||
.spinner_KS4S {
|
||||
animation: spinner_GWy6 1.2s linear infinite, spinner_6uKB 1.2s linear infinite;
|
||||
animation-delay: .3s
|
||||
}
|
||||
|
||||
.spinner_zVee {
|
||||
animation: spinner_GWy6 1.2s linear infinite, spinner_Qw4x 1.2s linear infinite;
|
||||
animation-delay: .45s
|
||||
}
|
||||
|
||||
@keyframes spinner_GWy6 {
|
||||
|
||||
0%,
|
||||
50% {
|
||||
width: 9px;
|
||||
height: 9px
|
||||
}
|
||||
|
||||
10% {
|
||||
width: 11px;
|
||||
height: 11px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinner_BNNO {
|
||||
|
||||
0%,
|
||||
50% {
|
||||
x: 1.5px;
|
||||
y: 1.5px
|
||||
}
|
||||
|
||||
10% {
|
||||
x: .5px;
|
||||
y: .5px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinner_pVqn {
|
||||
|
||||
0%,
|
||||
50% {
|
||||
x: 13.5px;
|
||||
y: 1.5px
|
||||
}
|
||||
|
||||
10% {
|
||||
x: 12.5px;
|
||||
y: .5px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinner_6uKB {
|
||||
|
||||
0%,
|
||||
50% {
|
||||
x: 13.5px;
|
||||
y: 13.5px
|
||||
}
|
||||
|
||||
10% {
|
||||
x: 12.5px;
|
||||
y: 12.5px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spinner_Qw4x {
|
||||
|
||||
0%,
|
||||
50% {
|
||||
x: 1.5px;
|
||||
y: 13.5px
|
||||
}
|
||||
|
||||
10% {
|
||||
x: .5px;
|
||||
y: 12.5px
|
||||
}
|
||||
}
|
||||
1
web/img/favicon.svg
Normal file
1
web/img/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Capa_1" enable-background="new 0 0 511.962 511.962" height="512" viewBox="0 0 511.962 511.962" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m489.965 120.063c0-5.77-3.31-11.028-8.512-13.524l-218.984-105.063c-4.102-1.967-8.875-1.967-12.977 0l-218.985 105.063c-5.202 2.496-8.511 7.755-8.511 13.524l-.003 271.834c0 5.77 3.31 11.028 8.512 13.524l218.989 105.064c2.051.983 4.27 1.476 6.488 1.476 2.219 0 4.438-.492 6.488-1.476l218.989-105.064c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#4e6ba6"/><path d="m489.965 120.063c0-5.77-3.31-11.028-8.512-13.524l-218.984-105.063c-2.051-.984-4.269-1.476-6.488-1.476v511.962c2.219 0 4.438-.492 6.488-1.476l218.989-105.064c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#28487a"/><path d="m425.812 160.441c0-2.27-.519-4.457-1.457-6.432l-336.701-.095c-.967 1.999-1.504 4.22-1.504 6.526l-.002 191.081c0 5.769 3.31 11.028 8.512 13.524l154.833 74.285c2.051.983 4.27 1.476 6.488 1.476 2.219 0 4.438-.492 6.488-1.476l154.834-74.285c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#8dc2eb"/><path d="m424.354 154.009h-168.373v286.798c2.219 0 4.438-.492 6.488-1.476l154.834-74.285c5.202-2.496 8.512-7.755 8.512-13.524l-.003-191.081c0-2.27-.52-4.458-1.458-6.432z" fill="#5e9ff6"/><path d="m417.3 146.916-154.831-74.284c-4.102-1.967-8.875-1.967-12.977 0l-154.831 74.284c-3.122 1.498-5.555 3.996-7.007 6.998l168.328 80.812 168.374-80.717c-1.448-3.044-3.9-5.579-7.056-7.093z" fill="#ecf9fd"/><path d="m417.3 146.916-154.831-74.284c-2.051-.983-4.27-1.476-6.488-1.476v163.569l168.374-80.717c-1.447-3.043-3.899-5.578-7.055-7.092z" fill="#d9f3fc"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
34
web/index.html
Normal file
34
web/index.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
[1]
|
||||
<meta http-equiv="Cache-Control" content="no-cache" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" type="text/css" href="/css/style.css" />
|
||||
<link rel="icon" href="/img/favicon.svg" type="image/x-icon">
|
||||
[2]
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="page">
|
||||
<div id="content">
|
||||
<svg id="spinner" width="64" height="64" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect class="spinner_LWk7" fill="#0c8aeb" x="1.5" y="1.5" rx="1" width="9" height="9"/>
|
||||
<rect class="spinner_yOMU" fill="#0c8aeb" x="13.5" y="1.5" rx="1" width="9" height="9"/>
|
||||
<rect class="spinner_KS4S" fill="#0c8aeb" x="13.5" y="13.5" rx="1" width="9" height="9"/>
|
||||
<rect class="spinner_zVee" fill="#0c8aeb" x="1.5" y="13.5" rx="1" width="9" height="9"/>
|
||||
</svg>
|
||||
<h1 id="info">[3]</h1>
|
||||
</div>
|
||||
<div id="empty">
|
||||
</div>
|
||||
<footer id="footer">
|
||||
[4]<br />
|
||||
[5]
|
||||
</footer>
|
||||
</div>
|
||||
<script type="text/javascript" src="/js/script.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
130
web/js/script.js
Normal file
130
web/js/script.js
Normal file
@@ -0,0 +1,130 @@
|
||||
var request;
|
||||
var interval = 1000;
|
||||
|
||||
function getInfo() {
|
||||
|
||||
var url = "/msg.html";
|
||||
|
||||
try {
|
||||
|
||||
if (window.XMLHttpRequest) {
|
||||
request = new XMLHttpRequest();
|
||||
} else {
|
||||
throw "XMLHttpRequest not available!";
|
||||
}
|
||||
|
||||
request.onreadystatechange = processInfo;
|
||||
request.open("GET", url, true);
|
||||
request.send();
|
||||
|
||||
} catch (e) {
|
||||
var err = "Error: " + e.message;
|
||||
console.log(err);
|
||||
setError(err);
|
||||
}
|
||||
}
|
||||
|
||||
function processInfo() {
|
||||
try {
|
||||
if (request.readyState != 4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var msg = request.responseText;
|
||||
if (msg == null || msg.length == 0) {
|
||||
setInfo("Booting DSM instance", true);
|
||||
schedule();
|
||||
return false;
|
||||
}
|
||||
|
||||
var notFound = (request.status == 404);
|
||||
|
||||
if (request.status == 200) {
|
||||
if (msg.toLowerCase().indexOf("<html>") !== -1) {
|
||||
notFound = true;
|
||||
} else {
|
||||
if (msg.toLowerCase().indexOf("href=") !== -1) {
|
||||
var div = document.createElement("div");
|
||||
div.innerHTML = msg;
|
||||
var url = div.querySelector("a").href;
|
||||
setTimeout(() => {
|
||||
window.location.assign(url);
|
||||
}, 3000);
|
||||
setInfo(msg);
|
||||
return true;
|
||||
} else {
|
||||
setInfo(msg);
|
||||
schedule();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notFound) {
|
||||
setInfo("Connecting to web portal", true);
|
||||
reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
setError("Error: Received statuscode " + request.status);
|
||||
schedule();
|
||||
return false;
|
||||
|
||||
} catch (e) {
|
||||
var err = "Error: " + e.message;
|
||||
console.log(err);
|
||||
setError(err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setInfo(msg, loading, error) {
|
||||
|
||||
try {
|
||||
if (msg == null || msg.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var el = document.getElementById("spinner");
|
||||
|
||||
error = !!error;
|
||||
if (!error) {
|
||||
el.style.visibility = 'visible';
|
||||
} else {
|
||||
el.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
loading = !!loading;
|
||||
if (loading) {
|
||||
msg = "<p class=\"loading\">" + msg + "</p>";
|
||||
}
|
||||
|
||||
el = document.getElementById("info");
|
||||
|
||||
if (el.innerHTML != msg) {
|
||||
el.innerHTML = msg;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (e) {
|
||||
console.log("Error: " + e.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setError(text) {
|
||||
return setInfo(text, false, true);
|
||||
}
|
||||
|
||||
function schedule() {
|
||||
setTimeout(getInfo, interval);
|
||||
}
|
||||
|
||||
function reload() {
|
||||
setTimeout(() => {
|
||||
document.location.reload();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
schedule();
|
||||
33
web/nginx.conf
Normal file
33
web/nginx.conf
Normal file
@@ -0,0 +1,33 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
listen 5000 default_server;
|
||||
listen [::]:5000 default_server;
|
||||
|
||||
autoindex on;
|
||||
tcp_nodelay on;
|
||||
server_tokens off;
|
||||
absolute_redirect off;
|
||||
|
||||
error_log /dev/null;
|
||||
access_log /dev/null;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 5;
|
||||
gzip_min_length 500;
|
||||
gzip_disable "msie6";
|
||||
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
||||
|
||||
add_header Cache-Control "no-cache";
|
||||
|
||||
location / {
|
||||
|
||||
root /run/shm;
|
||||
index index.html;
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user