Compare commits
No commits in common. "master" and "v1.22" have entirely different histories.
20 changed files with 1429 additions and 484 deletions
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"name": "qemu",
|
|
||||||
"service": "qemu",
|
|
||||||
"forwardPorts": [8006],
|
|
||||||
"dockerComposeFile": "compose.yml"
|
|
||||||
}
|
|
29
.github/ISSUE_TEMPLATE/1-issue.yml
vendored
29
.github/ISSUE_TEMPLATE/1-issue.yml
vendored
|
@ -1,12 +1,35 @@
|
||||||
name: "\U0001F6A8 Technical issue"
|
name: "\U0001F6A8 Technical issue"
|
||||||
description: When you're experiencing problems using the container
|
description: When you're experiencing problems using the container
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there no existing issue for this?
|
||||||
|
description: Please search to see if no solution was already provided before.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing issues
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: cpu
|
||||||
|
attributes:
|
||||||
|
label: Machine specifications
|
||||||
|
description: The processor and RAM amount in your machine.
|
||||||
|
placeholder: e.g. Raspberry Pi 5 / 8 GB
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: os
|
id: os
|
||||||
attributes:
|
attributes:
|
||||||
label: Operating system
|
label: Operating system
|
||||||
description: Your Linux distribution (can be shown by `lsb_release -a`).
|
description: The distribution and kernel version (as shown by `uname -a`).
|
||||||
placeholder: e.g. Ubuntu 24.04
|
placeholder: e.g. Ubuntu 24.04 / Kernel 6.8.0-22-generic
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: docker
|
||||||
|
attributes:
|
||||||
|
label: Docker version
|
||||||
|
description: The version of the Docker engine (as shown by `docker -v`).
|
||||||
|
placeholder: e.g. 26.0.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
@ -21,7 +44,6 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Docker compose
|
label: Docker compose
|
||||||
description: The compose file (or otherwise the `docker run` command used).
|
description: The compose file (or otherwise the `docker run` command used).
|
||||||
render: yaml
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
@ -29,7 +51,6 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Docker log
|
label: Docker log
|
||||||
description: The logfile of the container (as shown by `docker logs qemu`).
|
description: The logfile of the container (as shown by `docker logs qemu`).
|
||||||
render: shell
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|
7
.github/ISSUE_TEMPLATE/2-feature.yml
vendored
7
.github/ISSUE_TEMPLATE/2-feature.yml
vendored
|
@ -3,6 +3,13 @@ description: Suggest an idea for improving the container
|
||||||
title: "[Feature]: "
|
title: "[Feature]: "
|
||||||
labels: ["enhancement"]
|
labels: ["enhancement"]
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there no existing feature request for this?
|
||||||
|
description: Please search to see if the feature was not already requested before.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing feature requests
|
||||||
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: problem
|
id: problem
|
||||||
attributes:
|
attributes:
|
||||||
|
|
29
.github/ISSUE_TEMPLATE/3-bug.yml
vendored
29
.github/ISSUE_TEMPLATE/3-bug.yml
vendored
|
@ -3,12 +3,35 @@ description: Create a report to help us improve the container
|
||||||
title: "[Bug]: "
|
title: "[Bug]: "
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
body:
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Is there no existing bug report for this?
|
||||||
|
description: Please search to see if the bug was not already reported before.
|
||||||
|
options:
|
||||||
|
- label: I have searched the existing bug reports
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: cpu
|
||||||
|
attributes:
|
||||||
|
label: Machine specifications
|
||||||
|
description: The processor and RAM amount in your machine.
|
||||||
|
placeholder: e.g. Raspberry Pi 5 / 8 GB
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
id: os
|
id: os
|
||||||
attributes:
|
attributes:
|
||||||
label: Operating system
|
label: Operating system
|
||||||
description: Your Linux distribution (can be shown by `lsb_release -a`).
|
description: The distribution and kernel version (as shown by `uname -a`).
|
||||||
placeholder: e.g. Ubuntu 24.04
|
placeholder: e.g. Ubuntu 24.04 / Kernel 6.8.0-22-generic
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: docker
|
||||||
|
attributes:
|
||||||
|
label: Docker version
|
||||||
|
description: The version of the Docker engine (as shown by `docker -v`).
|
||||||
|
placeholder: e.g. 26.0.1
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
@ -23,7 +46,6 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Docker compose
|
label: Docker compose
|
||||||
description: The compose file (or otherwise the `docker run` command used).
|
description: The compose file (or otherwise the `docker run` command used).
|
||||||
render: yaml
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
@ -31,7 +53,6 @@ body:
|
||||||
attributes:
|
attributes:
|
||||||
label: Docker log
|
label: Docker log
|
||||||
description: The logfile of the container (as shown by `docker logs qemu`).
|
description: The logfile of the container (as shown by `docker logs qemu`).
|
||||||
render: shell
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -46,8 +46,8 @@ jobs:
|
||||||
with:
|
with:
|
||||||
context: git
|
context: git
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/${{ github.repository }}
|
|
||||||
${{ secrets.DOCKERHUB_REPO }}
|
${{ secrets.DOCKERHUB_REPO }}
|
||||||
|
ghcr.io/${{ github.repository }}
|
||||||
tags: |
|
tags: |
|
||||||
type=raw,value=latest,priority=100
|
type=raw,value=latest,priority=100
|
||||||
type=raw,value=${{ vars.MAJOR }}.${{ vars.MINOR }}
|
type=raw,value=${{ vars.MAJOR }}.${{ vars.MINOR }}
|
||||||
|
@ -73,7 +73,7 @@ jobs:
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
-
|
-
|
||||||
name: Build Docker image
|
name: Build Docker image
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
push: true
|
push: true
|
||||||
|
|
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
||||||
name: Run ShellCheck
|
name: Run ShellCheck
|
||||||
uses: ludeeus/action-shellcheck@master
|
uses: ludeeus/action-shellcheck@master
|
||||||
env:
|
env:
|
||||||
SHELLCHECK_OPTS: -x --source-path=src -e SC1091 -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153
|
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153
|
||||||
-
|
-
|
||||||
name: Lint Dockerfile
|
name: Lint Dockerfile
|
||||||
uses: hadolint/hadolint-action@v3.1.0
|
uses: hadolint/hadolint-action@v3.1.0
|
||||||
|
|
41
Dockerfile
41
Dockerfile
|
@ -1,10 +1,7 @@
|
||||||
ARG VERSION_ARG="latest"
|
|
||||||
|
|
||||||
FROM qemux/qemu:${VERSION_ARG} AS src
|
|
||||||
FROM debian:trixie-slim
|
FROM debian:trixie-slim
|
||||||
|
|
||||||
ARG VERSION_ARG="0.0"
|
ARG VERSION_ARG="0.0"
|
||||||
ARG VERSION_VNC="1.6.0"
|
ARG VERSION_VNC="1.4.0"
|
||||||
|
|
||||||
ARG DEBCONF_NOWARNINGS="yes"
|
ARG DEBCONF_NOWARNINGS="yes"
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
|
@ -13,13 +10,8 @@ ARG DEBCONF_NONINTERACTIVE_SEEN="true"
|
||||||
RUN set -eu && \
|
RUN set -eu && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get --no-install-recommends -y install \
|
apt-get --no-install-recommends -y install \
|
||||||
bc \
|
|
||||||
jq \
|
|
||||||
tini \
|
tini \
|
||||||
wget \
|
wget \
|
||||||
7zip \
|
|
||||||
curl \
|
|
||||||
fdisk \
|
|
||||||
nginx \
|
nginx \
|
||||||
procps \
|
procps \
|
||||||
seabios \
|
seabios \
|
||||||
|
@ -27,19 +19,13 @@ RUN set -eu && \
|
||||||
iproute2 \
|
iproute2 \
|
||||||
apt-utils \
|
apt-utils \
|
||||||
dnsmasq \
|
dnsmasq \
|
||||||
xz-utils \
|
|
||||||
net-tools \
|
net-tools \
|
||||||
e2fsprogs \
|
|
||||||
qemu-utils \
|
qemu-utils \
|
||||||
iputils-ping \
|
|
||||||
genisoimage \
|
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
qemu-system-arm && \
|
netcat-openbsd \
|
||||||
|
qemu-system-arm \
|
||||||
|
qemu-efi-aarch64 && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
mkdir -p /etc/qemu && \
|
|
||||||
echo "allow br0" > /etc/qemu/bridge.conf && \
|
|
||||||
wget "https://snapshot.debian.org/archive/debian/20250128T092032Z/pool/main/e/edk2/qemu-efi-aarch64_2024.11-5_all.deb" -O /tmp/aavmf.deb -q --timeout=10 && \
|
|
||||||
dpkg -i /tmp/aavmf.deb && \
|
|
||||||
mkdir -p /usr/share/novnc && \
|
mkdir -p /usr/share/novnc && \
|
||||||
wget "https://github.com/novnc/noVNC/archive/refs/tags/v${VERSION_VNC}.tar.gz" -O /tmp/novnc.tar.gz -q --timeout=10 && \
|
wget "https://github.com/novnc/noVNC/archive/refs/tags/v${VERSION_VNC}.tar.gz" -O /tmp/novnc.tar.gz -q --timeout=10 && \
|
||||||
tar -xf /tmp/novnc.tar.gz -C /tmp/ && \
|
tar -xf /tmp/novnc.tar.gz -C /tmp/ && \
|
||||||
|
@ -50,19 +36,20 @@ RUN set -eu && \
|
||||||
echo "$VERSION_ARG" > /run/version && \
|
echo "$VERSION_ARG" > /run/version && \
|
||||||
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
COPY --from=src /run/*.sh /run
|
|
||||||
COPY --from=src /var/www /var/www
|
|
||||||
COPY --from=src /usr/share/novnc /usr/share/novnc
|
|
||||||
COPY --from=src /etc/nginx/sites-enabled /etc/nginx/sites-enabled
|
|
||||||
|
|
||||||
COPY --chmod=755 ./src /run/
|
COPY --chmod=755 ./src /run/
|
||||||
|
|
||||||
|
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu-docker/master/web/index.html /var/www/index.html
|
||||||
|
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu-docker/master/web/js/script.js /var/www/js/script.js
|
||||||
|
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu-docker/master/web/css/style.css /var/www/css/style.css
|
||||||
|
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu-docker/master/web/img/favicon.svg /var/www/img/favicon.svg
|
||||||
|
ADD --chmod=744 https://raw.githubusercontent.com/qemus/qemu-docker/master/web/nginx.conf /etc/nginx/sites-enabled/web.conf
|
||||||
|
|
||||||
VOLUME /storage
|
VOLUME /storage
|
||||||
EXPOSE 22 5900 8006
|
EXPOSE 22 5900 8006
|
||||||
|
|
||||||
ENV BOOT="alpine"
|
ENV CPU_CORES "1"
|
||||||
ENV CPU_CORES="2"
|
ENV RAM_SIZE "1G"
|
||||||
ENV RAM_SIZE="2G"
|
ENV DISK_SIZE "16G"
|
||||||
ENV DISK_SIZE="16G"
|
ENV BOOT "http://example.com/image.iso"
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
|
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
|
||||||
|
|
|
@ -3,15 +3,11 @@ services:
|
||||||
container_name: qemu
|
container_name: qemu
|
||||||
image: qemux/qemu-arm
|
image: qemux/qemu-arm
|
||||||
environment:
|
environment:
|
||||||
BOOT: "alpine"
|
BOOT: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso"
|
||||||
devices:
|
devices:
|
||||||
- /dev/kvm
|
- /dev/kvm
|
||||||
- /dev/net/tun
|
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
ports:
|
ports:
|
||||||
- 8006:8006
|
- 8006:8006
|
||||||
volumes:
|
|
||||||
- ./qemu:/storage
|
|
||||||
restart: always
|
|
||||||
stop_grace_period: 2m
|
stop_grace_period: 2m
|
||||||
|
|
|
@ -1,86 +1,61 @@
|
||||||
---
|
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: PersistentVolumeClaim
|
kind: PersistentVolumeClaim
|
||||||
metadata:
|
metadata:
|
||||||
name: qemu-pvc
|
name: qemu-pvc
|
||||||
spec:
|
spec:
|
||||||
accessModes:
|
accessModes:
|
||||||
- ReadWriteOnce
|
- ReadWriteOnce
|
||||||
resources:
|
resources:
|
||||||
requests:
|
requests:
|
||||||
storage: 16Gi
|
storage: 16Gi
|
||||||
---
|
---
|
||||||
apiVersion: apps/v1
|
apiVersion: v1
|
||||||
kind: Deployment
|
kind: Pod
|
||||||
metadata:
|
metadata:
|
||||||
name: qemu
|
name: qemu
|
||||||
labels:
|
labels:
|
||||||
name: qemu
|
name: qemu
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
terminationGracePeriodSeconds: 120 # the Kubernetes default is 30 seconds and it may be not enough
|
||||||
selector:
|
containers:
|
||||||
matchLabels:
|
- name: qemu
|
||||||
app: qemu
|
image: qemux/qemu-arm
|
||||||
template:
|
ports:
|
||||||
metadata:
|
- containerPort: 8006
|
||||||
labels:
|
protocol: TCP
|
||||||
app: qemu
|
resources:
|
||||||
spec:
|
limits:
|
||||||
containers:
|
devices.kubevirt.io/kvm: 1
|
||||||
- name: qemu
|
securityContext:
|
||||||
image: qemux/qemu-arm
|
privileged: true
|
||||||
env:
|
env:
|
||||||
- name: BOOT
|
- name: BOOT
|
||||||
value: "alpine"
|
value: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso"
|
||||||
|
- name: RAM_SIZE
|
||||||
|
value: 1G
|
||||||
|
- name: CPU_CORES
|
||||||
|
value: "1"
|
||||||
- name: DISK_SIZE
|
- name: DISK_SIZE
|
||||||
value: "16G"
|
value: "16G"
|
||||||
ports:
|
volumeMounts:
|
||||||
- containerPort: 8006
|
|
||||||
name: http
|
|
||||||
protocol: TCP
|
|
||||||
- containerPort: 5900
|
|
||||||
name: vnc
|
|
||||||
protocol: TCP
|
|
||||||
securityContext:
|
|
||||||
capabilities:
|
|
||||||
add:
|
|
||||||
- NET_ADMIN
|
|
||||||
privileged: true
|
|
||||||
volumeMounts:
|
|
||||||
- mountPath: /storage
|
- mountPath: /storage
|
||||||
name: storage
|
name: storage
|
||||||
- mountPath: /dev/kvm
|
volumes:
|
||||||
name: dev-kvm
|
- name: storage
|
||||||
- mountPath: /dev/net/tun
|
persistentVolumeClaim:
|
||||||
name: dev-tun
|
claimName: qemu-pvc
|
||||||
terminationGracePeriodSeconds: 120
|
|
||||||
volumes:
|
|
||||||
- name: storage
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: qemu-pvc
|
|
||||||
- hostPath:
|
|
||||||
path: /dev/kvm
|
|
||||||
name: dev-kvm
|
|
||||||
- hostPath:
|
|
||||||
path: /dev/net/tun
|
|
||||||
type: CharDevice
|
|
||||||
name: dev-tun
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: qemu
|
name: qemu
|
||||||
spec:
|
spec:
|
||||||
internalTrafficPolicy: Cluster
|
type: NodePort
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 8006
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: 8006
|
|
||||||
- name: vnc
|
|
||||||
port: 5900
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: 5900
|
|
||||||
selector:
|
selector:
|
||||||
app: qemu
|
name: qemu
|
||||||
type: ClusterIP
|
ports:
|
||||||
|
- name: tcp-8006
|
||||||
|
protocol: TCP
|
||||||
|
port: 8006
|
||||||
|
targetPort: 8006
|
||||||
|
nodePort: 48006
|
||||||
|
|
244
readme.md
244
readme.md
|
@ -7,24 +7,25 @@
|
||||||
[![Build]][build_url]
|
[![Build]][build_url]
|
||||||
[![Version]][tag_url]
|
[![Version]][tag_url]
|
||||||
[![Size]][tag_url]
|
[![Size]][tag_url]
|
||||||
[![Package]][pkg_url]
|
|
||||||
[![Pulls]][hub_url]
|
[![Pulls]][hub_url]
|
||||||
|
|
||||||
</div></h1>
|
</div></h1>
|
||||||
|
|
||||||
Docker container for running ARM-based virtual machines using QEMU, for devices like the Raspberry Pi 5 and many others.
|
QEMU in a docker container for running ARM-based virtual machines, for devices like the Raspberry Pi 5 and many others.
|
||||||
|
|
||||||
## Features ✨
|
It uses high-performance QEMU options (like KVM acceleration, kernel-mode networking, IO threading, etc.) to achieve near-native speed.
|
||||||
|
|
||||||
- Web-based viewer to control the machine directly from your browser
|
Note: for KVM acceleration you need a Linux-based operating system, as it's not available on MacOS unfortunately.
|
||||||
|
|
||||||
- Supports `.iso`, `.img`, `.qcow2`, `.vhd`, `.vhdx`, `.vdi`, `.vmdk` and `.raw` disk formats
|
## Features
|
||||||
|
|
||||||
- High-performance options (like KVM acceleration, kernel-mode networking, IO threading, etc.) to achieve near-native speed
|
- Multi-platform
|
||||||
|
- KVM acceleration
|
||||||
|
- Web-based viewer
|
||||||
|
|
||||||
## Usage 🐳
|
## Usage
|
||||||
|
|
||||||
##### Via Docker Compose:
|
Via Docker Compose:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
|
@ -32,122 +33,54 @@ services:
|
||||||
container_name: qemu
|
container_name: qemu
|
||||||
image: qemux/qemu-arm
|
image: qemux/qemu-arm
|
||||||
environment:
|
environment:
|
||||||
BOOT: "alpine"
|
BOOT: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso"
|
||||||
devices:
|
devices:
|
||||||
- /dev/kvm
|
- /dev/kvm
|
||||||
- /dev/net/tun
|
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
ports:
|
ports:
|
||||||
- 8006:8006
|
- 8006:8006
|
||||||
volumes:
|
|
||||||
- ./qemu:/storage
|
|
||||||
restart: always
|
|
||||||
stop_grace_period: 2m
|
stop_grace_period: 2m
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Via Docker CLI:
|
Via Docker CLI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -it --rm --name qemu -e "BOOT=alpine" -p 8006:8006 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/qemu:/storage" --stop-timeout 120 qemux/qemu-arm
|
docker run -it --name qemu -e "BOOT=http://example.com/image.iso" -p 8006:8006 --device=/dev/kvm --cap-add NET_ADMIN qemux/qemu-arm
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Via Kubernetes:
|
Via Kubernetes:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/master/kubernetes.yml
|
kubectl apply -f kubernetes.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Via Github Codespaces:
|
## FAQ
|
||||||
|
|
||||||
[](https://codespaces.new/qemus/qemu)
|
* ### How do I use it?
|
||||||
|
|
||||||
## FAQ 💬
|
|
||||||
|
|
||||||
### How do I use it?
|
|
||||||
|
|
||||||
Very simple! These are the steps:
|
Very simple! These are the steps:
|
||||||
|
|
||||||
- Set the `BOOT` variable to the [operating system](#how-do-i-select-the-operating-system) you want to install.
|
- Set the `BOOT` environment variable to the URL of an ISO image you want to install.
|
||||||
|
|
||||||
- Start the container and connect to [port 8006](http://127.0.0.1:8006/) using your web browser.
|
- Start the container and connect to [port 8006](http://localhost:8006) using your web browser.
|
||||||
|
|
||||||
- You will see the screen and can now install the OS of your choice using your keyboard and mouse.
|
- You will see the screen and can now install the OS of your choice using your keyboard and mouse.
|
||||||
|
|
||||||
Enjoy your brand new machine, and don't forget to star this repo!
|
Enjoy your brand new machine, and don't forget to star this repo!
|
||||||
|
|
||||||
### How do I select the operating system?
|
* ### How do I change the storage location?
|
||||||
|
|
||||||
You can use the `BOOT` environment variable in order to specify the operating system that will be downloaded:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
BOOT: "alpine"
|
|
||||||
```
|
|
||||||
Select from the values below:
|
|
||||||
|
|
||||||
| **Value** | **Operating System** | **Size** |
|
|
||||||
|---|---|---|
|
|
||||||
| `alma` | Alma Linux | 1.7 GB |
|
|
||||||
| `alpine` | Alpine Linux | 60 MB |
|
|
||||||
| `cachy` | CachyOS | 2.6 GB |
|
|
||||||
| `centos` | CentOS | 6.4 GB |
|
|
||||||
| `debian` | Debian | 3.7 GB |
|
|
||||||
| `fedora` | Fedora | 2.9 GB |
|
|
||||||
| `gentoo` | Gentoo | 1.3 GB |
|
|
||||||
| `kali` | Kali Linux | 3.4 GB |
|
|
||||||
| `nixos` | NixOS | 2.4 GB |
|
|
||||||
| `suse` | OpenSUSE | 1.0 GB |
|
|
||||||
| `oracle` | Oracle Linux | 1.0 GB |
|
|
||||||
| `rocky` | Rocky Linux | 1.9 GB |
|
|
||||||
| `ubuntu` | Ubuntu Desktop | 3.3 GB |
|
|
||||||
| `ubuntus` | Ubuntu Server | 2.7 GB |
|
|
||||||
|
|
||||||
### How can I use my own image?
|
|
||||||
|
|
||||||
If you want to download an operating system that is not in the list above, you can set the `BOOT` variable to the URL of the image:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
BOOT: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso"
|
|
||||||
```
|
|
||||||
|
|
||||||
The `BOOT` URL accepts files in any of the following formats:
|
|
||||||
|
|
||||||
| **Extension** | **Format** |
|
|
||||||
|---|---|
|
|
||||||
| `.img` | Raw |
|
|
||||||
| `.raw` | Raw |
|
|
||||||
| `.iso` | Optical |
|
|
||||||
| `.qcow2` | QEMU |
|
|
||||||
| `.vmdk` | VMware |
|
|
||||||
| `.vhd` | VirtualPC |
|
|
||||||
| `.vhdx` | Hyper-V |
|
|
||||||
| `.vdi` | VirtualBox |
|
|
||||||
|
|
||||||
It will also accept files such as `.img.gz`, `.qcow2.xz`, `.iso.zip` and many more, because it will automaticly extract compressed files.
|
|
||||||
|
|
||||||
Alternatively you can use a local image file directly, by binding it in your compose file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
volumes:
|
|
||||||
- ./example.iso:/boot.iso
|
|
||||||
```
|
|
||||||
|
|
||||||
This way you can supply either a `/boot.iso`, `/boot.img` or a `/boot.qcow2` file. The value of `BOOT` will be ignored in this case.
|
|
||||||
|
|
||||||
### How do I change the storage location?
|
|
||||||
|
|
||||||
To change the storage location, include the following bind mount in your compose file:
|
To change the storage location, include the following bind mount in your compose file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
volumes:
|
volumes:
|
||||||
- ./qemu:/storage
|
- /var/qemu:/storage
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace the example path `./qemu` with the desired storage folder or named volume.
|
Replace the example path `/var/qemu` with the desired storage folder.
|
||||||
|
|
||||||
### How do I change the size of the disk?
|
* ### How do I change the size of the disk?
|
||||||
|
|
||||||
To expand the default size of 16 GB, add the `DISK_SIZE` setting to your compose file and set it to your preferred capacity:
|
To expand the default size of 16 GB, add the `DISK_SIZE` setting to your compose file and set it to your preferred capacity:
|
||||||
|
|
||||||
|
@ -156,85 +89,47 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
||||||
DISK_SIZE: "128G"
|
DISK_SIZE: "128G"
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!TIP]
|
This can also be used to resize the existing disk to a larger capacity without any data loss.
|
||||||
> This can also be used to resize the existing disk to a larger capacity without any data loss.
|
|
||||||
|
|
||||||
### How do I change the amount of CPU or RAM?
|
* ### How do I boot a local image?
|
||||||
|
|
||||||
By default, the container will be allowed to use a maximum of 2 CPU cores and 2 GB of RAM.
|
You can use a local file directly, and skip the download altogether, by binding it in your compose file in this way:
|
||||||
|
|
||||||
If you want to adjust this, you can specify the desired amount using the following environment variables:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
volumes:
|
||||||
RAM_SIZE: "8G"
|
- /home/user/example.iso:/boot.iso
|
||||||
CPU_CORES: "4"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### How do I increase the display resolution?
|
Replace the example path `/home/user/example.iso` with the filename of the desired ISO file.
|
||||||
|
|
||||||
For maximum compatibility, the display output will be a simple framebuffer by default. While this isn't the most optimal, it doesn't require any drivers.
|
* ### How do I boot a x86 image?
|
||||||
|
|
||||||
If your guest OS bundles the `virtio-gpu` driver (as most Linux distributions do), you can add the following to your compose file:
|
You can use [qemu-docker](https://github.com/qemus/qemu-docker/) to run x86 and x64 images on ARM.
|
||||||
|
|
||||||
```yaml
|
* ### How do I verify if my system supports KVM?
|
||||||
environment:
|
|
||||||
VGA: "virtio-gpu"
|
|
||||||
```
|
|
||||||
|
|
||||||
to add a virtual graphics cards to your machine that allows for higher resolutions.
|
To verify if your system supports KVM, run the following commands:
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Using this method your screen will stay black during the boot process, until the point where the driver is actually loaded.
|
|
||||||
|
|
||||||
### How do I boot Windows?
|
|
||||||
|
|
||||||
Use [dockur/windows-arm](https://github.com/dockur/windows-arm) instead, as it includes all the drivers required during installation, amongst many other features.
|
|
||||||
|
|
||||||
### How do I boot x86/x64 images?
|
|
||||||
|
|
||||||
You can use the [qemu](https://github.com/qemus/qemu/) container to run x86 and x64 images on ARM.
|
|
||||||
|
|
||||||
### How do I verify if my system supports KVM?
|
|
||||||
|
|
||||||
First check if your software is compatible using this chart:
|
|
||||||
|
|
||||||
| **Product** | **Linux** | **Win11** | **Win10** | **macOS** |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| Docker CLI | ✅ | ✅ | ❌ | ❌ |
|
|
||||||
| Docker Desktop | ❌ | ✅ | ❌ | ❌ |
|
|
||||||
| Podman CLI | ✅ | ✅ | ❌ | ❌ |
|
|
||||||
| Podman Desktop | ✅ | ✅ | ❌ | ❌ |
|
|
||||||
|
|
||||||
After that you can run the following commands in Linux to check your system:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install cpu-checker
|
sudo apt install cpu-checker
|
||||||
sudo kvm-ok
|
sudo kvm-ok
|
||||||
```
|
```
|
||||||
|
|
||||||
If you receive an error from `kvm-ok` indicating that KVM cannot be used, please check whether:
|
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check the virtualization settings in the BIOS.
|
||||||
|
|
||||||
- the virtualization extensions (`Intel VT-x` or `AMD SVM`) are enabled in your BIOS.
|
* ### How do I increase the amount of CPU or RAM?
|
||||||
|
|
||||||
- you enabled "nested virtualization" if you are running the container inside a virtual machine.
|
By default, a single CPU core and 1 GB of RAM are allocated to the container.
|
||||||
|
|
||||||
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's.
|
If there arises a need to increase this, add the following environment variables:
|
||||||
|
|
||||||
If you did not receive any error from `kvm-ok` but the container still complains about a missing KVM device, it could help to add `privileged: true` to your compose file (or `sudo` to your `docker` command) to rule out any permission issue.
|
```yaml
|
||||||
|
environment:
|
||||||
|
RAM_SIZE: "4G"
|
||||||
|
CPU_CORES: "4"
|
||||||
|
```
|
||||||
|
|
||||||
### How do I expose network ports?
|
* ### How do I assign an individual IP address to the container?
|
||||||
|
|
||||||
You can expose ports just by adding them to your compose file. If you want to be able to connect to the SSH service of the machine for example, you would add it like this:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
ports:
|
|
||||||
- 2222:22
|
|
||||||
```
|
|
||||||
|
|
||||||
This will make port 2222 on your host redirect to port 22 of the virtual machine.
|
|
||||||
|
|
||||||
### How do I assign an individual IP address to the container?
|
|
||||||
|
|
||||||
By default, the container uses bridge networking, which shares the IP address with the host.
|
By default, the container uses bridge networking, which shares the IP address with the host.
|
||||||
|
|
||||||
|
@ -268,14 +163,13 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
||||||
|
|
||||||
An added benefit of this approach is that you won't have to perform any port mapping anymore, since all ports will be exposed by default.
|
An added benefit of this approach is that you won't have to perform any port mapping anymore, since all ports will be exposed by default.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
Please note that this IP address won't be accessible from the Docker host due to the design of macvlan, which doesn't permit communication between the two. If this is a concern, you need to create a [second macvlan](https://blog.oddbit.com/post/2018-03-12-using-docker-macvlan-networks/#host-access) as a workaround.
|
||||||
> This IP address won't be accessible from the Docker host due to the design of macvlan, which doesn't permit communication between the two. If this is a concern, you need to create a [second macvlan](https://blog.oddbit.com/post/2018-03-12-using-docker-macvlan-networks/#host-access) as a workaround.
|
|
||||||
|
|
||||||
### How can the VM acquire an IP address from my router?
|
* ### How can the VM acquire an IP address from my router?
|
||||||
|
|
||||||
After configuring the container for [macvlan](#how-do-i-assign-an-individual-ip-address-to-the-container), it is possible for the VM to become part of your home network by requesting an IP from your router, just like a real PC.
|
After configuring the container for macvlan (see above), it is possible for the VM to become part of your home network by requesting an IP from your router, just like a real PC.
|
||||||
|
|
||||||
To enable this mode, in which the container and the VM will have separate IP addresses, add the following lines to your compose file:
|
To enable this mode, add the following lines to your compose file:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
environment:
|
environment:
|
||||||
|
@ -286,7 +180,9 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
||||||
- 'c *:* rwm'
|
- 'c *:* rwm'
|
||||||
```
|
```
|
||||||
|
|
||||||
### How do I add multiple disks?
|
Please note that in this mode, the container and the VM will each have their own separate IPs. The container will keep the macvlan IP, and the VM will use the DHCP IP.
|
||||||
|
|
||||||
|
* ### How do I add multiple disks?
|
||||||
|
|
||||||
To create additional disks, modify your compose file like this:
|
To create additional disks, modify your compose file like this:
|
||||||
|
|
||||||
|
@ -295,23 +191,23 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
||||||
DISK2_SIZE: "32G"
|
DISK2_SIZE: "32G"
|
||||||
DISK3_SIZE: "64G"
|
DISK3_SIZE: "64G"
|
||||||
volumes:
|
volumes:
|
||||||
- ./example2:/storage2
|
- /home/example:/storage2
|
||||||
- ./example3:/storage3
|
- /mnt/data/example:/storage3
|
||||||
```
|
```
|
||||||
|
|
||||||
### How do I pass-through a disk?
|
* ### How do I pass-through a disk?
|
||||||
|
|
||||||
It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way:
|
It is possible to pass-through disk devices directly by adding them to your compose file in this way:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
devices:
|
devices:
|
||||||
- /dev/sdb:/disk1
|
- /dev/sdb:/disk1
|
||||||
- /dev/sdc1:/disk2
|
- /dev/sdc:/disk2
|
||||||
```
|
```
|
||||||
|
|
||||||
Use `/disk1` if you want it to become your main drive, and use `/disk2` and higher to add them as secondary drives.
|
Use `/disk1` if you want it to become your main drive, and use `/disk2` and higher to add them as secondary drives.
|
||||||
|
|
||||||
### How do I pass-through a USB device?
|
* ### How do I pass-through a USB device?
|
||||||
|
|
||||||
To pass-through a USB device, first lookup its vendor and product id via the `lsusb` command, then add them to your compose file like this:
|
To pass-through a USB device, first lookup its vendor and product id via the `lsusb` command, then add them to your compose file like this:
|
||||||
|
|
||||||
|
@ -322,24 +218,7 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
||||||
- /dev/bus/usb
|
- /dev/bus/usb
|
||||||
```
|
```
|
||||||
|
|
||||||
### How do I share files with the host?
|
* ### How can I provide custom arguments to QEMU?
|
||||||
|
|
||||||
To share files with the host, first ensure that your guest OS has `9pfs` support compiled in or available as a kernel module. If so, add the following volume to your compose file:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
volumes:
|
|
||||||
- ./example:/shared
|
|
||||||
```
|
|
||||||
|
|
||||||
Then start the container and execute the following command in the guest:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
mount -t 9p -o trans=virtio shared /mnt/example
|
|
||||||
```
|
|
||||||
|
|
||||||
Now the `./example` directory on the host will be available as `/mnt/example` in the guest.
|
|
||||||
|
|
||||||
### How can I provide custom arguments to QEMU?
|
|
||||||
|
|
||||||
You can create the `ARGUMENTS` environment variable to provide additional arguments to QEMU at runtime:
|
You can create the `ARGUMENTS` environment variable to provide additional arguments to QEMU at runtime:
|
||||||
|
|
||||||
|
@ -348,23 +227,14 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
||||||
ARGUMENTS: "-device usb-tablet"
|
ARGUMENTS: "-device usb-tablet"
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to see the full command-line arguments used, you can set:
|
## Stars
|
||||||
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
DEBUG: "Y"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Stars 🌟
|
|
||||||
[](https://starchart.cc/qemus/qemu-arm)
|
[](https://starchart.cc/qemus/qemu-arm)
|
||||||
|
|
||||||
[build_url]: https://github.com/qemus/qemu-arm/
|
[build_url]: https://github.com/qemus/qemu-arm/
|
||||||
[hub_url]: https://hub.docker.com/r/qemux/qemu-arm/
|
[hub_url]: https://hub.docker.com/r/qemux/qemu-arm/
|
||||||
[tag_url]: https://hub.docker.com/r/qemux/qemu-arm/tags
|
[tag_url]: https://hub.docker.com/r/qemux/qemu-arm/tags
|
||||||
[pkg_url]: https://github.com/qemus/qemu-arm/pkgs/container/qemu-arm
|
|
||||||
|
|
||||||
[Build]: https://github.com/qemus/qemu-arm/actions/workflows/build.yml/badge.svg
|
[Build]: https://github.com/qemus/qemu-arm/actions/workflows/build.yml/badge.svg
|
||||||
[Size]: https://img.shields.io/docker/image-size/qemux/qemu-arm/latest?color=066da5&label=size
|
[Size]: https://img.shields.io/docker/image-size/qemux/qemu-arm/latest?color=066da5&label=size
|
||||||
[Pulls]: https://img.shields.io/docker/pulls/qemux/qemu-arm.svg?style=flat&label=pulls&logo=docker
|
[Pulls]: https://img.shields.io/docker/pulls/qemux/qemu-arm.svg?style=flat&label=pulls&logo=docker
|
||||||
[Version]: https://img.shields.io/docker/v/qemux/qemu-arm/latest?arch=arm64&sort=semver&color=066da5
|
[Version]: https://img.shields.io/docker/v/qemux/qemu-arm/latest?arch=arm64&sort=semver&color=066da5
|
||||||
[Package]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2Fqemus%2Fqemu-arm%2Fqemu-arm.json&query=%24.downloads&logo=github&style=flat&color=066da5&label=pulls
|
|
||||||
|
|
120
src/boot.sh
120
src/boot.sh
|
@ -2,115 +2,69 @@
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
: "${BIOS:=""}" # BIOS file
|
: "${BIOS:=""}" # Bios file
|
||||||
: "${SECURE:="off"}" # Secure boot
|
|
||||||
|
|
||||||
BOOT_DESC=""
|
|
||||||
BOOT_OPTS=""
|
BOOT_OPTS=""
|
||||||
[ -n "$BIOS" ] && BOOT_MODE="custom"
|
BOOT_DESC=""
|
||||||
|
SECURE=",secure=off"
|
||||||
|
DIR="/usr/share/qemu"
|
||||||
|
|
||||||
case "${BOOT_MODE,,}" in
|
case "${BOOT_MODE,,}" in
|
||||||
"uefi" | "" )
|
uefi)
|
||||||
BOOT_MODE="uefi"
|
|
||||||
ROM="AAVMF_CODE.no-secboot.fd"
|
ROM="AAVMF_CODE.no-secboot.fd"
|
||||||
VARS="AAVMF_VARS.fd"
|
VARS="AAVMF_VARS.fd"
|
||||||
;;
|
;;
|
||||||
"secure" )
|
secure)
|
||||||
SECURE="on"
|
SECURE=",secure=on"
|
||||||
BOOT_DESC=" securely"
|
BOOT_DESC=" securely"
|
||||||
ROM="AAVMF_CODE.secboot.fd"
|
ROM="AAVMF_CODE.secboot.fd"
|
||||||
VARS="AAVMF_VARS.fd"
|
VARS="AAVMF_VARS.fd"
|
||||||
;;
|
;;
|
||||||
"windows" )
|
windows)
|
||||||
ROM="AAVMF_CODE.no-secboot.fd"
|
ROM="AAVMF_CODE.no-secboot.fd"
|
||||||
VARS="AAVMF_VARS.fd"
|
VARS="AAVMF_VARS.fd"
|
||||||
BOOT_OPTS="-rtc base=localtime"
|
BOOT_OPTS="-rtc base=localtime"
|
||||||
;;
|
;;
|
||||||
"windows_secure" )
|
windows_secure)
|
||||||
SECURE="on"
|
SECURE=",secure=on"
|
||||||
BOOT_DESC=" securely"
|
BOOT_DESC=" securely"
|
||||||
ROM="AAVMF_CODE.ms.fd"
|
ROM="AAVMF_CODE.ms.fd"
|
||||||
VARS="AAVMF_VARS.ms.fd"
|
VARS="AAVMF_VARS.ms.fd"
|
||||||
BOOT_OPTS="-rtc base=localtime"
|
BOOT_OPTS="-rtc base=localtime"
|
||||||
;;
|
;;
|
||||||
"legacy" )
|
|
||||||
BOOT_DESC=" with SeaBIOS"
|
|
||||||
;;
|
|
||||||
"custom" )
|
|
||||||
BOOT_OPTS="-bios $BIOS"
|
|
||||||
BOOT_DESC=" with custom BIOS file"
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
error "Unknown BOOT_MODE, value \"${BOOT_MODE}\" is not recognized!"
|
info "Unknown boot mode '${BOOT_MODE}', defaulting to 'uefi'"
|
||||||
exit 33
|
BOOT_MODE="uefi"
|
||||||
|
ROM="AAVMF_CODE.no-secboot.fd"
|
||||||
|
VARS="AAVMF_VARS.fd"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case "${BOOT_MODE,,}" in
|
if [ -n "$BIOS" ]; then
|
||||||
"uefi" | "secure" | "windows" | "windows_secure" )
|
|
||||||
|
|
||||||
AAVMF="/usr/share/AAVMF/"
|
BOOT_OPTS="$BOOT_OPTS -bios $DIR/$BIOS"
|
||||||
DEST="$STORAGE/${BOOT_MODE,,}"
|
return 0
|
||||||
|
|
||||||
if [ ! -s "$DEST.rom" ] || [ ! -f "$DEST.rom" ]; then
|
|
||||||
[ ! -s "$AAVMF/$ROM" ] || [ ! -f "$AAVMF/$ROM" ] && error "UEFI boot file ($AAVMF/$ROM) not found!" && exit 44
|
|
||||||
rm -f "$DEST.rom"
|
|
||||||
dd if=/dev/zero "of=$DEST.rom" bs=1M count=64 status=none
|
|
||||||
dd "if=$AAVMF/$ROM" "of=$DEST.rom" conv=notrunc status=none
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -s "$DEST.vars" ] || [ ! -f "$DEST.vars" ]; then
|
|
||||||
[ ! -s "$AAVMF/$VARS" ] || [ ! -f "$AAVMF/$VARS" ] && error "UEFI vars file ($AAVMF/$VARS) not found!" && exit 45
|
|
||||||
rm -f "$DEST.vars"
|
|
||||||
dd if=/dev/zero "of=$DEST.vars" bs=1M count=64 status=none
|
|
||||||
dd "if=$AAVMF/$VARS" "of=$DEST.vars" conv=notrunc status=none
|
|
||||||
fi
|
|
||||||
|
|
||||||
BOOT_OPTS+=" -drive file=$DEST.rom,if=pflash,unit=0,format=raw,readonly=on"
|
|
||||||
BOOT_OPTS+=" -drive file=$DEST.vars,if=pflash,unit=1,format=raw"
|
|
||||||
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
MSRS="/sys/module/kvm/parameters/ignore_msrs"
|
|
||||||
if [ -e "$MSRS" ]; then
|
|
||||||
result=$(<"$MSRS")
|
|
||||||
result="${result//[![:print:]]/}"
|
|
||||||
if [[ "$result" == "0" ]] || [[ "${result^^}" == "N" ]]; then
|
|
||||||
echo 1 | tee "$MSRS" > /dev/null 2>&1 || true
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
CLOCKSOURCE="tsc"
|
|
||||||
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
|
|
||||||
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
|
|
||||||
|
|
||||||
if [ ! -f "$CLOCK" ]; then
|
|
||||||
warn "file \"$CLOCK\" cannot not found?"
|
|
||||||
else
|
|
||||||
result=$(<"$CLOCK")
|
|
||||||
result="${result//[![:print:]]/}"
|
|
||||||
case "${result,,}" in
|
|
||||||
"${CLOCKSOURCE,,}" ) ;;
|
|
||||||
"kvm-clock" ) info "Nested KVM virtualization detected.." ;;
|
|
||||||
"hyperv_clocksource_tsc_page" ) info "Nested Hyper-V virtualization detected.." ;;
|
|
||||||
"hpet" ) warn "unsupported clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
|
||||||
*) warn "unexpected clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
SM_BIOS=""
|
|
||||||
PS="/sys/class/dmi/id/product_serial"
|
|
||||||
|
|
||||||
if [ -s "$PS" ] && [ -r "$PS" ]; then
|
|
||||||
|
|
||||||
BIOS_SERIAL=$(<"$PS")
|
|
||||||
BIOS_SERIAL="${BIOS_SERIAL//[![:alnum:]]/}"
|
|
||||||
|
|
||||||
if [ -n "$BIOS_SERIAL" ]; then
|
|
||||||
SM_BIOS="-smbios type=1,serial=$BIOS_SERIAL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AAVMF="/usr/share/AAVMF/"
|
||||||
|
DEST="$STORAGE/${BOOT_MODE,,}"
|
||||||
|
|
||||||
|
if [ ! -s "$DEST.rom" ] || [ ! -f "$DEST.rom" ]; then
|
||||||
|
[ ! -s "$AAVMF/$ROM" ] || [ ! -f "$AAVMF/$ROM" ] && error "UEFI boot file ($AAVMF/$ROM) not found!" && exit 44
|
||||||
|
rm -f "$DEST.rom"
|
||||||
|
dd if=/dev/zero "of=$DEST.rom" bs=1M count=64 status=none
|
||||||
|
dd "if=$AAVMF/$ROM" "of=$DEST.rom" conv=notrunc status=none
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$DEST.vars" ] || [ ! -f "$DEST.vars" ]; then
|
||||||
|
[ ! -s "$AAVMF/$VARS" ] || [ ! -f "$AAVMF/$VARS" ] && error "UEFI vars file ($AAVMF/$VARS) not found!" && exit 45
|
||||||
|
rm -f "$DEST.vars"
|
||||||
|
dd if=/dev/zero "of=$DEST.vars" bs=1M count=64 status=none
|
||||||
|
dd "if=$AAVMF/$VARS" "of=$DEST.vars" conv=notrunc status=none
|
||||||
|
fi
|
||||||
|
|
||||||
|
BOOT_OPTS="$BOOT_OPTS -drive file=$DEST.rom,if=pflash,unit=0,format=raw,readonly=on"
|
||||||
|
BOOT_OPTS="$BOOT_OPTS -drive file=$DEST.vars,if=pflash,unit=1,format=raw"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
|
@ -1,37 +1,22 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
: "${UUID:=""}"
|
|
||||||
: "${SERIAL:="mon:stdio"}"
|
: "${SERIAL:="mon:stdio"}"
|
||||||
: "${USB:="qemu-xhci,id=xhci,p2=7,p3=7"}"
|
: "${USB:="qemu-xhci,id=xhci"}"
|
||||||
: "${MONITOR:="telnet:localhost:7100,server,nowait,nodelay"}"
|
: "${MONITOR:="telnet:localhost:7100,server,nowait,nodelay"}"
|
||||||
: "${SMP:="$CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"}"
|
|
||||||
|
|
||||||
DEF_OPTS="-nodefaults"
|
DEF_OPTS="-nodefaults"
|
||||||
SERIAL_OPTS="-serial $SERIAL"
|
SERIAL_OPTS="-serial $SERIAL"
|
||||||
CPU_OPTS="-cpu $CPU_FLAGS -smp $SMP"
|
USB_OPTS="-device $USB -device usb-kbd -device usb-tablet"
|
||||||
RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||||
|
CPU_OPTS="-cpu $CPU_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
|
||||||
MON_OPTS="-monitor $MONITOR -name $PROCESS,process=$PROCESS,debug-threads=on"
|
MON_OPTS="-monitor $MONITOR -name $PROCESS,process=$PROCESS,debug-threads=on"
|
||||||
MAC_OPTS="-machine type=${MACHINE},secure=${SECURE},dump-guest-core=off${KVM_OPTS}"
|
MAC_OPTS="-machine type=${MACHINE}${SECURE},dump-guest-core=off${KVM_OPTS}"
|
||||||
|
|
||||||
[ -n "$UUID" ] && MAC_OPTS+=" -uuid $UUID"
|
|
||||||
[ -n "$SM_BIOS" ] && MAC_OPTS+=" $SM_BIOS"
|
|
||||||
|
|
||||||
DEV_OPTS="-object rng-random,id=objrng0,filename=/dev/urandom"
|
DEV_OPTS="-object rng-random,id=objrng0,filename=/dev/urandom"
|
||||||
DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0"
|
DEV_OPTS="$DEV_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
|
||||||
|
[[ "${BOOT_MODE,,}" != "windows"* ]] && DEV_OPTS="$DEV_OPTS -device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
|
||||||
|
|
||||||
if [[ "${BOOT_MODE,,}" != "windows"* ]]; then
|
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"
|
||||||
DEV_OPTS+=" -device virtio-balloon-pci,id=balloon0,bus=pcie.0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "/shared" ] && [[ "${BOOT_MODE,,}" != "windows"* ]]; then
|
|
||||||
DEV_OPTS+=" -fsdev local,id=fsdev0,path=/shared,security_model=none"
|
|
||||||
DEV_OPTS+=" -device virtio-9p-pci,id=fs0,fsdev=fsdev0,mount_tag=shared"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[ -n "$USB" ] && [[ "${USB,,}" != "no"* ]] && USB_OPTS="-device $USB -device usb-kbd -device usb-tablet"
|
|
||||||
|
|
||||||
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"
|
|
||||||
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
|
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
|
||||||
|
|
||||||
if [[ "${DISPLAY,,}" == "web" ]]; then
|
if [[ "${DISPLAY,,}" == "web" ]]; then
|
||||||
|
@ -49,30 +34,16 @@ fi
|
||||||
|
|
||||||
# Check available memory as the very last step
|
# Check available memory as the very last step
|
||||||
|
|
||||||
if [[ "$RAM_CHECK" != [Nn]* ]]; then
|
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||||
|
AVAIL_GB=$(( (RAM_AVAIL + 1073741823)/1073741824 ))
|
||||||
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
|
||||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
|
||||||
|
|
||||||
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
|
|
||||||
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
|
|
||||||
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
|
|
||||||
info "$msg"
|
|
||||||
else
|
|
||||||
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
|
|
||||||
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available, please consider a lower value."
|
|
||||||
if [[ "${FS,,}" != "zfs" ]]; then
|
|
||||||
warn "$msg"
|
|
||||||
else
|
|
||||||
info "$msg"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
if (( (RAM_WANTED + 500000000) > RAM_AVAIL )); then
|
||||||
|
error "Your configured RAM_SIZE of $WANTED_GB GB is higher than the $AVAIL_GB GB of memory available, please set a lower value."
|
||||||
|
exit 17
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
if (( (RAM_WANTED + 1450000000) > RAM_AVAIL )); then
|
||||||
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
|
warn "your configured RAM_SIZE of $WANTED_GB GB is much too close to the $AVAIL_GB GB of memory available, please set a lower value."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
537
src/disk.sh
Normal file
537
src/disk.sh
Normal file
|
@ -0,0 +1,537 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
# Docker environment variables
|
||||||
|
|
||||||
|
: "${DISK_IO:="native"}" # I/O Mode, can be set to 'native', 'threads' or 'io_turing'
|
||||||
|
: "${DISK_FMT:=""}" # Disk file format, can be set to "raw" (default) or "qcow2"
|
||||||
|
: "${DISK_FLAGS:=""}" # Specifies the options for use with the qcow2 disk format
|
||||||
|
: "${DISK_CACHE:="none"}" # Caching mode, can be set to 'writeback' for better performance
|
||||||
|
: "${DISK_DISCARD:="on"}" # Controls whether unmap (TRIM) commands are passed to the host.
|
||||||
|
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
|
||||||
|
|
||||||
|
[ ! -f "$BOOT" ] || [ ! -s "$BOOT" ] && BOOT="/dev/null"
|
||||||
|
|
||||||
|
DISK_OPTS="-object iothread,id=io2"
|
||||||
|
DISK_OPTS="$DISK_OPTS -drive id=cdrom0,media=cdrom,if=none,format=raw,readonly=on,file=$BOOT"
|
||||||
|
DISK_OPTS="$DISK_OPTS -device virtio-scsi-pci,id=scsi0,iothread=io2,addr=0x5"
|
||||||
|
DISK_OPTS="$DISK_OPTS -device scsi-cd,bus=scsi0.0,drive=cdrom0,bootindex=$BOOT_INDEX"
|
||||||
|
|
||||||
|
DRIVERS="/drivers.iso"
|
||||||
|
[ ! -f "$DRIVERS" ] || [ ! -s "$DRIVERS" ] && DRIVERS="$STORAGE/drivers.iso"
|
||||||
|
[ ! -f "$DRIVERS" ] || [ ! -s "$DRIVERS" ] && DRIVERS="/run/drivers.iso"
|
||||||
|
|
||||||
|
if [ -f "$DRIVERS" ]; then
|
||||||
|
DISK_OPTS="$DISK_OPTS -drive id=cdrom1,media=cdrom,if=none,format=raw,readonly=on,file=$DRIVERS -device usb-storage,drive=cdrom1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fmt2ext() {
|
||||||
|
local DISK_FMT=$1
|
||||||
|
|
||||||
|
case "${DISK_FMT,,}" in
|
||||||
|
qcow2)
|
||||||
|
echo "qcow2"
|
||||||
|
;;
|
||||||
|
raw)
|
||||||
|
echo "img"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unrecognized disk format: $DISK_FMT" && exit 78
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
ext2fmt() {
|
||||||
|
local DISK_EXT=$1
|
||||||
|
|
||||||
|
case "${DISK_EXT,,}" in
|
||||||
|
qcow2)
|
||||||
|
echo "qcow2"
|
||||||
|
;;
|
||||||
|
img)
|
||||||
|
echo "raw"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unrecognized file extension: .$DISK_EXT" && exit 78
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
getSize() {
|
||||||
|
local DISK_FILE=$1
|
||||||
|
local DISK_EXT DISK_FMT
|
||||||
|
|
||||||
|
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
|
||||||
|
DISK_FMT=$(ext2fmt "$DISK_EXT")
|
||||||
|
|
||||||
|
case "${DISK_FMT,,}" in
|
||||||
|
raw)
|
||||||
|
stat -c%s "$DISK_FILE"
|
||||||
|
;;
|
||||||
|
qcow2)
|
||||||
|
qemu-img info "$DISK_FILE" -f "$DISK_FMT" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/'
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unrecognized disk format: $DISK_FMT" && exit 78
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
isCow() {
|
||||||
|
local FS=$1
|
||||||
|
|
||||||
|
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
supportsDirect() {
|
||||||
|
local FS=$1
|
||||||
|
|
||||||
|
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
createDisk() {
|
||||||
|
local DISK_FILE=$1
|
||||||
|
local DISK_SPACE=$2
|
||||||
|
local DISK_DESC=$3
|
||||||
|
local DISK_FMT=$4
|
||||||
|
local FS=$5
|
||||||
|
local DATA_SIZE DIR SPACE FA
|
||||||
|
|
||||||
|
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||||
|
|
||||||
|
rm -f "$DISK_FILE"
|
||||||
|
|
||||||
|
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||||
|
|
||||||
|
# Check free diskspace
|
||||||
|
DIR=$(dirname "$DISK_FILE")
|
||||||
|
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||||
|
|
||||||
|
if (( DATA_SIZE > SPACE )); then
|
||||||
|
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
|
||||||
|
error "Not enough free space to create a $DISK_DESC of $DISK_SPACE in $DIR, it has only $SPACE_GB GB available..."
|
||||||
|
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
html "Creating a $DISK_DESC image..."
|
||||||
|
info "Creating a $DISK_SPACE $DISK_TYPE $DISK_DESC image in $DISK_FMT format..."
|
||||||
|
|
||||||
|
local FAIL="Could not create a $DISK_TYPE $DISK_FMT $DISK_DESC image of $DISK_SPACE ($DISK_FILE)"
|
||||||
|
|
||||||
|
case "${DISK_FMT,,}" in
|
||||||
|
raw)
|
||||||
|
|
||||||
|
if isCow "$FS"; then
|
||||||
|
if ! touch "$DISK_FILE"; then
|
||||||
|
error "$FAIL" && exit 77
|
||||||
|
fi
|
||||||
|
{ chattr +C "$DISK_FILE"; } || :
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||||
|
|
||||||
|
# Create an empty file
|
||||||
|
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
||||||
|
rm -f "$DISK_FILE"
|
||||||
|
error "$FAIL" && exit 77
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# Create an empty file
|
||||||
|
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then
|
||||||
|
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
||||||
|
rm -f "$DISK_FILE"
|
||||||
|
error "$FAIL" && exit 77
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
qcow2)
|
||||||
|
|
||||||
|
local DISK_PARAM="$DISK_ALLOC"
|
||||||
|
isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
|
||||||
|
[ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
|
||||||
|
|
||||||
|
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
|
||||||
|
rm -f "$DISK_FILE"
|
||||||
|
error "$FAIL" && exit 70
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if isCow "$FS"; then
|
||||||
|
FA=$(lsattr "$DISK_FILE")
|
||||||
|
if [[ "$FA" != *"C"* ]]; then
|
||||||
|
error "Failed to disable COW for $DISK_DESC image $DISK_FILE on ${FS^^} filesystem (returned $FA)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeDisk() {
|
||||||
|
local DISK_FILE=$1
|
||||||
|
local DISK_SPACE=$2
|
||||||
|
local DISK_DESC=$3
|
||||||
|
local DISK_FMT=$4
|
||||||
|
local FS=$5
|
||||||
|
local CUR_SIZE DATA_SIZE DIR SPACE
|
||||||
|
|
||||||
|
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||||
|
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||||
|
local REQ=$((DATA_SIZE-CUR_SIZE))
|
||||||
|
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
|
||||||
|
|
||||||
|
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||||
|
|
||||||
|
# Check free diskspace
|
||||||
|
DIR=$(dirname "$DISK_FILE")
|
||||||
|
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||||
|
|
||||||
|
if (( REQ > SPACE )); then
|
||||||
|
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
|
||||||
|
error "Not enough free space to resize $DISK_DESC to $DISK_SPACE in $DIR, it has only $SPACE_GB GB available.."
|
||||||
|
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
|
||||||
|
MSG="Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
|
||||||
|
info "$MSG" && html "$MSG"
|
||||||
|
|
||||||
|
local FAIL="Could not resize the $DISK_TYPE $DISK_FMT $DISK_DESC image from ${GB}G to $DISK_SPACE ($DISK_FILE)"
|
||||||
|
|
||||||
|
case "${DISK_FMT,,}" in
|
||||||
|
raw)
|
||||||
|
|
||||||
|
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||||
|
|
||||||
|
# Resize file by changing its length
|
||||||
|
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
||||||
|
error "$FAIL" && exit 75
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
# Resize file by allocating more space
|
||||||
|
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then
|
||||||
|
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
||||||
|
error "$FAIL" && exit 75
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
qcow2)
|
||||||
|
|
||||||
|
if ! qemu-img resize -f "$DISK_FMT" "--$DISK_ALLOC" "$DISK_FILE" "$DATA_SIZE" ; then
|
||||||
|
error "$FAIL" && exit 72
|
||||||
|
fi
|
||||||
|
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
convertDisk() {
|
||||||
|
local SOURCE_FILE=$1
|
||||||
|
local SOURCE_FMT=$2
|
||||||
|
local DST_FILE=$3
|
||||||
|
local DST_FMT=$4
|
||||||
|
local DISK_BASE=$5
|
||||||
|
local DISK_DESC=$6
|
||||||
|
local FS=$7
|
||||||
|
|
||||||
|
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
|
||||||
|
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
|
||||||
|
|
||||||
|
local TMP_FILE="$DISK_BASE.tmp"
|
||||||
|
rm -f "$TMP_FILE"
|
||||||
|
|
||||||
|
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||||
|
|
||||||
|
local DIR CUR_SIZE SPACE
|
||||||
|
|
||||||
|
# Check free diskspace
|
||||||
|
DIR=$(dirname "$TMP_FILE")
|
||||||
|
CUR_SIZE=$(getSize "$SOURCE_FILE")
|
||||||
|
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
|
||||||
|
|
||||||
|
if (( CUR_SIZE > SPACE )); then
|
||||||
|
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
|
||||||
|
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $SPACE_GB GB available..."
|
||||||
|
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
html "Converting $DISK_DESC to $DST_FMT..."
|
||||||
|
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
|
||||||
|
|
||||||
|
local CONV_FLAGS="-p"
|
||||||
|
local DISK_PARAM="$DISK_ALLOC"
|
||||||
|
isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
|
||||||
|
|
||||||
|
if [[ "$DST_FMT" != "raw" ]]; then
|
||||||
|
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||||
|
CONV_FLAGS="$CONV_FLAGS -c"
|
||||||
|
fi
|
||||||
|
[ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
if ! qemu-img convert -f "$SOURCE_FMT" $CONV_FLAGS -o "$DISK_PARAM" -O "$DST_FMT" -- "$SOURCE_FILE" "$TMP_FILE"; then
|
||||||
|
rm -f "$TMP_FILE"
|
||||||
|
error "Failed to convert $DISK_TYPE $DISK_DESC image to $DST_FMT format in $DIR, is there enough space available?" && exit 79
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DST_FMT" == "raw" ]]; then
|
||||||
|
if [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||||
|
# Work around qemu-img bug
|
||||||
|
CUR_SIZE=$(stat -c%s "$TMP_FILE")
|
||||||
|
if ! fallocate -l "$CUR_SIZE" "$TMP_FILE"; then
|
||||||
|
error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$SOURCE_FILE"
|
||||||
|
mv "$TMP_FILE" "$DST_FILE"
|
||||||
|
|
||||||
|
if isCow "$FS"; then
|
||||||
|
FA=$(lsattr "$DST_FILE")
|
||||||
|
if [[ "$FA" != *"C"* ]]; then
|
||||||
|
error "Failed to disable COW for $DISK_DESC image $DST_FILE on ${FS^^} filesystem (returned $FA)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
html "Conversion of $DISK_DESC completed..."
|
||||||
|
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFS () {
|
||||||
|
local FS=$1
|
||||||
|
local DISK_FILE=$2
|
||||||
|
local DISK_DESC=$3
|
||||||
|
local DIR FA
|
||||||
|
|
||||||
|
DIR=$(dirname "$DISK_FILE")
|
||||||
|
[ ! -d "$DIR" ] && return 0
|
||||||
|
|
||||||
|
if [[ "${FS,,}" == "overlay"* ]]; then
|
||||||
|
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${FS,,}" == "fuse"* ]]; then
|
||||||
|
info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! supportsDirect "$FS"; then
|
||||||
|
info "Warning: the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if isCow "$FS"; then
|
||||||
|
if [ -f "$DISK_FILE" ]; then
|
||||||
|
FA=$(lsattr "$DISK_FILE")
|
||||||
|
if [[ "$FA" != *"C"* ]]; then
|
||||||
|
info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
createDevice () {
|
||||||
|
|
||||||
|
local DISK_ID=$1
|
||||||
|
local DISK_FILE=$2
|
||||||
|
local DISK_INDEX=$3
|
||||||
|
local DISK_ADDRESS=$4
|
||||||
|
local DISK_FMT=$5
|
||||||
|
local DISK_IO=$6
|
||||||
|
local DISK_CACHE=$7
|
||||||
|
|
||||||
|
local result="-drive file=$DISK_FILE,if=none,id=drive-$DISK_ID,format=$DISK_FMT,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on"
|
||||||
|
|
||||||
|
result="$result \
|
||||||
|
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
|
||||||
|
-device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
|
||||||
|
|
||||||
|
echo "$result"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
addDisk () {
|
||||||
|
local DISK_BASE=$1
|
||||||
|
local DISK_EXT=$2
|
||||||
|
local DISK_DESC=$3
|
||||||
|
local DISK_SPACE=$4
|
||||||
|
local DISK_INDEX=$5
|
||||||
|
local DISK_ADDRESS=$6
|
||||||
|
local DISK_FMT=$7
|
||||||
|
local DISK_IO=$8
|
||||||
|
local DISK_CACHE=$9
|
||||||
|
local DISK_ID="userdata$DISK_INDEX"
|
||||||
|
local DISK_FILE="$DISK_BASE.$DISK_EXT"
|
||||||
|
local DIR DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE OPTS
|
||||||
|
|
||||||
|
DIR=$(dirname "$DISK_FILE")
|
||||||
|
[ ! -d "$DIR" ] && return 0
|
||||||
|
|
||||||
|
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G"
|
||||||
|
DISK_SPACE=$(echo "${DISK_SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||||
|
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
|
||||||
|
|
||||||
|
if (( DATA_SIZE < 1 )); then
|
||||||
|
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
|
||||||
|
fi
|
||||||
|
|
||||||
|
FS=$(stat -f -c %T "$DIR")
|
||||||
|
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
|
||||||
|
|
||||||
|
if ! supportsDirect "$FS"; then
|
||||||
|
DISK_IO="threads"
|
||||||
|
DISK_CACHE="writeback"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [ -s "$DISK_FILE" ] ; then
|
||||||
|
|
||||||
|
if [[ "${DISK_FMT,,}" != "raw" ]]; then
|
||||||
|
PREV_FMT="raw"
|
||||||
|
else
|
||||||
|
PREV_FMT="qcow2"
|
||||||
|
fi
|
||||||
|
PREV_EXT=$(fmt2ext "$PREV_FMT")
|
||||||
|
|
||||||
|
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
|
||||||
|
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -s "$DISK_FILE" ]; then
|
||||||
|
|
||||||
|
CUR_SIZE=$(getSize "$DISK_FILE")
|
||||||
|
|
||||||
|
if (( DATA_SIZE > CUR_SIZE )); then
|
||||||
|
resizeDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
OPTS=$(createDevice "$DISK_ID" "$DISK_FILE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE")
|
||||||
|
DISK_OPTS="$DISK_OPTS $OPTS"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
addDevice () {
|
||||||
|
|
||||||
|
local DISK_DEV=$1
|
||||||
|
local DISK_DESC=$2
|
||||||
|
local DISK_INDEX=$3
|
||||||
|
local DISK_ADDRESS=$4
|
||||||
|
local DISK_ID="userdata$DISK_INDEX"
|
||||||
|
|
||||||
|
[ -z "$DISK_DEV" ] && return 0
|
||||||
|
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
|
||||||
|
|
||||||
|
local OPTS
|
||||||
|
OPTS=$(createDevice "$DISK_ID" "$DISK_DEV" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE")
|
||||||
|
DISK_OPTS="$DISK_OPTS $OPTS"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
html "Initializing disks..."
|
||||||
|
|
||||||
|
DISK1_FILE="$STORAGE/data"
|
||||||
|
DISK2_FILE="/storage2/data2"
|
||||||
|
DISK3_FILE="/storage3/data3"
|
||||||
|
DISK4_FILE="/storage4/data4"
|
||||||
|
|
||||||
|
if [ -z "$DISK_FMT" ]; then
|
||||||
|
if [ -f "$DISK1_FILE.qcow2" ]; then
|
||||||
|
DISK_FMT="qcow2"
|
||||||
|
else
|
||||||
|
DISK_FMT="raw"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
DISK_EXT=$(fmt2ext "$DISK_FMT")
|
||||||
|
|
||||||
|
if [ -z "$ALLOCATE" ]; then
|
||||||
|
ALLOCATE="N"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||||
|
DISK_TYPE="growable"
|
||||||
|
DISK_ALLOC="preallocation=off"
|
||||||
|
else
|
||||||
|
DISK_TYPE="preallocated"
|
||||||
|
DISK_ALLOC="preallocation=falloc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
: "${DISK2_SIZE:=""}"
|
||||||
|
: "${DISK3_SIZE:=""}"
|
||||||
|
: "${DISK4_SIZE:=""}"
|
||||||
|
|
||||||
|
: "${DEVICE:=""}" # Docker variables to passthrough a block device, like /dev/vdc1.
|
||||||
|
: "${DEVICE2:=""}"
|
||||||
|
: "${DEVICE3:=""}"
|
||||||
|
: "${DEVICE4:=""}"
|
||||||
|
|
||||||
|
[ -z "$DEVICE" ] && [ -b "/disk" ] && DEVICE="/disk"
|
||||||
|
[ -z "$DEVICE" ] && [ -b "/disk1" ] && DEVICE="/disk1"
|
||||||
|
[ -z "$DEVICE2" ] && [ -b "/disk2" ] && DEVICE2="/disk2"
|
||||||
|
[ -z "$DEVICE3" ] && [ -b "/disk3" ] && DEVICE3="/disk3"
|
||||||
|
[ -z "$DEVICE4" ] && [ -b "/disk4" ] && DEVICE4="/disk4"
|
||||||
|
|
||||||
|
[ -z "$DEVICE" ] && [ -b "/dev/disk1" ] && DEVICE="/dev/disk1"
|
||||||
|
[ -z "$DEVICE2" ] && [ -b "/dev/disk2" ] && DEVICE2="/dev/disk2"
|
||||||
|
[ -z "$DEVICE3" ] && [ -b "/dev/disk3" ] && DEVICE3="/dev/disk3"
|
||||||
|
[ -z "$DEVICE4" ] && [ -b "/dev/disk4" ] && DEVICE4="/dev/disk4"
|
||||||
|
|
||||||
|
if [ -n "$DEVICE" ]; then
|
||||||
|
addDevice "$DEVICE" "device" "3" "0xa" || exit $?
|
||||||
|
else
|
||||||
|
addDisk "$DISK1_FILE" "$DISK_EXT" "disk" "$DISK_SIZE" "3" "0xa" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEVICE2" ]; then
|
||||||
|
addDevice "$DEVICE2" "device2" "4" "0xb" || exit $?
|
||||||
|
else
|
||||||
|
addDisk "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xb" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEVICE3" ]; then
|
||||||
|
addDevice "$DEVICE3" "device3" "5" "0xc" || exit $?
|
||||||
|
else
|
||||||
|
addDisk "$DISK3_FILE" "$DISK_EXT" "disk3" "$DISK3_SIZE" "5" "0xc" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$DEVICE4" ]; then
|
||||||
|
addDevice "$DEVICE4" "device4" "6" "0xd" || exit $?
|
||||||
|
else
|
||||||
|
addDisk "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "6" "0xd" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
html "Initialized disks successfully..."
|
||||||
|
return 0
|
|
@ -3,25 +3,29 @@ set -Eeuo pipefail
|
||||||
|
|
||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
|
|
||||||
: "${VGA:="ramfb"}" # VGA adaptor
|
: "${VGA:=""}" # VGA adaptor
|
||||||
: "${DISPLAY:="web"}" # Display type
|
: "${DISPLAY:="web"}" # Display type
|
||||||
|
|
||||||
[[ "$DISPLAY" == ":0" ]] && DISPLAY="web"
|
if [[ "${BOOT_MODE,,}" != "windows" ]]; then
|
||||||
|
[ -z "$VGA" ] && VGA="virtio-gpu"
|
||||||
|
else
|
||||||
|
[ -z "$VGA" ] && VGA="ramfb"
|
||||||
|
fi
|
||||||
|
|
||||||
case "${DISPLAY,,}" in
|
case "${DISPLAY,,}" in
|
||||||
"vnc" )
|
vnc)
|
||||||
DISPLAY_OPTS="-display vnc=:0 -device $VGA"
|
DISPLAY_OPTS="-display vnc=:0 -device $VGA"
|
||||||
;;
|
;;
|
||||||
"web" )
|
web)
|
||||||
DISPLAY_OPTS="-display vnc=:0,websocket=5700 -device $VGA"
|
DISPLAY_OPTS="-display vnc=:0,websocket=5700 -device $VGA"
|
||||||
;;
|
;;
|
||||||
"ramfb" )
|
ramfb)
|
||||||
DISPLAY_OPTS="-display vnc=:0,websocket=5700 -device ramfb"
|
DISPLAY_OPTS="-display vnc=:0,websocket=5700 -device ramfb"
|
||||||
;;
|
;;
|
||||||
"disabled" )
|
disabled)
|
||||||
DISPLAY_OPTS="-display none -device $VGA"
|
DISPLAY_OPTS="-display none -device $VGA"
|
||||||
;;
|
;;
|
||||||
"none" )
|
none)
|
||||||
DISPLAY_OPTS="-display none"
|
DISPLAY_OPTS="-display none"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
|
20
src/entry.sh
20
src/entry.sh
|
@ -1,17 +1,13 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
: "${APP:="QEMU"}"
|
APP="QEMU"
|
||||||
: "${MACHINE:="virt"}"
|
SUPPORT="https://github.com/qemus/qemu-arm"
|
||||||
: "${PLATFORM:="arm64"}"
|
|
||||||
: "${SUPPORT:="https://github.com/qemus/qemu-arm"}"
|
|
||||||
|
|
||||||
cd /run
|
cd /run
|
||||||
|
|
||||||
. utils.sh # Load functions
|
|
||||||
. reset.sh # Initialize system
|
. reset.sh # Initialize system
|
||||||
. define.sh # Define images
|
. install.sh # Get bootdisk
|
||||||
. install.sh # Download image
|
|
||||||
. disk.sh # Initialize disks
|
. disk.sh # Initialize disks
|
||||||
. display.sh # Initialize graphics
|
. display.sh # Initialize graphics
|
||||||
. network.sh # Initialize network
|
. network.sh # Initialize network
|
||||||
|
@ -21,11 +17,7 @@ cd /run
|
||||||
|
|
||||||
trap - ERR
|
trap - ERR
|
||||||
|
|
||||||
version=$(qemu-system-aarch64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }')
|
info "Booting image${BOOT_DESC}..."
|
||||||
info "Booting image${BOOT_DESC} using QEMU v$version..."
|
|
||||||
|
|
||||||
if [ -z "$CPU_PIN" ]; then
|
[[ "$DEBUG" == [Yy1]* ]] && set -x
|
||||||
exec qemu-system-aarch64 ${ARGS:+ $ARGS}
|
exec qemu-system-aarch64 ${ARGS:+ $ARGS}
|
||||||
else
|
|
||||||
exec taskset -c "$CPU_PIN" qemu-system-aarch64 ${ARGS:+ $ARGS}
|
|
||||||
fi
|
|
||||||
|
|
58
src/install.sh
Normal file
58
src/install.sh
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
# Check if running with interactive TTY or redirected to docker log
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
progress="--progress=bar:noscroll"
|
||||||
|
else
|
||||||
|
progress="--progress=dot:giga"
|
||||||
|
fi
|
||||||
|
|
||||||
|
file="/boot.iso" && [ -f "$file" ] && [ -s "$file" ] && BOOT="$file" && return 0
|
||||||
|
file="/boot.img" && [ -f "$file" ] && [ -s "$file" ] && BOOT="$file" && return 0
|
||||||
|
|
||||||
|
file=$(find "$STORAGE" -maxdepth 1 -type f -iname boot.iso -printf "%f\n" | head -n 1)
|
||||||
|
[ -z "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname boot.img -printf "%f\n" | head -n 1)
|
||||||
|
[ -n "$file" ] && file="$STORAGE/$file"
|
||||||
|
[ -f "$file" ] && [ -s "$file" ] && BOOT="$file" && return 0
|
||||||
|
|
||||||
|
if [ -z "$BOOT" ]; then
|
||||||
|
error "No boot disk specified, set BOOT= to the URL of an ISO file." && exit 64
|
||||||
|
fi
|
||||||
|
|
||||||
|
base=$(basename "$BOOT")
|
||||||
|
[ -n "$base" ] && file="$STORAGE/$base"
|
||||||
|
[ -f "$file" ] && [ -s "$file" ] && BOOT="$file" && return 0
|
||||||
|
|
||||||
|
base=$(basename "${BOOT%%\?*}")
|
||||||
|
: "${base//+/ }"; printf -v base '%b' "${_//%/\\x}"
|
||||||
|
base=$(echo "$base" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
||||||
|
[ -n "$base" ] && file="$STORAGE/$base"
|
||||||
|
[ -f "$file" ] && [ -s "$file" ] && BOOT="$file" && return 0
|
||||||
|
|
||||||
|
TMP="$STORAGE/${base%.*}.tmp"
|
||||||
|
rm -f "$TMP"
|
||||||
|
|
||||||
|
msg="Downloading $base..."
|
||||||
|
info "$msg" && html "$msg"
|
||||||
|
|
||||||
|
/run/progress.sh "$TMP" "" "Downloading $base ([P])..." &
|
||||||
|
{ wget "$BOOT" -O "$TMP" -q --timeout=10 --show-progress "$progress"; rc=$?; } || :
|
||||||
|
|
||||||
|
fKill "progress.sh"
|
||||||
|
|
||||||
|
(( rc == 4 )) && error "Failed to download $BOOT , network failure!" && exit 60
|
||||||
|
(( rc != 0 )) && error "Failed to download $BOOT , reason: $rc" && exit 60
|
||||||
|
[ ! -s "$TMP" ] && error "Failed to download $BOOT" && exit 61
|
||||||
|
|
||||||
|
html "Download finished successfully..."
|
||||||
|
|
||||||
|
size=$(stat -c%s "$TMP")
|
||||||
|
|
||||||
|
if ((size<100000)); then
|
||||||
|
error "Invalid ISO file: Size is smaller than 100 KB" && exit 62
|
||||||
|
fi
|
||||||
|
|
||||||
|
mv -f "$TMP" "$file"
|
||||||
|
|
||||||
|
return 0
|
348
src/network.sh
Normal file
348
src/network.sh
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
# Docker environment variables
|
||||||
|
|
||||||
|
: "${MAC:=""}"
|
||||||
|
: "${DHCP:="N"}"
|
||||||
|
: "${NETWORK:="Y"}"
|
||||||
|
: "${HOST_PORTS:=""}"
|
||||||
|
|
||||||
|
: "${VM_NET_DEV:=""}"
|
||||||
|
: "${VM_NET_TAP:="qemu"}"
|
||||||
|
: "${VM_NET_MAC:="$MAC"}"
|
||||||
|
: "${VM_NET_HOST:="QEMU"}"
|
||||||
|
|
||||||
|
: "${DNSMASQ_OPTS:=""}"
|
||||||
|
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
|
||||||
|
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
|
||||||
|
|
||||||
|
ADD_ERR="Please add the following setting to your container:"
|
||||||
|
|
||||||
|
# ######################################
|
||||||
|
# Functions
|
||||||
|
# ######################################
|
||||||
|
|
||||||
|
configureDHCP() {
|
||||||
|
|
||||||
|
# Create the necessary file structure for /dev/vhost-net
|
||||||
|
if [ ! -c /dev/vhost-net ]; then
|
||||||
|
if mknod /dev/vhost-net c 10 238; then
|
||||||
|
chmod 660 /dev/vhost-net
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create a macvtap network for the VM guest
|
||||||
|
{ ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge ; rc=$?; } || :
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
error "Cannot create macvtap interface. Please make sure that the network type is 'macvlan' and not 'ipvlan',"
|
||||||
|
error "that your kernel is recent (>4) and supports it, and that the container has the NET_ADMIN capability set." && exit 16
|
||||||
|
fi
|
||||||
|
|
||||||
|
while ! ip link set "$VM_NET_TAP" up; do
|
||||||
|
info "Waiting for MAC address $VM_NET_MAC to become available..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
local TAP_NR TAP_PATH MAJOR MINOR
|
||||||
|
TAP_NR=$(</sys/class/net/"$VM_NET_TAP"/ifindex)
|
||||||
|
TAP_PATH="/dev/tap${TAP_NR}"
|
||||||
|
|
||||||
|
# Create dev file (there is no udev in container: need to be done manually)
|
||||||
|
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev)
|
||||||
|
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && exit 18
|
||||||
|
|
||||||
|
[[ ! -e "$TAP_PATH" ]] && [[ -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
|
||||||
|
|
||||||
|
if [[ ! -e "$TAP_PATH" ]]; then
|
||||||
|
{ mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
|
||||||
|
(( rc != 0 )) && error "Cannot mknod: $TAP_PATH ($rc)" && exit 20
|
||||||
|
fi
|
||||||
|
|
||||||
|
{ exec 30>>"$TAP_PATH"; rc=$?; } 2>/dev/null || :
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
error "Cannot create TAP interface ($rc). $ADD_ERR --device-cgroup-rule='c *:* rwm'" && exit 21
|
||||||
|
fi
|
||||||
|
|
||||||
|
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
error "VHOST can not be found ($rc). $ADD_ERR --device=/dev/vhost-net" && exit 22
|
||||||
|
fi
|
||||||
|
|
||||||
|
NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureDNS() {
|
||||||
|
|
||||||
|
# dnsmasq configuration:
|
||||||
|
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0"
|
||||||
|
|
||||||
|
# Create lease file for faster resolve
|
||||||
|
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
|
||||||
|
chmod 644 /var/lib/misc/dnsmasq.leases
|
||||||
|
|
||||||
|
# Set DNS server and gateway
|
||||||
|
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1 --dhcp-option=option:router,${VM_NET_IP%.*}.1"
|
||||||
|
|
||||||
|
# Add DNS entry for container
|
||||||
|
DNSMASQ_OPTS="$DNSMASQ_OPTS --address=/host.lan/${VM_NET_IP%.*}.1"
|
||||||
|
|
||||||
|
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && set -x
|
||||||
|
|
||||||
|
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
|
||||||
|
error "Failed to start dnsmasq, reason: $?" && exit 29
|
||||||
|
fi
|
||||||
|
{ set +x; } 2>/dev/null
|
||||||
|
[[ "$DEBUG" == [Yy1]* ]] && echo
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
getPorts() {
|
||||||
|
|
||||||
|
local list=$1
|
||||||
|
local vnc="5900"
|
||||||
|
local web="8006"
|
||||||
|
|
||||||
|
[ -z "$list" ] && list="$web" || list="$list,$web"
|
||||||
|
|
||||||
|
if [[ "${DISPLAY,,}" == "vnc" ]] || [[ "${DISPLAY,,}" == "web" ]]; then
|
||||||
|
[ -z "$list" ] && list="$vnc" || list="$list,$vnc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -z "$list" ] && echo "" && return 0
|
||||||
|
|
||||||
|
if [[ "$list" != *","* ]]; then
|
||||||
|
echo " ! --dport $list"
|
||||||
|
else
|
||||||
|
echo " -m multiport ! --dports $list"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
configureNAT() {
|
||||||
|
|
||||||
|
# Create the necessary file structure for /dev/net/tun
|
||||||
|
if [ ! -c /dev/net/tun ]; then
|
||||||
|
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
|
||||||
|
if mknod /dev/net/tun c 10 200; then
|
||||||
|
chmod 666 /dev/net/tun
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -c /dev/net/tun ]; then
|
||||||
|
error "TUN device missing. $ADD_ERR --device /dev/net/tun --cap-add NET_ADMIN" && exit 25
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check port forwarding flag
|
||||||
|
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||||
|
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null; rc=$?; } || :
|
||||||
|
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
|
||||||
|
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
||||||
|
local tuntap="The 'tun' kernel module is not available. Try this command: 'sudo modprobe tun' or run the container with 'privileged: true'."
|
||||||
|
|
||||||
|
# Create a bridge with a static IP for the VM guest
|
||||||
|
|
||||||
|
VM_NET_IP='20.20.20.21'
|
||||||
|
|
||||||
|
{ ip link add dev dockerbridge type bridge ; rc=$?; } || :
|
||||||
|
|
||||||
|
if (( rc != 0 )); then
|
||||||
|
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && exit 23
|
||||||
|
fi
|
||||||
|
|
||||||
|
ip address add ${VM_NET_IP%.*}.1/24 broadcast ${VM_NET_IP%.*}.255 dev dockerbridge
|
||||||
|
|
||||||
|
while ! ip link set dockerbridge up; do
|
||||||
|
info "Waiting for IP address to become available..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
# QEMU Works with taps, set tap to the bridge created
|
||||||
|
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
|
||||||
|
error "$tuntap" && exit 31
|
||||||
|
fi
|
||||||
|
|
||||||
|
while ! ip link set "$VM_NET_TAP" up promisc on; do
|
||||||
|
info "Waiting for TAP to become available..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
ip link set dev "$VM_NET_TAP" master dockerbridge
|
||||||
|
|
||||||
|
# Add internet connection to the VM
|
||||||
|
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
|
||||||
|
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
|
||||||
|
|
||||||
|
exclude=$(getPorts "$HOST_PORTS")
|
||||||
|
|
||||||
|
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
|
||||||
|
error "$tables" && exit 30
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"
|
||||||
|
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"
|
||||||
|
|
||||||
|
if (( KERNEL > 4 )); then
|
||||||
|
# Hack for guest VMs complaining about "bad udp checksums in 5 packets"
|
||||||
|
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0"
|
||||||
|
|
||||||
|
if [ -c /dev/vhost-net ]; then
|
||||||
|
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
|
||||||
|
(( rc == 0 )) && NET_OPTS="$NET_OPTS,vhost=on,vhostfd=40"
|
||||||
|
fi
|
||||||
|
|
||||||
|
configureDNS
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
closeNetwork() {
|
||||||
|
|
||||||
|
# Shutdown nginx
|
||||||
|
nginx -s stop 2> /dev/null
|
||||||
|
fWait "nginx"
|
||||||
|
|
||||||
|
[[ "$NETWORK" != [Yy1]* ]] && return 0
|
||||||
|
|
||||||
|
exec 30<&- || true
|
||||||
|
exec 40<&- || true
|
||||||
|
|
||||||
|
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||||
|
|
||||||
|
ip link set "$VM_NET_TAP" down || true
|
||||||
|
ip link delete "$VM_NET_TAP" || true
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
local pid="/var/run/dnsmasq.pid"
|
||||||
|
[ -s "$pid" ] && pKill "$(<"$pid")"
|
||||||
|
|
||||||
|
ip link set "$VM_NET_TAP" down promisc off || true
|
||||||
|
ip link delete "$VM_NET_TAP" || true
|
||||||
|
|
||||||
|
ip link set dockerbridge down || true
|
||||||
|
ip link delete dockerbridge || true
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
checkOS() {
|
||||||
|
|
||||||
|
local name
|
||||||
|
local os=""
|
||||||
|
name=$(uname -a)
|
||||||
|
|
||||||
|
[[ "${name,,}" == *"darwin"* ]] && os="MacOS"
|
||||||
|
[[ "${name,,}" == *"microsoft"* ]] && os="Windows"
|
||||||
|
|
||||||
|
if [ -n "$os" ]; then
|
||||||
|
error "You are using Docker Desktop for $os which does not support macvlan, please revert to bridge networking!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
getInfo() {
|
||||||
|
|
||||||
|
if [ -z "$VM_NET_DEV" ]; then
|
||||||
|
# Automaticly detect the default network interface
|
||||||
|
VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
|
||||||
|
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then
|
||||||
|
error "Network interface '$VM_NET_DEV' does not exist inside the container!"
|
||||||
|
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 27
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$MAC" ]; then
|
||||||
|
local file="$STORAGE/$PROCESS.mac"
|
||||||
|
[ -s "$file" ] && MAC=$(<"$file")
|
||||||
|
if [ -z "$MAC" ]; then
|
||||||
|
# Generate MAC address based on Docker container ID in hostname
|
||||||
|
MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
||||||
|
echo "${MAC^^}" > "$file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
VM_NET_MAC="${MAC^^}"
|
||||||
|
VM_NET_MAC="${VM_NET_MAC//-/:}"
|
||||||
|
|
||||||
|
if [[ ${#VM_NET_MAC} == 12 ]]; then
|
||||||
|
m="$VM_NET_MAC"
|
||||||
|
VM_NET_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#VM_NET_MAC} != 17 ]]; then
|
||||||
|
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
|
||||||
|
fi
|
||||||
|
|
||||||
|
GATEWAY=$(ip r | grep default | awk '{print $3}')
|
||||||
|
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ######################################
|
||||||
|
# Configure Network
|
||||||
|
# ######################################
|
||||||
|
|
||||||
|
if [[ "$NETWORK" != [Yy1]* ]]; then
|
||||||
|
NET_OPTS=""
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
getInfo
|
||||||
|
html "Initializing network..."
|
||||||
|
|
||||||
|
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||||
|
info "Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC"
|
||||||
|
[ -f /etc/resolv.conf ] && grep '^nameserver*' /etc/resolv.conf
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||||
|
|
||||||
|
! checkOS && exit 19
|
||||||
|
|
||||||
|
if [[ "$GATEWAY" == "172."* ]]; then
|
||||||
|
warn "your gateway IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configuration for DHCP IP
|
||||||
|
configureDHCP
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if [[ "$GATEWAY" != "172."* ]]; then
|
||||||
|
! checkOS && exit 19
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configuration for static IP
|
||||||
|
configureNAT
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
NET_OPTS="$NET_OPTS -device virtio-net-pci,romfile=,netdev=hostnet0,mac=$VM_NET_MAC,id=net0"
|
||||||
|
|
||||||
|
html "Initialized network successfully..."
|
||||||
|
return 0
|
49
src/proc.sh
49
src/proc.sh
|
@ -4,59 +4,36 @@ set -Eeuo pipefail
|
||||||
# Docker environment variables
|
# Docker environment variables
|
||||||
|
|
||||||
: "${KVM:="Y"}"
|
: "${KVM:="Y"}"
|
||||||
: "${CPU_PIN:=""}"
|
|
||||||
: "${CPU_FLAGS:=""}"
|
: "${CPU_FLAGS:=""}"
|
||||||
: "${CPU_MODEL:=""}"
|
: "${CPU_MODEL:=""}"
|
||||||
: "${DEF_MODEL:="neoverse-n1"}"
|
: "${DEF_MODEL:="neoverse-n1"}"
|
||||||
|
|
||||||
if [[ "$CPU" == "Cortex A53" ]] && [[ "$CORES" == "6" ]]; then
|
[[ "${ARCH,,}" != "arm"* ]] && KVM="N"
|
||||||
# Pin to performance cores on Rockchip Orange Pi 4
|
|
||||||
[ -z "$CPU_PIN" ] && CPU_PIN="4,5"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$CPU" == "Cortex A55" ]] && [[ "$CORES" == "8" ]]; then
|
|
||||||
# Pin to performance cores on Rockchip Orange Pi 5
|
|
||||||
[ -z "$CPU_PIN" ] && CPU_PIN="4,5,6,7"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$CPU" == "Rockchip RK3588"* ]] && [[ "$CORES" == "8" ]]; then
|
|
||||||
# Pin to performance cores on Rockchip Orange Pi 5 Plus
|
|
||||||
[ -z "$CPU_PIN" ] && CPU_PIN="4,5,6,7"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${ARCH,,}" != "arm64" ]]; then
|
|
||||||
KVM="N"
|
|
||||||
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ARM64 instructions, this will cause a major loss of performance."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$KVM" != [Nn]* ]]; then
|
if [[ "$KVM" != [Nn]* ]]; then
|
||||||
|
|
||||||
KVM_ERR=""
|
KVM_ERR=""
|
||||||
|
|
||||||
if [ ! -e /dev/kvm ]; then
|
if [ ! -e /dev/kvm ]; then
|
||||||
KVM_ERR="(/dev/kvm is missing)"
|
KVM_ERR="(device file missing)"
|
||||||
else
|
else
|
||||||
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
||||||
KVM_ERR="(/dev/kvm is unwriteable)"
|
KVM_ERR="(no write access)"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$KVM_ERR" ]; then
|
if [ -n "$KVM_ERR" ]; then
|
||||||
KVM="N"
|
KVM="N"
|
||||||
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
||||||
warn "you are using macOS which has no KVM support, this will cause a major loss of performance."
|
warn "you are using MacOS which has no KVM support, this will cause a major loss of performance."
|
||||||
else
|
else
|
||||||
kernel=$(uname -a)
|
if grep -qi Microsoft /proc/version; then
|
||||||
case "${kernel,,}" in
|
warn "you are using Windows 10 which has no KVM support, this will cause a major loss of performance."
|
||||||
*"microsoft"* )
|
else
|
||||||
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
|
error "KVM acceleration not available $KVM_ERR, this will cause a major loss of performance."
|
||||||
*"synology"* )
|
error "See the FAQ on how to enable it, or continue without KVM by setting KVM=N (not recommended)."
|
||||||
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
|
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
||||||
*)
|
fi
|
||||||
error "KVM acceleration is not available $KVM_ERR, this will cause a major loss of performance."
|
|
||||||
error "See the FAQ for possible causes, or continue without it by adding KVM: \"N\" (not recommended)." ;;
|
|
||||||
esac
|
|
||||||
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -77,7 +54,7 @@ else
|
||||||
KVM_OPTS=" -accel tcg,thread=multi"
|
KVM_OPTS=" -accel tcg,thread=multi"
|
||||||
|
|
||||||
if [ -z "$CPU_MODEL" ]; then
|
if [ -z "$CPU_MODEL" ]; then
|
||||||
if [[ "${ARCH,,}" == "arm64" ]]; then
|
if [[ "$ARCH" == "arm"* ]]; then
|
||||||
CPU_MODEL="max,pauth-impdef=on"
|
CPU_MODEL="max,pauth-impdef=on"
|
||||||
else
|
else
|
||||||
CPU_MODEL="$DEF_MODEL"
|
CPU_MODEL="$DEF_MODEL"
|
||||||
|
@ -85,7 +62,7 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "${BOOT_MODE,,}" == "windows" ]]; then
|
if [[ "${BOOT_MODE,,}" == "windows" ]]; then
|
||||||
MACHINE+=",virtualization=on"
|
MACHINE="$MACHINE,virtualization=on"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
38
src/progress.sh
Normal file
38
src/progress.sh
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
escape () {
|
||||||
|
local s
|
||||||
|
s=${1//&/\&}
|
||||||
|
s=${s//</\<}
|
||||||
|
s=${s//>/\>}
|
||||||
|
s=${s//'"'/\"}
|
||||||
|
printf -- %s "$s"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
file="$1"
|
||||||
|
total="$2"
|
||||||
|
body=$(escape "$3")
|
||||||
|
info="/run/shm/msg.html"
|
||||||
|
|
||||||
|
if [[ "$body" == *"..." ]]; then
|
||||||
|
body="<p class=\"loading\">${body/.../}</p>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
if [ -s "$file" ]; then
|
||||||
|
bytes=$(du -sb "$file" | cut -f1)
|
||||||
|
if (( bytes > 1000 )); then
|
||||||
|
if [ -z "$total" ] || [[ "$total" == "0" ]]; then
|
||||||
|
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
|
||||||
|
else
|
||||||
|
size=$(printf '%.1f\n' "$((bytes*100*100/total))e-2")
|
||||||
|
size="$size%"
|
||||||
|
fi
|
||||||
|
echo "${body//(\[P\])/($size)}"> "$info"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
sleep 1 & wait $!
|
||||||
|
done
|
195
src/reset.sh
Normal file
195
src/reset.sh
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
info () { printf "%b%s%b" "\E[1;34m❯ \E[1;36m" "${1:-}" "\E[0m\n"; }
|
||||||
|
error () { printf "%b%s%b" "\E[1;31m❯ " "ERROR: ${1:-}" "\E[0m\n" >&2; }
|
||||||
|
warn () { printf "%b%s%b" "\E[1;31m❯ " "Warning: ${1:-}" "\E[0m\n" >&2; }
|
||||||
|
|
||||||
|
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
|
||||||
|
|
||||||
|
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
|
||||||
|
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
|
||||||
|
|
||||||
|
echo "❯ Starting $APP for Docker v$(</run/version)..."
|
||||||
|
echo "❯ For support visit $SUPPORT"
|
||||||
|
|
||||||
|
# Docker environment variables
|
||||||
|
|
||||||
|
: "${BOOT:=""}" # URL of the ISO file
|
||||||
|
: "${DEBUG:="N"}" # Disable debugging
|
||||||
|
: "${MACHINE:="virt"}" # Machine selection
|
||||||
|
: "${ALLOCATE:=""}" # Preallocate diskspace
|
||||||
|
: "${ARGUMENTS:=""}" # Extra QEMU parameters
|
||||||
|
: "${CPU_CORES:="1"}" # Amount of CPU cores
|
||||||
|
: "${RAM_SIZE:="1G"}" # Maximum RAM amount
|
||||||
|
: "${DISK_SIZE:="16G"}" # Initial data disk size
|
||||||
|
: "${BOOT_INDEX:="10"}" # Boot index of CD drive
|
||||||
|
: "${BOOT_MODE:="uefi"}" # Boot in UEFI mode
|
||||||
|
|
||||||
|
# Helper variables
|
||||||
|
|
||||||
|
PROCESS="${APP,,}"
|
||||||
|
PROCESS="${PROCESS// /-}"
|
||||||
|
|
||||||
|
STORAGE="/storage"
|
||||||
|
INFO="/run/shm/msg.html"
|
||||||
|
PAGE="/run/shm/index.html"
|
||||||
|
TEMPLATE="/var/www/index.html"
|
||||||
|
FOOTER1="$APP for Docker v$(</run/version)"
|
||||||
|
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
|
||||||
|
|
||||||
|
SYS=$(uname -r)
|
||||||
|
HOST=$(hostname -s)
|
||||||
|
KERNEL=$(echo "$SYS" | cut -b 1)
|
||||||
|
MINOR=$(echo "$SYS" | cut -d '.' -f2)
|
||||||
|
ARCH=$(dpkg --print-architecture)
|
||||||
|
CPU=$(lscpu | grep -m 1 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
||||||
|
|
||||||
|
# Check system
|
||||||
|
|
||||||
|
if [ ! -d "/dev/shm" ]; then
|
||||||
|
error "Directory /dev/shm not found!" && exit 14
|
||||||
|
else
|
||||||
|
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check folder
|
||||||
|
|
||||||
|
if [ ! -d "$STORAGE" ]; then
|
||||||
|
error "Storage folder ($STORAGE) not found!" && exit 13
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Read memory
|
||||||
|
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
|
||||||
|
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
|
||||||
|
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||||
|
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
|
||||||
|
AVAIL_GB=$(( (RAM_AVAIL + 1073741823)/1073741824 ))
|
||||||
|
TOTAL_GB=$(( (RAM_TOTAL + 1073741823)/1073741824 ))
|
||||||
|
WANTED_GB=$(( (RAM_WANTED + 1073741823)/1073741824 ))
|
||||||
|
|
||||||
|
# Print system info
|
||||||
|
SYS="${SYS/-generic/}"
|
||||||
|
FS=$(stat -f -c %T "$STORAGE")
|
||||||
|
FS="${FS/ext2\/ext3/ext4}"
|
||||||
|
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
|
||||||
|
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
|
||||||
|
|
||||||
|
echo "❯ CPU: ${CPU} | RAM: $AVAIL_GB/$TOTAL_GB GB | DISK: $SPACE_GB GB (${FS}) | HOST: ${SYS}..."
|
||||||
|
echo
|
||||||
|
|
||||||
|
# Check memory
|
||||||
|
|
||||||
|
if (( (RAM_WANTED + 500000000) > RAM_AVAIL )); then
|
||||||
|
error "Your configured RAM_SIZE of $WANTED_GB GB is higher than the $AVAIL_GB GB of memory available, please set a lower value."
|
||||||
|
exit 17
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Helper functions
|
||||||
|
|
||||||
|
isAlive() {
|
||||||
|
local pid=$1
|
||||||
|
|
||||||
|
if kill -0 "$pid" 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pKill() {
|
||||||
|
local pid=$1
|
||||||
|
|
||||||
|
{ kill -15 "$pid" || true; } 2>/dev/null
|
||||||
|
|
||||||
|
while isAlive "$pid"; do
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fWait() {
|
||||||
|
local name=$1
|
||||||
|
|
||||||
|
while pgrep -f -l "$name" >/dev/null; do
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fKill() {
|
||||||
|
local name=$1
|
||||||
|
|
||||||
|
{ pkill -f "$name" || true; } 2>/dev/null
|
||||||
|
fWait "$name"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
escape () {
|
||||||
|
local s
|
||||||
|
s=${1//&/\&}
|
||||||
|
s=${s//</\<}
|
||||||
|
s=${s//>/\>}
|
||||||
|
s=${s//'"'/\"}
|
||||||
|
printf -- %s "$s"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
html()
|
||||||
|
{
|
||||||
|
local title
|
||||||
|
local body
|
||||||
|
local script
|
||||||
|
local footer
|
||||||
|
|
||||||
|
title=$(escape "$APP")
|
||||||
|
title="<title>$title</title>"
|
||||||
|
footer=$(escape "$FOOTER1")
|
||||||
|
|
||||||
|
body=$(escape "$1")
|
||||||
|
if [[ "$body" == *"..." ]]; then
|
||||||
|
body="<p class=\"loading\">${body/.../}</p>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -n "${2:-}" ] && script="$2" || script=""
|
||||||
|
|
||||||
|
local HTML
|
||||||
|
HTML=$(<"$TEMPLATE")
|
||||||
|
HTML="${HTML/\[1\]/$title}"
|
||||||
|
HTML="${HTML/\[2\]/$script}"
|
||||||
|
HTML="${HTML/\[3\]/$body}"
|
||||||
|
HTML="${HTML/\[4\]/$footer}"
|
||||||
|
HTML="${HTML/\[5\]/$FOOTER2}"
|
||||||
|
|
||||||
|
echo "$HTML" > "$PAGE"
|
||||||
|
echo "$body" > "$INFO"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
addPackage() {
|
||||||
|
local pkg=$1
|
||||||
|
local desc=$2
|
||||||
|
|
||||||
|
if apt-mark showinstall | grep -qx "$pkg"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
MSG="Installing $desc..."
|
||||||
|
info "$MSG" && html "$MSG"
|
||||||
|
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get -qq update
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start webserver
|
||||||
|
cp -r /var/www/* /run/shm
|
||||||
|
html "Starting $APP for Docker..."
|
||||||
|
nginx -e stderr
|
||||||
|
|
||||||
|
return 0
|
Loading…
Reference in a new issue