Upload 29 files
Browse files- .devcontainer.json +6 -0
- .dockerignore +15 -0
- .gitignore +1 -0
- Dockerfile +83 -0
- compose.yml +17 -0
- kubernetes.yml +86 -0
- src/boot.sh +168 -0
- src/config.sh +86 -0
- src/define.sh +230 -0
- src/disk.sh +728 -0
- src/display.sh +72 -0
- src/entry.sh +27 -0
- src/install.sh +458 -0
- src/network.sh +828 -0
- src/proc.sh +214 -0
- src/progress.sh +38 -0
- src/reset.sh +213 -0
- src/start.sh +4 -0
- src/utils.sh +162 -0
- web/conf/defaults.json +4 -0
- web/conf/mandatory.json +4 -0
- web/conf/nginx.conf +60 -0
- web/css/style.css +167 -0
- web/img/favicon.svg +1 -0
- web/img/macos.ffs +0 -0
- web/img/qemu.ffs +0 -0
- web/img/windows.ffs +0 -0
- web/index.html +34 -0
- web/js/script.js +136 -0
.devcontainer.json
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "qemu",
|
| 3 |
+
"service": "qemu",
|
| 4 |
+
"forwardPorts": [8006],
|
| 5 |
+
"dockerComposeFile": "compose.yml"
|
| 6 |
+
}
|
.dockerignore
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.dockerignore
|
| 2 |
+
.git
|
| 3 |
+
.github
|
| 4 |
+
.gitignore
|
| 5 |
+
.gitlab-ci.yml
|
| 6 |
+
.gitmodules
|
| 7 |
+
Dockerfile
|
| 8 |
+
Dockerfile.archive
|
| 9 |
+
compose.yml
|
| 10 |
+
compose.yaml
|
| 11 |
+
docker-compose.yml
|
| 12 |
+
docker-compose.yaml
|
| 13 |
+
|
| 14 |
+
*.md
|
| 15 |
+
|
.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
Dockerfile
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# syntax=docker/dockerfile:1
|
| 2 |
+
|
| 3 |
+
FROM debian:trixie-slim
|
| 4 |
+
|
| 5 |
+
ARG TARGETARCH
|
| 6 |
+
ARG VERSION_ARG="0.0"
|
| 7 |
+
ARG VERSION_VNC="1.6.0"
|
| 8 |
+
|
| 9 |
+
ARG DEBCONF_NOWARNINGS="yes"
|
| 10 |
+
ARG DEBIAN_FRONTEND="noninteractive"
|
| 11 |
+
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
| 12 |
+
|
| 13 |
+
RUN set -eu && \
|
| 14 |
+
apt-get update && \
|
| 15 |
+
apt-get --no-install-recommends -y install \
|
| 16 |
+
bc \
|
| 17 |
+
jq \
|
| 18 |
+
xxd \
|
| 19 |
+
tini \
|
| 20 |
+
wget \
|
| 21 |
+
7zip \
|
| 22 |
+
curl \
|
| 23 |
+
ovmf \
|
| 24 |
+
fdisk \
|
| 25 |
+
nginx \
|
| 26 |
+
swtpm \
|
| 27 |
+
procps \
|
| 28 |
+
ethtool \
|
| 29 |
+
iptables \
|
| 30 |
+
iproute2 \
|
| 31 |
+
apt-utils \
|
| 32 |
+
dnsmasq \
|
| 33 |
+
xz-utils \
|
| 34 |
+
net-tools \
|
| 35 |
+
e2fsprogs \
|
| 36 |
+
qemu-utils \
|
| 37 |
+
iputils-ping \
|
| 38 |
+
genisoimage \
|
| 39 |
+
netcat-openbsd \
|
| 40 |
+
ca-certificates \
|
| 41 |
+
qemu-system-x86 && \
|
| 42 |
+
wget "https://github.com/qemus/passt/releases/download/v2025_09_19/passt_2025_09_19_${TARGETARCH}.deb" -O /tmp/passt.deb -q && \
|
| 43 |
+
dpkg -i /tmp/passt.deb && \
|
| 44 |
+
apt-get clean && \
|
| 45 |
+
mkdir -p /etc/qemu && \
|
| 46 |
+
echo "allow br0" > /etc/qemu/bridge.conf && \
|
| 47 |
+
mkdir -p /usr/share/novnc && \
|
| 48 |
+
wget "https://github.com/novnc/noVNC/archive/refs/heads/master.tar.gz" -O /tmp/novnc.tar.gz -q --timeout=10 && \
|
| 49 |
+
tar -xf /tmp/novnc.tar.gz -C /tmp/ && \
|
| 50 |
+
cd "/tmp/noVNC-master" && \
|
| 51 |
+
mv app core vendor package.json ./*.html /usr/share/novnc && \
|
| 52 |
+
unlink /etc/nginx/sites-enabled/default && \
|
| 53 |
+
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \
|
| 54 |
+
echo "$VERSION_ARG" > /run/version && \
|
| 55 |
+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
| 56 |
+
|
| 57 |
+
COPY --chmod=755 ./src /run/
|
| 58 |
+
COPY --chmod=755 ./web /var/www/
|
| 59 |
+
COPY --chmod=664 ./web/conf/defaults.json /usr/share/novnc
|
| 60 |
+
COPY --chmod=664 ./web/conf/mandatory.json /usr/share/novnc
|
| 61 |
+
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/default.conf
|
| 62 |
+
|
| 63 |
+
ADD --chmod=755 "https://github.com/qemus/fiano/releases/download/v1.2.0/utk_1.2.0_${TARGETARCH}.bin" /run/utk.bin
|
| 64 |
+
|
| 65 |
+
VOLUME /storage
|
| 66 |
+
EXPOSE 22 5900 8006
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
# Make the entire /app directory fully writeable for all users
|
| 71 |
+
RUN chmod -R 777 /app
|
| 72 |
+
|
| 73 |
+
# Ensure the app runs as the same user as the Space UI
|
| 74 |
+
RUN useradd -m -u 1000 user
|
| 75 |
+
USER user
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
ENV BOOT="alpine"
|
| 79 |
+
ENV CPU_CORES="2"
|
| 80 |
+
ENV RAM_SIZE="2G"
|
| 81 |
+
ENV DISK_SIZE="64G"
|
| 82 |
+
|
| 83 |
+
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
|
compose.yml
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
services:
|
| 2 |
+
qemu:
|
| 3 |
+
image: qemux/qemu
|
| 4 |
+
container_name: qemu
|
| 5 |
+
environment:
|
| 6 |
+
BOOT: "mint"
|
| 7 |
+
devices:
|
| 8 |
+
- /dev/kvm
|
| 9 |
+
- /dev/net/tun
|
| 10 |
+
cap_add:
|
| 11 |
+
- NET_ADMIN
|
| 12 |
+
ports:
|
| 13 |
+
- 8006:8006
|
| 14 |
+
volumes:
|
| 15 |
+
- ./qemu:/storage
|
| 16 |
+
restart: always
|
| 17 |
+
stop_grace_period: 2m
|
kubernetes.yml
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
apiVersion: v1
|
| 3 |
+
kind: PersistentVolumeClaim
|
| 4 |
+
metadata:
|
| 5 |
+
name: qemu-pvc
|
| 6 |
+
spec:
|
| 7 |
+
accessModes:
|
| 8 |
+
- ReadWriteOnce
|
| 9 |
+
resources:
|
| 10 |
+
requests:
|
| 11 |
+
storage: 64Gi
|
| 12 |
+
---
|
| 13 |
+
apiVersion: apps/v1
|
| 14 |
+
kind: Deployment
|
| 15 |
+
metadata:
|
| 16 |
+
name: qemu
|
| 17 |
+
labels:
|
| 18 |
+
name: qemu
|
| 19 |
+
spec:
|
| 20 |
+
replicas: 1
|
| 21 |
+
selector:
|
| 22 |
+
matchLabels:
|
| 23 |
+
app: qemu
|
| 24 |
+
template:
|
| 25 |
+
metadata:
|
| 26 |
+
labels:
|
| 27 |
+
app: qemu
|
| 28 |
+
spec:
|
| 29 |
+
containers:
|
| 30 |
+
- name: qemu
|
| 31 |
+
image: qemux/qemu
|
| 32 |
+
env:
|
| 33 |
+
- name: BOOT
|
| 34 |
+
value: "mint"
|
| 35 |
+
- name: DISK_SIZE
|
| 36 |
+
value: "64G"
|
| 37 |
+
ports:
|
| 38 |
+
- containerPort: 8006
|
| 39 |
+
name: http
|
| 40 |
+
protocol: TCP
|
| 41 |
+
- containerPort: 5900
|
| 42 |
+
name: vnc
|
| 43 |
+
protocol: TCP
|
| 44 |
+
securityContext:
|
| 45 |
+
capabilities:
|
| 46 |
+
add:
|
| 47 |
+
- NET_ADMIN
|
| 48 |
+
privileged: true
|
| 49 |
+
volumeMounts:
|
| 50 |
+
- mountPath: /storage
|
| 51 |
+
name: storage
|
| 52 |
+
- mountPath: /dev/kvm
|
| 53 |
+
name: dev-kvm
|
| 54 |
+
- mountPath: /dev/net/tun
|
| 55 |
+
name: dev-tun
|
| 56 |
+
terminationGracePeriodSeconds: 120
|
| 57 |
+
volumes:
|
| 58 |
+
- name: storage
|
| 59 |
+
persistentVolumeClaim:
|
| 60 |
+
claimName: qemu-pvc
|
| 61 |
+
- hostPath:
|
| 62 |
+
path: /dev/kvm
|
| 63 |
+
name: dev-kvm
|
| 64 |
+
- hostPath:
|
| 65 |
+
path: /dev/net/tun
|
| 66 |
+
type: CharDevice
|
| 67 |
+
name: dev-tun
|
| 68 |
+
---
|
| 69 |
+
apiVersion: v1
|
| 70 |
+
kind: Service
|
| 71 |
+
metadata:
|
| 72 |
+
name: qemu
|
| 73 |
+
spec:
|
| 74 |
+
internalTrafficPolicy: Cluster
|
| 75 |
+
ports:
|
| 76 |
+
- name: http
|
| 77 |
+
port: 8006
|
| 78 |
+
protocol: TCP
|
| 79 |
+
targetPort: 8006
|
| 80 |
+
- name: vnc
|
| 81 |
+
port: 5900
|
| 82 |
+
protocol: TCP
|
| 83 |
+
targetPort: 5900
|
| 84 |
+
selector:
|
| 85 |
+
app: qemu
|
| 86 |
+
type: ClusterIP
|
src/boot.sh
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Docker environment variables
|
| 5 |
+
: "${BIOS:=""}" # BIOS file
|
| 6 |
+
: "${TPM:="N"}" # Disable TPM
|
| 7 |
+
: "${SMM:="N"}" # Disable SMM
|
| 8 |
+
|
| 9 |
+
BOOT_DESC=""
|
| 10 |
+
BOOT_OPTS=""
|
| 11 |
+
|
| 12 |
+
SECURE="off"
|
| 13 |
+
[[ "$SMM" == [Yy1]* ]] && SECURE="on"
|
| 14 |
+
[ -n "$BIOS" ] && BOOT_MODE="custom"
|
| 15 |
+
|
| 16 |
+
msg="Configuring boot..."
|
| 17 |
+
html "$msg"
|
| 18 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
| 19 |
+
|
| 20 |
+
case "${BOOT_MODE,,}" in
|
| 21 |
+
"uefi" | "" )
|
| 22 |
+
BOOT_MODE="uefi"
|
| 23 |
+
ROM="OVMF_CODE_4M.fd"
|
| 24 |
+
VARS="OVMF_VARS_4M.fd"
|
| 25 |
+
;;
|
| 26 |
+
"secure" )
|
| 27 |
+
SECURE="on"
|
| 28 |
+
BOOT_DESC=" securely"
|
| 29 |
+
ROM="OVMF_CODE_4M.secboot.fd"
|
| 30 |
+
VARS="OVMF_VARS_4M.secboot.fd"
|
| 31 |
+
;;
|
| 32 |
+
"windows" | "windows_plain" )
|
| 33 |
+
ROM="OVMF_CODE_4M.fd"
|
| 34 |
+
VARS="OVMF_VARS_4M.fd"
|
| 35 |
+
;;
|
| 36 |
+
"windows_secure" )
|
| 37 |
+
TPM="Y"
|
| 38 |
+
SECURE="on"
|
| 39 |
+
BOOT_DESC=" securely"
|
| 40 |
+
ROM="OVMF_CODE_4M.ms.fd"
|
| 41 |
+
VARS="OVMF_VARS_4M.ms.fd"
|
| 42 |
+
;;
|
| 43 |
+
"windows_legacy" )
|
| 44 |
+
HV="N"
|
| 45 |
+
SECURE="on"
|
| 46 |
+
BOOT_DESC=" (legacy)"
|
| 47 |
+
[ -z "${USB:-}" ] && USB="usb-ehci,id=ehci"
|
| 48 |
+
;;
|
| 49 |
+
"legacy" )
|
| 50 |
+
BOOT_DESC=" with SeaBIOS"
|
| 51 |
+
;;
|
| 52 |
+
"custom" )
|
| 53 |
+
BOOT_OPTS="-bios $BIOS"
|
| 54 |
+
BOOT_DESC=" with custom BIOS file"
|
| 55 |
+
;;
|
| 56 |
+
*)
|
| 57 |
+
error "Unknown BOOT_MODE, value \"${BOOT_MODE}\" is not recognized!"
|
| 58 |
+
exit 33
|
| 59 |
+
;;
|
| 60 |
+
esac
|
| 61 |
+
|
| 62 |
+
if [[ "${BOOT_MODE,,}" == "windows"* ]]; then
|
| 63 |
+
BOOT_OPTS+=" -rtc base=localtime"
|
| 64 |
+
BOOT_OPTS+=" -global ICH9-LPC.disable_s3=1"
|
| 65 |
+
BOOT_OPTS+=" -global ICH9-LPC.disable_s4=1"
|
| 66 |
+
fi
|
| 67 |
+
|
| 68 |
+
case "${BOOT_MODE,,}" in
|
| 69 |
+
"uefi" | "secure" | "windows" | "windows_plain" | "windows_secure" )
|
| 70 |
+
|
| 71 |
+
OVMF="/usr/share/OVMF"
|
| 72 |
+
DEST="$STORAGE/${BOOT_MODE,,}"
|
| 73 |
+
|
| 74 |
+
if [ ! -s "$DEST.rom" ] || [ ! -f "$DEST.rom" ]; then
|
| 75 |
+
[ ! -s "$OVMF/$ROM" ] || [ ! -f "$OVMF/$ROM" ] && error "UEFI boot file ($OVMF/$ROM) not found!" && exit 44
|
| 76 |
+
cp "$OVMF/$ROM" "$DEST.rom"
|
| 77 |
+
if [[ "${LOGO:-}" != [Nn]* ]]; then
|
| 78 |
+
/run/utk.bin "$DEST.rom" replace_ffs LogoDXE "/var/www/img/${PROCESS,,}.ffs" save "$DEST.rom"
|
| 79 |
+
fi
|
| 80 |
+
fi
|
| 81 |
+
|
| 82 |
+
if [ ! -s "$DEST.vars" ] || [ ! -f "$DEST.vars" ]; then
|
| 83 |
+
[ ! -s "$OVMF/$VARS" ] || [ ! -f "$OVMF/$VARS" ]&& error "UEFI vars file ($OVMF/$VARS) not found!" && exit 45
|
| 84 |
+
cp "$OVMF/$VARS" "$DEST.vars"
|
| 85 |
+
fi
|
| 86 |
+
|
| 87 |
+
if [[ "${BOOT_MODE,,}" == "secure" || "${BOOT_MODE,,}" == "windows_secure" ]]; then
|
| 88 |
+
BOOT_OPTS+=" -global driver=cfi.pflash01,property=secure,value=on"
|
| 89 |
+
fi
|
| 90 |
+
|
| 91 |
+
BOOT_OPTS+=" -drive file=$DEST.rom,if=pflash,unit=0,format=raw,readonly=on"
|
| 92 |
+
BOOT_OPTS+=" -drive file=$DEST.vars,if=pflash,unit=1,format=raw"
|
| 93 |
+
|
| 94 |
+
;;
|
| 95 |
+
esac
|
| 96 |
+
|
| 97 |
+
MSRS="/sys/module/kvm/parameters/ignore_msrs"
|
| 98 |
+
if [ -e "$MSRS" ]; then
|
| 99 |
+
result=$(<"$MSRS")
|
| 100 |
+
result="${result//[![:print:]]/}"
|
| 101 |
+
if [[ "$result" == "0" || "${result^^}" == "N" ]]; then
|
| 102 |
+
echo 1 | tee "$MSRS" > /dev/null 2>&1 || true
|
| 103 |
+
fi
|
| 104 |
+
fi
|
| 105 |
+
|
| 106 |
+
CLOCKSOURCE="tsc"
|
| 107 |
+
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
|
| 108 |
+
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
| 109 |
+
|
| 110 |
+
if [ ! -f "$CLOCK" ]; then
|
| 111 |
+
warn "file \"$CLOCK\" cannot not found?"
|
| 112 |
+
else
|
| 113 |
+
result=$(<"$CLOCK")
|
| 114 |
+
result="${result//[![:print:]]/}"
|
| 115 |
+
case "${result,,}" in
|
| 116 |
+
"${CLOCKSOURCE,,}" ) ;;
|
| 117 |
+
"kvm-clock" ) info "Nested KVM virtualization detected.." ;;
|
| 118 |
+
"hyperv_clocksource_tsc_page" ) info "Nested Hyper-V virtualization detected.." ;;
|
| 119 |
+
"hpet" ) warn "unsupported clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
| 120 |
+
*) warn "unexpected clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
| 121 |
+
esac
|
| 122 |
+
fi
|
| 123 |
+
|
| 124 |
+
SM_BIOS=""
|
| 125 |
+
PS="/sys/class/dmi/id/product_serial"
|
| 126 |
+
|
| 127 |
+
if [ -s "$PS" ] && [ -r "$PS" ]; then
|
| 128 |
+
|
| 129 |
+
BIOS_SERIAL=$(<"$PS")
|
| 130 |
+
BIOS_SERIAL="${BIOS_SERIAL//[![:alnum:]]/}"
|
| 131 |
+
|
| 132 |
+
if [ -n "$BIOS_SERIAL" ]; then
|
| 133 |
+
SM_BIOS="-smbios type=1,serial=$BIOS_SERIAL"
|
| 134 |
+
fi
|
| 135 |
+
|
| 136 |
+
fi
|
| 137 |
+
|
| 138 |
+
rm -f /var/run/tpm.pid
|
| 139 |
+
|
| 140 |
+
if [[ "$TPM" == [Yy1]* ]]; then
|
| 141 |
+
|
| 142 |
+
if ! swtpm socket -t -d --tpmstate "backend-uri=file://$STORAGE/${BOOT_MODE,,}.tpm" --ctrl type=unixio,path=/run/swtpm-sock --pid file=/var/run/tpm.pid --tpm2; then
|
| 143 |
+
error "Failed to start TPM emulator, reason: $?"
|
| 144 |
+
else
|
| 145 |
+
|
| 146 |
+
for (( i = 1; i < 20; i++ )); do
|
| 147 |
+
|
| 148 |
+
[ -S "/run/swtpm-sock" ] && break
|
| 149 |
+
|
| 150 |
+
if (( i % 10 == 0 )); then
|
| 151 |
+
echo "Waiting for TPM emulator to become available..."
|
| 152 |
+
fi
|
| 153 |
+
|
| 154 |
+
sleep 0.1
|
| 155 |
+
|
| 156 |
+
done
|
| 157 |
+
|
| 158 |
+
if [ ! -S "/run/swtpm-sock" ]; then
|
| 159 |
+
error "TPM socket not found? Disabling TPM module..."
|
| 160 |
+
else
|
| 161 |
+
BOOT_OPTS+=" -chardev socket,id=chrtpm,path=/run/swtpm-sock"
|
| 162 |
+
BOOT_OPTS+=" -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0"
|
| 163 |
+
fi
|
| 164 |
+
|
| 165 |
+
fi
|
| 166 |
+
fi
|
| 167 |
+
|
| 168 |
+
return 0
|
src/config.sh
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
: "${UUID:=""}"
|
| 5 |
+
: "${HPET:="off"}"
|
| 6 |
+
: "${VMPORT:="off"}"
|
| 7 |
+
: "${SERIAL:="mon:stdio"}"
|
| 8 |
+
: "${USB:="qemu-xhci,id=xhci,p2=7,p3=7"}"
|
| 9 |
+
: "${MONITOR:="telnet:localhost:$MON_PORT,server,nowait,nodelay"}"
|
| 10 |
+
: "${SMP:="$CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"}"
|
| 11 |
+
|
| 12 |
+
msg="Configuring QEMU..."
|
| 13 |
+
html "$msg"
|
| 14 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
| 15 |
+
|
| 16 |
+
DEV_OPTS=""
|
| 17 |
+
DEF_OPTS="-nodefaults"
|
| 18 |
+
SERIAL_OPTS="-serial $SERIAL"
|
| 19 |
+
CPU_OPTS="-cpu $CPU_FLAGS -smp $SMP"
|
| 20 |
+
RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
| 21 |
+
MON_OPTS="-monitor $MONITOR -name $PROCESS,process=$PROCESS,debug-threads=on"
|
| 22 |
+
MAC_OPTS="-machine type=${MACHINE},smm=${SECURE},graphics=off,vmport=${VMPORT},dump-guest-core=off,hpet=${HPET}${KVM_OPTS}"
|
| 23 |
+
|
| 24 |
+
[ -n "$UUID" ] && MAC_OPTS+=" -uuid $UUID"
|
| 25 |
+
[ -n "$SM_BIOS" ] && MAC_OPTS+=" $SM_BIOS"
|
| 26 |
+
|
| 27 |
+
if [[ "${MACHINE,,}" != "pc"* ]]; then
|
| 28 |
+
DEV_OPTS="-object rng-random,id=objrng0,filename=/dev/urandom"
|
| 29 |
+
DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0"
|
| 30 |
+
if [[ "${BOOT_MODE,,}" != "windows"* ]]; then
|
| 31 |
+
DEV_OPTS+=" -device virtio-balloon-pci,id=balloon0,bus=pcie.0"
|
| 32 |
+
fi
|
| 33 |
+
fi
|
| 34 |
+
|
| 35 |
+
if [ -d "/shared" ] && [[ "${BOOT_MODE,,}" != "windows"* ]]; then
|
| 36 |
+
DEV_OPTS+=" -fsdev local,id=fsdev0,path=/shared,security_model=none"
|
| 37 |
+
DEV_OPTS+=" -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=shared"
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
[ -n "$USB" ] && [[ "${USB,,}" != "no"* ]] && USB_OPTS="-device $USB -device usb-tablet"
|
| 41 |
+
|
| 42 |
+
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS ${USB_OPTS:-} $NET_OPTS $DISK_OPTS $BOOT_OPTS $DEV_OPTS $ARGUMENTS"
|
| 43 |
+
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
|
| 44 |
+
|
| 45 |
+
if [[ "${DISPLAY,,}" == "web" ]]; then
|
| 46 |
+
[ ! -f "$INFO" ] && error "File $INFO not found?!"
|
| 47 |
+
rm -f "$INFO"
|
| 48 |
+
[ ! -f "$PAGE" ] && error "File $PAGE not found?!"
|
| 49 |
+
rm -f "$PAGE"
|
| 50 |
+
else
|
| 51 |
+
if [[ "${DISPLAY,,}" == "vnc" ]]; then
|
| 52 |
+
html "You can now connect to VNC on port $VNC_PORT." "0"
|
| 53 |
+
else
|
| 54 |
+
html "The virtual machine was booted successfully." "0"
|
| 55 |
+
fi
|
| 56 |
+
fi
|
| 57 |
+
|
| 58 |
+
# Check available memory as the very last step
|
| 59 |
+
|
| 60 |
+
if [[ "$RAM_CHECK" != [Nn]* ]]; then
|
| 61 |
+
|
| 62 |
+
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
| 63 |
+
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
| 64 |
+
|
| 65 |
+
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
|
| 66 |
+
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."
|
| 67 |
+
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
|
| 68 |
+
info "$msg"
|
| 69 |
+
else
|
| 70 |
+
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
|
| 71 |
+
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."
|
| 72 |
+
if [[ "${FS,,}" != "zfs" ]]; then
|
| 73 |
+
warn "$msg"
|
| 74 |
+
else
|
| 75 |
+
info "$msg"
|
| 76 |
+
fi
|
| 77 |
+
fi
|
| 78 |
+
fi
|
| 79 |
+
|
| 80 |
+
fi
|
| 81 |
+
|
| 82 |
+
if [[ "$DEBUG" == [Yy1]* ]]; then
|
| 83 |
+
printf "QEMU arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
|
| 84 |
+
fi
|
| 85 |
+
|
| 86 |
+
return 0
|
src/define.sh
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
pipe() {
|
| 5 |
+
local code="99"
|
| 6 |
+
msg="Failed to connect to $1, reason:"
|
| 7 |
+
|
| 8 |
+
curl --disable --silent --max-time 15 --fail --location "${1}" || {
|
| 9 |
+
code="$?"
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
case "${code,,}" in
|
| 13 |
+
"6" ) error "$msg could not resolve host!" ;;
|
| 14 |
+
"7" ) error "$msg no internet connection available!" ;;
|
| 15 |
+
"28" ) error "$msg connection timed out!" ;;
|
| 16 |
+
"99" ) return 0 ;;
|
| 17 |
+
*) error "$msg $code" ;;
|
| 18 |
+
esac
|
| 19 |
+
|
| 20 |
+
return 1
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
getURL() {
|
| 24 |
+
local id="${1/ /}"
|
| 25 |
+
local ret="$2"
|
| 26 |
+
local url=""
|
| 27 |
+
local arm=""
|
| 28 |
+
local name=""
|
| 29 |
+
local body=""
|
| 30 |
+
local version=""
|
| 31 |
+
|
| 32 |
+
case "${id,,}" in
|
| 33 |
+
"alma" | "almalinux" | "alma-linux" )
|
| 34 |
+
version="9"
|
| 35 |
+
name="AlmaLinux"
|
| 36 |
+
if [[ "$ret" == "url" ]]; then
|
| 37 |
+
url="https://repo.almalinux.org/almalinux/${version}/live/x86_64/AlmaLinux-${version}-latest-x86_64-Live-GNOME.iso"
|
| 38 |
+
arm="https://repo.almalinux.org/almalinux/${version}/live/aarch64/AlmaLinux-${version}-latest-aarch64-Live-GNOME.iso"
|
| 39 |
+
fi ;;
|
| 40 |
+
"alpine" | "alpinelinux" | "alpine-linux" )
|
| 41 |
+
name="Alpine Linux"
|
| 42 |
+
if [[ "$ret" == "url" ]]; then
|
| 43 |
+
body=$(pipe "https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/latest-releases.yaml") || exit 65
|
| 44 |
+
version=$(echo "$body" | awk '/"Xen"/{found=0} {if(found) print} /"Virtual"/{found=1}' | grep 'version:' | awk '{print $2}')
|
| 45 |
+
url="https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/alpine-virt-$version-x86_64.iso"
|
| 46 |
+
arm="https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/aarch64/alpine-virt-$version-aarch64.iso"
|
| 47 |
+
fi ;;
|
| 48 |
+
"arch" | "archlinux" | "arch-linux" )
|
| 49 |
+
name="Arch Linux"
|
| 50 |
+
if [[ "$ret" == "url" ]]; then
|
| 51 |
+
url="https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso"
|
| 52 |
+
fi ;;
|
| 53 |
+
"cachy" | "cachyos" )
|
| 54 |
+
name="CachyOS"
|
| 55 |
+
if [[ "$ret" == "url" ]]; then
|
| 56 |
+
body=$(pipe "https://cachyos.org/download/") || exit 65
|
| 57 |
+
url=$(echo "$body" | tr '&' '\n' | grep "ISO/desktop" | grep -v 'iso.sha' | grep -v 'iso.sig' | cut -d';' -f2)
|
| 58 |
+
arm=$(echo "$body" | tr '&' '\n' | grep "ISO/handheld" | grep -v 'iso.sha' | grep -v 'iso.sig' | cut -d';' -f2)
|
| 59 |
+
fi ;;
|
| 60 |
+
"centos" | "centosstream" | "centos-stream" )
|
| 61 |
+
name="CentOS Stream"
|
| 62 |
+
if [[ "$ret" == "url" ]]; then
|
| 63 |
+
body=$(pipe "https://linuxsoft.cern.ch/centos-stream/") || exit 65
|
| 64 |
+
version=$(echo "$body" | grep "\-stream" | cut -d'"' -f 6 | cut -d'-' -f 1 | head -n 1)
|
| 65 |
+
url="https://mirrors.xtom.de/centos-stream/$version-stream/BaseOS/x86_64/iso/CentOS-Stream-$version-latest-x86_64-dvd1.iso"
|
| 66 |
+
arm="https://mirrors.xtom.de/centos-stream/$version-stream/BaseOS/aarch64/iso/CentOS-Stream-$version-latest-aarch64-dvd1.iso"
|
| 67 |
+
fi ;;
|
| 68 |
+
"debian" )
|
| 69 |
+
name="Debian"
|
| 70 |
+
if [[ "$ret" == "url" ]]; then
|
| 71 |
+
body=$(pipe "https://cdimage.debian.org/debian-cd/") || exit 65
|
| 72 |
+
version=$(echo "$body" | grep '\.[0-9]/' | cut -d'>' -f 9 | cut -d'/' -f 1)
|
| 73 |
+
url="https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/debian-live-$version-amd64-standard.iso"
|
| 74 |
+
arm="https://cdimage.debian.org/debian-cd/current/arm64/iso-dvd/debian-$version-arm64-DVD-1.iso"
|
| 75 |
+
fi ;;
|
| 76 |
+
"fedora" | "fedoralinux" | "fedora-linux" )
|
| 77 |
+
name="Fedora Linux"
|
| 78 |
+
if [[ "$ret" == "url" ]]; then
|
| 79 |
+
body=$(pipe "https://getfedora.org/releases.json") || exit 65
|
| 80 |
+
version=$(echo "$body" | jq -r 'map(.version) | unique | .[]' | sed 's/ /_/g' | sed '/_Beta/d' | sort -r | head -n 1)
|
| 81 |
+
url=$(echo "$body" | jq -r "map(select(.arch==\"x86_64\" and .version==\"${version}\" and .variant==\"Workstation\" and .subvariant==\"Workstation\" )) | .[].link" | grep -m 1 .iso)
|
| 82 |
+
arm=$(echo "$body" | jq -r "map(select(.arch==\"aarch64\" and .version==\"${version}\" and .variant==\"Workstation\" and .subvariant==\"Workstation\" )) | .[].link" | grep -m 1 .iso)
|
| 83 |
+
fi ;;
|
| 84 |
+
"gentoo" | "gentoolinux" | "gentoo-linux" )
|
| 85 |
+
name="Gentoo Linux"
|
| 86 |
+
if [[ "$ret" == "url" ]]; then
|
| 87 |
+
if [[ "${PLATFORM,,}" != "arm64" ]]; then
|
| 88 |
+
body=$(pipe "https://distfiles.gentoo.org/releases/amd64/autobuilds/latest-iso.txt") || exit 65
|
| 89 |
+
version=$(echo "$body" | grep livegui | cut -d' ' -f1)
|
| 90 |
+
url="https://distfiles.gentoo.org/releases/amd64/autobuilds/$version"
|
| 91 |
+
else
|
| 92 |
+
body=$(pipe "https://distfiles.gentoo.org/releases/arm64/autobuilds/latest-qcow2.txt") || exit 65
|
| 93 |
+
version=$(echo "$body" | grep cloudinit | cut -d' ' -f1)
|
| 94 |
+
arm="https://distfiles.gentoo.org/releases/arm64/autobuilds/$version"
|
| 95 |
+
fi
|
| 96 |
+
fi ;;
|
| 97 |
+
"kali" | "kalilinux" | "kali-linux" )
|
| 98 |
+
name="Kali Linux"
|
| 99 |
+
if [[ "$ret" == "url" ]]; then
|
| 100 |
+
body=$(pipe "https://cdimage.kali.org/current/?C=M;O=D") || exit 65
|
| 101 |
+
version=$(echo "$body" | grep -o ">kali-linux-.*-live-amd64.iso" | head -n 1 | cut -c 2-)
|
| 102 |
+
url="https://cdimage.kali.org/current/$version"
|
| 103 |
+
version=$(echo "$body" | grep -o ">kali-linux-.*-live-arm64.iso" | head -n 1 | cut -c 2-)
|
| 104 |
+
arm="https://cdimage.kali.org/current/$version"
|
| 105 |
+
fi ;;
|
| 106 |
+
"kubuntu" )
|
| 107 |
+
name="Kubuntu"
|
| 108 |
+
if [[ "$ret" == "url" ]]; then
|
| 109 |
+
body=$(pipe "https://api.launchpad.net/devel/ubuntu/series") || exit 65
|
| 110 |
+
version=$(echo "$body" | jq -r '.entries | .[] | select(.status=="Current Stable Release").version')
|
| 111 |
+
url="https://cdimage.ubuntu.com/kubuntu/releases/${version}/release/kubuntu-${version}-desktop-amd64.iso"
|
| 112 |
+
fi ;;
|
| 113 |
+
"lmde" )
|
| 114 |
+
version="6"
|
| 115 |
+
name="Linux Mint Debian Edition"
|
| 116 |
+
if [[ "$ret" == "url" ]]; then
|
| 117 |
+
url="https://pub.linuxmint.io/debian/lmde-${version}-cinnamon-64bit.iso"
|
| 118 |
+
fi ;;
|
| 119 |
+
"macos" | "osx" )
|
| 120 |
+
name="macOS"
|
| 121 |
+
error "To install $name use: https://github.com/dockur/macos" && return 1 ;;
|
| 122 |
+
"mint" | "linuxmint" | "linux-mint" )
|
| 123 |
+
version="22.2"
|
| 124 |
+
name="Linux Mint"
|
| 125 |
+
if [[ "$ret" == "url" ]]; then
|
| 126 |
+
url="https://pub.linuxmint.io/stable/${version}/linuxmint-${version}-cinnamon-64bit.iso"
|
| 127 |
+
fi ;;
|
| 128 |
+
"manjaro" )
|
| 129 |
+
name="Manjaro"
|
| 130 |
+
if [[ "$ret" == "url" ]]; then
|
| 131 |
+
body=$(pipe "https://gitlab.manjaro.org/web/iso-info/-/raw/master/file-info.json") || exit 65
|
| 132 |
+
url=$(echo "$body" | jq -r .official.plasma.image)
|
| 133 |
+
fi ;;
|
| 134 |
+
"mx" | "mxlinux" | "mx-linux" )
|
| 135 |
+
name="MX Linux"
|
| 136 |
+
if [[ "$ret" == "url" ]]; then
|
| 137 |
+
version=$(curl --disable -Ils "https://sourceforge.net/projects/mx-linux/files/latest/download" | grep -i 'location:' | cut -d? -f1 | cut -d_ -f1 | cut -d- -f3) || exit 65
|
| 138 |
+
url="https://mirror.umd.edu/mxlinux-iso/MX/Final/Xfce/MX-${version}_x64.iso"
|
| 139 |
+
fi ;;
|
| 140 |
+
"nixos" )
|
| 141 |
+
name="NixOS"
|
| 142 |
+
if [[ "$ret" == "url" ]]; then
|
| 143 |
+
body=$(pipe "https://nix-channels.s3.amazonaws.com/?delimiter=/") || exit 65
|
| 144 |
+
version=$(echo "$body" | grep -o -E 'nixos-[[:digit:]]+\.[[:digit:]]+' | cut -d- -f2 | sort -nru | head -n 1)
|
| 145 |
+
url="https://channels.nixos.org/nixos-$version/latest-nixos-gnome-x86_64-linux.iso"
|
| 146 |
+
arm="https://channels.nixos.org/nixos-$version/latest-nixos-gnome-aarch64-linux.iso"
|
| 147 |
+
fi ;;
|
| 148 |
+
"opensuse" | "open-suse" | "suse" )
|
| 149 |
+
name="OpenSUSE"
|
| 150 |
+
if [[ "$ret" == "url" ]]; then
|
| 151 |
+
body=$(pipe "https://download.opensuse.org/distribution/leap/") || exit 65
|
| 152 |
+
version=$(echo "$body" | grep 'class="name"' | cut -d '/' -f2 | grep -v 42 | sort -r | head -n 1)
|
| 153 |
+
url="https://download.opensuse.org/distribution/leap/$version/installer/iso/agama-installer.x86_64-Leap_${version}.iso"
|
| 154 |
+
arm="https://download.opensuse.org/distribution/leap/$version/installer/iso/agama-installer.aarch64-Leap_${version}.iso"
|
| 155 |
+
fi ;;
|
| 156 |
+
"rocky" | "rockylinux" | "rocky-linux" )
|
| 157 |
+
version="9"
|
| 158 |
+
name="Rocky Linux"
|
| 159 |
+
if [[ "$ret" == "url" ]]; then
|
| 160 |
+
url="https://dl.rockylinux.org/pub/rocky/${version}/live/x86_64/Rocky-${version}-Workstation-x86_64-latest.iso"
|
| 161 |
+
arm="https://dl.rockylinux.org/pub/rocky/${version}/live/aarch64/Rocky-${version}-Workstation-aarch64-latest.iso"
|
| 162 |
+
fi ;;
|
| 163 |
+
"slack" | "slackware" )
|
| 164 |
+
name="Slackware"
|
| 165 |
+
if [[ "$ret" == "url" ]]; then
|
| 166 |
+
url="https://slackware.nl/slackware-live/slackware64-current-live/slackware64-live-current.iso"
|
| 167 |
+
fi ;;
|
| 168 |
+
"tails" )
|
| 169 |
+
name="Tails"
|
| 170 |
+
if [[ "$ret" == "url" ]]; then
|
| 171 |
+
body=$(pipe "https://tails.net/install/v2/Tails/amd64/stable/latest.json") || exit 65
|
| 172 |
+
url=$(echo "$body" | jq -r '.installations[0]."installation-paths"[]|select(.type=="iso")|."target-files"[0].url')
|
| 173 |
+
fi ;;
|
| 174 |
+
"ubuntu" | "ubuntu-desktop" )
|
| 175 |
+
name="Ubuntu Desktop"
|
| 176 |
+
if [[ "$ret" == "url" ]]; then
|
| 177 |
+
body=$(pipe "https://api.launchpad.net/devel/ubuntu/series") || exit 65
|
| 178 |
+
version=$(echo "$body" | jq -r '.entries | .[] | select(.status=="Current Stable Release").version')
|
| 179 |
+
url="https://releases.ubuntu.com/${version}/ubuntu-${version}-desktop-amd64.iso"
|
| 180 |
+
arm="https://cdimage.ubuntu.com/releases/${version}/release/ubuntu-${version}-desktop-arm64.iso"
|
| 181 |
+
fi ;;
|
| 182 |
+
"ubuntus" | "ubuntu-server")
|
| 183 |
+
name="Ubuntu Server"
|
| 184 |
+
if [[ "$ret" == "url" ]]; then
|
| 185 |
+
body=$(pipe "https://api.launchpad.net/devel/ubuntu/series") || exit 65
|
| 186 |
+
version=$(echo "$body" | jq -r '.entries | .[] | select(.status=="Current Stable Release").version')
|
| 187 |
+
url="https://releases.ubuntu.com/${version}/ubuntu-${version}-live-server-amd64.iso"
|
| 188 |
+
arm="https://cdimage.ubuntu.com/releases/${version}/release/ubuntu-${version}-live-server-arm64.iso"
|
| 189 |
+
fi ;;
|
| 190 |
+
"windows" )
|
| 191 |
+
name="Windows"
|
| 192 |
+
error "To install $name use: https://github.com/dockur/windows" && return 1 ;;
|
| 193 |
+
"xubuntu" )
|
| 194 |
+
name="Xubuntu"
|
| 195 |
+
if [[ "$ret" == "url" ]]; then
|
| 196 |
+
body=$(pipe "https://api.launchpad.net/devel/ubuntu/series") || exit 65
|
| 197 |
+
version=$(echo "$body" | jq -r '.entries | .[] | select(.status=="Current Stable Release").version')
|
| 198 |
+
url="https://cdimages.ubuntu.com/xubuntu/releases/${version}/release/xubuntu-${version}-desktop-amd64.iso"
|
| 199 |
+
fi ;;
|
| 200 |
+
esac
|
| 201 |
+
|
| 202 |
+
case "${ret,,}" in
|
| 203 |
+
"name" )
|
| 204 |
+
echo "$name"
|
| 205 |
+
;;
|
| 206 |
+
"url" )
|
| 207 |
+
|
| 208 |
+
if [[ "${PLATFORM,,}" != "arm64" ]]; then
|
| 209 |
+
if [ -n "$name" ] && [ -z "$url" ]; then
|
| 210 |
+
error "No image for $name available!"
|
| 211 |
+
return 1
|
| 212 |
+
fi
|
| 213 |
+
else
|
| 214 |
+
if [ -n "$name" ] && [ -z "$arm" ]; then
|
| 215 |
+
error "No image for $name is available for ARM64 yet! "
|
| 216 |
+
return 1
|
| 217 |
+
fi
|
| 218 |
+
fi
|
| 219 |
+
|
| 220 |
+
if [[ "${PLATFORM,,}" != "arm64" ]]; then
|
| 221 |
+
echo "$url"
|
| 222 |
+
else
|
| 223 |
+
echo "$arm"
|
| 224 |
+
fi ;;
|
| 225 |
+
esac
|
| 226 |
+
|
| 227 |
+
return 0
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
return 0
|
src/disk.sh
ADDED
|
@@ -0,0 +1,728 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Docker environment variables
|
| 5 |
+
|
| 6 |
+
: "${DISK_IO:="native"}" # I/O Mode, can be set to 'native', 'threads' or 'io_uring'
|
| 7 |
+
: "${DISK_FMT:=""}" # Disk file format, can be set to "raw" (default) or "qcow2"
|
| 8 |
+
: "${DISK_TYPE:=""}" # Device type to be used, "sata", "nvme", "blk" or "scsi"
|
| 9 |
+
: "${DISK_FLAGS:=""}" # Specifies the options for use with the qcow2 disk format
|
| 10 |
+
: "${DISK_CACHE:="none"}" # Caching mode, can be set to 'writeback' for better performance
|
| 11 |
+
: "${DISK_DISCARD:="on"}" # Controls whether unmap (TRIM) commands are passed to the host.
|
| 12 |
+
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
|
| 13 |
+
|
| 14 |
+
fmt2ext() {
|
| 15 |
+
local DISK_FMT=$1
|
| 16 |
+
|
| 17 |
+
case "${DISK_FMT,,}" in
|
| 18 |
+
qcow2)
|
| 19 |
+
echo "qcow2"
|
| 20 |
+
;;
|
| 21 |
+
raw)
|
| 22 |
+
echo "img"
|
| 23 |
+
;;
|
| 24 |
+
*)
|
| 25 |
+
error "Unrecognized disk format: $DISK_FMT" && exit 78
|
| 26 |
+
;;
|
| 27 |
+
esac
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
ext2fmt() {
|
| 31 |
+
local DISK_EXT=$1
|
| 32 |
+
|
| 33 |
+
case "${DISK_EXT,,}" in
|
| 34 |
+
qcow2)
|
| 35 |
+
echo "qcow2"
|
| 36 |
+
;;
|
| 37 |
+
img)
|
| 38 |
+
echo "raw"
|
| 39 |
+
;;
|
| 40 |
+
*)
|
| 41 |
+
error "Unrecognized file extension: .$DISK_EXT" && exit 78
|
| 42 |
+
;;
|
| 43 |
+
esac
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
getSize() {
|
| 47 |
+
local DISK_FILE=$1
|
| 48 |
+
local DISK_EXT DISK_FMT
|
| 49 |
+
|
| 50 |
+
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
| 51 |
+
DISK_FMT=$(ext2fmt "$DISK_EXT")
|
| 52 |
+
|
| 53 |
+
case "${DISK_FMT,,}" in
|
| 54 |
+
raw)
|
| 55 |
+
stat -c%s "$DISK_FILE"
|
| 56 |
+
;;
|
| 57 |
+
qcow2)
|
| 58 |
+
qemu-img info "$DISK_FILE" -f "$DISK_FMT" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/'
|
| 59 |
+
;;
|
| 60 |
+
*)
|
| 61 |
+
error "Unrecognized disk format: $DISK_FMT" && exit 78
|
| 62 |
+
;;
|
| 63 |
+
esac
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
isCow() {
|
| 67 |
+
local FS=$1
|
| 68 |
+
|
| 69 |
+
if [[ "${FS,,}" == "btrfs" ]]; then
|
| 70 |
+
return 0
|
| 71 |
+
fi
|
| 72 |
+
|
| 73 |
+
return 1
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
supportsDirect() {
|
| 77 |
+
local FS=$1
|
| 78 |
+
|
| 79 |
+
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
| 80 |
+
return 1
|
| 81 |
+
fi
|
| 82 |
+
|
| 83 |
+
return 0
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
createDisk() {
|
| 87 |
+
|
| 88 |
+
local DISK_FILE=$1
|
| 89 |
+
local DISK_SPACE=$2
|
| 90 |
+
local DISK_DESC=$3
|
| 91 |
+
local DISK_FMT=$4
|
| 92 |
+
local FS=$5
|
| 93 |
+
local DATA_SIZE DIR SPACE GB FA
|
| 94 |
+
|
| 95 |
+
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
| 96 |
+
|
| 97 |
+
rm -f "$DISK_FILE"
|
| 98 |
+
|
| 99 |
+
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
| 100 |
+
|
| 101 |
+
# Check free diskspace
|
| 102 |
+
DIR=$(dirname "$DISK_FILE")
|
| 103 |
+
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
| 104 |
+
|
| 105 |
+
if (( DATA_SIZE > SPACE )); then
|
| 106 |
+
GB=$(formatBytes "$SPACE")
|
| 107 |
+
error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..."
|
| 108 |
+
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
|
| 109 |
+
fi
|
| 110 |
+
fi
|
| 111 |
+
|
| 112 |
+
html "Creating a $DISK_DESC image..."
|
| 113 |
+
info "Creating a ${DISK_SPACE/G/ GB} $DISK_STYLE $DISK_DESC image in $DISK_FMT format..."
|
| 114 |
+
|
| 115 |
+
local FAIL="Could not create a $DISK_STYLE $DISK_FMT $DISK_DESC image of ${DISK_SPACE/G/ GB} ($DISK_FILE)"
|
| 116 |
+
|
| 117 |
+
case "${DISK_FMT,,}" in
|
| 118 |
+
raw)
|
| 119 |
+
|
| 120 |
+
if isCow "$FS"; then
|
| 121 |
+
if ! touch "$DISK_FILE"; then
|
| 122 |
+
error "$FAIL" && exit 77
|
| 123 |
+
fi
|
| 124 |
+
{ chattr +C "$DISK_FILE"; } || :
|
| 125 |
+
fi
|
| 126 |
+
|
| 127 |
+
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
| 128 |
+
|
| 129 |
+
# Create an empty file
|
| 130 |
+
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
| 131 |
+
rm -f "$DISK_FILE"
|
| 132 |
+
error "$FAIL" && exit 77
|
| 133 |
+
fi
|
| 134 |
+
|
| 135 |
+
else
|
| 136 |
+
|
| 137 |
+
# Create an empty file
|
| 138 |
+
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
|
| 139 |
+
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
|
| 140 |
+
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
| 141 |
+
rm -f "$DISK_FILE"
|
| 142 |
+
error "$FAIL" && exit 77
|
| 143 |
+
fi
|
| 144 |
+
fi
|
| 145 |
+
fi
|
| 146 |
+
|
| 147 |
+
fi
|
| 148 |
+
;;
|
| 149 |
+
qcow2)
|
| 150 |
+
|
| 151 |
+
local DISK_PARAM="$DISK_ALLOC"
|
| 152 |
+
isCow "$FS" && DISK_PARAM+=",nocow=on"
|
| 153 |
+
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
|
| 154 |
+
|
| 155 |
+
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
|
| 156 |
+
rm -f "$DISK_FILE"
|
| 157 |
+
error "$FAIL" && exit 70
|
| 158 |
+
fi
|
| 159 |
+
;;
|
| 160 |
+
esac
|
| 161 |
+
|
| 162 |
+
if isCow "$FS"; then
|
| 163 |
+
FA=$(lsattr "$DISK_FILE")
|
| 164 |
+
if [[ "$FA" != *"C"* ]]; then
|
| 165 |
+
error "Failed to disable COW for $DISK_DESC image $DISK_FILE on ${FS^^} filesystem (returned $FA)"
|
| 166 |
+
fi
|
| 167 |
+
fi
|
| 168 |
+
|
| 169 |
+
return 0
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
resizeDisk() {
|
| 173 |
+
|
| 174 |
+
local DISK_FILE=$1
|
| 175 |
+
local DISK_SPACE=$2
|
| 176 |
+
local DISK_DESC=$3
|
| 177 |
+
local DISK_FMT=$4
|
| 178 |
+
local FS=$5
|
| 179 |
+
local CUR_SIZE DATA_SIZE DIR SPACE GB
|
| 180 |
+
|
| 181 |
+
CUR_SIZE=$(getSize "$DISK_FILE")
|
| 182 |
+
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
| 183 |
+
local REQ=$((DATA_SIZE-CUR_SIZE))
|
| 184 |
+
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
|
| 185 |
+
|
| 186 |
+
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
| 187 |
+
|
| 188 |
+
# Check free diskspace
|
| 189 |
+
DIR=$(dirname "$DISK_FILE")
|
| 190 |
+
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
| 191 |
+
|
| 192 |
+
if (( REQ > SPACE )); then
|
| 193 |
+
GB=$(formatBytes "$SPACE")
|
| 194 |
+
error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.."
|
| 195 |
+
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
|
| 196 |
+
fi
|
| 197 |
+
fi
|
| 198 |
+
|
| 199 |
+
GB=$(formatBytes "$CUR_SIZE")
|
| 200 |
+
MSG="Resizing $DISK_DESC from $GB to ${DISK_SPACE/G/ GB}..."
|
| 201 |
+
info "$MSG" && html "$MSG"
|
| 202 |
+
|
| 203 |
+
local FAIL="Could not resize the $DISK_STYLE $DISK_FMT $DISK_DESC image from ${GB} to ${DISK_SPACE/G/ GB} ($DISK_FILE)"
|
| 204 |
+
|
| 205 |
+
case "${DISK_FMT,,}" in
|
| 206 |
+
raw)
|
| 207 |
+
|
| 208 |
+
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
| 209 |
+
|
| 210 |
+
# Resize file by changing its length
|
| 211 |
+
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
| 212 |
+
error "$FAIL" && exit 75
|
| 213 |
+
fi
|
| 214 |
+
|
| 215 |
+
else
|
| 216 |
+
|
| 217 |
+
# Resize file by allocating more space
|
| 218 |
+
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
|
| 219 |
+
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
|
| 220 |
+
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
| 221 |
+
error "$FAIL" && exit 75
|
| 222 |
+
fi
|
| 223 |
+
fi
|
| 224 |
+
fi
|
| 225 |
+
|
| 226 |
+
fi
|
| 227 |
+
;;
|
| 228 |
+
qcow2)
|
| 229 |
+
|
| 230 |
+
if ! qemu-img resize -f "$DISK_FMT" "--$DISK_ALLOC" "$DISK_FILE" "$DATA_SIZE" ; then
|
| 231 |
+
error "$FAIL" && exit 72
|
| 232 |
+
fi
|
| 233 |
+
|
| 234 |
+
;;
|
| 235 |
+
esac
|
| 236 |
+
|
| 237 |
+
return 0
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
convertDisk() {
|
| 241 |
+
|
| 242 |
+
local SOURCE_FILE=$1
|
| 243 |
+
local SOURCE_FMT=$2
|
| 244 |
+
local DST_FILE=$3
|
| 245 |
+
local DST_FMT=$4
|
| 246 |
+
local DISK_BASE=$5
|
| 247 |
+
local DISK_DESC=$6
|
| 248 |
+
local FS=$7
|
| 249 |
+
|
| 250 |
+
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
|
| 251 |
+
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
|
| 252 |
+
|
| 253 |
+
local TMP_FILE="$DISK_BASE.tmp"
|
| 254 |
+
rm -f "$TMP_FILE"
|
| 255 |
+
|
| 256 |
+
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
| 257 |
+
|
| 258 |
+
local DIR CUR_SIZE SPACE GB
|
| 259 |
+
|
| 260 |
+
# Check free diskspace
|
| 261 |
+
DIR=$(dirname "$TMP_FILE")
|
| 262 |
+
CUR_SIZE=$(getSize "$SOURCE_FILE")
|
| 263 |
+
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
| 264 |
+
|
| 265 |
+
if (( CUR_SIZE > SPACE )); then
|
| 266 |
+
GB=$(formatBytes "$SPACE")
|
| 267 |
+
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..."
|
| 268 |
+
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
|
| 269 |
+
fi
|
| 270 |
+
fi
|
| 271 |
+
|
| 272 |
+
local msg="Converting $DISK_DESC to $DST_FMT"
|
| 273 |
+
html "$msg..."
|
| 274 |
+
info "$msg, please wait until completed..."
|
| 275 |
+
|
| 276 |
+
local CONV_FLAGS="-p"
|
| 277 |
+
local DISK_PARAM="$DISK_ALLOC"
|
| 278 |
+
isCow "$FS" && DISK_PARAM+=",nocow=on"
|
| 279 |
+
|
| 280 |
+
if [[ "$DST_FMT" != "raw" ]]; then
|
| 281 |
+
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
| 282 |
+
CONV_FLAGS+=" -c"
|
| 283 |
+
fi
|
| 284 |
+
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
|
| 285 |
+
fi
|
| 286 |
+
|
| 287 |
+
# shellcheck disable=SC2086
|
| 288 |
+
if ! qemu-img convert -f "$SOURCE_FMT" $CONV_FLAGS -o "$DISK_PARAM" -O "$DST_FMT" -- "$SOURCE_FILE" "$TMP_FILE"; then
|
| 289 |
+
rm -f "$TMP_FILE"
|
| 290 |
+
error "Failed to convert $DISK_STYLE $DISK_DESC image to $DST_FMT format in $DIR, is there enough space available?" && exit 79
|
| 291 |
+
fi
|
| 292 |
+
|
| 293 |
+
if [[ "$DST_FMT" == "raw" ]]; then
|
| 294 |
+
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
| 295 |
+
# Work around qemu-img bug
|
| 296 |
+
CUR_SIZE=$(stat -c%s "$TMP_FILE")
|
| 297 |
+
if ! fallocate -l "$CUR_SIZE" "$TMP_FILE" &>/dev/null; then
|
| 298 |
+
if ! fallocate -l -x "$CUR_SIZE" "$TMP_FILE"; then
|
| 299 |
+
error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
|
| 300 |
+
fi
|
| 301 |
+
fi
|
| 302 |
+
fi
|
| 303 |
+
fi
|
| 304 |
+
|
| 305 |
+
rm -f "$SOURCE_FILE"
|
| 306 |
+
mv "$TMP_FILE" "$DST_FILE"
|
| 307 |
+
|
| 308 |
+
if isCow "$FS"; then
|
| 309 |
+
FA=$(lsattr "$DST_FILE")
|
| 310 |
+
if [[ "$FA" != *"C"* ]]; then
|
| 311 |
+
error "Failed to disable COW for $DISK_DESC image $DST_FILE on ${FS^^} filesystem (returned $FA)"
|
| 312 |
+
fi
|
| 313 |
+
fi
|
| 314 |
+
|
| 315 |
+
msg="Conversion of $DISK_DESC"
|
| 316 |
+
html "$msg completed..."
|
| 317 |
+
info "$msg to $DST_FMT completed successfully!"
|
| 318 |
+
|
| 319 |
+
return 0
|
| 320 |
+
}
|
| 321 |
+
|
| 322 |
+
checkFS () {
|
| 323 |
+
|
| 324 |
+
local FS=$1
|
| 325 |
+
local DISK_FILE=$2
|
| 326 |
+
local DISK_DESC=$3
|
| 327 |
+
local DIR FA
|
| 328 |
+
|
| 329 |
+
DIR=$(dirname "$DISK_FILE")
|
| 330 |
+
[ ! -d "$DIR" ] && return 0
|
| 331 |
+
|
| 332 |
+
if [[ "${FS,,}" == "overlay"* ]]; then
|
| 333 |
+
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
| 334 |
+
fi
|
| 335 |
+
|
| 336 |
+
if [[ "${FS,,}" == "fuse"* ]]; then
|
| 337 |
+
info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
|
| 338 |
+
fi
|
| 339 |
+
|
| 340 |
+
if ! supportsDirect "$FS"; then
|
| 341 |
+
info "Warning: the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
|
| 342 |
+
fi
|
| 343 |
+
|
| 344 |
+
if isCow "$FS"; then
|
| 345 |
+
if [ -f "$DISK_FILE" ]; then
|
| 346 |
+
FA=$(lsattr "$DISK_FILE")
|
| 347 |
+
if [[ "$FA" != *"C"* ]]; then
|
| 348 |
+
info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
|
| 349 |
+
fi
|
| 350 |
+
fi
|
| 351 |
+
fi
|
| 352 |
+
|
| 353 |
+
return 0
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
createDevice () {
|
| 357 |
+
|
| 358 |
+
local DISK_FILE=$1
|
| 359 |
+
local DISK_TYPE=$2
|
| 360 |
+
local DISK_INDEX=$3
|
| 361 |
+
local DISK_ADDRESS=$4
|
| 362 |
+
local DISK_FMT=$5
|
| 363 |
+
local DISK_IO=$6
|
| 364 |
+
local DISK_CACHE=$7
|
| 365 |
+
local DISK_SERIAL=$8
|
| 366 |
+
local DISK_SECTORS=$9
|
| 367 |
+
local DISK_ID="data$DISK_INDEX"
|
| 368 |
+
|
| 369 |
+
local index=""
|
| 370 |
+
[ -n "$DISK_INDEX" ] && index=",bootindex=$DISK_INDEX"
|
| 371 |
+
local result=" -drive file=$DISK_FILE,id=$DISK_ID,format=$DISK_FMT,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on"
|
| 372 |
+
|
| 373 |
+
case "${DISK_TYPE,,}" in
|
| 374 |
+
"none" ) ;;
|
| 375 |
+
"auto" )
|
| 376 |
+
echo "$result"
|
| 377 |
+
;;
|
| 378 |
+
"usb" )
|
| 379 |
+
result+=",if=none \
|
| 380 |
+
-device usb-storage,drive=${DISK_ID}${index}${DISK_SERIAL}${DISK_SECTORS}"
|
| 381 |
+
echo "$result"
|
| 382 |
+
;;
|
| 383 |
+
"nvme" )
|
| 384 |
+
result+=",if=none \
|
| 385 |
+
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}${DISK_SERIAL}${DISK_SECTORS}"
|
| 386 |
+
echo "$result"
|
| 387 |
+
;;
|
| 388 |
+
"ide" | "sata" )
|
| 389 |
+
result+=",if=none \
|
| 390 |
+
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \
|
| 391 |
+
-device ide-hd,drive=${DISK_ID},bus=ahci$DISK_INDEX.0,rotation_rate=$DISK_ROTATION${index}${DISK_SERIAL}${DISK_SECTORS}"
|
| 392 |
+
echo "$result"
|
| 393 |
+
;;
|
| 394 |
+
"blk" | "virtio-blk" )
|
| 395 |
+
result+=",if=none \
|
| 396 |
+
-device virtio-blk-pci,drive=${DISK_ID},bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}${DISK_SERIAL}${DISK_SECTORS}"
|
| 397 |
+
echo "$result"
|
| 398 |
+
;;
|
| 399 |
+
"scsi" | "virtio-scsi" )
|
| 400 |
+
result+=",if=none \
|
| 401 |
+
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \
|
| 402 |
+
-device scsi-hd,drive=${DISK_ID},bus=${DISK_ID}b.0,channel=0,scsi-id=0,lun=0,rotation_rate=$DISK_ROTATION${index}${DISK_SERIAL}${DISK_SECTORS}"
|
| 403 |
+
echo "$result"
|
| 404 |
+
;;
|
| 405 |
+
esac
|
| 406 |
+
|
| 407 |
+
return 0
|
| 408 |
+
}
|
| 409 |
+
|
| 410 |
+
addMedia () {
|
| 411 |
+
|
| 412 |
+
local DISK_FILE=$1
|
| 413 |
+
local DISK_TYPE=$2
|
| 414 |
+
local DISK_INDEX=$3
|
| 415 |
+
local DISK_ADDRESS=$4
|
| 416 |
+
|
| 417 |
+
local index=""
|
| 418 |
+
local DISK_ID="cdrom$DISK_INDEX"
|
| 419 |
+
[ -n "$DISK_INDEX" ] && index=",bootindex=$DISK_INDEX"
|
| 420 |
+
local result=" -drive file=$DISK_FILE,id=$DISK_ID,format=raw,cache=unsafe,readonly=on,media=cdrom"
|
| 421 |
+
|
| 422 |
+
case "${DISK_TYPE,,}" in
|
| 423 |
+
"none" ) ;;
|
| 424 |
+
"auto" )
|
| 425 |
+
echo "$result"
|
| 426 |
+
;;
|
| 427 |
+
"usb" )
|
| 428 |
+
result+=",if=none \
|
| 429 |
+
-device usb-storage,drive=${DISK_ID}${index},removable=on"
|
| 430 |
+
echo "$result"
|
| 431 |
+
;;
|
| 432 |
+
"nvme" )
|
| 433 |
+
result+=",if=none \
|
| 434 |
+
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}"
|
| 435 |
+
echo "$result"
|
| 436 |
+
;;
|
| 437 |
+
"ide" | "sata" )
|
| 438 |
+
result+=",if=none \
|
| 439 |
+
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \
|
| 440 |
+
-device ide-cd,drive=${DISK_ID},bus=ahci${DISK_INDEX}.0${index}"
|
| 441 |
+
echo "$result"
|
| 442 |
+
;;
|
| 443 |
+
"blk" | "virtio-blk" )
|
| 444 |
+
result+=",if=none \
|
| 445 |
+
-device virtio-blk-pci,drive=${DISK_ID},bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}"
|
| 446 |
+
echo "$result"
|
| 447 |
+
;;
|
| 448 |
+
"scsi" | "virtio-scsi" )
|
| 449 |
+
result+=",if=none \
|
| 450 |
+
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \
|
| 451 |
+
-device scsi-cd,drive=${DISK_ID},bus=${DISK_ID}b.0${index}"
|
| 452 |
+
echo "$result"
|
| 453 |
+
;;
|
| 454 |
+
esac
|
| 455 |
+
|
| 456 |
+
return 0
|
| 457 |
+
}
|
| 458 |
+
|
| 459 |
+
addDisk () {
|
| 460 |
+
|
| 461 |
+
local DISK_BASE=$1
|
| 462 |
+
local DISK_TYPE=$2
|
| 463 |
+
local DISK_DESC=$3
|
| 464 |
+
local DISK_SPACE=$4
|
| 465 |
+
local DISK_INDEX=$5
|
| 466 |
+
local DISK_ADDRESS=$6
|
| 467 |
+
local DISK_FMT=$7
|
| 468 |
+
local DISK_IO=$8
|
| 469 |
+
local DISK_CACHE=$9
|
| 470 |
+
local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
|
| 471 |
+
|
| 472 |
+
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
| 473 |
+
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
| 474 |
+
|
| 475 |
+
DIR=$(dirname "$DISK_FILE")
|
| 476 |
+
[ ! -d "$DIR" ] && return 0
|
| 477 |
+
|
| 478 |
+
SPACE="${DISK_SPACE// /}"
|
| 479 |
+
[ -z "$SPACE" ] && SPACE="64G"
|
| 480 |
+
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
| 481 |
+
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
| 482 |
+
|
| 483 |
+
if ! numfmt --from=iec "$SPACE" &>/dev/null; then
|
| 484 |
+
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
|
| 485 |
+
fi
|
| 486 |
+
|
| 487 |
+
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
| 488 |
+
|
| 489 |
+
if (( DATA_SIZE < 104857600 )); then
|
| 490 |
+
error "Please increase ${DISK_DESC^^}_SIZE to at least 100 MB." && exit 73
|
| 491 |
+
fi
|
| 492 |
+
|
| 493 |
+
FS=$(stat -f -c %T "$DIR")
|
| 494 |
+
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
|
| 495 |
+
|
| 496 |
+
if ! supportsDirect "$FS"; then
|
| 497 |
+
DISK_IO="threads"
|
| 498 |
+
DISK_CACHE="writeback"
|
| 499 |
+
fi
|
| 500 |
+
|
| 501 |
+
if ! [ -s "$DISK_FILE" ] ; then
|
| 502 |
+
|
| 503 |
+
if [[ "${DISK_FMT,,}" != "raw" ]]; then
|
| 504 |
+
PREV_FMT="raw"
|
| 505 |
+
else
|
| 506 |
+
PREV_FMT="qcow2"
|
| 507 |
+
fi
|
| 508 |
+
|
| 509 |
+
PREV_EXT=$(fmt2ext "$PREV_FMT")
|
| 510 |
+
|
| 511 |
+
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
|
| 512 |
+
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
|
| 513 |
+
fi
|
| 514 |
+
fi
|
| 515 |
+
|
| 516 |
+
if [ -s "$DISK_FILE" ]; then
|
| 517 |
+
|
| 518 |
+
CUR_SIZE=$(getSize "$DISK_FILE")
|
| 519 |
+
|
| 520 |
+
if (( DATA_SIZE > CUR_SIZE )); then
|
| 521 |
+
resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
| 522 |
+
fi
|
| 523 |
+
|
| 524 |
+
else
|
| 525 |
+
|
| 526 |
+
createDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
| 527 |
+
|
| 528 |
+
fi
|
| 529 |
+
|
| 530 |
+
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "")
|
| 531 |
+
|
| 532 |
+
return 0
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
addDevice () {
|
| 536 |
+
|
| 537 |
+
local DISK_DEV=$1
|
| 538 |
+
local DISK_TYPE=$2
|
| 539 |
+
local DISK_INDEX=$3
|
| 540 |
+
local DISK_ADDRESS=$4
|
| 541 |
+
|
| 542 |
+
[ -z "$DISK_DEV" ] && return 0
|
| 543 |
+
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
|
| 544 |
+
|
| 545 |
+
local sectors=""
|
| 546 |
+
local result logical physical
|
| 547 |
+
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
|
| 548 |
+
logical="${result%% *}"
|
| 549 |
+
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
|
| 550 |
+
physical="${physical%% *}"
|
| 551 |
+
|
| 552 |
+
if [ -n "$physical" ]; then
|
| 553 |
+
if [[ "$physical" != "512" ]]; then
|
| 554 |
+
sectors=",logical_block_size=$logical,physical_block_size=$physical"
|
| 555 |
+
fi
|
| 556 |
+
else
|
| 557 |
+
warn "Failed to determine the sector size for $DISK_DEV"
|
| 558 |
+
fi
|
| 559 |
+
|
| 560 |
+
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
|
| 561 |
+
|
| 562 |
+
return 0
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
msg="Initializing disks..."
|
| 566 |
+
html "$msg"
|
| 567 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
| 568 |
+
|
| 569 |
+
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
|
| 570 |
+
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
|
| 571 |
+
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
|
| 572 |
+
|
| 573 |
+
case "${DISK_TYPE,,}" in
|
| 574 |
+
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
| 575 |
+
* ) error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is not recognized!" && exit 80 ;;
|
| 576 |
+
esac
|
| 577 |
+
|
| 578 |
+
if [[ "${PLATFORM,,}" != "arm64" ]]; then
|
| 579 |
+
FALLBACK="ide"
|
| 580 |
+
else
|
| 581 |
+
FALLBACK="usb"
|
| 582 |
+
fi
|
| 583 |
+
|
| 584 |
+
[[ "${BOOT_MODE:-}" == "windows_legacy" ]] && FALLBACK="auto"
|
| 585 |
+
|
| 586 |
+
if [ -z "${MEDIA_TYPE:-}" ]; then
|
| 587 |
+
if [[ "${BOOT_MODE:-}" != "windows"* ]]; then
|
| 588 |
+
if [[ "${DISK_TYPE,,}" == "blk" ]]; then
|
| 589 |
+
MEDIA_TYPE="$FALLBACK"
|
| 590 |
+
else
|
| 591 |
+
MEDIA_TYPE="$DISK_TYPE"
|
| 592 |
+
fi
|
| 593 |
+
else
|
| 594 |
+
MEDIA_TYPE="$FALLBACK"
|
| 595 |
+
fi
|
| 596 |
+
fi
|
| 597 |
+
|
| 598 |
+
case "${MEDIA_TYPE,,}" in
|
| 599 |
+
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
| 600 |
+
* ) error "Invalid MEDIA_TYPE specified, value \"$MEDIA_TYPE\" is not recognized!" && exit 80 ;;
|
| 601 |
+
esac
|
| 602 |
+
|
| 603 |
+
if [ -f "$BOOT" ] && [ -s "$BOOT" ]; then
|
| 604 |
+
case "${BOOT,,}" in
|
| 605 |
+
*".iso" )
|
| 606 |
+
if [[ "${HYBRID:-}" == [Yy]* ]]; then
|
| 607 |
+
DISK_OPTS+=$(addMedia "$BOOT" "usb" "$BOOT_INDEX" "0x5")
|
| 608 |
+
else
|
| 609 |
+
DISK_OPTS+=$(addMedia "$BOOT" "$MEDIA_TYPE" "$BOOT_INDEX" "0x5")
|
| 610 |
+
fi ;;
|
| 611 |
+
*".img" | *".raw" )
|
| 612 |
+
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "$BOOT_INDEX" "0x5" "raw" "$DISK_IO" "$DISK_CACHE" "" "") ;;
|
| 613 |
+
*".qcow2" )
|
| 614 |
+
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "$BOOT_INDEX" "0x5" "qcow2" "$DISK_IO" "$DISK_CACHE" "" "") ;;
|
| 615 |
+
* )
|
| 616 |
+
error "Invalid BOOT image specified, extension \".${BOOT/*./}\" is not recognized!" && exit 80 ;;
|
| 617 |
+
esac
|
| 618 |
+
fi
|
| 619 |
+
|
| 620 |
+
DRIVERS="/drivers.iso"
|
| 621 |
+
[ ! -f "$DRIVERS" ] || [ ! -s "$DRIVERS" ] && DRIVERS="$STORAGE/drivers.iso"
|
| 622 |
+
|
| 623 |
+
if [ -f "$DRIVERS" ] && [ -s "$DRIVERS" ]; then
|
| 624 |
+
DISK_OPTS+=$(addMedia "$DRIVERS" "$FALLBACK" "" "0x6")
|
| 625 |
+
fi
|
| 626 |
+
|
| 627 |
+
RESCUE="/start.iso"
|
| 628 |
+
[ ! -f "$RESCUE" ] || [ ! -s "$RESCUE" ] && RESCUE="$STORAGE/start.iso"
|
| 629 |
+
|
| 630 |
+
if [ -f "$RESCUE" ] && [ -s "$RESCUE" ]; then
|
| 631 |
+
DISK_OPTS+=$(addMedia "$RESCUE" "$FALLBACK" "1" "0x6")
|
| 632 |
+
fi
|
| 633 |
+
|
| 634 |
+
DISK1_FILE="$STORAGE/${DISK_NAME}"
|
| 635 |
+
DISK2_FILE="/storage2/${DISK_NAME}2"
|
| 636 |
+
DISK3_FILE="/storage3/${DISK_NAME}3"
|
| 637 |
+
DISK4_FILE="/storage4/${DISK_NAME}4"
|
| 638 |
+
DISK5_FILE="/storage5/${DISK_NAME}5"
|
| 639 |
+
DISK6_FILE="/storage6/${DISK_NAME}6"
|
| 640 |
+
|
| 641 |
+
if [ -z "$DISK_FMT" ]; then
|
| 642 |
+
if [ -f "$DISK1_FILE.qcow2" ]; then
|
| 643 |
+
DISK_FMT="qcow2"
|
| 644 |
+
else
|
| 645 |
+
DISK_FMT="raw"
|
| 646 |
+
fi
|
| 647 |
+
fi
|
| 648 |
+
|
| 649 |
+
if [ -z "$ALLOCATE" ]; then
|
| 650 |
+
ALLOCATE="N"
|
| 651 |
+
fi
|
| 652 |
+
|
| 653 |
+
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
| 654 |
+
DISK_STYLE="growable"
|
| 655 |
+
DISK_ALLOC="preallocation=off"
|
| 656 |
+
else
|
| 657 |
+
DISK_STYLE="preallocated"
|
| 658 |
+
DISK_ALLOC="preallocation=falloc"
|
| 659 |
+
fi
|
| 660 |
+
|
| 661 |
+
: "${DISK2_SIZE:=""}"
|
| 662 |
+
: "${DISK3_SIZE:=""}"
|
| 663 |
+
: "${DISK4_SIZE:=""}"
|
| 664 |
+
: "${DISK5_SIZE:=""}"
|
| 665 |
+
: "${DISK6_SIZE:=""}"
|
| 666 |
+
|
| 667 |
+
: "${DEVICE:=""}" # Docker variables to passthrough a block device, like /dev/vdc1.
|
| 668 |
+
: "${DEVICE2:=""}"
|
| 669 |
+
: "${DEVICE3:=""}"
|
| 670 |
+
: "${DEVICE4:=""}"
|
| 671 |
+
: "${DEVICE5:=""}"
|
| 672 |
+
: "${DEVICE6:=""}"
|
| 673 |
+
|
| 674 |
+
[ -z "$DEVICE" ] && [ -b "/disk" ] && DEVICE="/disk"
|
| 675 |
+
[ -z "$DEVICE" ] && [ -b "/disk1" ] && DEVICE="/disk1"
|
| 676 |
+
[ -z "$DEVICE2" ] && [ -b "/disk2" ] && DEVICE2="/disk2"
|
| 677 |
+
[ -z "$DEVICE3" ] && [ -b "/disk3" ] && DEVICE3="/disk3"
|
| 678 |
+
[ -z "$DEVICE4" ] && [ -b "/disk4" ] && DEVICE4="/disk4"
|
| 679 |
+
[ -z "$DEVICE5" ] && [ -b "/disk5" ] && DEVICE5="/disk5"
|
| 680 |
+
[ -z "$DEVICE6" ] && [ -b "/disk6" ] && DEVICE6="/disk6"
|
| 681 |
+
|
| 682 |
+
[ -z "$DEVICE" ] && [ -b "/dev/disk1" ] && DEVICE="/dev/disk1"
|
| 683 |
+
[ -z "$DEVICE2" ] && [ -b "/dev/disk2" ] && DEVICE2="/dev/disk2"
|
| 684 |
+
[ -z "$DEVICE3" ] && [ -b "/dev/disk3" ] && DEVICE3="/dev/disk3"
|
| 685 |
+
[ -z "$DEVICE4" ] && [ -b "/dev/disk4" ] && DEVICE4="/dev/disk4"
|
| 686 |
+
[ -z "$DEVICE5" ] && [ -b "/dev/disk5" ] && DEVICE4="/dev/disk5"
|
| 687 |
+
[ -z "$DEVICE6" ] && [ -b "/dev/disk6" ] && DEVICE4="/dev/disk6"
|
| 688 |
+
|
| 689 |
+
if [ -n "$DEVICE" ]; then
|
| 690 |
+
addDevice "$DEVICE" "$DISK_TYPE" "3" "0xa" || exit $?
|
| 691 |
+
else
|
| 692 |
+
addDisk "$DISK1_FILE" "$DISK_TYPE" "disk" "$DISK_SIZE" "3" "0xa" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
| 693 |
+
fi
|
| 694 |
+
|
| 695 |
+
if [ -n "$DEVICE2" ]; then
|
| 696 |
+
addDevice "$DEVICE2" "$DISK_TYPE" "4" "0xb" || exit $?
|
| 697 |
+
else
|
| 698 |
+
addDisk "$DISK2_FILE" "$DISK_TYPE" "disk2" "$DISK2_SIZE" "4" "0xb" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
| 699 |
+
fi
|
| 700 |
+
|
| 701 |
+
if [ -n "$DEVICE3" ]; then
|
| 702 |
+
addDevice "$DEVICE3" "$DISK_TYPE" "5" "0xc" || exit $?
|
| 703 |
+
else
|
| 704 |
+
addDisk "$DISK3_FILE" "$DISK_TYPE" "disk3" "$DISK3_SIZE" "5" "0xc" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
| 705 |
+
fi
|
| 706 |
+
|
| 707 |
+
if [ -n "$DEVICE4" ]; then
|
| 708 |
+
addDevice "$DEVICE4" "$DISK_TYPE" "6" "0xd" || exit $?
|
| 709 |
+
else
|
| 710 |
+
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xd" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
| 711 |
+
fi
|
| 712 |
+
|
| 713 |
+
if [ -n "$DEVICE5" ]; then
|
| 714 |
+
addDevice "$DEVICE5" "$DISK_TYPE" "7" "0xe" || exit $?
|
| 715 |
+
else
|
| 716 |
+
addDisk "$DISK5_FILE" "$DISK_TYPE" "disk5" "$DISK5_SIZE" "7" "0xe" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
| 717 |
+
fi
|
| 718 |
+
|
| 719 |
+
if [ -n "$DEVICE6" ]; then
|
| 720 |
+
addDevice "$DEVICE6" "$DISK_TYPE" "8" "0xf" || exit $?
|
| 721 |
+
else
|
| 722 |
+
addDisk "$DISK6_FILE" "$DISK_TYPE" "disk6" "$DISK6_SIZE" "8" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
| 723 |
+
fi
|
| 724 |
+
|
| 725 |
+
DISK_OPTS+=" -object iothread,id=io2"
|
| 726 |
+
|
| 727 |
+
html "Initialized disks successfully..."
|
| 728 |
+
return 0
|
src/display.sh
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Docker environment variables
|
| 5 |
+
|
| 6 |
+
: "${GPU:="N"}" # GPU passthrough
|
| 7 |
+
: "${VGA:="virtio"}" # VGA adaptor
|
| 8 |
+
: "${DISPLAY:="web"}" # Display type
|
| 9 |
+
: "${RENDERNODE:="/dev/dri/renderD128"}" # Render node
|
| 10 |
+
|
| 11 |
+
port=$(( VNC_PORT - 5900 ))
|
| 12 |
+
[[ "$DISPLAY" == ":0" ]] && DISPLAY="web"
|
| 13 |
+
|
| 14 |
+
case "${DISPLAY,,}" in
|
| 15 |
+
"vnc" )
|
| 16 |
+
DISPLAY_OPTS="-display vnc=:$port -vga $VGA"
|
| 17 |
+
;;
|
| 18 |
+
"web" )
|
| 19 |
+
DISPLAY_OPTS="-display vnc=:$port,websocket=$WSS_PORT -vga $VGA"
|
| 20 |
+
;;
|
| 21 |
+
"disabled" )
|
| 22 |
+
DISPLAY_OPTS="-display none -vga $VGA"
|
| 23 |
+
;;
|
| 24 |
+
"none" )
|
| 25 |
+
DISPLAY_OPTS="-display none -vga none"
|
| 26 |
+
;;
|
| 27 |
+
*)
|
| 28 |
+
DISPLAY_OPTS="-display $DISPLAY -vga $VGA"
|
| 29 |
+
;;
|
| 30 |
+
esac
|
| 31 |
+
|
| 32 |
+
if [[ "$GPU" != [Yy1]* || "$ARCH" != "amd64" ]]; then
|
| 33 |
+
return 0
|
| 34 |
+
fi
|
| 35 |
+
|
| 36 |
+
CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}')
|
| 37 |
+
|
| 38 |
+
if [[ "$CPU_VENDOR" != "GenuineIntel" ]]; then
|
| 39 |
+
return 0
|
| 40 |
+
fi
|
| 41 |
+
|
| 42 |
+
msg="Configuring display drivers..."
|
| 43 |
+
html "$msg"
|
| 44 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
| 45 |
+
|
| 46 |
+
[[ "${VGA,,}" == "virtio" ]] && VGA="virtio-vga-gl"
|
| 47 |
+
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
|
| 48 |
+
DISPLAY_OPTS+=" -device $VGA"
|
| 49 |
+
|
| 50 |
+
[[ "${DISPLAY,,}" == "vnc" ]] && DISPLAY_OPTS+=" -vnc :$port"
|
| 51 |
+
[[ "${DISPLAY,,}" == "web" ]] && DISPLAY_OPTS+=" -vnc :$port,websocket=$WSS_PORT"
|
| 52 |
+
|
| 53 |
+
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
|
| 54 |
+
|
| 55 |
+
# Extract the card number from the render node
|
| 56 |
+
CARD_NUMBER=$(echo "$RENDERNODE" | grep -oP '(?<=renderD)\d+')
|
| 57 |
+
CARD_DEVICE="/dev/dri/card$((CARD_NUMBER - 128))"
|
| 58 |
+
|
| 59 |
+
if [ ! -c "$CARD_DEVICE" ]; then
|
| 60 |
+
if mknod "$CARD_DEVICE" c 226 $((CARD_NUMBER - 128)); then
|
| 61 |
+
chmod 666 "$CARD_DEVICE"
|
| 62 |
+
fi
|
| 63 |
+
fi
|
| 64 |
+
|
| 65 |
+
if [ ! -c "$RENDERNODE" ]; then
|
| 66 |
+
if mknod "$RENDERNODE" c 226 "$CARD_NUMBER"; then
|
| 67 |
+
chmod 666 "$RENDERNODE"
|
| 68 |
+
fi
|
| 69 |
+
fi
|
| 70 |
+
|
| 71 |
+
addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
|
| 72 |
+
addPackage "qemu-system-modules-opengl" "OpenGL module"
|
src/entry.sh
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
: "${APP:="QEMU"}"
|
| 5 |
+
: "${PLATFORM:="x64"}"
|
| 6 |
+
: "${SUPPORT:="https://github.com/qemus/qemu"}"
|
| 7 |
+
|
| 8 |
+
cd /run
|
| 9 |
+
|
| 10 |
+
. start.sh # Placeholder
|
| 11 |
+
. utils.sh # Load functions
|
| 12 |
+
. reset.sh # Initialize system
|
| 13 |
+
. define.sh # Define images
|
| 14 |
+
. install.sh # Download image
|
| 15 |
+
. disk.sh # Initialize disks
|
| 16 |
+
. display.sh # Initialize graphics
|
| 17 |
+
. network.sh # Initialize network
|
| 18 |
+
. boot.sh # Configure boot
|
| 19 |
+
. proc.sh # Initialize processor
|
| 20 |
+
. config.sh # Configure arguments
|
| 21 |
+
|
| 22 |
+
trap - ERR
|
| 23 |
+
|
| 24 |
+
version=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }')
|
| 25 |
+
info "Booting image${BOOT_DESC} using QEMU v$version..."
|
| 26 |
+
|
| 27 |
+
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
|
src/install.sh
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
getBase() {
|
| 5 |
+
|
| 6 |
+
local base="${1%%\?*}"
|
| 7 |
+
base=$(basename "$base")
|
| 8 |
+
base="${base//+/ }"
|
| 9 |
+
printf -v base '%b' "${base//%/\\x}"
|
| 10 |
+
base="${base//[!A-Za-z0-9._-]/_}"
|
| 11 |
+
|
| 12 |
+
echo "$base"
|
| 13 |
+
return 0
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
getFolder() {
|
| 17 |
+
|
| 18 |
+
local base=""
|
| 19 |
+
local result="$1"
|
| 20 |
+
|
| 21 |
+
if [[ "$result" != *"."* ]]; then
|
| 22 |
+
|
| 23 |
+
result="${result,,}"
|
| 24 |
+
|
| 25 |
+
else
|
| 26 |
+
|
| 27 |
+
base=$(getBase "$result")
|
| 28 |
+
result="${base%.*}"
|
| 29 |
+
|
| 30 |
+
case "${base,,}" in
|
| 31 |
+
|
| 32 |
+
*".gz" | *".gzip" | *".xz" | *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
|
| 33 |
+
|
| 34 |
+
[[ "$result" == *"."* ]] && result="${result%.*}" ;;
|
| 35 |
+
|
| 36 |
+
esac
|
| 37 |
+
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
[ -z "$result" ] && result="unknown"
|
| 41 |
+
echo "$result"
|
| 42 |
+
|
| 43 |
+
return 0
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
moveFile() {
|
| 47 |
+
|
| 48 |
+
local file="$1"
|
| 49 |
+
local ext="${file##*.}"
|
| 50 |
+
local dest="$STORAGE/boot.$ext"
|
| 51 |
+
|
| 52 |
+
if [[ "$file" == "$dest" ]]; then
|
| 53 |
+
BOOT="$file"
|
| 54 |
+
return 0
|
| 55 |
+
fi
|
| 56 |
+
|
| 57 |
+
if [[ "${file,,}" == "/boot.${ext,,}" || "${file,,}" == "/custom.${ext,,}" ]]; then
|
| 58 |
+
BOOT="$file"
|
| 59 |
+
return 0
|
| 60 |
+
fi
|
| 61 |
+
|
| 62 |
+
if ! mv -f "$file" "$dest"; then
|
| 63 |
+
error "Failed to move $file to $dest !"
|
| 64 |
+
return 1
|
| 65 |
+
fi
|
| 66 |
+
|
| 67 |
+
BOOT="$dest"
|
| 68 |
+
return 0
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
detectType() {
|
| 72 |
+
|
| 73 |
+
local file="$1"
|
| 74 |
+
local result=""
|
| 75 |
+
|
| 76 |
+
[ ! -f "$file" ] && return 1
|
| 77 |
+
[ ! -s "$file" ] && return 1
|
| 78 |
+
|
| 79 |
+
case "${file,,}" in
|
| 80 |
+
*".iso" | *".img" | *".raw" | *".qcow2" ) ;;
|
| 81 |
+
* ) return 1 ;;
|
| 82 |
+
esac
|
| 83 |
+
|
| 84 |
+
if [ -n "$BOOT_MODE" ] || [[ "${file,,}" == *".qcow2" ]]; then
|
| 85 |
+
moveFile "$file" && return 0
|
| 86 |
+
return 1
|
| 87 |
+
fi
|
| 88 |
+
|
| 89 |
+
if [[ "${file,,}" == *".iso" ]]; then
|
| 90 |
+
|
| 91 |
+
result=$(head -c 512 "$file" | tail -c 2 | xxd -p)
|
| 92 |
+
|
| 93 |
+
if [[ "$result" != "0000" ]]; then
|
| 94 |
+
[ -z "${HYBRID:-}" ] && HYBRID="Y"
|
| 95 |
+
fi
|
| 96 |
+
|
| 97 |
+
if [[ "${HYBRID:-}" != [Yy]* ]]; then
|
| 98 |
+
|
| 99 |
+
result=$(isoinfo -f -i "$file" 2>/dev/null)
|
| 100 |
+
|
| 101 |
+
if [ -z "$result" ]; then
|
| 102 |
+
error "Failed to read ISO file, invalid format!"
|
| 103 |
+
return 1
|
| 104 |
+
fi
|
| 105 |
+
|
| 106 |
+
result=$(echo "${result^^}" | grep "^/EFI")
|
| 107 |
+
[ -z "$result" ] && BOOT_MODE="legacy"
|
| 108 |
+
|
| 109 |
+
moveFile "$file" && return 0
|
| 110 |
+
return 1
|
| 111 |
+
|
| 112 |
+
fi
|
| 113 |
+
fi
|
| 114 |
+
|
| 115 |
+
result=$(fdisk -l "$file" 2>/dev/null)
|
| 116 |
+
[[ "${result^^}" != *"EFI "* ]] && BOOT_MODE="legacy"
|
| 117 |
+
|
| 118 |
+
moveFile "$file" && return 0
|
| 119 |
+
return 1
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
delay() {
|
| 123 |
+
|
| 124 |
+
local i
|
| 125 |
+
local delay="$1"
|
| 126 |
+
local msg="Retrying failed download in X seconds..."
|
| 127 |
+
|
| 128 |
+
info "${msg/X/$delay}"
|
| 129 |
+
|
| 130 |
+
for i in $(seq "$delay" -1 1); do
|
| 131 |
+
html "${msg/X/$i}"
|
| 132 |
+
sleep 1
|
| 133 |
+
done
|
| 134 |
+
|
| 135 |
+
return 0
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
downloadFile() {
|
| 139 |
+
|
| 140 |
+
local url="$1"
|
| 141 |
+
local base="$2"
|
| 142 |
+
local name="$3"
|
| 143 |
+
local msg rc total size progress
|
| 144 |
+
|
| 145 |
+
local dest="$STORAGE/$base"
|
| 146 |
+
|
| 147 |
+
# Check if running with interactive TTY or redirected to docker log
|
| 148 |
+
if [ -t 1 ]; then
|
| 149 |
+
progress="--progress=bar:noscroll"
|
| 150 |
+
else
|
| 151 |
+
progress="--progress=dot:giga"
|
| 152 |
+
fi
|
| 153 |
+
|
| 154 |
+
if [ -z "$name" ]; then
|
| 155 |
+
msg="Downloading image"
|
| 156 |
+
info "Downloading $base..."
|
| 157 |
+
else
|
| 158 |
+
msg="Downloading $name"
|
| 159 |
+
info "Downloading $name..."
|
| 160 |
+
fi
|
| 161 |
+
|
| 162 |
+
html "$msg..."
|
| 163 |
+
|
| 164 |
+
/run/progress.sh "$dest" "0" "$msg ([P])..." &
|
| 165 |
+
|
| 166 |
+
{ wget "$url" -O "$dest" --continue -q --timeout=30 --no-http-keep-alive --show-progress "$progress"; rc=$?; } || :
|
| 167 |
+
|
| 168 |
+
fKill "progress.sh"
|
| 169 |
+
|
| 170 |
+
if (( rc == 0 )) && [ -f "$dest" ]; then
|
| 171 |
+
total=$(stat -c%s "$dest")
|
| 172 |
+
size=$(formatBytes "$total")
|
| 173 |
+
if [ "$total" -lt 100000 ]; then
|
| 174 |
+
error "Invalid image file: is only $size ?" && return 1
|
| 175 |
+
fi
|
| 176 |
+
html "Download finished successfully..."
|
| 177 |
+
return 0
|
| 178 |
+
fi
|
| 179 |
+
|
| 180 |
+
msg="Failed to download $url"
|
| 181 |
+
(( rc == 3 )) && error "$msg , cannot write file (disk full?)" && return 1
|
| 182 |
+
(( rc == 4 )) && error "$msg , network failure!" && return 1
|
| 183 |
+
(( rc == 8 )) && error "$msg , server issued an error response!" && return 1
|
| 184 |
+
|
| 185 |
+
error "$msg , reason: $rc"
|
| 186 |
+
return 1
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
convertImage() {
|
| 190 |
+
|
| 191 |
+
local source_file=$1
|
| 192 |
+
local source_fmt=$2
|
| 193 |
+
local dst_file=$3
|
| 194 |
+
local dst_fmt=$4
|
| 195 |
+
local dir base fs fa space space_gb
|
| 196 |
+
local cur_size cur_gb src_size disk_param
|
| 197 |
+
|
| 198 |
+
[ -f "$dst_file" ] && error "Conversion failed, destination file $dst_file already exists?" && return 1
|
| 199 |
+
[ ! -f "$source_file" ] && error "Conversion failed, source file $source_file does not exists?" && return 1
|
| 200 |
+
|
| 201 |
+
if [[ "${source_fmt,,}" == "${dst_fmt,,}" ]]; then
|
| 202 |
+
mv -f "$source_file" "$dst_file"
|
| 203 |
+
return 0
|
| 204 |
+
fi
|
| 205 |
+
|
| 206 |
+
local tmp_file="$dst_file.tmp"
|
| 207 |
+
dir=$(dirname "$tmp_file")
|
| 208 |
+
|
| 209 |
+
rm -f "$tmp_file"
|
| 210 |
+
|
| 211 |
+
if [ -n "$ALLOCATE" ] && [[ "$ALLOCATE" != [Nn]* ]]; then
|
| 212 |
+
|
| 213 |
+
# Check free diskspace
|
| 214 |
+
src_size=$(qemu-img info "$source_file" -f "$source_fmt" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/')
|
| 215 |
+
space=$(df --output=avail -B 1 "$dir" | tail -n 1)
|
| 216 |
+
|
| 217 |
+
if (( src_size > space )); then
|
| 218 |
+
space_gb=$(formatBytes "$space")
|
| 219 |
+
error "Not enough free space to convert image in $dir, it has only $space_gb available..." && return 1
|
| 220 |
+
fi
|
| 221 |
+
fi
|
| 222 |
+
|
| 223 |
+
base=$(basename "$source_file")
|
| 224 |
+
info "Converting $base..."
|
| 225 |
+
html "Converting image..."
|
| 226 |
+
|
| 227 |
+
local conv_flags="-p"
|
| 228 |
+
|
| 229 |
+
if [ -z "$ALLOCATE" ] || [[ "$ALLOCATE" == [Nn]* ]]; then
|
| 230 |
+
disk_param="preallocation=off"
|
| 231 |
+
else
|
| 232 |
+
disk_param="preallocation=falloc"
|
| 233 |
+
fi
|
| 234 |
+
|
| 235 |
+
fs=$(stat -f -c %T "$dir")
|
| 236 |
+
[[ "${fs,,}" == "btrfs" ]] && disk_param+=",nocow=on"
|
| 237 |
+
|
| 238 |
+
if [[ "$dst_fmt" != "raw" ]]; then
|
| 239 |
+
if [ -z "$ALLOCATE" ] || [[ "$ALLOCATE" == [Nn]* ]]; then
|
| 240 |
+
conv_flags+=" -c"
|
| 241 |
+
fi
|
| 242 |
+
[ -n "${DISK_FLAGS:-}" ] && disk_param+=",$DISK_FLAGS"
|
| 243 |
+
fi
|
| 244 |
+
|
| 245 |
+
# shellcheck disable=SC2086
|
| 246 |
+
if ! qemu-img convert -f "$source_fmt" $conv_flags -o "$disk_param" -O "$dst_fmt" -- "$source_file" "$tmp_file"; then
|
| 247 |
+
rm -f "$tmp_file"
|
| 248 |
+
error "Failed to convert image in $dir, is there enough space available?" && return 1
|
| 249 |
+
fi
|
| 250 |
+
|
| 251 |
+
if [[ "$dst_fmt" == "raw" ]]; then
|
| 252 |
+
if [ -n "$ALLOCATE" ] && [[ "$ALLOCATE" != [Nn]* ]]; then
|
| 253 |
+
# Work around qemu-img bug
|
| 254 |
+
cur_size=$(stat -c%s "$tmp_file")
|
| 255 |
+
cur_gb=$(formatBytes "$cur_size")
|
| 256 |
+
if ! fallocate -l "$cur_size" "$tmp_file" &>/dev/null; then
|
| 257 |
+
if ! fallocate -l -x "$cur_size" "$tmp_file"; then
|
| 258 |
+
error "Failed to allocate $cur_gb for image!"
|
| 259 |
+
fi
|
| 260 |
+
fi
|
| 261 |
+
fi
|
| 262 |
+
fi
|
| 263 |
+
|
| 264 |
+
rm -f "$source_file"
|
| 265 |
+
mv "$tmp_file" "$dst_file"
|
| 266 |
+
|
| 267 |
+
if [[ "${fs,,}" == "btrfs" ]]; then
|
| 268 |
+
fa=$(lsattr "$dst_file")
|
| 269 |
+
if [[ "$fa" != *"C"* ]]; then
|
| 270 |
+
error "Failed to disable COW for image on ${fs^^} filesystem!"
|
| 271 |
+
fi
|
| 272 |
+
fi
|
| 273 |
+
|
| 274 |
+
html "Conversion completed..."
|
| 275 |
+
return 0
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
findFile() {
|
| 279 |
+
|
| 280 |
+
local dir file
|
| 281 |
+
local base="$1"
|
| 282 |
+
local ext="$2"
|
| 283 |
+
local fname="${base}.${ext}"
|
| 284 |
+
|
| 285 |
+
dir=$(find / -maxdepth 1 -type d -iname "$fname" -print -quit)
|
| 286 |
+
[ ! -d "$dir" ] && dir=$(find "$STORAGE" -maxdepth 1 -type d -iname "$fname" -print -quit)
|
| 287 |
+
|
| 288 |
+
if [ -d "$dir" ]; then
|
| 289 |
+
if hasDisk; then
|
| 290 |
+
BOOT="none"
|
| 291 |
+
return 0
|
| 292 |
+
fi
|
| 293 |
+
error "The bind $dir maps to a file that does not exist!" && exit 37
|
| 294 |
+
fi
|
| 295 |
+
|
| 296 |
+
file=$(find / -maxdepth 1 -type f -iname "$fname" -print -quit)
|
| 297 |
+
[ ! -s "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname "$fname" -print -quit)
|
| 298 |
+
|
| 299 |
+
detectType "$file" && return 0
|
| 300 |
+
|
| 301 |
+
return 1
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
findFile "boot" "img" && return 0
|
| 305 |
+
findFile "boot" "raw" && return 0
|
| 306 |
+
findFile "boot" "iso" && return 0
|
| 307 |
+
findFile "boot" "qcow2" && return 0
|
| 308 |
+
findFile "custom" "iso" && return 0
|
| 309 |
+
|
| 310 |
+
if hasDisk; then
|
| 311 |
+
BOOT="none"
|
| 312 |
+
return 0
|
| 313 |
+
fi
|
| 314 |
+
|
| 315 |
+
if [[ "${BOOT}" == \"*\" || "${BOOT}" == \'*\' ]]; then
|
| 316 |
+
VERSION="${BOOT:1:-1}"
|
| 317 |
+
fi
|
| 318 |
+
|
| 319 |
+
BOOT=$(expr "$BOOT" : "^\ *\(.*[^ ]\)\ *$")
|
| 320 |
+
|
| 321 |
+
if [ -z "$BOOT" ] || [[ "$BOOT" == *"example.com/"* ]]; then
|
| 322 |
+
|
| 323 |
+
BOOT="alpine"
|
| 324 |
+
warn "no value specified for the BOOT variable, defaulting to \"${BOOT}\"."
|
| 325 |
+
|
| 326 |
+
fi
|
| 327 |
+
|
| 328 |
+
folder=$(getFolder "$BOOT")
|
| 329 |
+
STORAGE="$STORAGE/$folder"
|
| 330 |
+
|
| 331 |
+
if [ -d "$STORAGE" ]; then
|
| 332 |
+
|
| 333 |
+
findFile "boot" "img" && return 0
|
| 334 |
+
findFile "boot" "raw" && return 0
|
| 335 |
+
findFile "boot" "iso" && return 0
|
| 336 |
+
findFile "boot" "qcow2" && return 0
|
| 337 |
+
findFile "custom" "iso" && return 0
|
| 338 |
+
|
| 339 |
+
if hasDisk; then
|
| 340 |
+
BOOT="none"
|
| 341 |
+
return 0
|
| 342 |
+
fi
|
| 343 |
+
|
| 344 |
+
fi
|
| 345 |
+
|
| 346 |
+
name=$(getURL "$BOOT" "name") || exit 34
|
| 347 |
+
|
| 348 |
+
if [ -n "$name" ]; then
|
| 349 |
+
|
| 350 |
+
msg="Retrieving latest $name version..."
|
| 351 |
+
info "$msg" && html "$msg..."
|
| 352 |
+
|
| 353 |
+
url=$(getURL "$BOOT" "url") || exit 34
|
| 354 |
+
|
| 355 |
+
[ -n "$url" ] && BOOT="$url"
|
| 356 |
+
|
| 357 |
+
fi
|
| 358 |
+
|
| 359 |
+
if [[ "$BOOT" != *"."* ]]; then
|
| 360 |
+
if [ -z "$BOOT" ]; then
|
| 361 |
+
error "No BOOT value specified!"
|
| 362 |
+
else
|
| 363 |
+
error "Invalid BOOT value specified, option \"$BOOT\" is not recognized!"
|
| 364 |
+
fi
|
| 365 |
+
exit 64
|
| 366 |
+
fi
|
| 367 |
+
|
| 368 |
+
if [[ "${BOOT,,}" != "http"* ]]; then
|
| 369 |
+
error "Invalid BOOT value specified, \"$BOOT\" is not a valid URL!" && exit 64
|
| 370 |
+
fi
|
| 371 |
+
|
| 372 |
+
mkdir -p "$STORAGE"
|
| 373 |
+
|
| 374 |
+
find "$STORAGE" -maxdepth 1 -type f \( -iname '*.rom' -or -iname '*.vars' \) -delete
|
| 375 |
+
find "$STORAGE" -maxdepth 1 -type f \( -iname 'data.*' -or -iname 'qemu.*' \) -delete
|
| 376 |
+
|
| 377 |
+
base=$(getBase "$BOOT")
|
| 378 |
+
|
| 379 |
+
rm -f "$STORAGE/$base"
|
| 380 |
+
|
| 381 |
+
if ! downloadFile "$BOOT" "$base" "$name"; then
|
| 382 |
+
delay 5
|
| 383 |
+
if ! downloadFile "$BOOT" "$base" "$name"; then
|
| 384 |
+
delay 10
|
| 385 |
+
if ! downloadFile "$BOOT" "$base" "$name"; then
|
| 386 |
+
rm -f "$STORAGE/$base" && exit 60
|
| 387 |
+
fi
|
| 388 |
+
fi
|
| 389 |
+
fi
|
| 390 |
+
|
| 391 |
+
case "${base,,}" in
|
| 392 |
+
*".gz" | *".gzip" | *".xz" | *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
|
| 393 |
+
info "Extracting $base..."
|
| 394 |
+
html "Extracting image..." ;;
|
| 395 |
+
esac
|
| 396 |
+
|
| 397 |
+
case "${base,,}" in
|
| 398 |
+
*".gz" | *".gzip" )
|
| 399 |
+
|
| 400 |
+
gzip -dc "$STORAGE/$base" > "$STORAGE/${base%.*}"
|
| 401 |
+
rm -f "$STORAGE/$base"
|
| 402 |
+
base="${base%.*}"
|
| 403 |
+
|
| 404 |
+
;;
|
| 405 |
+
*".xz" )
|
| 406 |
+
|
| 407 |
+
xz -dc "$STORAGE/$base" > "$STORAGE/${base%.*}"
|
| 408 |
+
rm -f "$STORAGE/$base"
|
| 409 |
+
base="${base%.*}"
|
| 410 |
+
|
| 411 |
+
;;
|
| 412 |
+
*".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
|
| 413 |
+
|
| 414 |
+
tmp="$STORAGE/extract"
|
| 415 |
+
rm -rf "$tmp"
|
| 416 |
+
mkdir -p "$tmp"
|
| 417 |
+
7z x "$STORAGE/$base" -o"$tmp" > /dev/null
|
| 418 |
+
|
| 419 |
+
rm -f "$STORAGE/$base"
|
| 420 |
+
base="${base%.*}"
|
| 421 |
+
|
| 422 |
+
if [ ! -s "$tmp/$base" ]; then
|
| 423 |
+
rm -rf "$tmp"
|
| 424 |
+
error "Cannot find file \"${base}\" in .${BOOT/*./} archive!" && exit 32
|
| 425 |
+
fi
|
| 426 |
+
|
| 427 |
+
mv "$tmp/$base" "$STORAGE/$base"
|
| 428 |
+
rm -rf "$tmp"
|
| 429 |
+
|
| 430 |
+
;;
|
| 431 |
+
esac
|
| 432 |
+
|
| 433 |
+
case "${base,,}" in
|
| 434 |
+
*".iso" | *".img" | *".raw" | *".qcow2" )
|
| 435 |
+
detectType "$STORAGE/$base" && return 0
|
| 436 |
+
error "Cannot read file \"${base}\"" && exit 63 ;;
|
| 437 |
+
esac
|
| 438 |
+
|
| 439 |
+
target_ext="img"
|
| 440 |
+
target_fmt="${DISK_FMT:-}"
|
| 441 |
+
[ -z "$target_fmt" ] && target_fmt="raw"
|
| 442 |
+
[[ "$target_fmt" != "raw" ]] && target_ext="qcow2"
|
| 443 |
+
|
| 444 |
+
case "${base,,}" in
|
| 445 |
+
*".vdi" ) source_fmt="vdi" ;;
|
| 446 |
+
*".vhd" ) source_fmt="vpc" ;;
|
| 447 |
+
*".vhdx" ) source_fmt="vpc" ;;
|
| 448 |
+
*".vmdk" ) source_fmt="vmdk" ;;
|
| 449 |
+
* ) error "Unknown file extension, type \".${base/*./}\" is not recognized!" && exit 33 ;;
|
| 450 |
+
esac
|
| 451 |
+
|
| 452 |
+
dst="$STORAGE/${base%.*}.$target_ext"
|
| 453 |
+
|
| 454 |
+
! convertImage "$STORAGE/$base" "$source_fmt" "$dst" "$target_fmt" && exit 35
|
| 455 |
+
|
| 456 |
+
base=$(basename "$dst")
|
| 457 |
+
detectType "$STORAGE/$base" && return 0
|
| 458 |
+
error "Cannot convert file \"${base}\"" && exit 36
|
src/network.sh
ADDED
|
@@ -0,0 +1,828 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Docker environment variables
|
| 5 |
+
|
| 6 |
+
: "${MAC:=""}"
|
| 7 |
+
: "${MTU:=""}"
|
| 8 |
+
: "${DHCP:="N"}"
|
| 9 |
+
: "${NETWORK:="Y"}"
|
| 10 |
+
: "${HOST_PORTS:=""}"
|
| 11 |
+
: "${USER_PORTS:=""}"
|
| 12 |
+
: "${ADAPTER:="virtio-net-pci"}"
|
| 13 |
+
|
| 14 |
+
: "${VM_NET_IP:=""}"
|
| 15 |
+
: "${VM_NET_DEV:=""}"
|
| 16 |
+
: "${VM_NET_TAP:="qemu"}"
|
| 17 |
+
: "${VM_NET_MAC:="$MAC"}"
|
| 18 |
+
: "${VM_NET_HOST:="$APP"}"
|
| 19 |
+
: "${VM_NET_BRIDGE:="docker"}"
|
| 20 |
+
: "${VM_NET_MASK:="255.255.255.0"}"
|
| 21 |
+
|
| 22 |
+
: "${PASST:="passt"}"
|
| 23 |
+
: "${PASST_OPTS:=""}"
|
| 24 |
+
: "${PASST_DEBUG:=""}"
|
| 25 |
+
|
| 26 |
+
: "${DNSMASQ_OPTS:=""}"
|
| 27 |
+
: "${DNSMASQ_DEBUG:=""}"
|
| 28 |
+
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
|
| 29 |
+
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
|
| 30 |
+
|
| 31 |
+
ADD_ERR="Please add the following setting to your container:"
|
| 32 |
+
|
| 33 |
+
# ######################################
|
| 34 |
+
# Functions
|
| 35 |
+
# ######################################
|
| 36 |
+
|
| 37 |
+
configureDHCP() {
|
| 38 |
+
|
| 39 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring MACVTAP networking..."
|
| 40 |
+
|
| 41 |
+
# Create the necessary file structure for /dev/vhost-net
|
| 42 |
+
if [ ! -c /dev/vhost-net ]; then
|
| 43 |
+
if mknod /dev/vhost-net c 10 238; then
|
| 44 |
+
chmod 660 /dev/vhost-net
|
| 45 |
+
fi
|
| 46 |
+
fi
|
| 47 |
+
|
| 48 |
+
# Create a macvtap network for the VM guest
|
| 49 |
+
{ msg=$(ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge 2>&1); rc=$?; } || :
|
| 50 |
+
|
| 51 |
+
case "$msg" in
|
| 52 |
+
"RTNETLINK answers: File exists"* )
|
| 53 |
+
while ! ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge; do
|
| 54 |
+
info "Waiting for macvtap interface to become available.."
|
| 55 |
+
sleep 5
|
| 56 |
+
done ;;
|
| 57 |
+
"RTNETLINK answers: Invalid argument"* )
|
| 58 |
+
error "Cannot create macvtap interface. Please make sure that the network type of the container is 'macvlan' and not 'ipvlan'."
|
| 59 |
+
return 1 ;;
|
| 60 |
+
"RTNETLINK answers: Operation not permitted"* )
|
| 61 |
+
error "No permission to create macvtap interface. Please make sure that your host kernel supports it and that the NET_ADMIN capability is set."
|
| 62 |
+
return 1 ;;
|
| 63 |
+
*)
|
| 64 |
+
[ -n "$msg" ] && echo "$msg" >&2
|
| 65 |
+
if (( rc != 0 )); then
|
| 66 |
+
error "Cannot create macvtap interface."
|
| 67 |
+
return 1
|
| 68 |
+
fi ;;
|
| 69 |
+
esac
|
| 70 |
+
|
| 71 |
+
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
| 72 |
+
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
| 73 |
+
warn "Failed to set MTU size to $MTU."
|
| 74 |
+
fi
|
| 75 |
+
fi
|
| 76 |
+
|
| 77 |
+
while ! ip link set "$VM_NET_TAP" up; do
|
| 78 |
+
info "Waiting for MAC address $VM_NET_MAC to become available..."
|
| 79 |
+
info "If you cloned this machine, please delete the '$PROCESS.mac' file to generate a different MAC address."
|
| 80 |
+
sleep 2
|
| 81 |
+
done
|
| 82 |
+
|
| 83 |
+
local TAP_NR TAP_PATH MAJOR MINOR
|
| 84 |
+
TAP_NR=$(</sys/class/net/"$VM_NET_TAP"/ifindex)
|
| 85 |
+
TAP_PATH="/dev/tap${TAP_NR}"
|
| 86 |
+
|
| 87 |
+
# Create dev file (there is no udev in container: need to be done manually)
|
| 88 |
+
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev)
|
| 89 |
+
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && return 1
|
| 90 |
+
|
| 91 |
+
[[ ! -e "$TAP_PATH" && -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
|
| 92 |
+
|
| 93 |
+
if [[ ! -e "$TAP_PATH" ]]; then
|
| 94 |
+
{ mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
|
| 95 |
+
(( rc != 0 )) && error "Cannot mknod: $TAP_PATH ($rc)" && return 1
|
| 96 |
+
fi
|
| 97 |
+
|
| 98 |
+
{ exec 30>>"$TAP_PATH"; rc=$?; } 2>/dev/null || :
|
| 99 |
+
|
| 100 |
+
if (( rc != 0 )); then
|
| 101 |
+
error "Cannot create TAP interface ($rc). $ADD_ERR --device-cgroup-rule='c *:* rwm'" && return 1
|
| 102 |
+
fi
|
| 103 |
+
|
| 104 |
+
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
|
| 105 |
+
|
| 106 |
+
if (( rc != 0 )); then
|
| 107 |
+
error "VHOST can not be found ($rc). $ADD_ERR --device=/dev/vhost-net" && return 1
|
| 108 |
+
fi
|
| 109 |
+
|
| 110 |
+
NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30"
|
| 111 |
+
|
| 112 |
+
return 0
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
configureDNS() {
|
| 116 |
+
|
| 117 |
+
local if="$1"
|
| 118 |
+
local ip="$2"
|
| 119 |
+
local mac="$3"
|
| 120 |
+
local host="$4"
|
| 121 |
+
local mask="$5"
|
| 122 |
+
local gateway="$6"
|
| 123 |
+
|
| 124 |
+
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
|
| 125 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
|
| 126 |
+
|
| 127 |
+
local log="/var/log/dnsmasq.log"
|
| 128 |
+
rm -f "$log"
|
| 129 |
+
|
| 130 |
+
case "${NETWORK,,}" in
|
| 131 |
+
"nat" | "tap" | "tun" | "tuntap" | "y" )
|
| 132 |
+
|
| 133 |
+
# Create lease file for faster resolve
|
| 134 |
+
echo "0 $mac $ip $host 01:$mac" > /var/lib/misc/dnsmasq.leases
|
| 135 |
+
chmod 644 /var/lib/misc/dnsmasq.leases
|
| 136 |
+
|
| 137 |
+
# dnsmasq configuration:
|
| 138 |
+
DNSMASQ_OPTS+=" --dhcp-authoritative"
|
| 139 |
+
|
| 140 |
+
# Set DHCP range and host
|
| 141 |
+
DNSMASQ_OPTS+=" --dhcp-range=$ip,$ip"
|
| 142 |
+
DNSMASQ_OPTS+=" --dhcp-host=$mac,,$ip,$host,infinite"
|
| 143 |
+
|
| 144 |
+
# Set DNS server and gateway
|
| 145 |
+
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,$mask"
|
| 146 |
+
DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway"
|
| 147 |
+
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway"
|
| 148 |
+
|
| 149 |
+
esac
|
| 150 |
+
|
| 151 |
+
# Set interfaces
|
| 152 |
+
DNSMASQ_OPTS+=" --interface=$if"
|
| 153 |
+
DNSMASQ_OPTS+=" --bind-interfaces"
|
| 154 |
+
|
| 155 |
+
# Add DNS entry for container
|
| 156 |
+
DNSMASQ_OPTS+=" --address=/host.lan/$gateway"
|
| 157 |
+
|
| 158 |
+
# Set local dns resolver to dnsmasq when needed
|
| 159 |
+
[ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"
|
| 160 |
+
|
| 161 |
+
# Enable logging to file
|
| 162 |
+
DNSMASQ_OPTS+=" --log-facility=$log"
|
| 163 |
+
|
| 164 |
+
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
| 165 |
+
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}"
|
| 166 |
+
|
| 167 |
+
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
|
| 168 |
+
|
| 169 |
+
local msg="Failed to start Dnsmasq, reason: $?"
|
| 170 |
+
[ -f "$log" ] && cat "$log"
|
| 171 |
+
error "$msg"
|
| 172 |
+
|
| 173 |
+
return 1
|
| 174 |
+
fi
|
| 175 |
+
|
| 176 |
+
if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then
|
| 177 |
+
tail -fn +0 "$log" &
|
| 178 |
+
fi
|
| 179 |
+
|
| 180 |
+
return 0
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
getUserPorts() {
|
| 184 |
+
|
| 185 |
+
local args=""
|
| 186 |
+
local list=$1
|
| 187 |
+
local ssh="22"
|
| 188 |
+
|
| 189 |
+
[[ "${BOOT_MODE:-}" == "windows"* ]] && ssh="3389"
|
| 190 |
+
[ -z "$list" ] && list="$ssh" || list+=",$ssh"
|
| 191 |
+
|
| 192 |
+
list="${list//,/ }"
|
| 193 |
+
list="${list## }"
|
| 194 |
+
list="${list%% }"
|
| 195 |
+
|
| 196 |
+
for port in $list; do
|
| 197 |
+
proto="tcp"
|
| 198 |
+
num="$port"
|
| 199 |
+
|
| 200 |
+
if [[ "$port" == */udp ]]; then
|
| 201 |
+
proto="udp"
|
| 202 |
+
num="${port%/udp}"
|
| 203 |
+
elif [[ "$port" == */tcp ]]; then
|
| 204 |
+
proto="tcp"
|
| 205 |
+
num="${port%/tcp}"
|
| 206 |
+
fi
|
| 207 |
+
|
| 208 |
+
args+="hostfwd=$proto::$num-$VM_NET_IP:$num,"
|
| 209 |
+
done
|
| 210 |
+
|
| 211 |
+
echo "${args%?}"
|
| 212 |
+
return 0
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
getHostPorts() {
|
| 216 |
+
|
| 217 |
+
local list="$1"
|
| 218 |
+
list=$(echo "${list// /}" | sed 's/,*$//g')
|
| 219 |
+
|
| 220 |
+
if [[ "${DISPLAY,,}" == "web" ]]; then
|
| 221 |
+
[ -z "$list" ] && list="$WSS_PORT" || list+=",$WSS_PORT"
|
| 222 |
+
fi
|
| 223 |
+
|
| 224 |
+
if [[ "${DISPLAY,,}" == "vnc" || "${DISPLAY,,}" == "web" ]]; then
|
| 225 |
+
[ -z "$list" ] && list="$VNC_PORT" || list+=",$VNC_PORT"
|
| 226 |
+
fi
|
| 227 |
+
|
| 228 |
+
[ -z "$list" ] && list="$MON_PORT" || list+=",$MON_PORT"
|
| 229 |
+
|
| 230 |
+
if [[ "${WEB:-}" != [Nn]* ]]; then
|
| 231 |
+
[ -z "$list" ] && list="$WEB_PORT" || list+=",$WEB_PORT"
|
| 232 |
+
fi
|
| 233 |
+
|
| 234 |
+
if [[ "${NETWORK,,}" == "passt" ]]; then
|
| 235 |
+
|
| 236 |
+
local DNS_PORT="53"
|
| 237 |
+
local SAMBA_PORT="445"
|
| 238 |
+
|
| 239 |
+
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
|
| 240 |
+
[ -z "$list" ] && list="$DNS_PORT" || list+=",$DNS_PORT"
|
| 241 |
+
fi
|
| 242 |
+
|
| 243 |
+
if [[ "${BOOT_MODE:-}" == "windows"* ]]; then
|
| 244 |
+
if [[ "${SAMBA:-}" != [Nn]* ]]; then
|
| 245 |
+
[ -z "$list" ] && list="$SAMBA_PORT" || list+=",$SAMBA_PORT"
|
| 246 |
+
fi
|
| 247 |
+
fi
|
| 248 |
+
|
| 249 |
+
fi
|
| 250 |
+
|
| 251 |
+
echo "$list"
|
| 252 |
+
return 0
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
compat() {
|
| 256 |
+
|
| 257 |
+
local gateway="$1"
|
| 258 |
+
local interface="$2"
|
| 259 |
+
local samba="20.20.20.1"
|
| 260 |
+
|
| 261 |
+
[[ "$samba" == "$gateway" ]] && return 0
|
| 262 |
+
[[ "${BOOT_MODE:-}" != "windows"* ]] && return 0
|
| 263 |
+
|
| 264 |
+
if [[ "$interface" != "${interface:0:8}" ]]; then
|
| 265 |
+
error "Bridge name too long!" && return 1
|
| 266 |
+
fi
|
| 267 |
+
|
| 268 |
+
# Backwards compatibility with old installations
|
| 269 |
+
if ip address add dev "$interface" "$samba/24" label "$interface:compat"; then
|
| 270 |
+
SAMBA_INTERFACE="$samba"
|
| 271 |
+
else
|
| 272 |
+
warn "failed to configure IP alias!"
|
| 273 |
+
fi
|
| 274 |
+
|
| 275 |
+
return 0
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
configureSlirp() {
|
| 279 |
+
|
| 280 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."
|
| 281 |
+
|
| 282 |
+
local ip="$IP"
|
| 283 |
+
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
|
| 284 |
+
local base="${ip%.*}."
|
| 285 |
+
[ "${ip/$base/}" -lt "4" ] && ip="${ip%.*}.4"
|
| 286 |
+
local gateway="${ip%.*}.1"
|
| 287 |
+
|
| 288 |
+
# Backwards compatibility
|
| 289 |
+
! compat "$gateway" "$VM_NET_DEV" && exit 24
|
| 290 |
+
|
| 291 |
+
local ipv6=""
|
| 292 |
+
[ -n "$IP6" ] && ipv6="ipv6=on,"
|
| 293 |
+
|
| 294 |
+
NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=$gateway,net=${gateway%.*}.0/24,dhcpstart=$ip,${ipv6}hostname=$VM_NET_HOST"
|
| 295 |
+
|
| 296 |
+
local forward
|
| 297 |
+
forward=$(getUserPorts "${USER_PORTS:-}")
|
| 298 |
+
[ -n "$forward" ] && NET_OPTS+=",$forward"
|
| 299 |
+
|
| 300 |
+
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
|
| 301 |
+
cp /etc/resolv.conf /etc/resolv.dnsmasq
|
| 302 |
+
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
|
| 303 |
+
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
|
| 304 |
+
fi
|
| 305 |
+
|
| 306 |
+
VM_NET_IP="$ip"
|
| 307 |
+
return 0
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
configurePasst() {
|
| 311 |
+
|
| 312 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
|
| 313 |
+
|
| 314 |
+
local log="/var/log/passt.log"
|
| 315 |
+
rm -f "$log"
|
| 316 |
+
|
| 317 |
+
local pid="/var/run/dnsmasq.pid"
|
| 318 |
+
[ -s "$pid" ] && pKill "$(<"$pid")"
|
| 319 |
+
|
| 320 |
+
local ip="$IP"
|
| 321 |
+
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
|
| 322 |
+
|
| 323 |
+
local gateway=""
|
| 324 |
+
if [[ "$ip" != *".1" ]]; then
|
| 325 |
+
gateway="${ip%.*}.1"
|
| 326 |
+
else
|
| 327 |
+
gateway="${ip%.*}.2"
|
| 328 |
+
fi
|
| 329 |
+
|
| 330 |
+
# Backwards compatibility
|
| 331 |
+
! compat "$gateway" "$VM_NET_DEV" && exit 24
|
| 332 |
+
|
| 333 |
+
# passt configuration:
|
| 334 |
+
[ -z "$IP6" ] && PASST_OPTS+=" -4"
|
| 335 |
+
|
| 336 |
+
PASST_OPTS+=" -a $ip"
|
| 337 |
+
PASST_OPTS+=" -g $gateway"
|
| 338 |
+
PASST_OPTS+=" -n $VM_NET_MASK"
|
| 339 |
+
|
| 340 |
+
exclude=$(getHostPorts "$HOST_PORTS")
|
| 341 |
+
|
| 342 |
+
if [ -z "$exclude" ]; then
|
| 343 |
+
exclude="all"
|
| 344 |
+
else
|
| 345 |
+
exclude="~${exclude//,/,~}"
|
| 346 |
+
fi
|
| 347 |
+
|
| 348 |
+
PASST_OPTS+=" -t $exclude"
|
| 349 |
+
PASST_OPTS+=" -u $exclude"
|
| 350 |
+
PASST_OPTS+=" -H $VM_NET_HOST"
|
| 351 |
+
PASST_OPTS+=" -M $GATEWAY_MAC"
|
| 352 |
+
PASST_OPTS+=" -P /var/run/passt.pid"
|
| 353 |
+
PASST_OPTS+=" -l $log"
|
| 354 |
+
PASST_OPTS+=" -q"
|
| 355 |
+
|
| 356 |
+
if [[ "${DNSMASQ_DISABLE:-}" != [Yy1]* ]]; then
|
| 357 |
+
cp /etc/resolv.conf /etc/resolv.dnsmasq
|
| 358 |
+
echo -e "nameserver 127.0.0.1\nsearch .\noptions ndots:0" >/etc/resolv.conf
|
| 359 |
+
fi
|
| 360 |
+
|
| 361 |
+
PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
| 362 |
+
[[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
|
| 363 |
+
|
| 364 |
+
if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then
|
| 365 |
+
local msg="Failed to start passt, reason: $?"
|
| 366 |
+
[ -f "$log" ] && cat "$log"
|
| 367 |
+
error "$msg"
|
| 368 |
+
return 1
|
| 369 |
+
fi
|
| 370 |
+
|
| 371 |
+
if [[ "$PASST_DEBUG" == [Yy1]* ]]; then
|
| 372 |
+
tail -fn +0 "$log" &
|
| 373 |
+
else
|
| 374 |
+
if [[ "$DEBUG" == [Yy1]* ]]; then
|
| 375 |
+
[ -f "$log" ] && cat "$log" && echo ""
|
| 376 |
+
fi
|
| 377 |
+
fi
|
| 378 |
+
|
| 379 |
+
NET_OPTS="-netdev stream,id=hostnet0,server=off,addr.type=unix,addr.path=/tmp/passt_1.socket"
|
| 380 |
+
|
| 381 |
+
configureDNS "lo" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
|
| 382 |
+
|
| 383 |
+
VM_NET_IP="$ip"
|
| 384 |
+
return 0
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
configureNAT() {
|
| 388 |
+
|
| 389 |
+
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
|
| 390 |
+
local tables="the 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
| 391 |
+
|
| 392 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring NAT networking..."
|
| 393 |
+
|
| 394 |
+
# Create the necessary file structure for /dev/net/tun
|
| 395 |
+
if [ ! -c /dev/net/tun ]; then
|
| 396 |
+
[[ "$PODMAN" == [Yy1]* ]] && return 1
|
| 397 |
+
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
|
| 398 |
+
if mknod /dev/net/tun c 10 200; then
|
| 399 |
+
chmod 666 /dev/net/tun
|
| 400 |
+
fi
|
| 401 |
+
fi
|
| 402 |
+
|
| 403 |
+
if [ ! -c /dev/net/tun ]; then
|
| 404 |
+
warn "$tuntap" && return 1
|
| 405 |
+
fi
|
| 406 |
+
|
| 407 |
+
# Check port forwarding flag
|
| 408 |
+
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
| 409 |
+
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
|
| 410 |
+
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
| 411 |
+
warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
|
| 412 |
+
return 1
|
| 413 |
+
fi
|
| 414 |
+
fi
|
| 415 |
+
|
| 416 |
+
local ip base
|
| 417 |
+
base=$(echo "$IP" | sed -r 's/([^.]*.){2}//')
|
| 418 |
+
if [[ "$IP" != "172.30."* ]]; then
|
| 419 |
+
ip="172.30.$base"
|
| 420 |
+
else
|
| 421 |
+
ip="172.31.$base"
|
| 422 |
+
fi
|
| 423 |
+
|
| 424 |
+
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
|
| 425 |
+
|
| 426 |
+
local gateway=""
|
| 427 |
+
if [[ "$ip" != *".1" ]]; then
|
| 428 |
+
gateway="${ip%.*}.1"
|
| 429 |
+
else
|
| 430 |
+
gateway="${ip%.*}.2"
|
| 431 |
+
fi
|
| 432 |
+
|
| 433 |
+
# Create a bridge with a static IP for the VM guest
|
| 434 |
+
{ ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
|
| 435 |
+
|
| 436 |
+
if (( rc != 0 )); then
|
| 437 |
+
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
|
| 438 |
+
fi
|
| 439 |
+
|
| 440 |
+
if ! ip address add "$gateway/24" broadcast "${ip%.*}.255" dev "$VM_NET_BRIDGE"; then
|
| 441 |
+
warn "failed to add IP address pool!" && return 1
|
| 442 |
+
fi
|
| 443 |
+
|
| 444 |
+
# Backwards compatibility
|
| 445 |
+
! compat "$gateway" "$VM_NET_BRIDGE" && exit 24
|
| 446 |
+
|
| 447 |
+
while ! ip link set "$VM_NET_BRIDGE" up; do
|
| 448 |
+
info "Waiting for IP address to become available..."
|
| 449 |
+
sleep 2
|
| 450 |
+
done
|
| 451 |
+
|
| 452 |
+
# QEMU Works with taps, set tap to the bridge created
|
| 453 |
+
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
|
| 454 |
+
warn "$tuntap" && return 1
|
| 455 |
+
fi
|
| 456 |
+
|
| 457 |
+
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
| 458 |
+
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
| 459 |
+
warn "failed to set MTU size to $MTU."
|
| 460 |
+
fi
|
| 461 |
+
fi
|
| 462 |
+
|
| 463 |
+
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
|
| 464 |
+
warn "failed to set gateway MAC address.."
|
| 465 |
+
fi
|
| 466 |
+
|
| 467 |
+
while ! ip link set "$VM_NET_TAP" up promisc on; do
|
| 468 |
+
info "Waiting for TAP to become available..."
|
| 469 |
+
sleep 2
|
| 470 |
+
done
|
| 471 |
+
|
| 472 |
+
if ! ip link set dev "$VM_NET_TAP" master "$VM_NET_BRIDGE"; then
|
| 473 |
+
warn "failed to set master bridge!" && return 1
|
| 474 |
+
fi
|
| 475 |
+
|
| 476 |
+
if grep -wq "nf_tables" /proc/modules; then
|
| 477 |
+
update-alternatives --set iptables /usr/sbin/iptables-nft > /dev/null
|
| 478 |
+
update-alternatives --set ip6tables /usr/sbin/ip6tables-nft > /dev/null
|
| 479 |
+
else
|
| 480 |
+
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
|
| 481 |
+
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
|
| 482 |
+
fi
|
| 483 |
+
|
| 484 |
+
exclude=$(getHostPorts "$HOST_PORTS")
|
| 485 |
+
|
| 486 |
+
if [ -n "$exclude" ]; then
|
| 487 |
+
if [[ "$exclude" != *","* ]]; then
|
| 488 |
+
exclude=" ! --dport $exclude"
|
| 489 |
+
else
|
| 490 |
+
exclude=" -m multiport ! --dports $exclude"
|
| 491 |
+
fi
|
| 492 |
+
fi
|
| 493 |
+
|
| 494 |
+
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
|
| 495 |
+
warn "$tables" && return 1
|
| 496 |
+
fi
|
| 497 |
+
|
| 498 |
+
# shellcheck disable=SC2086
|
| 499 |
+
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$ip"; then
|
| 500 |
+
warn "failed to configure IP tables!" && return 1
|
| 501 |
+
fi
|
| 502 |
+
|
| 503 |
+
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$ip"; then
|
| 504 |
+
warn "failed to configure IP tables!" && return 1
|
| 505 |
+
fi
|
| 506 |
+
|
| 507 |
+
if (( KERNEL > 4 )); then
|
| 508 |
+
# Hack for guest VMs complaining about "bad udp checksums in 5 packets"
|
| 509 |
+
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
|
| 510 |
+
fi
|
| 511 |
+
|
| 512 |
+
NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP"
|
| 513 |
+
|
| 514 |
+
if [ -c /dev/vhost-net ]; then
|
| 515 |
+
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
|
| 516 |
+
(( rc == 0 )) && NET_OPTS+=",vhost=on,vhostfd=40"
|
| 517 |
+
fi
|
| 518 |
+
|
| 519 |
+
NET_OPTS+=",script=no,downscript=no"
|
| 520 |
+
|
| 521 |
+
configureDNS "$VM_NET_BRIDGE" "$ip" "$VM_NET_MAC" "$VM_NET_HOST" "$VM_NET_MASK" "$gateway" || return 1
|
| 522 |
+
|
| 523 |
+
VM_NET_IP="$ip"
|
| 524 |
+
return 0
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
closeBridge() {
|
| 528 |
+
|
| 529 |
+
local pid="/var/run/dnsmasq.pid"
|
| 530 |
+
[ -s "$pid" ] && pKill "$(<"$pid")"
|
| 531 |
+
rm -f "$pid"
|
| 532 |
+
|
| 533 |
+
pid="/var/run/passt.pid"
|
| 534 |
+
[ -s "$pid" ] && pKill "$(<"$pid")"
|
| 535 |
+
rm -f "$pid"
|
| 536 |
+
|
| 537 |
+
case "${NETWORK,,}" in
|
| 538 |
+
"user"* | "passt" | "slirp" ) return 0 ;;
|
| 539 |
+
esac
|
| 540 |
+
|
| 541 |
+
ip link set "$VM_NET_TAP" down promisc off &> null || true
|
| 542 |
+
ip link delete "$VM_NET_TAP" &> null || true
|
| 543 |
+
|
| 544 |
+
ip link set "$VM_NET_BRIDGE" down &> null || true
|
| 545 |
+
ip link delete "$VM_NET_BRIDGE" &> null || true
|
| 546 |
+
|
| 547 |
+
return 0
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
closeNetwork() {
|
| 551 |
+
|
| 552 |
+
if [[ "${WEB:-}" != [Nn]* ]]; then
|
| 553 |
+
|
| 554 |
+
# Shutdown nginx
|
| 555 |
+
nginx -s stop 2> /dev/null
|
| 556 |
+
fWait "nginx"
|
| 557 |
+
|
| 558 |
+
fi
|
| 559 |
+
|
| 560 |
+
[[ "$NETWORK" == [Nn]* ]] && return 0
|
| 561 |
+
|
| 562 |
+
exec 30<&- || true
|
| 563 |
+
exec 40<&- || true
|
| 564 |
+
|
| 565 |
+
if [[ "$DHCP" != [Yy1]* ]]; then
|
| 566 |
+
|
| 567 |
+
closeBridge
|
| 568 |
+
return 0
|
| 569 |
+
|
| 570 |
+
fi
|
| 571 |
+
|
| 572 |
+
ip link set "$VM_NET_TAP" down || true
|
| 573 |
+
ip link delete "$VM_NET_TAP" || true
|
| 574 |
+
|
| 575 |
+
return 0
|
| 576 |
+
}
|
| 577 |
+
|
| 578 |
+
cleanUp() {
|
| 579 |
+
|
| 580 |
+
# Clean up old files
|
| 581 |
+
rm -f /etc/resolv.dnsmasq
|
| 582 |
+
rm -f /var/run/passt.pid
|
| 583 |
+
rm -f /var/run/dnsmasq.pid
|
| 584 |
+
|
| 585 |
+
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
|
| 586 |
+
info "Lingering interface will be removed..."
|
| 587 |
+
ip link delete "$VM_NET_TAP" || true
|
| 588 |
+
fi
|
| 589 |
+
|
| 590 |
+
return 0
|
| 591 |
+
}
|
| 592 |
+
|
| 593 |
+
checkOS() {
|
| 594 |
+
|
| 595 |
+
local kernel
|
| 596 |
+
local os=""
|
| 597 |
+
local if="macvlan"
|
| 598 |
+
kernel=$(uname -a)
|
| 599 |
+
|
| 600 |
+
[[ "${kernel,,}" == *"darwin"* ]] && os="$ENGINE Desktop for macOS"
|
| 601 |
+
[[ "${kernel,,}" == *"microsoft"* ]] && os="$ENGINE Desktop for Windows"
|
| 602 |
+
|
| 603 |
+
if [[ "$DHCP" == [Yy1]* ]]; then
|
| 604 |
+
if="macvtap"
|
| 605 |
+
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
|
| 606 |
+
fi
|
| 607 |
+
|
| 608 |
+
if [ -n "$os" ]; then
|
| 609 |
+
warn "you are using $os which does not support $if, please revert to bridge networking!"
|
| 610 |
+
fi
|
| 611 |
+
|
| 612 |
+
return 0
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
getInfo() {
|
| 616 |
+
|
| 617 |
+
if [ -z "$VM_NET_DEV" ]; then
|
| 618 |
+
# Give Kubernetes priority over the default interface
|
| 619 |
+
[ -d "/sys/class/net/net0" ] && VM_NET_DEV="net0"
|
| 620 |
+
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
|
| 621 |
+
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
|
| 622 |
+
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
|
| 623 |
+
# Automaticly detect the default network interface
|
| 624 |
+
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
|
| 625 |
+
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
|
| 626 |
+
fi
|
| 627 |
+
|
| 628 |
+
if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then
|
| 629 |
+
error "Network interface '$VM_NET_DEV' does not exist inside the container!"
|
| 630 |
+
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
|
| 631 |
+
fi
|
| 632 |
+
|
| 633 |
+
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
|
| 634 |
+
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
|
| 635 |
+
IP6=""
|
| 636 |
+
|
| 637 |
+
# shellcheck disable=SC2143
|
| 638 |
+
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
| 639 |
+
IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
|
| 640 |
+
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
|
| 641 |
+
fi
|
| 642 |
+
|
| 643 |
+
local result nic bus
|
| 644 |
+
result=$(ethtool -i "$VM_NET_DEV")
|
| 645 |
+
nic=$(grep -m 1 -i 'driver:' <<< "$result" | awk '{print $(2)}')
|
| 646 |
+
bus=$(grep -m 1 -i 'bus-info:' <<< "$result" | awk '{print $(2)}')
|
| 647 |
+
|
| 648 |
+
if [[ "${bus,,}" != "" && "${bus,,}" != "n/a" && "${bus,,}" != "tap" ]]; then
|
| 649 |
+
[[ "$DEBUG" == [Yy1]* ]] && info "Detected BUS: $bus"
|
| 650 |
+
error "This container does not support host mode networking!"
|
| 651 |
+
exit 29
|
| 652 |
+
fi
|
| 653 |
+
|
| 654 |
+
if [[ "$DHCP" == [Yy1]* ]]; then
|
| 655 |
+
|
| 656 |
+
checkOS
|
| 657 |
+
|
| 658 |
+
if [[ "${nic,,}" == "ipvlan" ]]; then
|
| 659 |
+
error "This container does not support IPVLAN networking when DHCP=Y."
|
| 660 |
+
exit 29
|
| 661 |
+
fi
|
| 662 |
+
|
| 663 |
+
if [[ "${nic,,}" != "macvlan" ]]; then
|
| 664 |
+
[[ "$DEBUG" == [Yy1]* ]] && info "Detected NIC: $nic"
|
| 665 |
+
error "The container needs to be in a MACVLAN network when DHCP=Y."
|
| 666 |
+
exit 29
|
| 667 |
+
fi
|
| 668 |
+
|
| 669 |
+
else
|
| 670 |
+
|
| 671 |
+
if [[ "$IP" != "172."* && "$IP" != "10.8"* && "$IP" != "10.9"* ]]; then
|
| 672 |
+
checkOS
|
| 673 |
+
fi
|
| 674 |
+
|
| 675 |
+
fi
|
| 676 |
+
|
| 677 |
+
local mtu=""
|
| 678 |
+
|
| 679 |
+
if [ -f "/sys/class/net/$VM_NET_DEV/mtu" ]; then
|
| 680 |
+
mtu=$(< "/sys/class/net/$VM_NET_DEV/mtu")
|
| 681 |
+
fi
|
| 682 |
+
|
| 683 |
+
[ -z "$MTU" ] && MTU="$mtu"
|
| 684 |
+
[ -z "$MTU" ] && MTU="0"
|
| 685 |
+
|
| 686 |
+
if [ "$MTU" -gt "1500" ]; then
|
| 687 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "MTU size is too large: $MTU, ignoring..."
|
| 688 |
+
MTU="0"
|
| 689 |
+
fi
|
| 690 |
+
|
| 691 |
+
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
|
| 692 |
+
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
| 693 |
+
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
|
| 694 |
+
fi
|
| 695 |
+
fi
|
| 696 |
+
|
| 697 |
+
if [[ "${BOOT_MODE:-}" == "windows_legacy" ]]; then
|
| 698 |
+
if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
|
| 699 |
+
warn "MTU size is $MTU, but cannot be set for legacy Windows versions!" && MTU="0"
|
| 700 |
+
fi
|
| 701 |
+
fi
|
| 702 |
+
|
| 703 |
+
if [ -z "$MAC" ]; then
|
| 704 |
+
local file="$STORAGE/$PROCESS.mac"
|
| 705 |
+
[ -s "$file" ] && MAC=$(<"$file")
|
| 706 |
+
MAC="${MAC//[![:print:]]/}"
|
| 707 |
+
if [ -z "$MAC" ]; then
|
| 708 |
+
# Generate MAC address based on Docker container ID in hostname
|
| 709 |
+
MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
| 710 |
+
echo "${MAC^^}" > "$file"
|
| 711 |
+
fi
|
| 712 |
+
fi
|
| 713 |
+
|
| 714 |
+
VM_NET_MAC="${MAC^^}"
|
| 715 |
+
VM_NET_MAC="${VM_NET_MAC//-/:}"
|
| 716 |
+
|
| 717 |
+
if [[ ${#VM_NET_MAC} == 12 ]]; then
|
| 718 |
+
m="$VM_NET_MAC"
|
| 719 |
+
VM_NET_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
|
| 720 |
+
fi
|
| 721 |
+
|
| 722 |
+
if [[ ${#VM_NET_MAC} != 17 ]]; then
|
| 723 |
+
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
|
| 724 |
+
fi
|
| 725 |
+
|
| 726 |
+
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
| 727 |
+
|
| 728 |
+
if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then
|
| 729 |
+
if [ -z "$NETWORK" ] || [[ "${NETWORK^^}" == "Y" ]]; then
|
| 730 |
+
# By default Podman has no permissions for NAT networking
|
| 731 |
+
NETWORK="user"
|
| 732 |
+
fi
|
| 733 |
+
fi
|
| 734 |
+
|
| 735 |
+
if [[ "$DEBUG" == [Yy1]* ]]; then
|
| 736 |
+
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
|
| 737 |
+
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
|
| 738 |
+
info "$line"
|
| 739 |
+
if [ -f /etc/resolv.conf ]; then
|
| 740 |
+
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
|
| 741 |
+
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
|
| 742 |
+
fi
|
| 743 |
+
echo
|
| 744 |
+
fi
|
| 745 |
+
|
| 746 |
+
return 0
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
# ######################################
|
| 750 |
+
# Configure Network
|
| 751 |
+
# ######################################
|
| 752 |
+
|
| 753 |
+
if [[ "$NETWORK" == [Nn]* ]]; then
|
| 754 |
+
NET_OPTS=""
|
| 755 |
+
return 0
|
| 756 |
+
fi
|
| 757 |
+
|
| 758 |
+
msg="Initializing network..."
|
| 759 |
+
html "$msg"
|
| 760 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "$msg"
|
| 761 |
+
|
| 762 |
+
getInfo
|
| 763 |
+
cleanUp
|
| 764 |
+
|
| 765 |
+
if [[ "$DHCP" == [Yy1]* ]]; then
|
| 766 |
+
|
| 767 |
+
# Configure for macvtap interface
|
| 768 |
+
configureDHCP || exit 20
|
| 769 |
+
|
| 770 |
+
else
|
| 771 |
+
|
| 772 |
+
case "${NETWORK,,}" in
|
| 773 |
+
"user"* | "passt" | "slirp" ) ;;
|
| 774 |
+
"nat" | "tap" | "tun" | "tuntap" | "y" )
|
| 775 |
+
|
| 776 |
+
# Configure tap interface
|
| 777 |
+
if ! configureNAT; then
|
| 778 |
+
|
| 779 |
+
closeBridge
|
| 780 |
+
NETWORK="user"
|
| 781 |
+
msg="falling back to user-mode networking!"
|
| 782 |
+
msg="failed to setup NAT networking, $msg"
|
| 783 |
+
|
| 784 |
+
fi ;;
|
| 785 |
+
|
| 786 |
+
esac
|
| 787 |
+
|
| 788 |
+
if [[ "${NETWORK,,}" == "user"* ]]; then
|
| 789 |
+
if [[ "${ADAPTER,,}" != "rtl8139" ]]; then
|
| 790 |
+
NETWORK="passt"
|
| 791 |
+
else
|
| 792 |
+
NETWORK="slirp"
|
| 793 |
+
fi
|
| 794 |
+
fi
|
| 795 |
+
|
| 796 |
+
case "${NETWORK,,}" in
|
| 797 |
+
"nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
|
| 798 |
+
"passt" )
|
| 799 |
+
|
| 800 |
+
# Configure for user-mode networking (passt)
|
| 801 |
+
if ! configurePasst; then
|
| 802 |
+
error "Failed to configure user-mode networking!"
|
| 803 |
+
exit 24
|
| 804 |
+
fi ;;
|
| 805 |
+
|
| 806 |
+
"slirp" )
|
| 807 |
+
|
| 808 |
+
# Configure for user-mode networking (slirp)
|
| 809 |
+
if ! configureSlirp; then
|
| 810 |
+
error "Failed to configure user-mode networking!"
|
| 811 |
+
exit 24
|
| 812 |
+
fi
|
| 813 |
+
|
| 814 |
+
if [ -z "$USER_PORTS" ]; then
|
| 815 |
+
info "Notice: slirp networking is active, so when you want to expose ports, you will need to map them using this variable: \"USER_PORTS=80,443\"."
|
| 816 |
+
fi ;;
|
| 817 |
+
|
| 818 |
+
*)
|
| 819 |
+
error "Unrecognized NETWORK value: \"$NETWORK\"" && exit 24 ;;
|
| 820 |
+
esac
|
| 821 |
+
|
| 822 |
+
fi
|
| 823 |
+
|
| 824 |
+
NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
|
| 825 |
+
[[ "$MTU" != "0" && "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
|
| 826 |
+
|
| 827 |
+
html "Initialized network successfully..."
|
| 828 |
+
return 0
|
src/proc.sh
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Docker environment variables
|
| 5 |
+
|
| 6 |
+
: "${HV="Y"}"
|
| 7 |
+
: "${KVM:="Y"}"
|
| 8 |
+
: "${VMX:="N"}"
|
| 9 |
+
: "${CPU_FLAGS:=""}"
|
| 10 |
+
: "${CPU_MODEL:=""}"
|
| 11 |
+
|
| 12 |
+
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring KVM..."
|
| 13 |
+
|
| 14 |
+
if [[ "$KVM" == [Nn]* ]]; then
|
| 15 |
+
warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!"
|
| 16 |
+
else
|
| 17 |
+
if [[ "${ARCH,,}" != "amd64" ]]; then
|
| 18 |
+
KVM="N"
|
| 19 |
+
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, so the machine will run about 10 times slower."
|
| 20 |
+
fi
|
| 21 |
+
fi
|
| 22 |
+
|
| 23 |
+
if [[ "$KVM" != [Nn]* ]]; then
|
| 24 |
+
|
| 25 |
+
KVM_ERR=""
|
| 26 |
+
|
| 27 |
+
if [ ! -e /dev/kvm ]; then
|
| 28 |
+
KVM_ERR="(/dev/kvm is missing)"
|
| 29 |
+
else
|
| 30 |
+
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
| 31 |
+
KVM_ERR="(/dev/kvm is unwriteable)"
|
| 32 |
+
else
|
| 33 |
+
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
|
| 34 |
+
if ! grep -qw "vmx\|svm" <<< "$flags"; then
|
| 35 |
+
KVM_ERR="(not enabled in BIOS)"
|
| 36 |
+
fi
|
| 37 |
+
fi
|
| 38 |
+
fi
|
| 39 |
+
|
| 40 |
+
if [ -n "$KVM_ERR" ]; then
|
| 41 |
+
KVM="N"
|
| 42 |
+
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
| 43 |
+
warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower."
|
| 44 |
+
else
|
| 45 |
+
kernel=$(uname -a)
|
| 46 |
+
case "${kernel,,}" in
|
| 47 |
+
*"microsoft"* )
|
| 48 |
+
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
|
| 49 |
+
*"synology"* )
|
| 50 |
+
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
|
| 51 |
+
*)
|
| 52 |
+
error "KVM acceleration is not available $KVM_ERR, this will cause the machine to run about 10 times slower."
|
| 53 |
+
error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;;
|
| 54 |
+
esac
|
| 55 |
+
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
| 56 |
+
fi
|
| 57 |
+
fi
|
| 58 |
+
|
| 59 |
+
fi
|
| 60 |
+
|
| 61 |
+
vendor=$(lscpu | awk '/Vendor ID/{print $3}')
|
| 62 |
+
|
| 63 |
+
if [[ "$KVM" != [Nn]* ]]; then
|
| 64 |
+
|
| 65 |
+
CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor"
|
| 66 |
+
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
|
| 67 |
+
|
| 68 |
+
if [ -z "$CPU_MODEL" ]; then
|
| 69 |
+
CPU_MODEL="host"
|
| 70 |
+
CPU_FEATURES+=",migratable=no"
|
| 71 |
+
fi
|
| 72 |
+
|
| 73 |
+
if [[ "$VMX" == [Nn]* && "${BOOT_MODE,,}" == "windows"* ]]; then
|
| 74 |
+
# Prevents a crash caused by a certain Windows update
|
| 75 |
+
CPU_FEATURES+=",-vmx"
|
| 76 |
+
fi
|
| 77 |
+
|
| 78 |
+
if [[ "$vendor" == "AuthenticAMD" ]]; then
|
| 79 |
+
|
| 80 |
+
# AMD processor
|
| 81 |
+
if grep -qw "tsc_scale" <<< "$flags"; then
|
| 82 |
+
CPU_FEATURES+=",+invtsc"
|
| 83 |
+
fi
|
| 84 |
+
|
| 85 |
+
if [[ "${BOOT_MODE,,}" == "windows"* ]]; then
|
| 86 |
+
CPU_FEATURES+=",arch_capabilities=off"
|
| 87 |
+
fi
|
| 88 |
+
|
| 89 |
+
else
|
| 90 |
+
|
| 91 |
+
# Intel processor
|
| 92 |
+
vmx=$(sed -ne '/^vmx flags/s/^.*: //p' /proc/cpuinfo)
|
| 93 |
+
|
| 94 |
+
if grep -qw "tsc_scaling" <<< "$vmx"; then
|
| 95 |
+
CPU_FEATURES+=",+invtsc"
|
| 96 |
+
fi
|
| 97 |
+
|
| 98 |
+
fi
|
| 99 |
+
|
| 100 |
+
if [[ "${BOOT_MODE,,}" == "windows"* && "$HV" != [Nn]* ]]; then
|
| 101 |
+
|
| 102 |
+
HV_FEATURES="hv_passthrough"
|
| 103 |
+
|
| 104 |
+
if [[ "$vendor" == "AuthenticAMD" ]]; then
|
| 105 |
+
|
| 106 |
+
# AMD processor
|
| 107 |
+
if ! grep -qw "avic" <<< "$flags"; then
|
| 108 |
+
HV_FEATURES+=",-hv-avic"
|
| 109 |
+
fi
|
| 110 |
+
|
| 111 |
+
HV_FEATURES+=",-hv-evmcs"
|
| 112 |
+
|
| 113 |
+
else
|
| 114 |
+
|
| 115 |
+
# Intel processor
|
| 116 |
+
if ! grep -qw "apicv" <<< "$vmx"; then
|
| 117 |
+
HV_FEATURES+=",-hv-apicv,-hv-evmcs"
|
| 118 |
+
else
|
| 119 |
+
if [[ "$CPU" == "Intel Atom "* || "$CPU" == "Intel Celeron "* || "$CPU" == "Intel Pentium "* ]]; then
|
| 120 |
+
# Prevent eVMCS version range error on budget CPU's
|
| 121 |
+
HV_FEATURES+=",-hv-evmcs"
|
| 122 |
+
fi
|
| 123 |
+
fi
|
| 124 |
+
|
| 125 |
+
fi
|
| 126 |
+
|
| 127 |
+
[ -n "$CPU_FEATURES" ] && CPU_FEATURES+=","
|
| 128 |
+
CPU_FEATURES+="${HV_FEATURES}"
|
| 129 |
+
|
| 130 |
+
fi
|
| 131 |
+
|
| 132 |
+
else
|
| 133 |
+
|
| 134 |
+
KVM_OPTS=""
|
| 135 |
+
CPU_FEATURES="l3-cache=on,+hypervisor"
|
| 136 |
+
|
| 137 |
+
if [[ "$ARCH" == "amd64" ]]; then
|
| 138 |
+
KVM_OPTS=" -accel tcg,thread=multi"
|
| 139 |
+
fi
|
| 140 |
+
|
| 141 |
+
if [ -z "$CPU_MODEL" ]; then
|
| 142 |
+
if [[ "$ARCH" == "amd64" ]]; then
|
| 143 |
+
|
| 144 |
+
if [[ "${BOOT_MODE,,}" != "windows"* ]]; then
|
| 145 |
+
|
| 146 |
+
CPU_MODEL="max"
|
| 147 |
+
CPU_FEATURES+=",migratable=no"
|
| 148 |
+
|
| 149 |
+
else
|
| 150 |
+
if [[ "$vendor" == "AuthenticAMD" ]]; then
|
| 151 |
+
|
| 152 |
+
# AMD processor
|
| 153 |
+
CPU_MODEL="EPYC"
|
| 154 |
+
CPU_FEATURES+=",svm=off,arch_capabilities=off,-fxsr-opt,-misalignsse,-osvw,-topoext,-nrip-save,-xsavec,check"
|
| 155 |
+
|
| 156 |
+
else
|
| 157 |
+
|
| 158 |
+
# Intel processor
|
| 159 |
+
CPU_MODEL="Skylake-Client-v4"
|
| 160 |
+
CPU_FEATURES+=",vmx=off,-pcid,-tsc-deadline,-invpcid,-spec-ctrl,-xsavec,-xsaves,check"
|
| 161 |
+
|
| 162 |
+
fi
|
| 163 |
+
fi
|
| 164 |
+
|
| 165 |
+
else
|
| 166 |
+
|
| 167 |
+
# Intel processor
|
| 168 |
+
CPU_MODEL="Skylake-Client-v4"
|
| 169 |
+
CPU_FEATURES+=",vmx=off,-pcid,-tsc-deadline,-invpcid,-spec-ctrl,-xsavec,-xsaves,check"
|
| 170 |
+
|
| 171 |
+
fi
|
| 172 |
+
fi
|
| 173 |
+
|
| 174 |
+
fi
|
| 175 |
+
|
| 176 |
+
if [[ "$ARGUMENTS" == *"-cpu host,"* ]]; then
|
| 177 |
+
|
| 178 |
+
args="${ARGUMENTS} "
|
| 179 |
+
prefix="${args/-cpu host,*/}"
|
| 180 |
+
suffix="${args/*-cpu host,/}"
|
| 181 |
+
param="${suffix%% *}"
|
| 182 |
+
suffix="${suffix#* }"
|
| 183 |
+
args="${prefix}${suffix}"
|
| 184 |
+
ARGUMENTS="${args::-1}"
|
| 185 |
+
|
| 186 |
+
if [ -z "$CPU_FLAGS" ]; then
|
| 187 |
+
CPU_FLAGS="$param"
|
| 188 |
+
else
|
| 189 |
+
CPU_FLAGS+=",$param"
|
| 190 |
+
fi
|
| 191 |
+
|
| 192 |
+
else
|
| 193 |
+
|
| 194 |
+
if [[ "$ARGUMENTS" == *"-cpu host"* ]]; then
|
| 195 |
+
ARGUMENTS="${ARGUMENTS//-cpu host/}"
|
| 196 |
+
fi
|
| 197 |
+
|
| 198 |
+
fi
|
| 199 |
+
|
| 200 |
+
if [ -z "$CPU_FLAGS" ]; then
|
| 201 |
+
if [ -z "$CPU_FEATURES" ]; then
|
| 202 |
+
CPU_FLAGS="$CPU_MODEL"
|
| 203 |
+
else
|
| 204 |
+
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES"
|
| 205 |
+
fi
|
| 206 |
+
else
|
| 207 |
+
if [ -z "$CPU_FEATURES" ]; then
|
| 208 |
+
CPU_FLAGS="$CPU_MODEL,$CPU_FLAGS"
|
| 209 |
+
else
|
| 210 |
+
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES,$CPU_FLAGS"
|
| 211 |
+
fi
|
| 212 |
+
fi
|
| 213 |
+
|
| 214 |
+
return 0
|
src/progress.sh
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
escape () {
|
| 5 |
+
local s
|
| 6 |
+
s=${1//&/\&}
|
| 7 |
+
s=${s//</\<}
|
| 8 |
+
s=${s//>/\>}
|
| 9 |
+
s=${s//'"'/\"}
|
| 10 |
+
printf -- %s "$s"
|
| 11 |
+
return 0
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
file="$1"
|
| 15 |
+
total="$2"
|
| 16 |
+
body=$(escape "$3")
|
| 17 |
+
info="/run/shm/msg.html"
|
| 18 |
+
|
| 19 |
+
if [[ "$body" == *"..." ]]; then
|
| 20 |
+
body="<p class=\"loading\">${body/.../}</p>"
|
| 21 |
+
fi
|
| 22 |
+
|
| 23 |
+
while true
|
| 24 |
+
do
|
| 25 |
+
if [ -s "$file" ]; then
|
| 26 |
+
bytes=$(du -sb "$file" | cut -f1)
|
| 27 |
+
if (( bytes > 1000 )); then
|
| 28 |
+
if [ -z "$total" ] || [[ "$total" == "0" ]]; then
|
| 29 |
+
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
|
| 30 |
+
else
|
| 31 |
+
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
|
| 32 |
+
size="$size%"
|
| 33 |
+
fi
|
| 34 |
+
echo "${body//(\[P\])/($size)}"> "$info"
|
| 35 |
+
fi
|
| 36 |
+
fi
|
| 37 |
+
sleep 1 & wait $!
|
| 38 |
+
done
|
src/reset.sh
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
|
| 5 |
+
[[ "${TRACE:-}" == [Yy1]* ]] && set -o functrace && trap 'echo "# $BASH_COMMAND" >&2' DEBUG
|
| 6 |
+
|
| 7 |
+
[ ! -f "/run/entry.sh" ] && error "Script must be run inside the container!" && exit 11
|
| 8 |
+
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
|
| 9 |
+
|
| 10 |
+
# Docker environment variables
|
| 11 |
+
|
| 12 |
+
: "${BOOT:=""}" # Path of ISO file
|
| 13 |
+
: "${DEBUG:="N"}" # Disable debugging
|
| 14 |
+
: "${MACHINE:="q35"}" # Machine selection
|
| 15 |
+
: "${ALLOCATE:=""}" # Preallocate diskspace
|
| 16 |
+
: "${ARGUMENTS:=""}" # Extra QEMU parameters
|
| 17 |
+
: "${CPU_CORES:="2"}" # Amount of CPU cores
|
| 18 |
+
: "${RAM_SIZE:="2G"}" # Maximum RAM amount
|
| 19 |
+
: "${RAM_CHECK:="Y"}" # Check available RAM
|
| 20 |
+
: "${DISK_SIZE:="64G"}" # Initial data disk size
|
| 21 |
+
: "${BOOT_MODE:=""}" # Boot system with UEFI
|
| 22 |
+
: "${BOOT_INDEX:="9"}" # Boot index of CD drive
|
| 23 |
+
: "${STORAGE:="/storage"}" # Storage folder location
|
| 24 |
+
|
| 25 |
+
# Helper variables
|
| 26 |
+
|
| 27 |
+
PODMAN="N"
|
| 28 |
+
ENGINE="Docker"
|
| 29 |
+
PROCESS="${APP,,}"
|
| 30 |
+
PROCESS="${PROCESS// /-}"
|
| 31 |
+
|
| 32 |
+
if [ -f "/run/.containerenv" ]; then
|
| 33 |
+
PODMAN="Y"
|
| 34 |
+
ENGINE="Podman"
|
| 35 |
+
fi
|
| 36 |
+
|
| 37 |
+
echo "❯ Starting $APP for $ENGINE v$(</run/version)..."
|
| 38 |
+
echo "❯ For support visit $SUPPORT"
|
| 39 |
+
|
| 40 |
+
INFO="/run/shm/msg.html"
|
| 41 |
+
PAGE="/run/shm/index.html"
|
| 42 |
+
TEMPLATE="/var/www/index.html"
|
| 43 |
+
FOOTER1="$APP for $ENGINE v$(</run/version)"
|
| 44 |
+
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
| 45 |
+
|
| 46 |
+
CPU=$(cpu)
|
| 47 |
+
SYS=$(uname -r)
|
| 48 |
+
HOST=$(hostname -s)
|
| 49 |
+
KERNEL=$(echo "$SYS" | cut -b 1)
|
| 50 |
+
MINOR=$(echo "$SYS" | cut -d '.' -f2)
|
| 51 |
+
ARCH=$(dpkg --print-architecture)
|
| 52 |
+
CORES=$(grep -c '^processor' /proc/cpuinfo)
|
| 53 |
+
|
| 54 |
+
if ! grep -qi "socket(s)" <<< "$(lscpu)"; then
|
| 55 |
+
SOCKETS=1
|
| 56 |
+
else
|
| 57 |
+
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
|
| 58 |
+
fi
|
| 59 |
+
|
| 60 |
+
CPU_CORES="${CPU_CORES// /}"
|
| 61 |
+
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
|
| 62 |
+
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
|
| 63 |
+
|
| 64 |
+
if [ "$CPU_CORES" -gt "$CORES" ]; then
|
| 65 |
+
warn "The amount for CPU_CORES (${CPU_CORES}) exceeds the amount of physical cores, so will be limited to ${CORES}."
|
| 66 |
+
CPU_CORES="$CORES"
|
| 67 |
+
fi
|
| 68 |
+
|
| 69 |
+
# Check system
|
| 70 |
+
|
| 71 |
+
if [ ! -d "/dev/shm" ]; then
|
| 72 |
+
error "Directory /dev/shm not found!" && exit 14
|
| 73 |
+
else
|
| 74 |
+
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
|
| 75 |
+
fi
|
| 76 |
+
|
| 77 |
+
# Check folder
|
| 78 |
+
|
| 79 |
+
if [[ "${COMMIT:-}" == [Yy1]* ]]; then
|
| 80 |
+
STORAGE="/local"
|
| 81 |
+
mkdir -p "$STORAGE"
|
| 82 |
+
fi
|
| 83 |
+
|
| 84 |
+
if [ ! -d "$STORAGE" ]; then
|
| 85 |
+
error "Storage folder ($STORAGE) not found!" && exit 13
|
| 86 |
+
fi
|
| 87 |
+
|
| 88 |
+
if [ ! -w "$STORAGE" ]; then
|
| 89 |
+
error "Storage folder ($STORAGE) is not writeable!" && exit 13
|
| 90 |
+
fi
|
| 91 |
+
|
| 92 |
+
# Read memory
|
| 93 |
+
RAM_SPARE=500000000
|
| 94 |
+
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
| 95 |
+
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
| 96 |
+
|
| 97 |
+
RAM_SIZE="${RAM_SIZE// /}"
|
| 98 |
+
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
|
| 99 |
+
|
| 100 |
+
if [[ "${RAM_SIZE,,}" == "max" ]]; then
|
| 101 |
+
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE - RAM_SPARE ))
|
| 102 |
+
RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
|
| 103 |
+
RAM_SIZE="${RAM_WANTED}G"
|
| 104 |
+
fi
|
| 105 |
+
|
| 106 |
+
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
|
| 107 |
+
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
|
| 108 |
+
fi
|
| 109 |
+
|
| 110 |
+
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
| 111 |
+
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
|
| 112 |
+
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
|
| 113 |
+
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
|
| 114 |
+
|
| 115 |
+
# Print system info
|
| 116 |
+
SYS="${SYS/-generic/}"
|
| 117 |
+
FS=$(stat -f -c %T "$STORAGE")
|
| 118 |
+
FS="${FS/UNKNOWN //}"
|
| 119 |
+
FS="${FS/ext2\/ext3/ext4}"
|
| 120 |
+
FS=$(echo "$FS" | sed 's/[)(]//g')
|
| 121 |
+
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
|
| 122 |
+
SPACE_GB=$(formatBytes "$SPACE" "down")
|
| 123 |
+
AVAIL_MEM=$(formatBytes "$RAM_AVAIL" "down")
|
| 124 |
+
TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
|
| 125 |
+
|
| 126 |
+
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
| 127 |
+
echo
|
| 128 |
+
|
| 129 |
+
# Check compatibilty
|
| 130 |
+
|
| 131 |
+
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
|
| 132 |
+
DISK_IO="threads"
|
| 133 |
+
DISK_CACHE="writeback"
|
| 134 |
+
fi
|
| 135 |
+
|
| 136 |
+
if [[ "${BOOT_MODE:-}" == "windows"* ]]; then
|
| 137 |
+
if [[ "${FS,,}" == "btrfs" ]]; then
|
| 138 |
+
warn "you are using the BTRFS filesystem for /storage, this might introduce issues with Windows Setup!"
|
| 139 |
+
fi
|
| 140 |
+
fi
|
| 141 |
+
|
| 142 |
+
# Check available memory
|
| 143 |
+
|
| 144 |
+
if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
|
| 145 |
+
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
| 146 |
+
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."
|
| 147 |
+
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
|
| 148 |
+
info "$msg"
|
| 149 |
+
fi
|
| 150 |
+
|
| 151 |
+
addPackage() {
|
| 152 |
+
local pkg=$1
|
| 153 |
+
local desc=$2
|
| 154 |
+
|
| 155 |
+
if apt-mark showinstall | grep -qx "$pkg"; then
|
| 156 |
+
return 0
|
| 157 |
+
fi
|
| 158 |
+
|
| 159 |
+
MSG="Installing $desc..."
|
| 160 |
+
info "$MSG" && html "$MSG"
|
| 161 |
+
|
| 162 |
+
DEBIAN_FRONTEND=noninteractive apt-get -qq update
|
| 163 |
+
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
| 164 |
+
|
| 165 |
+
return 0
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
: "${VNC_PORT:="5900"}" # VNC port
|
| 169 |
+
: "${MON_PORT:="7100"}" # Monitor port
|
| 170 |
+
: "${WEB_PORT:="8006"}" # Webserver port
|
| 171 |
+
: "${WSS_PORT:="5700"}" # Websockets port
|
| 172 |
+
|
| 173 |
+
if (( VNC_PORT < 5900 )); then
|
| 174 |
+
warn "VNC port cannot be set lower than 5900, ignoring value $VNC_PORT."
|
| 175 |
+
VNC_PORT="5900"
|
| 176 |
+
fi
|
| 177 |
+
|
| 178 |
+
cp -r /var/www/* /run/shm
|
| 179 |
+
html "Starting $APP for $ENGINE..."
|
| 180 |
+
|
| 181 |
+
if [[ "${WEB:-}" != [Nn]* ]]; then
|
| 182 |
+
|
| 183 |
+
mkdir -p /etc/nginx/sites-enabled
|
| 184 |
+
cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf
|
| 185 |
+
|
| 186 |
+
user="admin"
|
| 187 |
+
[ -n "${USER:-}" ] && user="${USER:-}"
|
| 188 |
+
|
| 189 |
+
if [ -n "${PASS:-}" ]; then
|
| 190 |
+
|
| 191 |
+
# Set password
|
| 192 |
+
echo "$user:{PLAIN}${PASS:-}" > /etc/nginx/.htpasswd
|
| 193 |
+
|
| 194 |
+
sed -i "s/auth_basic off/auth_basic \"NoVNC\"/g" /etc/nginx/sites-enabled/web.conf
|
| 195 |
+
|
| 196 |
+
fi
|
| 197 |
+
|
| 198 |
+
sed -i "s/listen 8006 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf
|
| 199 |
+
sed -i "s/proxy_pass http:\/\/127.0.0.1:5700\/;/proxy_pass http:\/\/127.0.0.1:$WSS_PORT\/;/g" /etc/nginx/sites-enabled/web.conf
|
| 200 |
+
|
| 201 |
+
# shellcheck disable=SC2143
|
| 202 |
+
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
| 203 |
+
|
| 204 |
+
sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
| 205 |
+
|
| 206 |
+
fi
|
| 207 |
+
|
| 208 |
+
# Start webserver
|
| 209 |
+
nginx -e stderr
|
| 210 |
+
|
| 211 |
+
fi
|
| 212 |
+
|
| 213 |
+
return 0
|
src/start.sh
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Override this placeholder file using a Docker bind to execute a script during startup!
|
src/utils.sh
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
set -Eeuo pipefail
|
| 3 |
+
|
| 4 |
+
# Helper functions
|
| 5 |
+
|
| 6 |
+
info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "${1:-}" "\E[0m\n"; }
|
| 7 |
+
error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: ${1:-}" "\E[0m\n" >&2; }
|
| 8 |
+
warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; }
|
| 9 |
+
|
| 10 |
+
formatBytes() {
|
| 11 |
+
local result
|
| 12 |
+
result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;')
|
| 13 |
+
local unit="${result//[0-9. ]}"
|
| 14 |
+
result="${result//[a-zA-Z ]/}"
|
| 15 |
+
if [[ "${2:-}" == "up" ]]; then
|
| 16 |
+
if [[ "$result" == *"."* ]]; then
|
| 17 |
+
result="${result%%.*}"
|
| 18 |
+
result=$((result+1))
|
| 19 |
+
fi
|
| 20 |
+
else
|
| 21 |
+
if [[ "${2:-}" == "down" ]]; then
|
| 22 |
+
result="${result%%.*}"
|
| 23 |
+
fi
|
| 24 |
+
fi
|
| 25 |
+
echo "$result $unit"
|
| 26 |
+
return 0
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
isAlive() {
|
| 30 |
+
local pid="$1"
|
| 31 |
+
|
| 32 |
+
if kill -0 "$pid" 2>/dev/null; then
|
| 33 |
+
return 0
|
| 34 |
+
fi
|
| 35 |
+
|
| 36 |
+
return 1
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
pKill() {
|
| 40 |
+
local pid="$1"
|
| 41 |
+
|
| 42 |
+
{ kill -15 "$pid" || true; } 2>/dev/null
|
| 43 |
+
|
| 44 |
+
while isAlive "$pid"; do
|
| 45 |
+
sleep 0.2
|
| 46 |
+
done
|
| 47 |
+
|
| 48 |
+
return 0
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
fWait() {
|
| 52 |
+
local name="$1"
|
| 53 |
+
|
| 54 |
+
while pgrep -f -l "$name" >/dev/null; do
|
| 55 |
+
sleep 0.2
|
| 56 |
+
done
|
| 57 |
+
|
| 58 |
+
return 0
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
fKill() {
|
| 62 |
+
local name="$1"
|
| 63 |
+
|
| 64 |
+
{ pkill -f "$name" || true; } 2>/dev/null
|
| 65 |
+
fWait "$name"
|
| 66 |
+
|
| 67 |
+
return 0
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
escape () {
|
| 71 |
+
local s
|
| 72 |
+
s=${1//&/\&}
|
| 73 |
+
s=${s//</\<}
|
| 74 |
+
s=${s//>/\>}
|
| 75 |
+
s=${s//'"'/\"}
|
| 76 |
+
printf -- %s "$s"
|
| 77 |
+
return 0
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
html() {
|
| 81 |
+
local title
|
| 82 |
+
local body
|
| 83 |
+
local script
|
| 84 |
+
local footer
|
| 85 |
+
|
| 86 |
+
title=$(escape "$APP")
|
| 87 |
+
title="<title>$title</title>"
|
| 88 |
+
footer=$(escape "$FOOTER1")
|
| 89 |
+
|
| 90 |
+
body=$(escape "$1")
|
| 91 |
+
if [[ "$body" == *"..." ]]; then
|
| 92 |
+
body="<p class=\"loading\">${body/.../}</p>"
|
| 93 |
+
fi
|
| 94 |
+
|
| 95 |
+
[ -n "${2:-}" ] && script="$2" || script=""
|
| 96 |
+
|
| 97 |
+
local HTML
|
| 98 |
+
HTML=$(<"$TEMPLATE")
|
| 99 |
+
HTML="${HTML/\[1\]/$title}"
|
| 100 |
+
HTML="${HTML/\[2\]/$script}"
|
| 101 |
+
HTML="${HTML/\[3\]/$body}"
|
| 102 |
+
HTML="${HTML/\[4\]/$footer}"
|
| 103 |
+
HTML="${HTML/\[5\]/$FOOTER2}"
|
| 104 |
+
|
| 105 |
+
echo "$HTML" > "$PAGE"
|
| 106 |
+
echo "$body" > "$INFO"
|
| 107 |
+
|
| 108 |
+
return 0
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
cpu() {
|
| 112 |
+
local ret
|
| 113 |
+
local cpu=""
|
| 114 |
+
|
| 115 |
+
ret=$(lscpu)
|
| 116 |
+
|
| 117 |
+
if grep -qi "model name" <<< "$ret"; then
|
| 118 |
+
cpu=$(echo "$ret" | grep -m 1 -i 'model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
| 119 |
+
fi
|
| 120 |
+
|
| 121 |
+
if [ -z "${cpu// /}" ] && grep -qi "model:" <<< "$ret"; then
|
| 122 |
+
cpu=$(echo "$ret" | grep -m 1 -i 'model:' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
| 123 |
+
fi
|
| 124 |
+
|
| 125 |
+
cpu="${cpu// CPU/}"
|
| 126 |
+
cpu="${cpu// [0-9] Core}"
|
| 127 |
+
cpu="${cpu// [0-9][0-9] Core}"
|
| 128 |
+
cpu="${cpu// [0-9][0-9][0-9] Core}"
|
| 129 |
+
cpu="${cpu//[0-9]th Gen }"
|
| 130 |
+
cpu="${cpu//[0-9][0-9]th Gen }"
|
| 131 |
+
cpu="${cpu// Processor/}"
|
| 132 |
+
cpu="${cpu// Quad core/}"
|
| 133 |
+
cpu="${cpu// Dual core/}"
|
| 134 |
+
cpu="${cpu// Octa core/}"
|
| 135 |
+
cpu="${cpu// Hexa core/}"
|
| 136 |
+
cpu="${cpu// Core TM/ Core}"
|
| 137 |
+
cpu="${cpu// with Radeon Graphics/}"
|
| 138 |
+
cpu="${cpu// with Radeon Vega Graphics/}"
|
| 139 |
+
cpu="${cpu// with Radeon Vega Mobile Gfx/}"
|
| 140 |
+
cpu="${cpu// w Radeon [0-9][0-9][0-9]M Graphics/}"
|
| 141 |
+
|
| 142 |
+
[ -z "${cpu// /}" ] && cpu="Unknown"
|
| 143 |
+
|
| 144 |
+
echo "$cpu"
|
| 145 |
+
return 0
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
hasDisk() {
|
| 149 |
+
|
| 150 |
+
[ -b "/disk" ] && return 0
|
| 151 |
+
[ -b "/disk1" ] && return 0
|
| 152 |
+
[ -b "/dev/disk1" ] && return 0
|
| 153 |
+
[ -b "${DEVICE:-}" ] && return 0
|
| 154 |
+
|
| 155 |
+
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
|
| 156 |
+
[ -s "$STORAGE/$DISK_NAME.img" ] && return 0
|
| 157 |
+
[ -s "$STORAGE/$DISK_NAME.qcow2" ] && return 0
|
| 158 |
+
|
| 159 |
+
return 1
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
return 0
|
web/conf/defaults.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"resize": "scale",
|
| 3 |
+
"reconnect_delay": 3000
|
| 4 |
+
}
|
web/conf/mandatory.json
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"reconnect": true,
|
| 3 |
+
"autoconnect": true
|
| 4 |
+
}
|
web/conf/nginx.conf
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
server {
|
| 2 |
+
|
| 3 |
+
listen 8006 default_server;
|
| 4 |
+
|
| 5 |
+
autoindex on;
|
| 6 |
+
tcp_nodelay on;
|
| 7 |
+
server_tokens off;
|
| 8 |
+
absolute_redirect off;
|
| 9 |
+
|
| 10 |
+
error_log /dev/null;
|
| 11 |
+
access_log /dev/null;
|
| 12 |
+
|
| 13 |
+
auth_basic off;
|
| 14 |
+
auth_basic_user_file /etc/nginx/.htpasswd;
|
| 15 |
+
|
| 16 |
+
include /etc/nginx/mime.types;
|
| 17 |
+
|
| 18 |
+
gzip on;
|
| 19 |
+
gzip_vary on;
|
| 20 |
+
gzip_proxied any;
|
| 21 |
+
gzip_comp_level 5;
|
| 22 |
+
gzip_min_length 500;
|
| 23 |
+
gzip_disable "msie6";
|
| 24 |
+
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
|
| 25 |
+
|
| 26 |
+
add_header Cache-Control "no-cache";
|
| 27 |
+
|
| 28 |
+
location / {
|
| 29 |
+
|
| 30 |
+
root /run/shm;
|
| 31 |
+
|
| 32 |
+
if ( -f /run/shm/index.html) {
|
| 33 |
+
break;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
try_files /index.html @vnc;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
location @vnc {
|
| 40 |
+
|
| 41 |
+
root /usr/share/novnc;
|
| 42 |
+
index vnc.html;
|
| 43 |
+
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
location /websockify {
|
| 47 |
+
|
| 48 |
+
proxy_http_version 1.1;
|
| 49 |
+
|
| 50 |
+
proxy_set_header Connection 'upgrade';
|
| 51 |
+
proxy_set_header Upgrade $http_upgrade;
|
| 52 |
+
|
| 53 |
+
proxy_buffering off;
|
| 54 |
+
proxy_read_timeout 3600s;
|
| 55 |
+
proxy_send_timeout 3600s;
|
| 56 |
+
|
| 57 |
+
proxy_pass http://127.0.0.1:5700/;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
}
|
web/css/style.css
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
body {
|
| 2 |
+
color: white;
|
| 3 |
+
background-color: #125bdb;
|
| 4 |
+
font-smoothing: antialiased;
|
| 5 |
+
-webkit-font-smoothing: antialiased;
|
| 6 |
+
-moz-osx-font-smoothing: grayscale;
|
| 7 |
+
font-family: Verdana, Geneva, sans-serif;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
#info {
|
| 11 |
+
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.25);
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
#content {
|
| 15 |
+
text-align: center;
|
| 16 |
+
padding: 20px;
|
| 17 |
+
margin-top: 50px;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
footer {
|
| 21 |
+
width: 98%;
|
| 22 |
+
position: fixed;
|
| 23 |
+
bottom: 0px;
|
| 24 |
+
height: 40px;
|
| 25 |
+
text-align: center;
|
| 26 |
+
color: #0c8aeb;
|
| 27 |
+
text-shadow: 0 0 1px #0c8aeb;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
#empty {
|
| 31 |
+
height: 40px;
|
| 32 |
+
/* Same height as footer */
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
a,
|
| 36 |
+
a:hover,
|
| 37 |
+
a:active,
|
| 38 |
+
a:visited {
|
| 39 |
+
color: white;
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
footer a:link,
|
| 43 |
+
footer a:visited,
|
| 44 |
+
footer a:active {
|
| 45 |
+
color: #0c8aeb;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
footer a:hover {
|
| 49 |
+
color: #73e6ff;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
.loading:after {
|
| 53 |
+
content: " .";
|
| 54 |
+
animation: dots 1s steps(5, end) infinite;
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
@keyframes dots {
|
| 58 |
+
|
| 59 |
+
0%,
|
| 60 |
+
20% {
|
| 61 |
+
color: rgba(0, 0, 0, 0);
|
| 62 |
+
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
40% {
|
| 66 |
+
color: white;
|
| 67 |
+
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
60% {
|
| 71 |
+
text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0);
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
80%,
|
| 75 |
+
100% {
|
| 76 |
+
text-shadow: 0.25em 0 0 white, 0.5em 0 0 white;
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
.spinner_LWk7 {
|
| 81 |
+
animation: spinner_GWy6 1.2s linear infinite, spinner_BNNO 1.2s linear infinite
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
.spinner_yOMU {
|
| 85 |
+
animation: spinner_GWy6 1.2s linear infinite, spinner_pVqn 1.2s linear infinite;
|
| 86 |
+
animation-delay: .15s
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
.spinner_KS4S {
|
| 90 |
+
animation: spinner_GWy6 1.2s linear infinite, spinner_6uKB 1.2s linear infinite;
|
| 91 |
+
animation-delay: .3s
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
.spinner_zVee {
|
| 95 |
+
animation: spinner_GWy6 1.2s linear infinite, spinner_Qw4x 1.2s linear infinite;
|
| 96 |
+
animation-delay: .45s
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
@keyframes spinner_GWy6 {
|
| 100 |
+
|
| 101 |
+
0%,
|
| 102 |
+
50% {
|
| 103 |
+
width: 9px;
|
| 104 |
+
height: 9px
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
10% {
|
| 108 |
+
width: 11px;
|
| 109 |
+
height: 11px
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
@keyframes spinner_BNNO {
|
| 114 |
+
|
| 115 |
+
0%,
|
| 116 |
+
50% {
|
| 117 |
+
x: 1.5px;
|
| 118 |
+
y: 1.5px
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
10% {
|
| 122 |
+
x: .5px;
|
| 123 |
+
y: .5px
|
| 124 |
+
}
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
@keyframes spinner_pVqn {
|
| 128 |
+
|
| 129 |
+
0%,
|
| 130 |
+
50% {
|
| 131 |
+
x: 13.5px;
|
| 132 |
+
y: 1.5px
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
10% {
|
| 136 |
+
x: 12.5px;
|
| 137 |
+
y: .5px
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
@keyframes spinner_6uKB {
|
| 142 |
+
|
| 143 |
+
0%,
|
| 144 |
+
50% {
|
| 145 |
+
x: 13.5px;
|
| 146 |
+
y: 13.5px
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
10% {
|
| 150 |
+
x: 12.5px;
|
| 151 |
+
y: 12.5px
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
@keyframes spinner_Qw4x {
|
| 156 |
+
|
| 157 |
+
0%,
|
| 158 |
+
50% {
|
| 159 |
+
x: 1.5px;
|
| 160 |
+
y: 13.5px
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
10% {
|
| 164 |
+
x: .5px;
|
| 165 |
+
y: 12.5px
|
| 166 |
+
}
|
| 167 |
+
}
|
web/img/favicon.svg
ADDED
|
|
web/img/macos.ffs
ADDED
|
Binary file (64.5 kB). View file
|
|
|
web/img/qemu.ffs
ADDED
|
Binary file (61.7 kB). View file
|
|
|
web/img/windows.ffs
ADDED
|
Binary file (72.8 kB). View file
|
|
|
web/index.html
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html>
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
[1]
|
| 6 |
+
<meta http-equiv="Cache-Control" content="no-cache" />
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 8 |
+
<link rel="stylesheet" type="text/css" href="css/style.css" />
|
| 9 |
+
<link rel="icon" href="img/favicon.svg" type="image/x-icon">
|
| 10 |
+
[2]
|
| 11 |
+
</head>
|
| 12 |
+
|
| 13 |
+
<body>
|
| 14 |
+
<div id="page">
|
| 15 |
+
<div id="content">
|
| 16 |
+
<svg id="spinner" width="64" height="64" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
| 17 |
+
<rect class="spinner_LWk7" fill="#0c8aeb" x="1.5" y="1.5" rx="1" width="9" height="9"/>
|
| 18 |
+
<rect class="spinner_yOMU" fill="#0c8aeb" x="13.5" y="1.5" rx="1" width="9" height="9"/>
|
| 19 |
+
<rect class="spinner_KS4S" fill="#0c8aeb" x="13.5" y="13.5" rx="1" width="9" height="9"/>
|
| 20 |
+
<rect class="spinner_zVee" fill="#0c8aeb" x="1.5" y="13.5" rx="1" width="9" height="9"/>
|
| 21 |
+
</svg>
|
| 22 |
+
<h1 id="info">[3]</h1>
|
| 23 |
+
</div>
|
| 24 |
+
<div id="empty">
|
| 25 |
+
</div>
|
| 26 |
+
<footer id="footer">
|
| 27 |
+
[4]<br />
|
| 28 |
+
[5]
|
| 29 |
+
</footer>
|
| 30 |
+
</div>
|
| 31 |
+
<script type="text/javascript" src="js/script.js"></script>
|
| 32 |
+
</body>
|
| 33 |
+
|
| 34 |
+
</html>
|
web/js/script.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var request;
|
| 2 |
+
var interval = 1000;
|
| 3 |
+
|
| 4 |
+
var webSocketFactory = {
|
| 5 |
+
connect: function(url) {
|
| 6 |
+
|
| 7 |
+
var ws = new WebSocket(url);
|
| 8 |
+
|
| 9 |
+
ws.addEventListener("open", e => {
|
| 10 |
+
ws.close();
|
| 11 |
+
document.location.reload();
|
| 12 |
+
});
|
| 13 |
+
|
| 14 |
+
ws.addEventListener("error", e => {
|
| 15 |
+
if (e.target.readyState === 3) {
|
| 16 |
+
setTimeout(() => this.connect(url), 1000);
|
| 17 |
+
}
|
| 18 |
+
});
|
| 19 |
+
}
|
| 20 |
+
};
|
| 21 |
+
|
| 22 |
+
function getInfo() {
|
| 23 |
+
|
| 24 |
+
var url = "msg.html";
|
| 25 |
+
|
| 26 |
+
try {
|
| 27 |
+
if (window.XMLHttpRequest) {
|
| 28 |
+
request = new XMLHttpRequest();
|
| 29 |
+
} else {
|
| 30 |
+
throw "XMLHttpRequest not available!";
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
request.onreadystatechange = processInfo;
|
| 34 |
+
request.open("GET", url, true);
|
| 35 |
+
request.send();
|
| 36 |
+
|
| 37 |
+
} catch (e) {
|
| 38 |
+
var err = "Error: " + e.message;
|
| 39 |
+
console.log(err);
|
| 40 |
+
setError(err);
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
function processInfo() {
|
| 45 |
+
try {
|
| 46 |
+
|
| 47 |
+
if (request.readyState != 4) {
|
| 48 |
+
return true;
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
var msg = request.responseText;
|
| 52 |
+
if (msg == null || msg.length == 0) {
|
| 53 |
+
setError("Lost connection");
|
| 54 |
+
schedule();
|
| 55 |
+
return false;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
var notFound = (request.status == 404);
|
| 59 |
+
|
| 60 |
+
if (request.status == 200) {
|
| 61 |
+
if (msg.toLowerCase().indexOf("<html>") !== -1) {
|
| 62 |
+
notFound = true;
|
| 63 |
+
} else {
|
| 64 |
+
setInfo(msg);
|
| 65 |
+
schedule();
|
| 66 |
+
return true;
|
| 67 |
+
}
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
if (notFound) {
|
| 71 |
+
setInfo("Connecting to VNC", true);
|
| 72 |
+
|
| 73 |
+
var protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
| 74 |
+
var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, '');
|
| 75 |
+
var wsUrl = protocol + "//" + window.location.host + path + "/websockify";
|
| 76 |
+
var webSocket = webSocketFactory.connect(wsUrl);
|
| 77 |
+
|
| 78 |
+
return true;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
setError("Error: Received statuscode " + request.status);
|
| 82 |
+
schedule();
|
| 83 |
+
return false;
|
| 84 |
+
|
| 85 |
+
} catch (e) {
|
| 86 |
+
var err = "Error: " + e.message;
|
| 87 |
+
console.log(err);
|
| 88 |
+
setError(err);
|
| 89 |
+
return false;
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
function setInfo(msg, loading, error) {
|
| 94 |
+
try {
|
| 95 |
+
|
| 96 |
+
if (msg == null || msg.length == 0) {
|
| 97 |
+
return false;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
var el = document.getElementById("spinner");
|
| 101 |
+
|
| 102 |
+
error = !!error;
|
| 103 |
+
if (!error) {
|
| 104 |
+
el.style.visibility = 'visible';
|
| 105 |
+
} else {
|
| 106 |
+
el.style.visibility = 'hidden';
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
loading = !!loading;
|
| 110 |
+
if (loading) {
|
| 111 |
+
msg = "<p class=\"loading\">" + msg + "</p>";
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
el = document.getElementById("info");
|
| 115 |
+
|
| 116 |
+
if (el.innerHTML != msg) {
|
| 117 |
+
el.innerHTML = msg;
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
return true;
|
| 121 |
+
|
| 122 |
+
} catch (e) {
|
| 123 |
+
console.log("Error: " + e.message);
|
| 124 |
+
return false;
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
function setError(text) {
|
| 129 |
+
return setInfo(text, false, true);
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
function schedule() {
|
| 133 |
+
setTimeout(getInfo, interval);
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
schedule();
|