Compare commits

..

2 Commits

Author SHA1 Message Date
databreach
345fe0c13a Merge 6afb3f1acc into 1b8054e847 2025-05-27 00:51:07 +02:00
databreach
6afb3f1acc Add user scripts folder with LXC scripts from issue #382
Added a user-scripts folder containing the LXC scripts referenced in vdsm/virtual-dsm#382, preserving them before the original repository is deleted.
2025-05-23 20:38:56 +02:00
36 changed files with 799 additions and 1361 deletions

6
.devcontainer.json Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "dsm",
"service": "dsm",
"forwardPorts": [5000],
"dockerComposeFile": "compose.yml"
}

View File

@@ -1,19 +0,0 @@
services:
dsm:
container_name: dsm
image: ghcr.io/vdsm/virtual-dsm
environment:
RAM_SIZE: "half"
DISK_SIZE: "max"
CPU_CORES: "max"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 5000:5000
volumes:
- ./dsm:/storage
restart: on-failure
stop_grace_period: 2m

View File

@@ -1,17 +0,0 @@
{
"name": "Virtual DSM",
"service": "vdsm",
"forwardPorts": [5000],
"portsAttributes": {
"5000": {
"label": "Web",
"onAutoForward": "notify"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"dockerComposeFile": "codespaces.yml",
"workspaceFolder": "/workspaces/vdsm",
"initializeCommand": "docker system prune --all --force"
}

View File

@@ -1,5 +1,4 @@
.dockerignore .dockerignore
.devcontainer
.git .git
.github .github
.gitignore .gitignore

View File

@@ -2,6 +2,20 @@ name: Build
on: on:
workflow_dispatch: workflow_dispatch:
push:
branches:
- master
paths-ignore:
- '**/*.md'
- '**/*.yml'
- '**/*.js'
- '**/*.css'
- '**/*.html'
- 'web/**'
- '.gitignore'
- '.dockerignore'
- '.github/**'
- '.github/workflows/**'
concurrency: concurrency:
group: build group: build
@@ -22,7 +36,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- -

View File

@@ -9,7 +9,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- -
name: Run ShellCheck name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
@@ -17,13 +17,8 @@ jobs:
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028 SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
- -
name: Lint Dockerfile name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.3.0 uses: hadolint/hadolint-action@v3.1.0
with: with:
dockerfile: Dockerfile dockerfile: Dockerfile
ignore: DL3008,DL3003,DL3006,DL3013 ignore: DL3008,DL3003,DL3006,DL3013
failure-threshold: warning failure-threshold: warning
-
name: Validate JSON and YML files
uses: GrantBirki/json-yaml-validate@v4
with:
yaml_exclude_regex: ".*\\kubernetes\\.yml$"

View File

@@ -12,10 +12,10 @@ jobs:
dockerHubDescription: dockerHubDescription:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- -
name: Docker Hub Description name: Docker Hub Description
uses: peter-evans/dockerhub-description@v5 uses: peter-evans/dockerhub-description@v4
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@@ -1,66 +0,0 @@
on:
pull_request:
name: "Review"
permissions:
contents: read
pull-requests: write
checks: write
jobs:
review:
name: review
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v5
-
name: Spelling
uses: reviewdog/action-misspell@v1
with:
locale: "US"
level: warning
pattern: |
*.md
*.sh
reporter: github-pr-review
github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
-
name: Hadolint
uses: reviewdog/action-hadolint@v1
with:
level: warning
reporter: github-pr-review
hadolint_ignore: DL3008 DL3003 DL3006 DL3013
github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
-
name: YamlLint
uses: reviewdog/action-yamllint@v1
with:
level: warning
reporter: github-pr-review
github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
-
name: ActionLint
uses: reviewdog/action-actionlint@v1
with:
level: warning
reporter: github-pr-review
github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
-
name: Shellformat
uses: reviewdog/action-shfmt@v1
with:
level: warning
shfmt_flags: "-i 2 -ci -bn"
github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
-
name: Shellcheck
uses: reviewdog/action-shellcheck@v1
with:
level: warning
reporter: github-pr-review
shellcheck_flags: -x -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
github_token: ${{ secrets.REPO_ACCESS_TOKEN }}

View File

@@ -1,6 +1,11 @@
on: on:
workflow_dispatch: workflow_dispatch:
pull_request: pull_request:
paths:
- '**/*.sh'
- 'Dockerfile'
- '.github/workflows/test.yml'
- '.github/workflows/check.yml'
name: "Test" name: "Test"
permissions: {} permissions: {}

View File

@@ -1,5 +1,3 @@
# syntax=docker/dockerfile:1
FROM qemux/qemu-host:2.05 AS builder FROM qemux/qemu-host:2.05 AS builder
# FROM golang as builder # FROM golang as builder
@@ -11,7 +9,6 @@ FROM qemux/qemu-host:2.05 AS builder
FROM debian:trixie-slim FROM debian:trixie-slim
ARG TARGETARCH
ARG TARGETPLATFORM ARG TARGETPLATFORM
ARG VERSION_ARG="0.0" ARG VERSION_ARG="0.0"
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
@@ -29,7 +26,6 @@ RUN set -eu && \
unzip \ unzip \
nginx \ nginx \
procps \ procps \
ethtool \
python3 \ python3 \
python3-pip \ python3-pip \
python3-msgpack \ python3-msgpack \
@@ -37,20 +33,16 @@ RUN set -eu && \
xz-utils \ xz-utils \
iptables \ iptables \
iproute2 \ iproute2 \
apt-utils \
dnsmasq \ dnsmasq \
fakeroot \ fakeroot \
apt-utils \
net-tools \ net-tools \
e2fsprogs \ e2fsprogs \
qemu-utils \ qemu-utils \
websocketd \
iputils-ping \ iputils-ping \
inotify-tools \
ca-certificates \ ca-certificates \
netcat-openbsd \ netcat-openbsd \
qemu-system-x86 && \ qemu-system-x86 && \
wget "https://github.com/qemus/passt/releases/download/v2025_09_19/passt_2025_09_19_${TARGETARCH}.deb" -O /tmp/passt.deb -q && \
dpkg -i /tmp/passt.deb && \
apt-get clean && \ apt-get clean && \
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \ pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
mkdir -p /etc/qemu && \ mkdir -p /etc/qemu && \
@@ -63,7 +55,7 @@ RUN set -eu && \
COPY --chmod=755 ./src /run/ COPY --chmod=755 ./src /run/
COPY --chmod=755 ./web /var/www/ COPY --chmod=755 ./web /var/www/
COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/default.conf COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/sites-enabled/web.conf
ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py
VOLUME /storage VOLUME /storage
@@ -71,7 +63,7 @@ EXPOSE 22 139 445 5000
ENV RAM_SIZE="2G" ENV RAM_SIZE="2G"
ENV CPU_CORES="2" ENV CPU_CORES="2"
ENV DISK_SIZE="256G" ENV DISK_SIZE="16G"
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh

View File

@@ -3,7 +3,7 @@ services:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
environment: environment:
DISK_SIZE: "256G" DISK_SIZE: "16G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun

View File

@@ -8,7 +8,7 @@ spec:
- ReadWriteOnce - ReadWriteOnce
resources: resources:
requests: requests:
storage: 256Gi storage: 16Gi
--- ---
apiVersion: apps/v1 apiVersion: apps/v1
kind: Deployment kind: Deployment
@@ -31,7 +31,7 @@ spec:
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
env: env:
- name: DISK_SIZE - name: DISK_SIZE
value: "256G" value: "16G"
ports: ports:
- containerPort: 5000 - containerPort: 5000
name: http name: http

View File

@@ -30,7 +30,7 @@ services:
container_name: dsm container_name: dsm
image: vdsm/virtual-dsm image: vdsm/virtual-dsm
environment: environment:
DISK_SIZE: "256G" DISK_SIZE: "16G"
devices: devices:
- /dev/kvm - /dev/kvm
- /dev/net/tun - /dev/net/tun
@@ -47,7 +47,7 @@ services:
##### Via Docker CLI: ##### Via Docker CLI:
```bash ```bash
docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 docker.io/vdsm/virtual-dsm docker run -it --rm --name dsm -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm
``` ```
##### Via Kubernetes: ##### Via Kubernetes:
@@ -87,24 +87,35 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I change the size of the disk? ### How do I change the size of the disk?
To expand the default size of 256 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity: To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
```yaml ```yaml
environment: environment:
DISK_SIZE: "512G" DISK_SIZE: "128G"
``` ```
> [!TIP] > [!TIP]
> This can also be used to resize the existing disk to a larger capacity without any data loss. > This can also be used to resize the existing disk to a larger capacity without any data loss.
### How do I create a growable disk?
By default, the entire capacity of the disk will be reserved in advance.
To create a growable disk that only allocates space that is actually used, add the following environment variable:
```yaml
environment:
DISK_FMT: "qcow2"
```
### How do I add multiple disks? ### How do I add multiple disks?
To create additional disks, modify your compose file like this: To create additional disks, modify your compose file like this:
```yaml ```yaml
environment: environment:
DISK2_SIZE: "500G" DISK2_SIZE: "32G"
DISK3_SIZE: "750G" DISK3_SIZE: "64G"
volumes: volumes:
- ./example2:/storage2 - ./example2:/storage2
- ./example3:/storage3 - ./example3:/storage3
@@ -124,7 +135,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I change the amount of CPU or RAM? ### How do I change the amount of CPU or RAM?
By default, Virtual DSM will be allowed to use 2 CPU cores and 2 GB of RAM. By default, the container will be allowed to use a maximum of 2 CPU cores and 2 GB of RAM.
If you want to adjust this, you can specify the desired amount using the following environment variables: If you want to adjust this, you can specify the desired amount using the following environment variables:
@@ -252,6 +263,14 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses. There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
### How do I run Windows in a container?
You can use [dockur/windows](https://github.com/dockur/windows) for that. It shares many of the same features, and even has completely automatic installation.
### How do I run a Linux desktop in a container?
You can use [qemus/qemu](https://github.com/qemus/qemu) in that case.
### Is this project legal? ### 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 will be considered 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 will be considered legal.

View File

@@ -0,0 +1,122 @@
#!/bin/bash
# Constants
CONFIG_DIR="/etc/pve/lxc"
TMP_DIR="/tmp"
# Function to display log messages
log() {
echo -e "$1"
}
# Function to display error and exit
function display_error_and_exit() {
log "Error: $1 Exiting."
exit 1
}
# Function to display information
function display_info {
clear
log "This script is used to configure prerequisites to run Synology Virtual DSM"
log "in a Docker container inside an unprivileged Proxmox LXC container."
log "Please run this script on the Proxmox host, not inside the LXC container.\n"
}
# Check for root privileges
if [ "$EUID" -ne 0 ]; then
display_error_and_exit "Please run this script as root."
fi
display_info
read -p "Do you want to continue? (y/n): " choice
if [[ $choice == "y" || $choice == "Y" ]]; then
read -p "Enter the LXC Container ID (CT ID): " ct_id
read -p "Enter the vGPU card ID (e.g. card1 = 1): " gpu_card
read -p "Enter the vGPU renderD ID (e.g. renderD129 = 129): " gpu_renderd
# Check if ct_id is a non-empty numeric value
if [[ ! $ct_id =~ ^[0-9]+$ ]]; then
display_error_and_exit "Invalid LXC Container ID. Please enter a numeric value."
fi
# Check if gpu_card is a non-empty numeric value
if [[ ! $gpu_card =~ ^[0-9]+$ ]]; then
display_error_and_exit "Invalid vGPU card number. Please enter a numeric value."
fi
# Check if gpu_renderd is a non-empty numeric value
if [[ ! $gpu_renderd =~ ^[0-9]+$ ]]; then
display_error_and_exit "Invalid vGPU renderD number. Please enter a numeric value."
fi
# Check if the configuration file exists
config_file="$CONFIG_DIR/$ct_id.conf"
if [[ ! -f "$config_file" ]]; then
display_error_and_exit "Configuration file $config_file does not exist."
fi
# Check if the LXC container is running
container_status=$(pct status $ct_id 2>&1)
if [[ "$container_status" == *"running"* ]]; then
log "Stopping running LXC container $ct_id..."
pct stop $ct_id || display_error_and_exit "Failed to stop LXC container $ct_id."
fi
# Remove existing dev folder and tun, kvm, and vhost-net devices
if [[ -d "/dev-$ct_id" ]]; then
log "Removing existing /dev-$ct_id folder..."
rm -r "/dev-$ct_id" || display_error_and_exit "Failed to remove existing /dev-$ct_id folder."
fi
# Function to configure devices
function configure_device() {
device=$1
module=$2
major=$3
minor=$4
log "Configuring $device..."
mkdir -p "/dev-$ct_id/net" || display_error_and_exit "Failed to create /dev-$ct_id/net"
mkdir -p "/dev-$ct_id/dri" || display_error_and_exit "Failed to create /dev-$ct_id/dri"
mknod "/dev-$ct_id/$device" c $major $minor || display_error_and_exit "Failed to mknod /dev-$ct_id/$device"
chown 100000:100000 "/dev-$ct_id/$device" || display_error_and_exit "Failed to chown /dev-$ct_id/$device"
#log "Checking if /dev-$ct_id/$device exists..."
if ! [[ -e "/dev-$ct_id/$device" ]]; then
display_error_and_exit "/dev-$ct_id/$device should have been created but does not exist."
fi
}
# Configure devices
configure_device "net/tun" "tun" 10 200
configure_device "kvm" "kvm" 10 232
configure_device "vhost-net" "vhost-net" 10 238
configure_device "dri/card0" "card0" 226 $gpu_card
configure_device "dri/renderD128" "renderD128" 226 $gpu_renderd
# Check and add configuration lines to /et/pve/lxc/<CT ID>.conf
log "Checking and adding configuration to $config_file..."
lines_to_add=(
"lxc.mount.entry: /dev-$ct_id/net/tun dev/net/tun none bind,create=file 0 0"
"lxc.mount.entry: /dev-$ct_id/kvm dev/kvm none bind,create=file 0 0"
"lxc.mount.entry: /dev-$ct_id/vhost-net dev/vhost-net none bind,create=file 0 0"
"lxc.mount.entry: /dev-$ct_id/dri/card0 dev/dri/card0 none bind,create=file 0 0"
"lxc.mount.entry: /dev-$ct_id/dri/renderD128 dev/dri/renderD128 none bind,create=file 0 0"
)
# Error handling for config file changes
for line in "${lines_to_add[@]}"; do
if ! grep -qF "$line" "$config_file"; then
echo "$line" >> "$config_file" || display_error_and_exit "Failed to add line '$line' to $config_file."
fi
done
log "Configuration completed successfully.\n\nStart the docker image (vdsm/virtual-dsm:latest) inside the LXC container."
else
clear
log "\nScript aborted. No changes were made."
fi

105
scripts/virtual-dsm-lxc.sh Normal file
View File

@@ -0,0 +1,105 @@
#!/bin/bash
# Constants
CONFIG_DIR="/etc/pve/lxc"
TMP_DIR="/tmp"
# Function to display log messages
log() {
echo -e "$1"
}
# Function to display error and exit
function display_error_and_exit() {
log "Error: $1 Exiting."
exit 1
}
# Function to display information
function display_info {
clear
log "This script is used to configure prerequisites to run Synology Virtual DSM"
log "in a Docker container inside an unprivileged Proxmox LXC container."
log "Please run this script on the Proxmox host, not inside the LXC container.\n"
}
# Check for root privileges
if [ "$EUID" -ne 0 ]; then
display_error_and_exit "Please run this script as root."
fi
display_info
read -p "Do you want to continue? (y/n): " choice
if [[ $choice == "y" || $choice == "Y" ]]; then
read -p "Enter the LXC Container ID (CT ID): " ct_id
# Check if ct_id is a non-empty numeric value
if [[ ! $ct_id =~ ^[0-9]+$ ]]; then
display_error_and_exit "Invalid LXC Container ID. Please enter a numeric value."
fi
# Check if the configuration file exists
config_file="$CONFIG_DIR/$ct_id.conf"
if [[ ! -f "$config_file" ]]; then
display_error_and_exit "Configuration file $config_file does not exist."
fi
# Check if the LXC container is running
container_status=$(pct status $ct_id 2>&1)
if [[ "$container_status" == *"running"* ]]; then
log "Stopping running LXC container $ct_id..."
pct stop $ct_id || display_error_and_exit "Failed to stop LXC container $ct_id."
fi
# Remove existing dev folder and tun, kvm, and vhost-net devices
if [[ -d "/dev-$ct_id" ]]; then
log "Removing existing /dev-$ct_id folder..."
rm -r "/dev-$ct_id" || display_error_and_exit "Failed to remove existing /dev-$ct_id folder."
fi
# Function to configure devices
function configure_device() {
device=$1
module=$2
major=$3
minor=$4
log "Configuring $device..."
mkdir -p "/dev-$ct_id/net" || display_error_and_exit "Failed to create /dev-$ct_id/net"
mknod "/dev-$ct_id/$device" c $major $minor || display_error_and_exit "Failed to mknod /dev-$ct_id/$device"
chown 100000:100000 "/dev-$ct_id/$device" || display_error_and_exit "Failed to chown /dev-$ct_id/$device"
#log "Checking if /dev-$ct_id/$device exists..."
if ! [[ -e "/dev-$ct_id/$device" ]]; then
display_error_and_exit "/dev-$ct_id/$device should have been created but does not exist."
fi
}
# Configure devices
configure_device "net/tun" "tun" 10 200
configure_device "kvm" "kvm" 10 232
configure_device "vhost-net" "vhost-net" 10 238
# Check and add configuration lines to /et/pve/lxc/<CT ID>.conf
log "Checking and adding configuration to $config_file..."
lines_to_add=(
"lxc.mount.entry: /dev-$ct_id/net/tun dev/net/tun none bind,create=file 0 0"
"lxc.mount.entry: /dev-$ct_id/kvm dev/kvm none bind,create=file 0 0"
"lxc.mount.entry: /dev-$ct_id/vhost-net dev/vhost-net none bind,create=file 0 0"
)
# Error handling for config file changes
for line in "${lines_to_add[@]}"; do
if ! grep -qF "$line" "$config_file"; then
echo "$line" >> "$config_file" || display_error_and_exit "Failed to add line '$line' to $config_file."
fi
done
log "Configuration completed successfully.\n\nStart the docker image (vdsm/virtual-dsm:latest) inside the LXC container."
else
clear
log "\nScript aborted. No changes were made."
fi

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${DHCP:="N"}"
: "${NETWORK:="Y"}" : "${NETWORK:="Y"}"
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1 [ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
@@ -10,7 +9,6 @@ set -Eeuo pipefail
file="/run/shm/dsm.url" file="/run/shm/dsm.url"
address="/run/shm/qemu.ip" address="/run/shm/qemu.ip"
gateway="/run/shm/qemu.gw"
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1 [ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
@@ -18,13 +16,13 @@ location=$(<"$file")
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$location" == "20.20"* ]]; then
ip=$(<"$address") ip="20.20.20.1"
echo "Failed to reach DSM at http://$location"
else
ip=$(<"$gateway")
port="${location##*:}" port="${location##*:}"
echo "Failed to reach DSM at port $port" echo "Failed to reach DSM at port $port"
else
echo "Failed to reach DSM at http://$location"
ip=$(<"$address")
fi fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1 echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1

View File

@@ -12,4 +12,32 @@ DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $DEV_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 ' ') ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
# Check available memory as the very last step
if [[ "$RAM_CHECK" != [Nn]* ]]; then
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
info "$msg"
else
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available, please consider a lower value."
if [[ "${FS,,}" != "zfs" ]]; then
warn "$msg"
else
info "$msg"
fi
fi
fi
fi
if [[ "$DEBUG" == [Yy1]* ]]; then
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi
return 0 return 0

View File

@@ -17,16 +17,8 @@ SYSTEM="$STORAGE/$BASE.system.img"
[ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81 [ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82 [ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
if ! setOwner "$BOOT"; then
error "Failed to set the owner for \"$BOOT\" !"
fi
if ! setOwner "$SYSTEM"; then
error "Failed to set the owner for \"$SYSTEM\" !"
fi
fmt2ext() { fmt2ext() {
local DISK_FMT="$1" local DISK_FMT=$1
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
qcow2) qcow2)
@@ -42,7 +34,7 @@ fmt2ext() {
} }
ext2fmt() { ext2fmt() {
local DISK_EXT="$1" local DISK_EXT=$1
case "${DISK_EXT,,}" in case "${DISK_EXT,,}" in
qcow2) qcow2)
@@ -58,7 +50,7 @@ ext2fmt() {
} }
getSize() { getSize() {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_EXT DISK_FMT local DISK_EXT DISK_FMT
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//') DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
@@ -78,7 +70,7 @@ getSize() {
} }
isCow() { isCow() {
local FS="$1" local FS=$1
if [[ "${FS,,}" == "btrfs" ]]; then if [[ "${FS,,}" == "btrfs" ]]; then
return 0 return 0
@@ -88,9 +80,9 @@ isCow() {
} }
supportsDirect() { supportsDirect() {
local FS="$1" local FS=$1
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
return 1 return 1
fi fi
@@ -99,17 +91,17 @@ supportsDirect() {
createDisk() { createDisk() {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_SPACE="$2" local DISK_SPACE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DISK_FMT="$4" local DISK_FMT=$4
local FS="$5" local FS=$5
local DATA_SIZE DIR SPACE GB FA local DATA_SIZE DIR SPACE GB FA
rm -f "$DISK_FILE"
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
rm -f "$DISK_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace # Check free diskspace
@@ -121,7 +113,6 @@ createDisk() {
error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..." error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76 error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
fi fi
fi fi
html "Creating a $DISK_DESC image..." html "Creating a $DISK_DESC image..."
@@ -186,16 +177,16 @@ createDisk() {
resizeDisk() { resizeDisk() {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_SPACE="$2" local DISK_SPACE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DISK_FMT="$4" local DISK_FMT=$4
local FS="$5" local FS=$5
local CUR_SIZE DATA_SIZE DIR SPACE GB local CUR_SIZE DATA_SIZE DIR SPACE GB
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
local REQ=$(( DATA_SIZE - CUR_SIZE )) local REQ=$((DATA_SIZE-CUR_SIZE))
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71 (( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
@@ -209,7 +200,6 @@ resizeDisk() {
error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.." error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74 error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
fi fi
fi fi
GB=$(formatBytes "$CUR_SIZE") GB=$(formatBytes "$CUR_SIZE")
@@ -255,13 +245,13 @@ resizeDisk() {
convertDisk() { convertDisk() {
local SOURCE_FILE="$1" local SOURCE_FILE=$1
local SOURCE_FMT="$2" local SOURCE_FMT=$2
local DST_FILE="$3" local DST_FILE=$3
local DST_FMT="$4" local DST_FMT=$4
local DISK_BASE="$5" local DISK_BASE=$5
local DISK_DESC="$6" local DISK_DESC=$6
local FS="$7" local FS=$7
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79 [ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79 [ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
@@ -283,7 +273,6 @@ convertDisk() {
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..." error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..."
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76 error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
fi fi
fi fi
local msg="Converting $DISK_DESC to $DST_FMT" local msg="Converting $DISK_DESC to $DST_FMT"
@@ -338,31 +327,31 @@ convertDisk() {
checkFS () { checkFS () {
local FS="$1" local FS=$1
local DISK_FILE="$2" local DISK_FILE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DIR FA local DIR FA
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
if [[ "${FS,,}" == "overlay"* && "$PODMAN" != [Yy1]* ]]; then if [[ "${FS,,}" == "overlay"* ]]; then
warn "the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!" info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
if [[ "${FS,,}" == "fuse"* ]]; then if [[ "${FS,,}" == "fuse"* ]]; then
warn "the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!" info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
fi fi
if ! supportsDirect "$FS"; then if ! supportsDirect "$FS"; then
warn "the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..." info "Warning: the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi fi
if isCow "$FS"; then if isCow "$FS"; then
if [ -f "$DISK_FILE" ]; then if [ -f "$DISK_FILE" ]; then
FA=$(lsattr "$DISK_FILE") FA=$(lsattr "$DISK_FILE")
if [[ "$FA" != *"C"* ]]; then if [[ "$FA" != *"C"* ]]; then
warn "COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!" info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
fi fi
fi fi
fi fi
@@ -372,15 +361,15 @@ checkFS () {
createDevice () { createDevice () {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_TYPE="$2" local DISK_TYPE=$2
local DISK_INDEX="$3" local DISK_INDEX=$3
local DISK_ADDRESS="$4" local DISK_ADDRESS=$4
local DISK_FMT="$5" local DISK_FMT=$5
local DISK_IO="$6" local DISK_IO=$6
local DISK_CACHE="$7" local DISK_CACHE=$7
local DISK_SERIAL="$8" local DISK_SERIAL=$8
local DISK_SECTORS="$9" local DISK_SECTORS=$9
local DISK_ID="data$DISK_INDEX" local DISK_ID="data$DISK_INDEX"
local index="" local index=""
@@ -426,16 +415,16 @@ createDevice () {
addDisk () { addDisk () {
local DISK_BASE="$1" local DISK_BASE=$1
local DISK_TYPE="$2" local DISK_TYPE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DISK_SPACE="$4" local DISK_SPACE=$4
local DISK_INDEX="$5" local DISK_INDEX=$5
local DISK_ADDRESS="$6" local DISK_ADDRESS=$6
local DISK_FMT="$7" local DISK_FMT=$7
local DISK_IO="$8" local DISK_IO=$8
local DISK_CACHE="$9" local DISK_CACHE=$9
local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE LEFT FREE USED local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DISK_EXT=$(fmt2ext "$DISK_FMT") DISK_EXT=$(fmt2ext "$DISK_FMT")
local DISK_FILE="$DISK_BASE.$DISK_EXT" local DISK_FILE="$DISK_BASE.$DISK_EXT"
@@ -443,25 +432,8 @@ addDisk () {
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
if [[ "${DISK_SPACE,,}" == "max" || "${DISK_SPACE,,}" == "half" ]]; then
local SPARE=1073741824
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if [[ "${DISK_SPACE,,}" == "max" ]]; then
FREE=$(( FREE - SPARE ))
else
FREE=$(( FREE / 2 ))
fi
(( FREE < SPARE )) && FREE="$SPARE"
GB=$(( FREE / 1073741825 ))
DISK_SPACE="${GB}G"
fi
SPACE="${DISK_SPACE// /}" SPACE="${DISK_SPACE// /}"
[ -z "$SPACE" ] && SPACE="256G" [ -z "$SPACE" ] && SPACE="16G"
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G" [ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
@@ -472,7 +444,7 @@ addDisk () {
DATA_SIZE=$(numfmt --from=iec "$SPACE") DATA_SIZE=$(numfmt --from=iec "$SPACE")
if (( DATA_SIZE < 6442450944 )); then if (( DATA_SIZE < 6442450944 )); then
error "Please increase the ${DISK_DESC^^}_SIZE variable to at least 6 GB." && exit 73 error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi fi
FS=$(stat -f -c %T "$DIR") FS=$(stat -f -c %T "$DIR")
@@ -483,7 +455,7 @@ addDisk () {
DISK_CACHE="writeback" DISK_CACHE="writeback"
fi fi
if [ ! -s "$DISK_FILE" ] ; then if ! [ -s "$DISK_FILE" ] ; then
if [[ "${DISK_FMT,,}" != "raw" ]]; then if [[ "${DISK_FMT,,}" != "raw" ]]; then
PREV_FMT="raw" PREV_FMT="raw"
@@ -496,7 +468,6 @@ addDisk () {
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $? convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
fi fi
fi fi
if [ -s "$DISK_FILE" ]; then if [ -s "$DISK_FILE" ]; then
@@ -504,18 +475,7 @@ addDisk () {
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
if (( DATA_SIZE > CUR_SIZE )); then if (( DATA_SIZE > CUR_SIZE )); then
resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $? resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
else
if (( DATA_SIZE < CUR_SIZE )); then
if [[ "${DISK_SPACE,,}" != "max" && "${DISK_SPACE,,}" != "half" ]]; then
info "You decreased the ${DISK_DESC^^}_SIZE variable to ${DISK_SPACE/G/ GB} but shrinking disks is not supported, will be ignored..."
fi
fi
fi fi
else else
@@ -524,39 +484,6 @@ addDisk () {
fi fi
if [ -f "$DISK_FILE" ] && [[ "$ALLOCATE" == [Nn]* ]]; then
CUR_SIZE=$(getSize "$DISK_FILE")
USED=$(du -sB 1 "$DISK_FILE" | cut -f1)
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
LEFT=$(( CUR_SIZE - USED - FREE ))
if (( LEFT > 0 )); then
GB=$(formatBytes "$FREE")
LEFT=$(formatBytes "$LEFT")
CUR_SIZE=$(formatBytes "$CUR_SIZE")
msg="the virtual size of the ${DISK_DESC,,} is $CUR_SIZE"
if [[ "$USED" == "0" ]]; then
msg+=","
else
USED=$(formatBytes "$USED")
msg+=" (of which $USED is used),"
fi
warn "$msg but there is only $GB of free space left in $DIR, make at least $LEFT more room available!"
fi
fi
if [ -f "$DISK_FILE" ]; then
if ! setOwner "$DISK_FILE"; then
error "Failed to set the owner for \"$DISK_FILE\" !"
fi
fi
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "") DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "")
return 0 return 0
@@ -564,10 +491,10 @@ addDisk () {
addDevice () { addDevice () {
local DISK_DEV="$1" local DISK_DEV=$1
local DISK_TYPE="$2" local DISK_TYPE=$2
local DISK_INDEX="$3" local DISK_INDEX=$3
local DISK_ADDRESS="$4" local DISK_ADDRESS=$4
[ -z "$DISK_DEV" ] && return 0 [ -z "$DISK_DEV" ] && return 0
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55 [ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
@@ -579,9 +506,13 @@ addDevice () {
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-) physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}" physical="${physical%% *}"
if [ -n "$physical" ]; then if [ -n "$physical" ]; then
if [[ "$physical" != "512" ]]; then if [[ "$physical" == "512" ]] || [[ "$physical" == "4096" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical" if [[ "$physical" == "4096" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
else
warn "Unknown physical sector size: $physical for $DISK_DEV"
fi fi
else else
warn "Failed to determine the sector size for $DISK_DEV" warn "Failed to determine the sector size for $DISK_DEV"
@@ -592,9 +523,7 @@ addDevice () {
return 0 return 0
} }
msg="Initializing disks..." html "Initializing disks..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS="" [ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi" [ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
@@ -606,7 +535,11 @@ case "${DISK_TYPE,,}" in
esac esac
if [ -z "$ALLOCATE" ]; then if [ -z "$ALLOCATE" ]; then
ALLOCATE="N" if [[ "${DISK_FMT,,}" == "raw" ]]; then
ALLOCATE="Y"
else
ALLOCATE="N"
fi
fi fi
if [[ "$ALLOCATE" == [Nn]* ]]; then if [[ "$ALLOCATE" == [Nn]* ]]; then
@@ -621,7 +554,7 @@ DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "") DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
DISK1_FILE="$STORAGE/${DISK_NAME}" DISK1_FILE="$STORAGE/${DISK_NAME}"
if [ ! -f "$DISK1_FILE.img" ] && [ -f "$STORAGE/data${DISK_SIZE}.img" ]; then if [[ ! -f "$DISK1_FILE.img" ]] && [[ -f "$STORAGE/data${DISK_SIZE}.img" ]]; then
# Fallback for legacy installs # Fallback for legacy installs
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img" mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi fi
@@ -630,7 +563,7 @@ DISK2_FILE="/storage2/${DISK_NAME}2"
if [ ! -f "$DISK2_FILE.img" ]; then if [ ! -f "$DISK2_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage2/data.img" FALLBACK="/storage2/data.img"
if [[ -f "$DISK1_FILE.img" && -f "$FALLBACK" ]]; then if [[ -f "$DISK1_FILE.img" ]] && [[ -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "$FALLBACK") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "$DISK1_FILE.img") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then
@@ -643,7 +576,7 @@ DISK3_FILE="/storage3/${DISK_NAME}3"
if [ ! -f "$DISK3_FILE.img" ]; then if [ ! -f "$DISK3_FILE.img" ]; then
# Fallback for legacy installs # Fallback for legacy installs
FALLBACK="/storage3/data.img" FALLBACK="/storage3/data.img"
if [[ -f "$DISK1_FILE.img" && -f "$FALLBACK" ]]; then if [[ -f "$DISK1_FILE.img" ]] && [[ -f "$FALLBACK" ]]; then
SIZE1=$(stat -c%s "$FALLBACK") SIZE1=$(stat -c%s "$FALLBACK")
SIZE2=$(stat -c%s "$DISK1_FILE.img") SIZE2=$(stat -c%s "$DISK1_FILE.img")
if [[ SIZE1 -ne SIZE2 ]]; then if [[ SIZE1 -ne SIZE2 ]]; then

View File

@@ -10,7 +10,7 @@ set -Eeuo pipefail
CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}') CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}')
if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64" ]]; then if [[ "$GPU" != [Yy1]* ]] || [[ "$CPU_VENDOR" != "GenuineIntel" ]] || [[ "$ARCH" != "amd64" ]]; then
[[ "${DISPLAY,,}" == "none" ]] && VGA="none" [[ "${DISPLAY,,}" == "none" ]] && VGA="none"
DISPLAY_OPTS="-display $DISPLAY -vga $VGA" DISPLAY_OPTS="-display $DISPLAY -vga $VGA"
@@ -18,10 +18,6 @@ if [[ "$GPU" != [Yy1]* || "$CPU_VENDOR" != "GenuineIntel" || "$ARCH" != "amd64"
fi fi
msg="Configuring display drivers..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE" DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
DISPLAY_OPTS+=" -vga $VGA" DISPLAY_OPTS+=" -vga $VGA"

View File

@@ -1,16 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${PLATFORM:="x64"}"
: "${APP:="Virtual DSM"}" : "${APP:="Virtual DSM"}"
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}" : "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
cd /run cd /run
. start.sh # Startup hook
. utils.sh # Load functions . utils.sh # Load functions
. reset.sh # Initialize system . reset.sh # Initialize system
. server.sh # Start webserver
. install.sh # Run installation . install.sh # Run installation
. disk.sh # Initialize disks . disk.sh # Initialize disks
. display.sh # Initialize graphics . display.sh # Initialize graphics
@@ -18,9 +15,7 @@ cd /run
. proc.sh # Initialize processor . proc.sh # Initialize processor
. serial.sh # Initialize serialport . serial.sh # Initialize serialport
. power.sh # Configure shutdown . power.sh # Configure shutdown
. memory.sh # Check available memory
. config.sh # Configure arguments . config.sh # Configure arguments
. finish.sh # Finish initialization
trap - ERR trap - ERR
@@ -35,7 +30,7 @@ fi
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15 (( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
terminal terminal
tail -fn +0 "$QEMU_LOG" --pid=$$ 2>/dev/null & tail -fn +0 "$QEMU_LOG" 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || : cat "$QEMU_TERM" 2>/dev/null & wait $! || :
sleep 1 & wait $! sleep 1 & wait $!

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
if [[ "$DEBUG" == [Yy1]* ]]; then
printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi
return 0

View File

@@ -18,7 +18,7 @@ DIR=$(find / -maxdepth 1 -type d -iname "$FN" -print -quit)
if [ -d "$DIR" ]; then if [ -d "$DIR" ]; then
BASE="DSM_VirtualDSM" && URL="file://$DIR" BASE="DSM_VirtualDSM" && URL="file://$DIR"
if [[ ! -s "$STORAGE/$BASE.boot.img" || ! -s "$STORAGE/$BASE.system.img" ]]; then if [[ ! -s "$STORAGE/$BASE.boot.img" ]] || [[ ! -s "$STORAGE/$BASE.system.img" ]]; then
error "The bind $DIR maps to a file that does not exist!" && exit 65 error "The bind $DIR maps to a file that does not exist!" && exit 65
fi fi
fi fi
@@ -31,17 +31,16 @@ if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
BASE=$(basename "$URL" .pat) BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }" : "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
printf -v BASE '%b' "${BASE//%/\\x}" BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
if [[ "${URL,,}" != "http"* && "${URL,,}" != "file:"* ]] ; then if [[ "${URL,,}" != "http"* ]] && [[ "${URL,,}" != "file:"* ]] ; then
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65 [ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
URL="file://$STORAGE/$BASE.pat" URL="file://$STORAGE/$BASE.pat"
fi fi
fi fi
if [[ -s "$STORAGE/$BASE.boot.img" && -s "$STORAGE/$BASE.system.img" ]]; then if [[ -s "$STORAGE/$BASE.boot.img" ]] && [[ -s "$STORAGE/$BASE.system.img" ]]; then
return 0 # Previous installation found return 0 # Previous installation found
fi fi
@@ -66,9 +65,8 @@ fi
if [ ! -s "$FILE" ]; then if [ ! -s "$FILE" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }" : "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
printf -v BASE '%b' "${BASE//%/\\x}" BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
@@ -82,16 +80,16 @@ rm -f "$STORAGE/$BASE.system.img"
# Check filesystem # Check filesystem
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "overlay"* && "$PODMAN" != [Yy1]* ]]; then if [[ "${FS,,}" == "overlay"* ]]; then
warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!" info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
if [[ "${FS,,}" == "fuse"* ]]; then if [[ "${FS,,}" == "fuse"* ]]; then
warn "the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!" info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
fi fi
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
warn "the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..." info "Warning: the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi fi
if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then
@@ -100,10 +98,6 @@ fi
if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then
TMP="$STORAGE/tmp" TMP="$STORAGE/tmp"
rm -rf "$TMP"
if ! makeDir "$TMP"; then
error "Failed to create directory \"$TMP\" !" && exit 93
fi
else else
TMP="/tmp/dsm" TMP="/tmp/dsm"
TMP_SPACE=2147483648 TMP_SPACE=2147483648
@@ -112,9 +106,10 @@ else
if (( TMP_SPACE > SPACE )); then if (( TMP_SPACE > SPACE )); then
error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93 error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93
fi fi
rm -rf "$TMP" && mkdir -p "$TMP"
fi fi
rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace # Check free diskspace
ROOT_SPACE=536870912 ROOT_SPACE=536870912
SPACE=$(df --output=avail -B 1 / | tail -n 1) SPACE=$(df --output=avail -B 1 / | tail -n 1)
@@ -227,8 +222,6 @@ if ! touch "$SYSTEM"; then
error "Could not create file $SYSTEM for the system disk." && exit 98 error "Could not create file $SYSTEM for the system disk." && exit 98
fi fi
! setOwner "$SYSTEM" && error "Failed to set the owner for \"$SYSTEM\" !"
if [[ "${FS,,}" == "btrfs" ]]; then if [[ "${FS,,}" == "btrfs" ]]; then
{ chattr +C "$SYSTEM"; } || : { chattr +C "$SYSTEM"; } || :
FA=$(lsattr "$SYSTEM") FA=$(lsattr "$SYSTEM")
@@ -261,11 +254,7 @@ PART="$TMP/partition.fdisk"
sfdisk -q "$SYSTEM" < "$PART" sfdisk -q "$SYSTEM" < "$PART"
MOUNT="$TMP/system" MOUNT="$TMP/system"
rm -rf "$MOUNT" rm -rf "$MOUNT" && mkdir -p "$MOUNT"
if ! makeDir "$MOUNT"; then
error "Failed to create directory \"$MOUNT\" !" && exit 93
fi
MSG="Extracting system partition..." MSG="Extracting system partition..."
info "Install: $MSG" && html "$MSG" info "Install: $MSG" && html "$MSG"
@@ -300,7 +289,6 @@ fakeroot -- bash -c "set -Eeu;\
rm -rf "$MOUNT" rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE/dsm.ver" echo "$BASE" > "$STORAGE/dsm.ver"
! setOwner "$STORAGE/dsm.ver" && error "Failed to set the owner for \"$STORAGE/dsm.ver\" !"
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$PAT" rm -f "$PAT"
@@ -308,13 +296,10 @@ else
mv -f "$PAT" "$STORAGE/$BASE.pat" mv -f "$PAT" "$STORAGE/$BASE.pat"
fi fi
if [ -f "$STORAGE/$BASE.pat" ]; then
! setOwner "$STORAGE/$BASE.pat" && error "Failed to set the owner for \"$STORAGE/$BASE.pat\" !"
fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img" mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
! setOwner "$STORAGE/$BASE.boot.img" && error "Failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
rm -rf "$TMP" rm -rf "$TMP"
html "Booting DSM instance..."
sleep 1.2
return 0 return 0

View File

@@ -1,86 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
if [[ "$RAM_CHECK" != [Nn]* && "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available,"
if [[ "${FS,,}" == "zfs" ]]; then
info "$msg but since ZFS is active this will be ignored."
else
RAM_SIZE="max"
warn "$msg it will automatically be adjusted to a lower amount."
fi
else
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available,"
if [[ "${FS,,}" == "zfs" ]]; then
info "$msg but since ZFS is active this will be ignored."
else
warn "$msg please consider a lower amount."
fi
fi
fi
fi
if [[ "${RAM_SIZE,,}" == "half" ]]; then
RAM_WANTED=$(( RAM_AVAIL / 2 ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL / 2 ))
RAM_WANTED=$(( RAM_WANTED / 1048577 ))
RAM_SIZE="${RAM_WANTED}M"
else
RAM_SIZE="${RAM_WANTED}G"
fi
fi
if [[ "${RAM_SIZE,,}" == "max" ]]; then
RAM_WANTED=$(( RAM_AVAIL - (RAM_SPARE * 3) ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL - (RAM_SPARE * 2) ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE ))
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE ))
RAM_WANTED=$(( RAM_WANTED / 1048577 ))
if (( "$RAM_WANTED" < 1 )); then
RAM_WANTED=$(( RAM_AVAIL ))
RAM_WANTED=$(( RAM_WANTED / 1048577 ))
fi
RAM_SIZE="${RAM_WANTED}M"
else
RAM_SIZE="${RAM_WANTED}G"
fi
else
RAM_SIZE="${RAM_WANTED}G"
fi
else
RAM_SIZE="${RAM_WANTED}G"
fi
fi
return 0

View File

@@ -7,25 +7,17 @@ set -Eeuo pipefail
: "${MTU:=""}" : "${MTU:=""}"
: "${DHCP:="N"}" : "${DHCP:="N"}"
: "${NETWORK:="Y"}" : "${NETWORK:="Y"}"
: "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}" : "${USER_PORTS:=""}"
: "${HOST_PORTS:=""}"
: "${ADAPTER:="virtio-net-pci"}" : "${ADAPTER:="virtio-net-pci"}"
: "${VM_NET_IP:=""}"
: "${VM_NET_DEV:=""}" : "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="dsm"}" : "${VM_NET_TAP:="dsm"}"
: "${VM_NET_MAC:="$MAC"}" : "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_BRIDGE:="docker"}" : "${VM_NET_IP:="20.20.20.21"}"
: "${VM_NET_HOST:="VirtualDSM"}" : "${VM_NET_HOST:="VirtualDSM"}"
: "${VM_NET_MASK:="255.255.255.0"}"
: "${PASST:="passt"}"
: "${PASST_MTU:=""}"
: "${PASST_OPTS:=""}"
: "${PASST_DEBUG:=""}"
: "${DNSMASQ_OPTS:=""}" : "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ_DEBUG:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}" : "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}" : "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
@@ -37,8 +29,6 @@ ADD_ERR="Please add the following setting to your container:"
configureDHCP() { configureDHCP() {
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
# Create the necessary file structure for /dev/vhost-net # Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then if mknod /dev/vhost-net c 10 238; then
@@ -69,15 +59,14 @@ configureDHCP() {
fi ;; fi ;;
esac esac
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size to $MTU." warn "Failed to set MTU size.."
fi fi
fi fi
while ! ip link set "$VM_NET_TAP" up; do while ! ip link set "$VM_NET_TAP" up; do
info "Waiting for MAC address $VM_NET_MAC to become available..." info "Waiting for MAC address $VM_NET_MAC to become available..."
info "If you cloned this machine, please delete the 'dsm.mac' file to generate a different MAC address."
sleep 2 sleep 2
done done
@@ -89,7 +78,7 @@ configureDHCP() {
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev) IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev)
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && return 1 (( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && return 1
[[ ! -e "$TAP_PATH" && -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH" [[ ! -e "$TAP_PATH" ]] && [[ -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
if [[ ! -e "$TAP_PATH" ]]; then if [[ ! -e "$TAP_PATH" ]]; then
{ mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || : { mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
@@ -115,291 +104,95 @@ configureDHCP() {
configureDNS() { configureDNS() {
local if="$1" # Create lease file for faster resolve
local ip="$2" echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
local mac="$3" chmod 644 /var/lib/misc/dnsmasq.leases
local host="$4"
local mask="$5"
local gateway="$6"
echo "$gateway" > /run/shm/qemu.gw # dnsmasq configuration:
DNSMASQ_OPTS+=" --dhcp-authoritative"
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0 # Set DHCP range and host
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..." DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP"
DNSMASQ_OPTS+=" --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite"
local log="/var/log/dnsmasq.log" # Set DNS server and gateway
rm -f "$log" DNSMASQ_OPTS+=" --dhcp-option=option:netmask,255.255.255.0"
DNSMASQ_OPTS+=" --dhcp-option=option:router,${VM_NET_IP%.*}.1"
case "${NETWORK,,}" in DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1"
"tap" | "tun" | "tuntap" | "y" )
# Create lease file for faster resolve
echo "0 $mac $ip $host 01:$mac" > /var/lib/misc/dnsmasq.leases
chmod 644 /var/lib/misc/dnsmasq.leases
# dnsmasq configuration:
DNSMASQ_OPTS+=" --dhcp-authoritative"
# Set DHCP range and host
DNSMASQ_OPTS+=" --dhcp-range=$ip,$ip"
DNSMASQ_OPTS+=" --dhcp-host=$mac,,$ip,$host,infinite"
# Set DNS server and gateway
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,$mask"
DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway"
esac
# Set interfaces
DNSMASQ_OPTS+=" --interface=$if"
DNSMASQ_OPTS+=" --bind-interfaces"
# Add DNS entry for container # Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/$gateway" DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1"
# Set local dns resolver to dnsmasq when needed
[ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"
# Enable logging to file
DNSMASQ_OPTS+=" --log-facility=$log"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}"
if [[ "${DEBUG_DNS:-}" == [Yy1]* ]]; then
DNSMASQ_OPTS+=" -d"
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
return 0
fi
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && return 1
local msg="Failed to start Dnsmasq, reason: $?"
[ -f "$log" ] && cat "$log"
error "$msg"
return 1
fi fi
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
fi
return 0
}
getHostPorts() {
local list=""
list+="$MON_PORT,"
list+="${HOST_PORTS// /},"
# Remove duplicates
list=$(echo "${list//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
echo "$list"
return 0 return 0
} }
getUserPorts() { getUserPorts() {
local ssh="22"
local dsm="5000,5001"
local list="$ssh,$dsm,"
list+="${USER_PORTS// /},"
local exclude
exclude=$(getHostPorts)
local ports=""
local userport=""
local hostport=""
for userport in ${list//,/ }; do
local num="${userport///tcp}"
num="${num///udp}"
for hostport in ${exclude//,/ }; do
local port="${hostport///tcp}"
port="${port///udp}"
if [[ "$num" == "$port" ]]; then
num=""
if [[ "$port" != "$WEB_PORT" ]]; then
warn "Could not assign port $port to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!"
fi
fi
done
[ -n "$num" ] && ports+="$userport,"
done
# Remove duplicates
ports=$(echo "${ports//,,/,}," | awk 'BEGIN{RS=ORS=","} !seen[$0]++' | sed 's/,*$//g')
echo "$ports"
return 0
}
getSlirp() {
local args="" local args=""
local list="" local list=$1
local ssh="22"
local dsm="5000"
list=$(getUserPorts) [ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm"
for port in ${list//,/ }; do list="${list//,/ }"
list="${list## }"
list="${list%% }"
local proto="tcp" for port in $list; do
local num="${port%/tcp}" args+="hostfwd=tcp::$port-$VM_NET_IP:$port,"
[ -z "$num" ] && continue
if [[ "$port" == *"/udp" ]]; then
proto="udp"
num="${port%/udp}"
elif [[ "$port" != *"/tcp" ]]; then
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
proto="udp"
num="${port%/udp}"
fi
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
done done
args=$(echo "$args" | sed 's/,*$//g')
echo "${args%?}" echo "${args%?}"
return 0 return 0
} }
configureSlirp() { getHostPorts() {
NETWORK="slirp" local list=$1
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."
local ip="$IP" [ -z "$list" ] && echo "" && return 0
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local base="${ip%.*}."
[ "${ip/$base/}" -lt "4" ] && ip="${ip%.*}.4"
local gateway="${ip%.*}.1"
local ipv6="" if [[ "$list" != *","* ]]; then
[ -n "$IP6" ] && ipv6="ipv6=on," echo " ! --dport $list"
NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=$gateway,net=${gateway%.*}.0/24,dhcpstart=$ip,${ipv6}hostname=$VM_NET_HOST"
local forward=""
forward=$(getSlirp)
[ -n "$forward" ] && NET_OPTS+=",$forward"
if [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]]; then
echo "$gateway" > /run/shm/qemu.gw
else else
[ ! -f /etc/resolv.dnsmasq ] && cp /etc/resolv.conf /etc/resolv.dnsmasq echo " -m multiport ! --dports $list"
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi fi
VM_NET_IP="$ip"
return 0 return 0
} }
configurePasst() { configureUser() {
NETWORK="passt" if [ -z "$IP6" ]; then
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..." NET_OPTS="-netdev user,id=hostnet0,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,hostname=$VM_NET_HOST"
local log="/var/log/passt.log"
rm -f "$log"
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else else
gateway="${ip%.*}.2" NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,ipv6=on,hostname=$VM_NET_HOST"
fi fi
# passt configuration: local forward
[ -z "$IP6" ] && PASST_OPTS+=" -4" forward=$(getUserPorts "$USER_PORTS")
[ -n "$forward" ] && NET_OPTS+=",$forward"
PASST_OPTS+=" -a $ip"
PASST_OPTS+=" -g $gateway"
PASST_OPTS+=" -n $VM_NET_MASK"
[ -n "$PASST_MTU" ] && PASST_OPTS+=" -m $PASST_MTU"
local forward=""
forward=$(getUserPorts)
forward="${forward///tcp}"
forward="${forward///udp}"
if [ -n "$forward" ]; then
forward="%${VM_NET_DEV}/$forward"
PASST_OPTS+=" -t $forward"
PASST_OPTS+=" -u $forward"
fi
PASST_OPTS+=" -H $VM_NET_HOST"
PASST_OPTS+=" -M $GATEWAY_MAC"
local uid gid
uid=$(id -u)
gid=$(id -g)
PASST_OPTS+=" --runas $uid:$gid"
PASST_OPTS+=" -P /var/run/passt.pid"
PASST_OPTS+=" -l $log"
PASST_OPTS+=" -q"
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
[ ! -f /etc/resolv.dnsmasq ] && cp /etc/resolv.conf /etc/resolv.dnsmasq
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
fi
PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then
rm -f "$log"
PASST_OPTS="${PASST_OPTS/ -q/}"
{ $PASST ${PASST_OPTS:+ $PASST_OPTS}; rc=$?; } || :
if (( rc != 0 )); then
[ -f "$log" ] && cat "$log"
warn "failed to start passt ($rc), falling back to slirp networking!"
configureSlirp && return 0 || return 1
fi
fi
if [[ "$PASST_DEBUG" == [Yy1]* ]]; then
tail -fn +0 "$log" --pid=$$ &
else
if [[ "$DEBUG" == [Yy1]* ]]; then
[ -f "$log" ] && cat "$log" && echo ""
fi
fi
NET_OPTS="-netdev stream,id=hostnet0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket"
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
VM_NET_IP="$ip"
return 0 return 0
} }
configureNAT() { configureNAT() {
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun" local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat" local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring NAT networking..."
# Create the necessary file structure for /dev/net/tun # Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
@@ -411,64 +204,50 @@ configureNAT() {
fi fi
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
warn "$tuntap" && return 1 error "$tuntap" && return 1
fi fi
# Check port forwarding flag # Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || : { sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" [[ "$PODMAN" == [Yy1]* ]] && return 1
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
return 1 return 1
fi fi
fi fi
local ip base
base=$(echo "$IP" | sed -r 's/([^.]*.){2}//')
if [[ "$IP" != "172.30."* ]]; then
ip="172.30.$base"
else
ip="172.31.$base"
fi
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
local gateway=""
if [[ "$ip" != *".1" ]]; then
gateway="${ip%.*}.1"
else
gateway="${ip%.*}.2"
fi
# Create a bridge with a static IP for the VM guest # Create a bridge with a static IP for the VM guest
{ ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || : { ip link add dev dockerbridge type bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1 error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi fi
if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then
warn "failed to add IP address pool!" && return 1 error "Failed to add IP address pool!" && return 1
fi fi
while ! ip link set "$VM_NET_BRIDGE" up; do while ! ip link set dockerbridge up; do
info "Waiting for IP address to become available..." info "Waiting for IP address to become available..."
sleep 2 sleep 2
done done
# QEMU Works with taps, set tap to the bridge created # QEMU Works with taps, set tap to the bridge created
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
warn "$tuntap" && return 1 error "$tuntap" && return 1
fi fi
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "failed to set MTU size to $MTU." warn "Failed to set MTU size.."
fi fi
fi fi
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
warn "failed to set gateway MAC address.." warn "Failed to set gateway MAC address.."
fi fi
while ! ip link set "$VM_NET_TAP" up promisc on; do while ! ip link set "$VM_NET_TAP" up promisc on; do
@@ -476,39 +255,27 @@ configureNAT() {
sleep 2 sleep 2
done done
if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
warn "failed to set master bridge!" && return 1 error "Failed to set IP link!" && return 1
fi fi
if grep -wq "nf_tables" /proc/modules; then # Add internet connection to the VM
update-alternatives --set iptables /usr/sbin/iptables-nft > /dev/null update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft > /dev/null update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
else
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
fi
exclude=$(getHostPorts) exclude=$(getHostPorts "$HOST_PORTS")
if [ -n "$exclude" ]; then
if [[ "$exclude" != *","* ]]; then
exclude=" ! --dport $exclude"
else
exclude=" -m multiport ! --dports $exclude"
fi
fi
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
warn "$tables" && return 1 error "$tables" && return 1
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$ip"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then
warn "failed to configure IP tables!" && return 1 error "Failed to configure IP tables!" && return 1
fi fi
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$ip"; then if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then
warn "failed to configure IP tables!" && return 1 error "Failed to configure IP tables!" && return 1
fi fi
if (( KERNEL > 4 )); then if (( KERNEL > 4 )); then
@@ -525,9 +292,8 @@ configureNAT() {
NET_OPTS+=",script=no,downscript=no" NET_OPTS+=",script=no,downscript=no"
configureDNS "$VM_NET_BRIDGE" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1 configureDNS || return 1
VM_NET_IP="$ip"
return 0 return 0
} }
@@ -535,43 +301,26 @@ closeBridge() {
local pid="/var/run/dnsmasq.pid" local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")" [ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
pid="/var/run/passt.pid" [[ "${NETWORK,,}" == "user"* ]] && return 0
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
case "${NETWORK,,}" in
"user"* | "passt" | "slirp" ) return 0 ;;
esac
ip link set "$VM_NET_TAP" down promisc off &> null || true ip link set "$VM_NET_TAP" down promisc off &> null || true
ip link delete "$VM_NET_TAP" &> null || true ip link delete "$VM_NET_TAP" &> null || true
ip link set "$VM_NET_BRIDGE" down &> null || true ip link set dockerbridge down &> null || true
ip link delete "$VM_NET_BRIDGE" &> null || true ip link delete dockerbridge &> null || true
return 0
}
closeWeb() {
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
# Shutdown websocket
local pid="/var/run/websocketd.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
return 0 return 0
} }
closeNetwork() { closeNetwork() {
if [[ "${WEB:-}" != [Nn]* && "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
closeWeb
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
fi fi
[[ "$NETWORK" == [Nn]* ]] && return 0 [[ "$NETWORK" == [Nn]* ]] && return 0
@@ -592,21 +341,6 @@ closeNetwork() {
return 0 return 0
} }
cleanUp() {
# Clean up old files
rm -f /etc/resolv.dnsmasq
rm -f /var/run/passt.pid
rm -f /var/run/dnsmasq.pid
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
info "Lingering interface will be removed..."
ip link delete "$VM_NET_TAP" || true
fi
return 0
}
checkOS() { checkOS() {
local kernel local kernel
@@ -614,8 +348,8 @@ checkOS() {
local if="macvlan" local if="macvlan"
kernel=$(uname -a) kernel=$(uname -a)
[[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS" [[ "${kernel,,}" == *"darwin"* ]] && os="Docker Desktop for macOS"
[[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows" [[ "${kernel,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows"
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
if="macvtap" if="macvtap"
@@ -647,66 +381,22 @@ getInfo() {
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26 error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
fi fi
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1) BASE_IP="${VM_NET_IP%.*}."
{ IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1); rc=$?; } 2>/dev/null || :
if (( rc != 0 )); then if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then
error "Could not determine container IP address!" && exit 26 error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27
fi fi
IP6="" if [ -z "$MTU" ]; then
# shellcheck disable=SC2143 MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
{ IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up); rc=$?; } 2>/dev/null || :
(( rc != 0 )) && IP6=""
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
fi fi
local result nic bus if [ "$MTU" -gt "1500" ]; then
result=$(ethtool -i "$VM_NET_DEV") info "MTU size is too large: $MTU, ignoring..." && MTU="0"
nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $(2)}')
bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $(2)}')
if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus"
error "This container does not support host mode networking!"
exit 29
fi fi
if [[ "$DHCP" == [Yy1]* ]]; then
checkOS
if [[ "${nic,,}" == "ipvlan" ]]; then
error "This container does not support IPVLAN networking when DHCP=Y."
exit 29
fi
if [[ "${nic,,}" != "macvlan" ]]; then
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $nic"
error "The container needs to be in a MACVLAN network when DHCP=Y."
exit 29
fi
else
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
checkOS
fi
fi
local mtu=""
if [ -f "/sys/class/net/$VM_NET_DEV/mtu" ]; then
mtu=$(< "/sys/class/net/$VM_NET_DEV/mtu")
fi
[ -z "$MTU" ] && MTU="$mtu"
[ -z "$MTU" ] && MTU="0"
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
if [[ "$MTU" != "0" ]] && [ "$MTU" -lt "1500" ]; then if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0" warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
fi fi
fi fi
@@ -719,7 +409,6 @@ getInfo() {
# Generate MAC address based on Docker container ID in hostname # Generate MAC address based on Docker container ID in hostname
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/') VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
echo "${VM_NET_MAC^^}" > "$file" echo "${VM_NET_MAC^^}" > "$file"
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
fi fi
fi fi
@@ -735,28 +424,18 @@ getInfo() {
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28 error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi fi
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/') GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then IP6=""
if [ -z "$NETWORK" ] || [[ "${NETWORK^^}" == "Y" ]]; then # shellcheck disable=SC2143
# By default Podman has no permissions for NAT networking if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
NETWORK="user" IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
fi [ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
fi
if [[ "$DEBUG" == [Yy1]* ]]; then
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi fi
[ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
echo "$IP" > /run/shm/qemu.ip echo "$IP" > /run/shm/qemu.ip
echo "$nic" > /run/shm/qemu.nic
return 0 return 0
} }
@@ -770,82 +449,84 @@ if [[ "$NETWORK" == [Nn]* ]]; then
return 0 return 0
fi fi
msg="Initializing network..."
html "$msg"
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
getInfo getInfo
cleanUp html "Initializing network..."
if [[ "$DEBUG" == [Yy1]* ]]; then
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" ]] && [[ "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi
if [[ "$IP" == "172.17."* ]]; then if [[ "$IP" == "172.17."* ]]; then
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!" warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
fi fi
MSG="Booting DSM instance..." if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
html "$MSG" info "Lingering interface will be removed..."
ip link delete "$VM_NET_TAP" || true
fi
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$DHCP" == [Yy1]* ]]; then
checkOS
if [[ "$IP" == "172."* ]]; then
warn "container IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
fi
# Configure for macvtap interface # Configure for macvtap interface
configureDHCP || exit 20 configureDHCP || exit 20
MSG="Booting DSM instance..."
html "$MSG"
else else
if [[ "${WEB:-}" != [Nn]* ]]; then if [[ "$IP" != "172."* ]] && [[ "$IP" != "10.8"* ]] && [[ "$IP" != "10.9"* ]]; then
sleep 1.2 checkOS
closeWeb
fi fi
case "${NETWORK,,}" in # Shutdown nginx
"passt" | "slirp" | "user"* ) ;; nginx -s stop 2> /dev/null
"tap" | "tun" | "tuntap" | "y" ) fWait "nginx"
# Configure tap interface if [[ "${NETWORK,,}" != "user"* ]]; then
if ! configureNAT; then
closeBridge # Configure for tap interface
NETWORK="user" if ! configureNAT; then
msg="falling back to user-mode networking!"
msg="failed to setup NAT networking, $msg"
fi ;; closeBridge
NETWORK="user"
msg="falling back to user-mode networking!"
if [[ "$PODMAN" != [Yy1]* ]]; then
msg="an error occured, $msg"
else
msg="podman detected, $msg"
fi
warn "$msg"
[ -z "$USER_PORTS" ] && info "Notice: port mapping will not work without \"USER_PORTS\" now."
esac fi
case "${NETWORK,,}" in fi
"tap" | "tun" | "tuntap" | "y" ) ;;
"passt" | "user"* )
# Configure for user-mode networking (passt) if [[ "${NETWORK,,}" == "user"* ]]; then
if ! configurePasst; then
error "Failed to configure user-mode networking!"
exit 24
fi ;;
"slirp" ) # Configure for user-mode networking (slirp)
configureUser || exit 24
# Configure for user-mode networking (slirp) fi
if ! configureSlirp; then
error "Failed to configure user-mode networking!"
exit 24
fi ;;
*)
error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
esac
case "${NETWORK,,}" in
"passt" | "slirp" )
if [ -z "$USER_PORTS" ]; then
info "Notice: because user-mode networking is active, when you need to forward custom ports to DSM, add them to the \"USER_PORTS\" variable."
fi ;;
esac
fi fi
NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC" NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
[[ "$MTU" != "0" && "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU" [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
return 0 return 0

View File

@@ -1,15 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
: "${API_TIMEOUT:="50"}" # API Call timeout
: "${QEMU_TIMEOUT:="50"}" # QEMU Termination timeout
# Configure QEMU for graceful shutdown # Configure QEMU for graceful shutdown
API_CMD=6 API_CMD=6
API_HOST="127.0.0.1:$COM_PORT" API_TIMEOUT=50
API_HOST="127.0.0.1:2210"
QEMU_TERM="" QEMU_TERM=""
QEMU_PORT=7100
QEMU_TIMEOUT=50
QEMU_DIR="/run/shm" QEMU_DIR="/run/shm"
QEMU_PID="$QEMU_DIR/qemu.pid" QEMU_PID="$QEMU_DIR/qemu.pid"
QEMU_LOG="$QEMU_DIR/qemu.log" QEMU_LOG="$QEMU_DIR/qemu.log"
@@ -33,7 +33,6 @@ _trap() {
finish() { finish() {
local pid local pid
local cnt=0
local reason=$1 local reason=$1
touch "$QEMU_END" touch "$QEMU_END"
@@ -41,24 +40,14 @@ finish() {
if [ -s "$QEMU_PID" ]; then if [ -s "$QEMU_PID" ]; then
pid=$(<"$QEMU_PID") pid=$(<"$QEMU_PID")
echo && error "Forcefully terminating Virtual DSM, reason: $reason..." echo && error "Forcefully terminating QEMU process, reason: $reason..."
{ kill -15 "$pid" || true; } 2>/dev/null { kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do while isAlive "$pid"; do
sleep 1 sleep 1
cnt=$((cnt+1))
# Workaround for zombie pid # Workaround for zombie pid
[ ! -s "$QEMU_PID" ] && break [ ! -s "$QEMU_PID" ] && break
if [ "$cnt" == "5" ]; then
echo && error "QEMU did not terminate itself, forcefully killing process..."
{ kill -9 "$pid" || true; } 2>/dev/null
fi
done done
fi fi
fKill "print.sh" fKill "print.sh"
@@ -94,7 +83,7 @@ terminal() {
fi fi
if [ ! -c "$dev" ]; then if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$MON_PORT" | tr -d '\000') dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev="${dev#*serial0}" dev="${dev#*serial0}"
dev="${dev#*pty:}" dev="${dev#*pty:}"
dev="${dev%%$'\n'*}" dev="${dev%%$'\n'*}"
@@ -138,7 +127,7 @@ _graceful_shutdown() {
fi fi
# Don't send the powerdown signal because vDSM ignores ACPI signals # Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "$MON_PORT" > /dev/null # echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
# Send shutdown command to guest agent via serial port # Send shutdown command to guest agent via serial port
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT" url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
@@ -183,7 +172,7 @@ _graceful_shutdown() {
MON_OPTS="\ MON_OPTS="\
-pidfile $QEMU_PID \ -pidfile $QEMU_PID \
-name $PROCESS,process=$PROCESS,debug-threads=on \ -name $PROCESS,process=$PROCESS,debug-threads=on \
-monitor telnet:localhost:$MON_PORT,server,nowait,nodelay" -monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
if [[ "$CONSOLE" != [Yy]* ]]; then if [[ "$CONSOLE" != [Yy]* ]]; then

View File

@@ -11,7 +11,6 @@ error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
file="/run/shm/dsm.url" file="/run/shm/dsm.url"
info="/run/shm/msg.html" info="/run/shm/msg.html"
driver="/run/shm/qemu.nic"
page="/run/shm/index.html" page="/run/shm/index.html"
address="/run/shm/qemu.ip" address="/run/shm/qemu.ip"
shutdown="/run/shm/qemu.end" shutdown="/run/shm/qemu.end"
@@ -57,7 +56,11 @@ do
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue (( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue [[ "$ip" == "null" ]] && error "$resp_err $json" && continue
[ -z "$ip" ] && continue if [ -z "$ip" ]; then
[[ "$DHCP" == [Yy1]* ]] && continue
ip="20.20.20.21"
fi
echo "$ip:$port" > $file echo "$ip:$port" > $file
done done
@@ -66,7 +69,7 @@ done
location=$(<"$file") location=$(<"$file")
if [[ "$DHCP" == [Yy1]* ]]; then if [[ "$location" != "20.20"* ]]; then
msg="http://$location" msg="http://$location"
title="<title>Virtual DSM</title>" title="<title>Virtual DSM</title>"
@@ -85,11 +88,10 @@ if [[ "$DHCP" == [Yy1]* ]]; then
else else
nic=$(<"$driver")
ip=$(<"$address") ip=$(<"$address")
port="${location##*:}" port="${location##*:}"
if [[ "${nic,,}" != "macvlan" ]]; then if [[ "$ip" == "172."* ]]; then
msg="port $port" msg="port $port"
else else
msg="http://$ip:$port" msg="http://$ip:$port"

View File

@@ -3,9 +3,11 @@ set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: "${KVM:="Y"}"
: "${HOST_CPU:=""}" : "${HOST_CPU:=""}"
: "${CPU_FLAGS:=""}" : "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}" : "${CPU_MODEL:=""}"
: "${DEF_MODEL:="qemu64"}"
CLOCKSOURCE="tsc" CLOCKSOURCE="tsc"
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter" [[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
@@ -25,7 +27,48 @@ else
esac esac
fi fi
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo) if [[ "${ARCH,,}" != "amd64" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, this will cause a major loss of performance."
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(/dev/kvm is unwriteable)"
else
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, this will cause a major loss of performance."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
error "KVM acceleration is not available $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ for possible causes, or continue without it by adding KVM: \"N\" (not recommended)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
if [[ "$KVM" != [Nn]* ]]; then if [[ "$KVM" != [Nn]* ]]; then
@@ -34,7 +77,7 @@ if [[ "$KVM" != [Nn]* ]]; then
if ! grep -qw "sse4_2" <<< "$flags"; then if ! grep -qw "sse4_2" <<< "$flags"; then
info "Your CPU does not have the SSE4 instruction set that Virtual DSM requires, it will be emulated..." info "Your CPU does not have the SSE4 instruction set that Virtual DSM requires, it will be emulated..."
[ -z "$CPU_MODEL" ] && CPU_MODEL="qemu64" [ -z "$CPU_MODEL" ] && CPU_MODEL="$DEF_MODEL"
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2" CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi fi
@@ -77,7 +120,7 @@ else
CPU_MODEL="max" CPU_MODEL="max"
CPU_FEATURES+=",migratable=no" CPU_FEATURES+=",migratable=no"
else else
CPU_MODEL="qemu64" CPU_MODEL="$DEF_MODEL"
fi fi
fi fi
@@ -85,30 +128,6 @@ else
fi fi
if [[ "$ARGUMENTS" == *"-cpu host,"* ]]; then
args="${ARGUMENTS} "
prefix="${args/-cpu host,*/}"
suffix="${args/*-cpu host,/}"
param="${suffix%% *}"
suffix="${suffix#* }"
args="${prefix}${suffix}"
ARGUMENTS="${args::-1}"
if [ -z "$CPU_FLAGS" ]; then
CPU_FLAGS="$param"
else
CPU_FLAGS+=",$param"
fi
else
if [[ "$ARGUMENTS" == *"-cpu host"* ]]; then
ARGUMENTS="${ARGUMENTS//-cpu host/}"
fi
fi
if [ -z "$CPU_FLAGS" ]; then if [ -z "$CPU_FLAGS" ]; then
if [ -z "$CPU_FEATURES" ]; then if [ -z "$CPU_FEATURES" ]; then
CPU_FLAGS="$CPU_MODEL" CPU_FLAGS="$CPU_MODEL"

View File

@@ -1,8 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
info="/run/shm/msg.html"
escape () { escape () {
local s local s
s=${1//&/\&amp;} s=${1//&/\&amp;}
@@ -13,33 +11,28 @@ escape () {
return 0 return 0
} }
path="$1" file="$1"
total="$2" total="$2"
body=$(escape "$3") body=$(escape "$3")
info="/run/shm/msg.html"
if [[ "$body" == *"..." ]]; then if [[ "$body" == *"..." ]]; then
body="<p class=\"loading\">${body::-3}</p>" body="<p class=\"loading\">${body/.../}</p>"
fi fi
while true while true
do do
if [ -s "$file" ]; then
if [ ! -s "$path" ] && [ ! -d "$path" ]; then bytes=$(du -sb "$file" | cut -f1)
bytes="0" if (( bytes > 1000 )); then
else if [ -z "$total" ] || [[ "$total" == "0" ]]; then
bytes=$(du -sb "$path" | cut -f1) size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
fi else
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
if (( bytes > 4096 )); then size="$size%"
if [ -z "$total" ] || [[ "$total" == "0" ]] || [ "$bytes" -gt "$total" ]; then fi
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/') echo "${body//(\[P\])/($size)}"> "$info"
else
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
size="$size%"
fi fi
[[ "$size" != "0.0%" ]] && echo "${body//(\[P\])/($size)}"> "$info"
fi fi
sleep 1 & wait $! sleep 1 & wait $!
done done

View File

@@ -2,15 +2,16 @@
set -Eeuo pipefail set -Eeuo pipefail
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG
[ ! -f "/run/entry.sh" ] && error "Script must be run inside the container!" && exit 11 [ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12 [ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
echo " Starting $APP for Docker v$(</run/version)..."
echo " For support visit $SUPPORT"
# Docker environment variables # Docker environment variables
: "${TZ:=""}" # System timezone : "${TZ:=""}" # System local timezone
: "${KVM:="Y"}" # KVM acceleration
: "${DEBUG:="N"}" # Disable debugging mode : "${DEBUG:="N"}" # Disable debugging mode
: "${COUNTRY:=""}" # Country code for mirror : "${COUNTRY:=""}" # Country code for mirror
: "${CONSOLE:="N"}" # Disable console mode : "${CONSOLE:="N"}" # Disable console mode
@@ -24,23 +25,13 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
# Helper variables # Helper variables
PODMAN="N"
ENGINE="Docker"
PROCESS="${APP,,}" PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}" PROCESS="${PROCESS// /-}"
if [ -f "/run/.containerenv" ]; then
PODMAN="Y"
ENGINE="Podman"
fi
echo " Starting $APP for $ENGINE v$(</run/version)..."
echo " For support visit $SUPPORT"
INFO="/run/shm/msg.html" INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html" PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html" TEMPLATE="/var/www/index.html"
FOOTER1="$APP for $ENGINE v$(</run/version)" FOOTER1="$APP for Docker v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>" FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
CPU=$(cpu) CPU=$(cpu)
@@ -57,17 +48,8 @@ else
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}') SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
fi fi
CPU_CORES="${CPU_CORES// /}"
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
[[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 ))
[[ "${CPU_CORES,,}" == "0" ]] && CPU_CORES="1"
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15 [ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
if [ "$CPU_CORES" -gt "$CORES" ]; then
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of logical cores available, so will be limited to ${CORES}."
CPU_CORES="$CORES"
fi
# Check system # Check system
if [ ! -d "/dev/shm" ]; then if [ ! -d "/dev/shm" ]; then
@@ -78,7 +60,8 @@ fi
# Check folder # Check folder
if [[ "${STORAGE,,}" != "/storage" ]]; then if [[ "${COMMIT:-}" == [Yy1]* ]]; then
STORAGE="/local"
mkdir -p "$STORAGE" mkdir -p "$STORAGE"
fi fi
@@ -86,16 +69,10 @@ if [ ! -d "$STORAGE" ]; then
error "Storage folder ($STORAGE) not found!" && exit 13 error "Storage folder ($STORAGE) not found!" && exit 13
fi fi
if [ ! -w "$STORAGE" ]; then
msg="Storage folder ($STORAGE) is not writeable!"
msg+=" If SELinux is active, you need to add the \":Z\" flag to the bind mount."
error "$msg" && exit 13
fi
# Check filesystem # Check filesystem
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
DISK_IO="threads" DISK_IO="threads"
DISK_CACHE="writeback" DISK_CACHE="writeback"
fi fi
@@ -108,19 +85,15 @@ RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
RAM_SIZE="${RAM_SIZE// /}" RAM_SIZE="${RAM_SIZE// /}"
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16 [ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
fi
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
fi fi
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
# Print system info # Print system info
SYS="${SYS/-generic/}" SYS="${SYS/-generic/}"
FS="${FS/UNKNOWN //}" FS="${FS/UNKNOWN //}"
@@ -134,61 +107,13 @@ TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
echo " CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..." echo " CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
echo echo
# Check KVM support # Check available memory
if [[ "${PLATFORM,,}" == "x64" ]]; then
TARGET="amd64"
else
TARGET="arm64"
fi
if [[ "$KVM" == [Nn]* ]]; then
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
else
if [[ "${ARCH,,}" != "$TARGET" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ${PLATFORM^^} instructions, so the machine will run about 10 times slower."
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(/dev/kvm is unwriteable)"
else
if [[ "${PLATFORM,,}" == "x64" ]]; then
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
info "$msg"
fi fi
# Cleanup files # Cleanup files
@@ -199,4 +124,80 @@ rm -f /run/shm/dsm.url
rm -rf /tmp/dsm rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp" rm -rf "$STORAGE/tmp"
getCountry() {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://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() {
local pkg=$1
local desc=$2
if apt-mark showinstall | grep -qx "$pkg"; then
return 0
fi
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
[ -z "$COUNTRY" ] && setCountry
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen 5000 default_server;/listen [::]:5000 default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
else
sed -i "s/listen [::]:5000 default_server ipv6only=off;/listen 5000 default_server;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
return 0 return 0

View File

@@ -45,7 +45,7 @@ fi
cnt=0 cnt=0
sleep 0.2 sleep 0.2
while ! nc -z -w2 127.0.0.1 "$COM_PORT" > /dev/null 2>&1; do while ! nc -z -w2 127.0.0.1 2210 > /dev/null 2>&1; do
sleep 0.1 sleep 0.1
cnt=$((cnt + 1)) cnt=$((cnt + 1))
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58 (( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 58
@@ -53,7 +53,7 @@ done
cnt=0 cnt=0
while ! nc -z -w2 127.0.0.1 "$CHR_PORT" > /dev/null 2>&1; do while ! nc -z -w2 127.0.0.1 12345 > /dev/null 2>&1; do
sleep 0.1 sleep 0.1
cnt=$((cnt + 1)) cnt=$((cnt + 1))
(( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59 (( cnt > 50 )) && error "Failed to connect to qemu-host.." && exit 59
@@ -69,7 +69,7 @@ fi
SERIAL_OPTS+=" \ SERIAL_OPTS+=" \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \ -device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=$CHR_PORT,reconnect=10 \ -chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel" -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"
return 0 return 0

View File

@@ -1,39 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${COM_PORT:="2210"}" # Comm port
: "${MON_PORT:="7100"}" # Monitor port
: "${WEB_PORT:="5000"}" # Webserver port
: "${CHR_PORT:="12345"}" # Character port
: "${WSD_PORT:="8004"}" # Websockets port
cp -r /var/www/* /run/shm
rm -f /var/run/websocketd.pid
html "Starting $APP for $ENGINE..."
if [[ "${WEB:-}" != [Nn]* ]]; then
mkdir -p /etc/nginx/sites-enabled
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
sed -i "s/listen 5000 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
nginx -e stderr
# Start websocket server
websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log &
echo "$!" > /var/run/websocketd.pid
fi
return 0

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
lastmsg=""
path="/run/shm/msg.html"
refresh() {
[ ! -f "$path" ] && return 0
[ ! -s "$path" ] && return 0
msg=$(< "$path")
msg="${msg%$'\n'}"
[ -z "$msg" ] && return 0
[[ "$msg" == "$lastmsg" ]] && return 0
lastmsg="$msg"
echo "s: $msg"
return 0
}
refresh
inotifywait -m "$path" |
while read -r fp event fn; do
case "${event,,}" in
"modify"* ) refresh ;;
"delete_self" ) echo "c: vnc" ;;
esac
done

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# You can override this hook to execute a script before startup!
return 0

View File

@@ -67,37 +67,6 @@ fKill() {
return 0 return 0
} }
setOwner() {
local file="$1"
local dir uid gid
[ ! -f "$file" ] && return 1
dir=$(dirname -- "$file")
uid=$(stat -c '%u' "$dir")
gid=$(stat -c '%g' "$dir")
! chown "$uid:$gid" "$file" && return 1
return 0
}
makeDir() {
local path="$1"
local dir uid gid
[ -d "$path" ] && return 0
! mkdir -p "$path" && return 1
dir=$(dirname -- "$path")
uid=$(stat -c '%u' "$dir")
gid=$(stat -c '%g' "$dir")
! chown "$uid:$gid" "$path" && return 1
return 0
}
escape () { escape () {
local s local s
s=${1//&/\&amp;} s=${1//&/\&amp;}
@@ -154,21 +123,34 @@ cpu() {
fi fi
cpu="${cpu// CPU/}" cpu="${cpu// CPU/}"
cpu="${cpu// [0-9][0-9][0-9] Core}" cpu="${cpu// 4 Core/}"
cpu="${cpu// [0-9][0-9] Core}" cpu="${cpu// 6 Core/}"
cpu="${cpu// [0-9] Core}" cpu="${cpu// 8 Core/}"
cpu="${cpu//[0-9][0-9]th Gen }" cpu="${cpu// 10 Core/}"
cpu="${cpu//[0-9]th Gen }" cpu="${cpu// 12 Core/}"
cpu="${cpu// 16 Core/}"
cpu="${cpu// 32 Core/}"
cpu="${cpu// 48 Core/}"
cpu="${cpu// 64 Core/}"
cpu="${cpu// 96 Core/}"
cpu="${cpu// 128 Core/}"
cpu="${cpu//7th Gen /}"
cpu="${cpu//8th Gen /}"
cpu="${cpu//9th Gen /}"
cpu="${cpu//10th Gen /}"
cpu="${cpu//11th Gen /}"
cpu="${cpu//12th Gen /}"
cpu="${cpu//13th Gen /}"
cpu="${cpu//14th Gen /}"
cpu="${cpu//15th Gen /}"
cpu="${cpu// Processor/}" cpu="${cpu// Processor/}"
cpu="${cpu// Quad core/}" cpu="${cpu// Quad core/}"
cpu="${cpu// Dual core/}" cpu="${cpu// Dual core/}"
cpu="${cpu// Octa core/}" cpu="${cpu// Octa core/}"
cpu="${cpu// Hexa core/}"
cpu="${cpu// Core TM/ Core}" cpu="${cpu// Core TM/ Core}"
cpu="${cpu// with Radeon Graphics/}" cpu="${cpu// with Radeon Graphics/}"
cpu="${cpu// with Radeon Vega Graphics/}" cpu="${cpu// with Radeon Vega Graphics/}"
cpu="${cpu// with Radeon Vega Mobile Gfx/}" cpu="${cpu// with Radeon Vega Mobile Gfx/}"
cpu="${cpu// w Radeon [0-9][0-9][0-9]M Graphics/}"
[ -z "${cpu// /}" ] && cpu="Unknown" [ -z "${cpu// /}" ] && cpu="Unknown"
@@ -190,65 +172,4 @@ hasDisk() {
return 1 return 1
} }
getCountry() {
local url=$1
local query=$2
local rc json result
{ json=$(curl -m 5 -H "Accept: application/json" -sfk "$url"); rc=$?; } || :
(( rc != 0 )) && return 0
{ result=$(echo "$json" | jq -r "$query" 2> /dev/null); rc=$?; } || :
(( rc != 0 )) && return 0
[[ ${#result} -ne 2 ]] && return 0
[[ "${result^^}" == "XX" ]] && return 0
COUNTRY="${result^^}"
return 0
}
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://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.ipquery.io/?format=json" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0
}
addPackage() {
local pkg=$1
local desc=$2
if apt-mark showinstall | grep -qx "$pkg"; then
return 0
fi
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
[ -z "$COUNTRY" ] && setCountry
if [[ "${COUNTRY^^}" == "CN" ]]; then
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
return 0 return 0

View File

@@ -27,18 +27,4 @@ server {
index index.html; index index.html;
} }
location /status {
proxy_http_version 1.1;
proxy_set_header Connection 'upgrade';
proxy_set_header Upgrade $http_upgrade;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_pass http://127.0.0.1:8004/;
}
} }

View File

@@ -1,5 +1,4 @@
var request; var request;
var booting = false;
var interval = 1000; var interval = 1000;
function getInfo() { function getInfo() {
@@ -7,6 +6,7 @@ function getInfo() {
var url = "msg.html"; var url = "msg.html";
try { try {
if (window.XMLHttpRequest) { if (window.XMLHttpRequest) {
request = new XMLHttpRequest(); request = new XMLHttpRequest();
} else { } else {
@@ -18,49 +18,22 @@ function getInfo() {
request.send(); request.send();
} catch (e) { } catch (e) {
setError("Error: " + e.message); var err = "Error: " + e.message;
console.log(err);
setError(err);
} }
} }
function getURL() {
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, '');
return protocol + "//" + window.location.host + path;
}
function processMsg(msg) {
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;
}
function processInfo() { function processInfo() {
try { try {
if (request.readyState != 4) { if (request.readyState != 4) {
return true; return true;
} }
var msg = request.responseText; var msg = request.responseText;
if (msg == null || msg.length == 0) { if (msg == null || msg.length == 0) {
setInfo("Booting DSM instance", true);
if (booting) { schedule();
schedule();
return true;
}
window.location.reload();
return false; return false;
} }
@@ -70,11 +43,20 @@ function processInfo() {
if (msg.toLowerCase().indexOf("<html>") !== -1) { if (msg.toLowerCase().indexOf("<html>") !== -1) {
notFound = true; notFound = true;
} else { } else {
processMsg(msg); if (msg.toLowerCase().indexOf("href=") !== -1) {
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(); schedule();
return true;
} }
return true;
} }
} }
@@ -85,60 +67,44 @@ function processInfo() {
} }
setError("Error: Received statuscode " + request.status); setError("Error: Received statuscode " + request.status);
schedule();
return false; return false;
} catch (e) { } catch (e) {
setError("Error: " + e.message); var err = "Error: " + e.message;
console.log(err);
setError(err);
return false; return false;
} }
} }
function extractContent(s) {
var span = document.createElement('span');
span.innerHTML = s;
return span.textContent || span.innerText;
};
function setInfo(msg, loading, error) { function setInfo(msg, loading, error) {
try {
try {
if (msg == null || msg.length == 0) { if (msg == null || msg.length == 0) {
return false; return false;
} }
if (msg.includes("Booting ")) { var el = document.getElementById("spinner");
booting = true;
}
var el = document.getElementById("info");
if (el.innerText == msg || el.innerHTML == msg) {
return true;
}
var spin = document.getElementById("spinner");
error = !!error; error = !!error;
if (!error) { if (!error) {
spin.style.visibility = 'visible'; el.style.visibility = 'visible';
} else { } else {
spin.style.visibility = 'hidden'; el.style.visibility = 'hidden';
} }
var p = "<p class=\"loading\">";
loading = !!loading; loading = !!loading;
if (loading) { if (loading) {
msg = p + msg + "</p>"; msg = "<p class=\"loading\">" + msg + "</p>";
} }
if (msg.includes(p)) { el = document.getElementById("info");
if (el.innerHTML.includes(p)) {
el.getElementsByClassName('loading')[0].textContent = extractContent(msg); if (el.innerHTML != msg) {
return true; el.innerHTML = msg;
}
} }
el.innerHTML = msg;
return true; return true;
} catch (e) { } catch (e) {
@@ -148,7 +114,6 @@ function setInfo(msg, loading, error) {
} }
function setError(text) { function setError(text) {
console.warn(text);
return setInfo(text, false, true); return setInfo(text, false, true);
} }
@@ -158,47 +123,8 @@ function schedule() {
function reload() { function reload() {
setTimeout(() => { setTimeout(() => {
window.location.reload(); document.location.reload();
}, 3000); }, 3000);
} }
function connect() {
var wsUrl = getURL() + "/status";
var ws = new WebSocket(wsUrl);
ws.onmessage = function(e) {
var pos = e.data.indexOf(":");
var cmd = e.data.substring(0, pos);
var msg = e.data.substring(pos + 2);
switch (cmd) {
case "s":
processMsg(msg);
break;
case "e":
setError(msg);
break;
default:
console.warn("Unknown event: " + cmd);
break;
}
};
ws.onclose = function(e) {
setTimeout(function() {
connect();
}, interval);
};
ws.onerror = function(e) {
ws.close();
if (!booting) {
window.location.reload();
}
};
}
schedule(); schedule();
connect();