Compare commits

...

133 commits

Author SHA1 Message Date
Kroese
9546b11150
docs: Add input types (#245) 2025-04-23 12:56:20 +02:00
Kroese
ffc4bb39c2
docs: Add quotes around $PWD (#244) 2025-04-16 12:05:26 +02:00
Kroese
41da658176
build: Workflow file (#243) 2025-04-14 20:34:51 +02:00
Kroese
caa7bc311d
feat: Detect Docker Desktop (#242) 2025-04-08 07:59:16 +02:00
Kroese
72738ce2a3
build: Add e2fsprogs package (#241) 2025-04-06 00:39:53 +02:00
Kroese
d5cb0b8c71
fix: Remove obsolete packages (#240) 2025-04-06 00:28:51 +02:00
Kroese
038c4c6e14
fix: Refactor USB (#239) 2025-04-06 00:14:39 +02:00
Kroese
f89cb831c4
build: Use debian download mirror (#238) 2025-04-04 22:28:20 +02:00
Kroese
a100af1fc5
build: Pin qemu-efi-aarch64 (#237) 2025-04-04 21:41:45 +02:00
Kroese
56cb411a23
build: Remove ovmf (#236) 2025-04-04 21:17:26 +02:00
Kroese
7622677489
fix: Downgrade ovmf to 2024.11 (#235) 2025-04-04 01:14:23 +02:00
Kroese
18dee3c07d
docs: Disk pass-through (#234) 2025-04-03 10:27:02 +02:00
Kroese
105db123af
feat: Default to alpine (#233) 2025-04-03 01:32:34 +02:00
Kroese
846c232bd0
fix: Check if serial is readable (#232) 2025-03-28 20:14:19 +01:00
Kroese
0e5cca6a1e
docs: Github Codespaces (#231) 2025-03-27 01:46:34 +01:00
Kroese
cd018a0cca
feat: Validate BIOS serial (#230) 2025-03-27 01:08:17 +01:00
Kroese
be36019608
build: Add dmidecode (#229) 2025-03-27 00:26:27 +01:00
Kroese
8f70833e1f
fix: Increase number of USB ports (#228) 2025-03-26 19:29:04 +01:00
Kroese
29adf083b7
docs: Github Codespaces (#227) 2025-03-25 14:43:33 +01:00
Kroese
95848f01a0
feat: Remove non-printable characters (#226) 2025-03-24 13:57:49 +01:00
Kroese
469875fed4
docs: KVM information (#225) 2025-03-20 23:21:50 +01:00
Kroese
5659b0245d
docs: Compatiblity chart (#224) 2025-03-20 23:04:36 +01:00
Kroese
4177dcd59b
docs: Add Podman (#223) 2025-03-20 20:07:28 +01:00
Kroese
ca650b2ffc
feat: Platform variable (#222) 2025-03-20 10:08:21 +01:00
Kroese
61a0db36b4
build: Ignore missing files (#221) 2025-03-20 02:51:14 +01:00
Kroese
ad2458ea42
build: Refactor shared code (#220) 2025-03-20 02:49:36 +01:00
Kroese
226ecd081a
fix: User-mode networking (#219) 2025-03-19 13:17:16 +01:00
Kroese
5b8d5059e5
feat: Add devcontainer (#218) 2025-03-19 08:56:32 +01:00
Kroese
e0a5657ad5
feat: Display nameservers (#217) 2025-03-18 19:20:36 +01:00
Kroese
ea81b4751a
docs: Operating Systems (#216) 2025-03-18 14:22:38 +01:00
Kroese
b54b4986dd
docs: Codespaces (#215) 2025-03-18 14:21:42 +01:00
Kroese
68aebafaf3
feat: Refactor helper functions (#214) 2025-03-18 12:58:11 +01:00
Kroese
e75dceb3b6
feat: Add devcontainer (#213) 2025-03-18 11:20:37 +01:00
Kroese
3b9e436c3d
fix: Remove port 80 (#212) 2025-03-18 05:07:29 +01:00
Kroese
66b70a8fe1
feat: IPv6 support (#211) 2025-03-18 03:56:08 +01:00
Kroese
f15feef69b
feat: Switch to port 80 (#210) 2025-03-17 16:42:58 +01:00
Kroese
824c7a42f4
fix: Fallback to POSIX fallocate (#209) 2025-03-17 11:11:21 +01:00
Kroese
87fbfecd92
docs: Readme (#208) 2025-03-16 14:31:43 +01:00
Kroese
b6eee86366
docs: Networking (#207) 2025-03-16 14:16:12 +01:00
Kroese
7014ab3ac9
feat: Multi-platform definitions (#206) 2025-03-16 09:05:27 +01:00
Kroese
d582f6543e
docs: Readme (#205) 2025-03-16 06:29:10 +01:00
Kroese
1abc636ede
feat: Select operating systems (#204) 2025-03-15 14:00:44 +01:00
Kroese
120171e826
feat: Verify clock source (#203) 2025-03-15 03:27:33 +01:00
Kroese
36cd38da73
docs: Add debug info (#202) 2025-03-14 19:56:06 +01:00
Kroese
de2512f480
fix: Format filesizes (#201) 2025-03-14 18:27:04 +01:00
Kroese
e3c54ca9a1
feat: Validate configured RAM (#200) 2025-03-14 14:43:45 +01:00
Kroese
318271db1c
docs: File sharing (#199) 2025-03-14 14:12:20 +01:00
Kroese
462fcfdf75
feat: Support file sharing (#198) 2025-03-14 06:57:32 +01:00
Kroese
85acaee0f4
feat: Validate configured RAM (#197) 2025-03-13 11:37:43 +01:00
Kroese
98a411c9f5
feat: Check CPU core configuration (#196) 2025-03-13 10:47:59 +01:00
Kroese
a63295d1c1
build: Update noVNC to v1.6.0 (#195) 2025-03-12 21:36:55 +01:00
Kroese
ca83662154
feat: Validate specified size (#194) 2025-03-12 21:12:16 +01:00
Kroese
edac315970
docs: Use relative paths (#193) 2025-03-12 17:10:00 +01:00
Kroese
1eaf327d19
docs: Kubernetes deployment (#192) 2025-03-07 00:43:06 +01:00
Kroese
2d56154644
fix: Network shutdown (#191) 2025-03-05 04:59:09 +01:00
Kroese
65cea83713
fix: Do not set MTU size for legacy Windows versions (#190) 2025-03-04 15:38:28 +01:00
Kroese
3ea684141d
fix: Gateway MAC generation (#189) 2025-03-03 13:54:59 +01:00
Kroese
bb77556ae8
feat: Set MTU size for TAP interface (#188) 2025-03-03 13:41:36 +01:00
Kroese
79b8aad8f3
feat: Automaticly match MTU size (#187) 2025-03-03 12:21:03 +01:00
Kroese
e2c5182162
docs: Readme (#186) 2025-03-01 14:21:00 +01:00
Kroese
0188a8f8a4
docs: Readme (#185) 2025-03-01 14:16:07 +01:00
Kroese
65fd7c839c
feat: Check path to boot.iso (#184) 2025-02-28 03:55:17 +01:00
Kroese
03115137c8
feat: Allow bridge networks (#183) 2025-02-27 11:51:38 +01:00
Kroese
cef08413e3
fix: Add e2fsprogs package (#182) 2025-02-26 22:28:31 +01:00
Kroese
50831e9f17
docs: Update links (#181) 2025-02-25 15:43:20 +01:00
Kroese
63afa68a5c
feat: Make app name configurable (#180) 2025-02-25 15:10:56 +01:00
Kroese
69ece08bcc
fix: Generate local MAC address (#179) 2025-02-25 05:50:39 +01:00
Kroese
71810373f9
fix: Preserve gateway MAC address (#178) 2025-02-24 03:36:28 +01:00
Kroese
14a0beddf2
feat: Add restart policy (#177) 2025-02-17 09:12:41 +01:00
Kroese
eb2c9cf437
feat: Implement password protection (#176) 2025-02-15 04:35:03 +01:00
Kroese
724a8cb720
feat: Implement password protection (#175) 2025-02-15 02:23:04 +01:00
Kroese
885ca224ff
feat: Remove existing TAP interface (#174) 2025-02-14 20:17:50 +01:00
Kroese
2f0bc64fd5
docs: Add restart policy (#173) 2025-02-10 00:16:57 +01:00
Kroese
4cbec8a9b5
feat: Improve CPU detection (#172) 2025-02-06 01:50:29 +01:00
Kroese
2ff8e929b5
feat: Remove fixed addressing (#171) 2025-01-15 23:14:48 +01:00
Kroese
cb548a0cfb
fix: Display fallback (#170) 2024-12-20 15:02:20 +01:00
Kroese
1091fc9dca
fix: TUN device error (#168) 2024-12-03 11:46:32 +01:00
Kroese
5b3a1d3fc1
docs: Add TUN device (#167) 2024-12-01 17:37:51 +01:00
Kroese
55db344c71
feat: Make network adapter configurable (#166) 2024-11-26 20:00:09 +01:00
Kroese
9ec698830e
feat: Improved network error handling (#165) 2024-11-20 16:24:50 +01:00
Kroese
ded63e0e52
feat: Improve CPU detection (#164) 2024-11-18 13:33:12 +01:00
Kroese
cc57dce4d1
feat: Use relative paths for noVNC (#163) 2024-11-15 04:31:39 +01:00
Kroese
57c5812085
feat: Support UUID flag (#162) 2024-11-15 04:14:39 +01:00
Kroese
68710fa750
docs: Compatibility chart (#161) 2024-11-13 03:39:24 +01:00
Kroese
d949351542
docs: Add Kubernetes URL (#160) 2024-11-13 03:38:26 +01:00
Kroese
42f5e53211
docs: Add compatibility chart (#159) 2024-11-13 03:28:06 +01:00
Kroese
608fb6bb25
fix: Disable HTTP keepalives (#158) 2024-11-13 02:49:07 +01:00
Kroese
06a7ea9090
docs: Readme (#157) 2024-11-13 00:33:02 +01:00
Kroese
4d1c1c476b
feat: Support image commit (#156) 2024-11-13 00:26:59 +01:00
Kroese
c244483847
feat: Rename host to kernel (#155) 2024-11-11 16:26:51 +01:00
Kroese
508ed449c4
docs: KVM troubleshooting (#154) 2024-11-11 14:22:22 +01:00
Kroese
8d3e2d8216
docs: Display output information (#153) 2024-11-11 13:01:45 +01:00
Kroese
64cb61d439
docs: Display output information (#152) 2024-11-11 12:52:51 +01:00
Kroese
5984834dbf
feat: Display unknown filesystem (#151) 2024-11-10 14:59:00 +01:00
Kroese
9c6fbe9ea8
feat: Improve CPU detection (#150) 2024-11-10 11:20:22 +01:00
Kroese
52c89503cc
feat: Use automatic CD-ROM interface for legacy boot (#149) 2024-11-10 11:19:20 +01:00
Kroese
e6af7e9782
feat: Make USB optional (#148) 2024-10-19 22:17:06 +02:00
Kroese
57f20481c7
feat: Make USB optional (#147) 2024-10-19 22:08:57 +02:00
Kroese
71eaf160f7
build: Use latest Debian image (#146) 2024-10-19 22:04:47 +02:00
Kroese
944924ab95
feat: Improve CPU detection (#145) 2024-10-15 10:29:02 +02:00
Kroese
518b815ba2
feat: Improve CPU detection (#144) 2024-10-14 18:47:24 +02:00
Kroese
26403222ba
feat: Support Rockchip Orange Pi 5 Plus (#143) 2024-10-13 00:30:58 +02:00
Kroese
3f3d32f820
feat: Add NVME disk type (#142) 2024-10-07 15:14:33 +02:00
Kroese
a7a6d0a7ee
feat: Add NVME device (#141) 2024-10-07 14:26:23 +02:00
Kroese
40e583f1b4
fix: Remove scsi parameter from virtio-blk-pci (#140) 2024-10-01 21:52:59 +02:00
Kroese
50ddafa884
build: Pin Debian version (#139) 2024-09-27 14:39:52 +02:00
Kroese
e1703d1493
fix: Split ports correctly (#138) 2024-09-10 13:06:55 +02:00
Kroese
9ed7b80038
fix: Port forwarding warning (#137) 2024-09-05 18:53:57 +02:00
Kroese
d842775e28
docs: Update package URL (#136) 2024-09-03 15:12:56 +02:00
Kroese
7e45d9fd30
docs: Update package URL (#135) 2024-08-27 18:03:33 +02:00
Kroese
4142b4ac5b
fix: Progress calculation (#134) 2024-08-18 17:53:59 +02:00
Kroese
6595bc7c34
fix: Check lscpu output (#133) 2024-08-18 03:30:59 +02:00
Kroese
540821ce5d
build: Environment variables (#132) 2024-08-18 02:12:14 +02:00
Kroese
49e41f2a1e
docs: Fix badge (#131) 2024-08-13 20:54:59 +02:00
Kroese
1438222f1c
build: Update noVNC to v1.5.0 (#130) 2024-06-18 16:45:52 +02:00
dependabot[bot]
f9fa7efd71
build(deps): Bump docker/build-push-action from 5 to 6 (#128)
Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 5 to 6.
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](https://github.com/docker/build-push-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: docker/build-push-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-17 12:17:42 +02:00
Kroese
0f50e3d4be
docs: Networking (#127) 2024-06-16 06:20:45 +02:00
Kroese
36dac0f0bf
fix: Don't set script with file descriptor (#125) 2024-06-14 04:42:33 +02:00
Kroese
1e851b3c82
docs: Features (#124) 2024-06-14 04:03:45 +02:00
Kroese
c0e935daa6
feat: Create data disk for external image (#123) 2024-06-14 02:49:45 +02:00
Kroese
25d033632f
docs: Removed Kubevirt dependancy (#121) 2024-06-13 19:47:24 +02:00
Kroese
cea30ec530
docs: Relative links (#122) 2024-06-13 19:46:31 +02:00
Kroese
91b1d23aa3
fix: Assume GB when no unit is present (#120) 2024-06-13 19:01:45 +02:00
Kroese
4e5cfa4eb7
docs: Add markdown alerts (#119) 2024-06-13 18:47:37 +02:00
Kroese
c6ed03f331
feat: Support download of disk images (#118) 2024-06-12 22:36:39 +02:00
Kroese
bdd0f1a1a7
feat: Detect EFI-compatible images (#117) 2024-06-12 04:42:40 +02:00
Kroese
c2a7932d8e
docs: Add GHCR badge (#116) 2024-06-11 23:38:34 +02:00
Kroese
e6c610a4e8
docs: Add GHCR badge (#115) 2024-06-11 23:33:01 +02:00
Kroese
642979e03c
docs: Features (#114) 2024-06-11 23:06:59 +02:00
Kroese
130d032614
docs: Features (#113) 2024-06-11 22:38:29 +02:00
Kroese
6e5de3669b
docs: Readme (#112) 2024-06-11 22:01:58 +02:00
Kroese
78e66a7f99
docs: Add icons (#111) 2024-06-11 21:08:26 +02:00
Kroese
0caf956e17
docs: KVM information (#110) 2024-06-11 19:53:10 +02:00
19 changed files with 436 additions and 1567 deletions

6
.devcontainer.json Normal file
View file

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

View file

@ -21,6 +21,7 @@ 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
@ -28,6 +29,7 @@ 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

View file

@ -23,6 +23,7 @@ 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
@ -30,6 +31,7 @@ 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

View file

@ -46,8 +46,8 @@ jobs:
with: with:
context: git context: git
images: | images: |
${{ secrets.DOCKERHUB_REPO }}
ghcr.io/${{ github.repository }} ghcr.io/${{ github.repository }}
${{ secrets.DOCKERHUB_REPO }}
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@v5 uses: docker/build-push-action@v6
with: with:
context: . context: .
push: true push: true

View file

@ -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 SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 SHELLCHECK_OPTS: -x --source-path=src -e SC1091 -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

View file

@ -1,7 +1,10 @@
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.4.0" ARG VERSION_VNC="1.6.0"
ARG DEBCONF_NOWARNINGS="yes" ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
@ -10,8 +13,13 @@ 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 \
@ -19,13 +27,19 @@ 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 \
netcat-openbsd \ qemu-system-arm && \
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/ && \
@ -36,20 +50,19 @@ 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 --chmod=755 ./src /run/ 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
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu-docker/master/web/index.html /var/www/index.html COPY --chmod=755 ./src /run/
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 CPU_CORES "1" ENV BOOT="alpine"
ENV RAM_SIZE "1G" ENV CPU_CORES="2"
ENV DISK_SIZE "16G" ENV RAM_SIZE="2G"
ENV BOOT "http://example.com/image.iso" ENV DISK_SIZE="16G"
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"] ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]

View file

@ -3,11 +3,15 @@ services:
container_name: qemu container_name: qemu
image: qemux/qemu-arm image: qemux/qemu-arm
environment: environment:
BOOT: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso" BOOT: "alpine"
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

View file

@ -1,3 +1,4 @@
---
apiVersion: v1 apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
@ -9,53 +10,77 @@ spec:
requests: requests:
storage: 16Gi storage: 16Gi
--- ---
apiVersion: v1 apiVersion: apps/v1
kind: Pod kind: Deployment
metadata: metadata:
name: qemu name: qemu
labels: labels:
name: qemu name: qemu
spec: spec:
terminationGracePeriodSeconds: 120 # the Kubernetes default is 30 seconds and it may be not enough replicas: 1
selector:
matchLabels:
app: qemu
template:
metadata:
labels:
app: qemu
spec:
containers: containers:
- name: qemu - name: qemu
image: qemux/qemu-arm image: qemux/qemu-arm
ports:
- containerPort: 8006
protocol: TCP
resources:
limits:
devices.kubevirt.io/kvm: 1
securityContext:
privileged: true
env: env:
- name: BOOT - name: BOOT
value: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso" value: "alpine"
- name: RAM_SIZE
value: 1G
- name: CPU_CORES
value: "1"
- name: DISK_SIZE - name: DISK_SIZE
value: "16G" value: "16G"
ports:
- containerPort: 8006
name: http
protocol: TCP
- containerPort: 5900
name: vnc
protocol: TCP
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
volumeMounts: volumeMounts:
- mountPath: /storage - mountPath: /storage
name: storage name: storage
- mountPath: /dev/kvm
name: dev-kvm
- mountPath: /dev/net/tun
name: dev-tun
terminationGracePeriodSeconds: 120
volumes: volumes:
- name: storage - name: storage
persistentVolumeClaim: persistentVolumeClaim:
claimName: qemu-pvc 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:
type: NodePort internalTrafficPolicy: Cluster
selector:
name: qemu
ports: ports:
- name: tcp-8006 - name: http
protocol: TCP
port: 8006 port: 8006
protocol: TCP
targetPort: 8006 targetPort: 8006
nodePort: 48006 - name: vnc
port: 5900
protocol: TCP
targetPort: 5900
selector:
app: qemu
type: ClusterIP

267
readme.md
View file

@ -7,29 +7,24 @@
[![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. Docker container for running ARM-based virtual machines using QEMU, for devices like the Raspberry Pi 5 and many others.
It allows you to create VM's which behave just like normal containers, meaning you can manage them using all your existing tools (like Portainer) and configure them in a language (YAML) you are already familiar with. ## Features ✨
This greatly reduces the learning curve and eliminates the need for a dedicated Proxmox or ESXi server in many cases. - Web-based viewer to control the machine directly from your browser
It uses high-performance QEMU options (like KVM acceleration, kernel-mode networking, IO threading, etc.) to achieve near-native speed. - Supports `.iso`, `.img`, `.qcow2`, `.vhd`, `.vhdx`, `.vdi`, `.vmdk` and `.raw` disk formats
Note: for KVM acceleration you need a Linux-based operating system, as it's not available on MacOS unfortunately. - High-performance options (like KVM acceleration, kernel-mode networking, IO threading, etc.) to achieve near-native speed
## Features ## Usage 🐳
- Multi-platform ##### Via Docker Compose:
- KVM acceleration
- Web-based viewer
## Usage
Via Docker Compose:
```yaml ```yaml
services: services:
@ -37,54 +32,122 @@ services:
container_name: qemu container_name: qemu
image: qemux/qemu-arm image: qemux/qemu-arm
environment: environment:
BOOT: "https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/aarch64/alpine-virt-3.19.1-aarch64.iso" BOOT: "alpine"
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 -e "BOOT=http://example.com/image.iso" -p 8006:8006 --device=/dev/kvm --cap-add NET_ADMIN qemux/qemu-arm 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
``` ```
Via Kubernetes: ##### Via Kubernetes:
```shell ```shell
kubectl apply -f kubernetes.yml kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/master/kubernetes.yml
``` ```
## FAQ ##### Via Github Codespaces:
* ### How do I use it? [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/qemus/qemu)
## FAQ 💬
### How do I use it?
Very simple! These are the steps: Very simple! These are the steps:
- Set the `BOOT` environment variable to the URL of an ISO image you want to install. - Set the `BOOT` variable to the [operating system](#how-do-i-select-the-operating-system) you want to install.
- Start the container and connect to [port 8006](http://localhost:8006) using your web browser. - Start the container and connect to [port 8006](http://127.0.0.1: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 change the storage location? ### How do I select the operating system?
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:
- /var/qemu:/storage - ./qemu:/storage
``` ```
Replace the example path `/var/qemu` with the desired storage folder. Replace the example path `./qemu` with the desired storage folder or named volume.
* ### 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:
@ -93,64 +156,85 @@ kubectl apply -f kubernetes.yml
DISK_SIZE: "128G" DISK_SIZE: "128G"
``` ```
This can also be used to resize the existing disk to a larger capacity without any data loss. > [!TIP]
> This can also be used to resize the existing disk to a larger capacity without any data loss.
* ### How do I boot a local ISO? ### How do I change the amount of CPU or RAM?
You can use a local file directly, and skip the download altogether, by binding it in your compose file in this way: By default, the container will be allowed to use a maximum of 2 CPU cores and 2 GB of RAM.
```yaml
volumes:
- /home/user/example.iso:/boot.iso
```
Replace the example path `/home/user/example.iso` with the filename of the desired ISO file, the value of `BOOT` will be ignored in this case.
* ### 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 a x86 image?
You can use [qemu-docker](https://github.com/qemus/qemu-docker/) to run x86 and x64 images on ARM.
* ### How do I boot without SCSI drivers?
By default, the machine makes use of `virtio-scsi` drives for performance reasons, and even though most Linux kernels bundle the necessary driver for this device, that may not always be the case for other operating systems.
If your machine fails to detect the hard drive, you can modify your compose file to use `virtio-blk` instead:
```yaml
environment:
DISK_TYPE: "blk"
```
* ### How do I change the amount of CPU or RAM?
By default, the container will be allowed to use a maximum of 1 CPU core and 1 GB of RAM.
If you want to adjust this, you can specify the desired amount using the following environment variables: If you want to adjust this, you can specify the desired amount using the following environment variables:
```yaml ```yaml
environment: environment:
RAM_SIZE: "4G" RAM_SIZE: "8G"
CPU_CORES: "4" CPU_CORES: "4"
``` ```
* ### How do I verify if my system supports KVM? ### How do I increase the display resolution?
To verify that your system supports KVM, run the following commands: 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.
If your guest OS bundles the `virtio-gpu` driver (as most Linux distributions do), you can add the following to your compose file:
```yaml
environment:
VGA: "virtio-gpu"
```
to add a virtual graphics cards to your machine that allows for higher resolutions.
> [!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 acceleration can't be used, check whether virtualization extensions are enabled in your BIOS. If you are running the container inside a VM instead of directly on the host, you will also need to enable nested virtualization in its settings. If you are using a cloud provider, you may be out of luck as most of them do not allow nested virtualization for their VPS's. If you are using MacOS, you are also out of luck, as only Linux and Windows support KVM. If you receive an error from `kvm-ok` indicating that KVM cannot be used, please check whether:
If you don't receive any error from `kvm-ok` at all, but the container still complains that `/dev/kvm` is missing, it might help to add `privileged: true` to your compose file (or `--privileged` to your `run` command), to rule out any permission issue. - the virtualization extensions (`Intel VT-x` or `AMD SVM`) are enabled in your BIOS.
* ### How do I assign an individual IP address to the container? - you enabled "nested virtualization" if you are running the container inside a virtual machine.
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's.
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.
### How do I expose network ports?
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.
@ -184,13 +268,14 @@ kubectl apply -f kubernetes.yml
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.
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. > [!IMPORTANT]
> 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 (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. 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.
To enable this mode, add the following lines to your compose file: To enable this mode, in which the container and the VM will have separate IP addresses, add the following lines to your compose file:
```yaml ```yaml
environment: environment:
@ -201,9 +286,7 @@ kubectl apply -f kubernetes.yml
- 'c *:* rwm' - 'c *:* rwm'
``` ```
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?
* ### 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:
@ -212,23 +295,23 @@ kubectl apply -f kubernetes.yml
DISK2_SIZE: "32G" DISK2_SIZE: "32G"
DISK3_SIZE: "64G" DISK3_SIZE: "64G"
volumes: volumes:
- /home/example:/storage2 - ./example2:/storage2
- /mnt/data/example:/storage3 - ./example3:/storage3
``` ```
* ### How do I pass-through a disk? ### How do I pass-through a disk?
It is possible to pass-through disk devices directly by adding them to your compose file in this way: It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way:
```yaml ```yaml
devices: devices:
- /dev/sdb:/disk1 - /dev/sdb:/disk1
- /dev/sdc:/disk2 - /dev/sdc1:/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:
@ -239,7 +322,24 @@ kubectl apply -f kubernetes.yml
- /dev/bus/usb - /dev/bus/usb
``` ```
* ### How can I provide custom arguments to QEMU? ### How do I share files with the host?
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:
@ -248,14 +348,23 @@ kubectl apply -f kubernetes.yml
ARGUMENTS: "-device usb-tablet" ARGUMENTS: "-device usb-tablet"
``` ```
## Stars If you want to see the full command-line arguments used, you can set:
```yaml
environment:
DEBUG: "Y"
```
## Stars 🌟
[![Stars](https://starchart.cc/qemus/qemu-arm.svg?variant=adaptive)](https://starchart.cc/qemus/qemu-arm) [![Stars](https://starchart.cc/qemus/qemu-arm.svg?variant=adaptive)](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

View file

@ -2,15 +2,16 @@
set -Eeuo pipefail set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: "${BIOS:=""}" # Bios file : "${BIOS:=""}" # BIOS file
: "${SECURE:="off"}" # Secure boot
SECURE="off"
BOOT_OPTS=""
BOOT_DESC="" BOOT_DESC=""
DIR="/usr/share/qemu" BOOT_OPTS=""
[ -n "$BIOS" ] && BOOT_MODE="custom"
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"
;; ;;
@ -32,37 +33,84 @@ case "${BOOT_MODE,,}" in
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!" error "Unknown BOOT_MODE, value \"${BOOT_MODE}\" is not recognized!"
exit 33 exit 33
;; ;;
esac esac
if [ -n "$BIOS" ]; then case "${BOOT_MODE,,}" in
"uefi" | "secure" | "windows" | "windows_secure" )
BOOT_OPTS+=" -bios $DIR/$BIOS" AAVMF="/usr/share/AAVMF/"
return 0 DEST="$STORAGE/${BOOT_MODE,,}"
fi if [ ! -s "$DEST.rom" ] || [ ! -f "$DEST.rom" ]; then
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 [ ! -s "$AAVMF/$ROM" ] || [ ! -f "$AAVMF/$ROM" ] && error "UEFI boot file ($AAVMF/$ROM) not found!" && exit 44
rm -f "$DEST.rom" rm -f "$DEST.rom"
dd if=/dev/zero "of=$DEST.rom" bs=1M count=64 status=none dd if=/dev/zero "of=$DEST.rom" bs=1M count=64 status=none
dd "if=$AAVMF/$ROM" "of=$DEST.rom" conv=notrunc status=none dd "if=$AAVMF/$ROM" "of=$DEST.rom" conv=notrunc status=none
fi fi
if [ ! -s "$DEST.vars" ] || [ ! -f "$DEST.vars" ]; then if [ ! -s "$DEST.vars" ] || [ ! -f "$DEST.vars" ]; then
[ ! -s "$AAVMF/$VARS" ] || [ ! -f "$AAVMF/$VARS" ] && error "UEFI vars file ($AAVMF/$VARS) not found!" && exit 45 [ ! -s "$AAVMF/$VARS" ] || [ ! -f "$AAVMF/$VARS" ] && error "UEFI vars file ($AAVMF/$VARS) not found!" && exit 45
rm -f "$DEST.vars" rm -f "$DEST.vars"
dd if=/dev/zero "of=$DEST.vars" bs=1M count=64 status=none dd if=/dev/zero "of=$DEST.vars" bs=1M count=64 status=none
dd "if=$AAVMF/$VARS" "of=$DEST.vars" conv=notrunc 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 fi
BOOT_OPTS+=" -drive file=$DEST.rom,if=pflash,unit=0,format=raw,readonly=on" CLOCKSOURCE="tsc"
BOOT_OPTS+=" -drive file=$DEST.vars,if=pflash,unit=1,format=raw" [[ "${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
return 0 return 0

View file

@ -1,22 +1,37 @@
#!/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"}" : "${USB:="qemu-xhci,id=xhci,p2=7,p3=7"}"
: "${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"
USB_OPTS="-device $USB -device usb-kbd -device usb-tablet" CPU_OPTS="-cpu $CPU_FLAGS -smp $SMP"
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=${SECURE},dump-guest-core=off${KVM_OPTS}"
DEV_OPTS="-object rng-random,id=objrng0,filename=/dev/urandom"
DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
[[ "${BOOT_MODE,,}" != "windows"* ]] && DEV_OPTS+=" -device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
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" [ -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+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0"
if [[ "${BOOT_MODE,,}" != "windows"* ]]; then
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
@ -37,21 +52,27 @@ fi
if [[ "$RAM_CHECK" != [Nn]* ]]; then if [[ "$RAM_CHECK" != [Nn]* ]]; then
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}') RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
AVAIL_GB=$(( RAM_AVAIL/1073741824 )) AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
error "Your configured RAM_SIZE of $WANTED_GB GB is too high for the $AVAIL_GB GB of memory available, please set a lower value." 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."
exit 17 [[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
fi info "$msg"
else
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
warn "your configured RAM_SIZE of $WANTED_GB GB is very close to the $AVAIL_GB GB of memory available, please consider a lower value." msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available, please consider a lower value."
if [[ "${FS,,}" != "zfs" ]]; then
warn "$msg"
else
info "$msg"
fi
fi
fi fi
fi fi
if [[ "$DEBUG" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
printf "Arguments:\n\n%s" "${ARGS// -/$'\n-'}" && echo printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi fi
return 0 return 0

View file

@ -1,638 +0,0 @@
#!/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_TYPE:=""}" # Device type to be used, choose "ide", "usb", "blk" or "scsi"
: "${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
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_STYLE $DISK_DESC image in $DISK_FMT format..."
local FAIL="Could not create a $DISK_STYLE $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+=",nocow=on"
[ -n "$DISK_FLAGS" ] && 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_STYLE $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
local msg="Converting $DISK_DESC to $DST_FMT"
html "$msg..."
info "$msg, please wait until completed..."
local CONV_FLAGS="-p"
local DISK_PARAM="$DISK_ALLOC"
isCow "$FS" && DISK_PARAM+=",nocow=on"
if [[ "$DST_FMT" != "raw" ]]; then
if [[ "$ALLOCATE" == [Nn]* ]]; then
CONV_FLAGS+=" -c"
fi
[ -n "$DISK_FLAGS" ] && 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_STYLE $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
msg="Conversion of $DISK_DESC"
html "$msg completed..."
info "$msg 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_FILE=$1
local DISK_TYPE=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
local DISK_FMT=$5
local DISK_IO=$6
local DISK_CACHE=$7
local DISK_ID="data$DISK_INDEX"
local index=""
[ -n "$DISK_INDEX" ] && index=",bootindex=$DISK_INDEX"
local result=" -drive file=$DISK_FILE,id=$DISK_ID,format=$DISK_FMT,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on"
case "${DISK_TYPE,,}" in
"auto" )
echo "$result"
;;
"usb" )
result+=",if=none \
-device usb-storage,drive=${DISK_ID}${index}"
echo "$result"
;;
"ide" )
result+=",if=none \
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \
-device ide-hd,drive=${DISK_ID},bus=ahci$DISK_INDEX.0,rotation_rate=$DISK_ROTATION${index}"
echo "$result"
;;
"blk" | "virtio-blk" )
result+=",if=none \
-device virtio-blk-pci,drive=${DISK_ID},scsi=off,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}"
echo "$result"
;;
"scsi" | "virtio-scsi" )
result+=",if=none \
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \
-device scsi-hd,drive=${DISK_ID},bus=${DISK_ID}b.0,channel=0,scsi-id=0,lun=0,rotation_rate=$DISK_ROTATION${index}"
echo "$result"
;;
esac
return 0
}
addMedia () {
local DISK_FILE=$1
local DISK_TYPE=$2
local DISK_BUS=$3
local DISK_INDEX=$4
local DISK_ADDRESS=$5
local index=""
local DISK_ID="cdrom$DISK_BUS"
[ -n "$DISK_INDEX" ] && index=",bootindex=$DISK_INDEX"
local result=" -drive file=$DISK_FILE,id=$DISK_ID,format=raw,cache=unsafe,readonly=on,media=cdrom"
case "${DISK_TYPE,,}" in
"auto" )
echo "$result"
;;
"usb" )
result+=",if=none \
-device usb-storage,drive=${DISK_ID}${index},removable=on"
echo "$result"
;;
"ide" )
result+=",if=none \
-device ich9-ahci,id=ahci${DISK_BUS},addr=$DISK_ADDRESS \
-device ide-cd,drive=${DISK_ID},bus=ahci${DISK_BUS}.0${index}"
echo "$result"
;;
"blk" | "virtio-blk" )
result+=",if=none \
-device virtio-blk-pci,drive=${DISK_ID},scsi=off,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}"
echo "$result"
;;
"scsi" | "virtio-scsi" )
result+=",if=none \
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \
-device scsi-cd,drive=${DISK_ID},bus=${DISK_ID}b.0${index}"
echo "$result"
;;
esac
return 0
}
addDisk () {
local DISK_BASE=$1
local DISK_TYPE=$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_EXT DIR DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DISK_EXT=$(fmt2ext "$DISK_FMT")
local DISK_FILE="$DISK_BASE.$DISK_EXT"
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
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE")
return 0
}
addDevice () {
local DISK_DEV=$1
local DISK_TYPE=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
[ -z "$DISK_DEV" ] && return 0
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE")
return 0
}
html "Initializing disks..."
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
case "${DISK_TYPE,,}" in
"ide" | "usb" | "scsi" | "blk" | "auto" ) ;;
* ) error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is unrecognized!" && exit 80 ;;
esac
if [ -z "${MEDIA_TYPE:-}" ]; then
case "${MACHINE,,}" in
"virt" | "pc-q35-2"* | "pc-i440fx-2"* )
MEDIA_TYPE="auto" ;;
* )
[[ "${DISK_TYPE,,}" != "blk" ]] && MEDIA_TYPE="$DISK_TYPE" || MEDIA_TYPE="ide" ;;
esac
fi
case "${MEDIA_TYPE,,}" in
"ide" | "usb" | "scsi" | "blk" | "auto" ) ;;
* ) error "Invalid MEDIA_TYPE specified, value \"$MEDIA_TYPE\" is unrecognized!" && exit 80 ;;
esac
if [ -f "$BOOT" ] && [ -s "$BOOT" ]; then
DISK_OPTS+=$(addMedia "$BOOT" "$MEDIA_TYPE" "0" "$BOOT_INDEX" "0x5")
fi
DRIVERS="/drivers.iso"
[ ! -f "$DRIVERS" ] || [ ! -s "$DRIVERS" ] && DRIVERS="$STORAGE/drivers.iso"
if [ -f "$DRIVERS" ] && [ -s "$DRIVERS" ]; then
case "${MACHINE,,}" in
"virt" | "pc-q35-2"* | "pc-i440fx-2"* )
DRIVER_TYPE="auto" ;;
* )
DRIVER_TYPE="ide" ;;
esac
DISK_OPTS+=$(addMedia "$DRIVERS" "$DRIVER_TYPE" "1" "" "0x6")
fi
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
if [ -z "$ALLOCATE" ]; then
ALLOCATE="N"
fi
if [[ "$ALLOCATE" == [Nn]* ]]; then
DISK_STYLE="growable"
DISK_ALLOC="preallocation=off"
else
DISK_STYLE="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" "$DISK_TYPE" "3" "0xa" || exit $?
else
addDisk "$DISK1_FILE" "$DISK_TYPE" "disk" "$DISK_SIZE" "3" "0xa" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
if [ -n "$DEVICE2" ]; then
addDevice "$DEVICE2" "$DISK_TYPE" "4" "0xb" || exit $?
else
addDisk "$DISK2_FILE" "$DISK_TYPE" "disk2" "$DISK2_SIZE" "4" "0xb" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
if [ -n "$DEVICE3" ]; then
addDevice "$DEVICE3" "$DISK_TYPE" "5" "0xc" || exit $?
else
addDisk "$DISK3_FILE" "$DISK_TYPE" "disk3" "$DISK3_SIZE" "5" "0xc" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
if [ -n "$DEVICE4" ]; then
addDevice "$DEVICE4" "$DISK_TYPE" "6" "0xd" || exit $?
else
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xd" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
DISK_OPTS+=" -object iothread,id=io2"
html "Initialized disks successfully..."
return 0

View file

@ -3,29 +3,25 @@ set -Eeuo pipefail
# Docker environment variables # Docker environment variables
: "${VGA:=""}" # VGA adaptor : "${VGA:="ramfb"}" # VGA adaptor
: "${DISPLAY:="web"}" # Display type : "${DISPLAY:="web"}" # Display type
if [[ "${BOOT_MODE,,}" != "windows" ]]; then [[ "$DISPLAY" == ":0" ]] && DISPLAY="web"
[ -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"
;; ;;
*) *)

View file

@ -1,13 +1,17 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail set -Eeuo pipefail
APP="QEMU" : "${APP:="QEMU"}"
SUPPORT="https://github.com/qemus/qemu-arm" : "${MACHINE:="virt"}"
: "${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
. install.sh # Get bootdisk . define.sh # Define images
. 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

View file

@ -1,66 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
iso () {
local file="$1"
[ ! -f "$file" ] && return 1
[ ! -s "$file" ] && return 1
BOOT="$file"
return 0
}
file=$(find / -maxdepth 1 -type f -iname boot.iso | head -n 1)
[ ! -s "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname boot.iso | head -n 1)
iso "$file" && return 0
if [ -z "$BOOT" ] || [[ "$BOOT" == *"example.com/image.iso" ]]; then
hasDisk && return 0
error "No boot disk specified, set BOOT= to the URL of an ISO file." && exit 64
fi
base=$(basename "$BOOT")
iso "$STORAGE/$base" && return 0
base=$(basename "${BOOT%%\?*}")
: "${base//+/ }"; printf -v base '%b' "${_//%/\\x}"
base=$(echo "$base" | sed -e 's/[^A-Za-z0-9._-]/_/g')
iso "$STORAGE/$base" && return 0
TMP="$STORAGE/${base%.*}.tmp"
rm -f "$TMP"
# 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
msg="Downloading $base"
info "$msg..." && html "$msg..."
/run/progress.sh "$TMP" "" "$msg ([P])..." &
{ wget "$BOOT" -O "$TMP" -q --timeout=30 --show-progress "$progress"; rc=$?; } || :
fKill "progress.sh"
msg="Failed to download $BOOT"
(( rc == 3 )) && error "$msg , cannot write file (disk full?)" && exit 60
(( rc == 4 )) && error "$msg , network failure!" && exit 60
(( rc == 8 )) && error "$msg , server issued an error response!" && exit 60
(( rc != 0 )) && error "$msg , reason: $rc" && exit 60
[ ! -s "$TMP" ] && error "$msg" && 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" "$STORAGE/$base"
! iso "$STORAGE/$base" && exit 63
return 0

View file

@ -1,416 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: "${MAC:=""}"
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
: "${HOST_PORTS:=""}"
: "${USER_PORTS:=""}"
: "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="qemu"}"
: "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_HOST:="QEMU"}"
: "${VM_NET_IP:="20.20.20.21"}"
: "${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." && return 1
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" && return 1
[[ ! -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)" && return 1
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'" && return 1
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" && return 1
fi
NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30,script=no,downscript=no"
return 0
}
configureDNS() {
# dnsmasq configuration:
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+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1 --dhcp-option=option:router,${VM_NET_IP%.*}.1"
# Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && return 1
fi
return 0
}
getUserPorts() {
local args=""
local list=$1
local ssh="22"
local rdp="3389"
[ -z "$list" ] && list="$ssh,$rdp" || list+=",$ssh,$rdp"
list="${list/,/ }"
list="${list## }"
list="${list%% }"
for port in $list; do
args+="hostfwd=tcp::$port-$VM_NET_IP:$port,"
done
echo "${args%?}"
return 0
}
getHostPorts() {
local list=$1
local vnc="5900"
local web="8006"
[ -z "$list" ] && list="$web" || list+=",$web"
if [[ "${DISPLAY,,}" == "vnc" ]] || [[ "${DISPLAY,,}" == "web" ]]; then
[ -z "$list" ] && list="$vnc" || list+=",$vnc"
fi
[ -z "$list" ] && echo "" && return 0
if [[ "$list" != *","* ]]; then
echo " ! --dport $list"
else
echo " -m multiport ! --dports $list"
fi
return 0
}
configureUser() {
NET_OPTS="-netdev user,id=hostnet0,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,hostname=$VM_NET_HOST"
local forward
forward=$(getUserPorts "$USER_PORTS")
[ -n "$forward" ] && NET_OPTS+=",$forward"
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" && return 1
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" && return 1
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
{ ip link add dev dockerbridge type bridge ; rc=$?; } || :
if (( rc != 0 )); then
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then
error "Failed to add IP address!" && return 1
fi
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" && return 1
fi
while ! ip link set "$VM_NET_TAP" up promisc on; do
info "Waiting for TAP to become available..."
sleep 2
done
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
error "Failed to set IP link!" && return 1
fi
# 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=$(getHostPorts "$HOST_PORTS")
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
error "$tables" && return 1
fi
# shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then
error "Failed to configure IP tables!" && return 1
fi
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then
error "Failed to configure IP tables!" && return 1
fi
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,id=hostnet0,ifname=$VM_NET_TAP"
if [ -c /dev/vhost-net ]; then
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
(( rc == 0 )) && NET_OPTS+=",vhost=on,vhostfd=40"
fi
NET_OPTS+=",script=no,downscript=no"
! configureDNS && return 1
return 0
}
closeNetwork() {
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
[[ "$NETWORK" == [Nn]* ]] && 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")"
[[ "${NETWORK,,}" == "user"* ]] && return 0
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
warn "you are using Docker Desktop for $os which does not support macvlan, please revert to bridge networking!"
fi
return 0
}
getInfo() {
if [ -z "$VM_NET_DEV" ]; then
# Give Kubernetes priority over the default interface
[ -d "/sys/class/net/net0" ] && VM_NET_DEV="net0"
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
# Automaticly detect the default network interface
[ -z "$VM_NET_DEV" ] && 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 route list dev "$VM_NET_DEV" | awk ' /^default/ {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" == [Nn]* ]]; 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
if [[ "$IP" == "172."* ]]; then
warn "container IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
fi
# Configure for macvtap interface
! configureDHCP && exit 20
else
if [[ "$IP" != "172."* ]] && [[ "$IP" != "10.8"* ]] && [[ "$IP" != "10.9"* ]]; then
checkOS
fi
if [[ "${NETWORK,,}" != "user"* ]]; then
# Configure for tap interface
if ! configureNAT; then
NETWORK="user"
warn "falling back to usermode networking (slow)!"
ip link set "$VM_NET_TAP" down promisc off &> null || true
ip link delete "$VM_NET_TAP" &> null || true
ip link set dockerbridge down &> null || true
ip link delete dockerbridge &> null || true
fi
fi
if [[ "${NETWORK,,}" == "user"* ]]; then
# Configure for usermode networking (slirp)
! configureUser && exit 24
fi
fi
NET_OPTS+=" -device virtio-net-pci,romfile=,netdev=hostnet0,mac=$VM_NET_MAC,id=net0"
html "Initialized network successfully..."
return 0

View file

@ -19,7 +19,12 @@ if [[ "$CPU" == "Cortex A55" ]] && [[ "$CORES" == "8" ]]; then
[ -z "$CPU_PIN" ] && CPU_PIN="4,5,6,7" [ -z "$CPU_PIN" ] && CPU_PIN="4,5,6,7"
fi fi
if [[ "${ARCH,,}" != "arm64" ]]; then 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" KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ARM64 instructions, this will cause a major loss of performance." warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ARM64 instructions, this will cause a major loss of performance."
fi fi
@ -29,27 +34,31 @@ if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR="" KVM_ERR=""
if [ ! -e /dev/kvm ]; then if [ ! -e /dev/kvm ]; then
KVM_ERR="(device file missing)" KVM_ERR="(/dev/kvm is 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="(no write access)" KVM_ERR="(/dev/kvm is unwriteable)"
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
if grep -qi Microsoft /proc/version; then kernel=$(uname -a)
warn "you are using Windows 10 which has no KVM support, this will cause a major loss of performance." case "${kernel,,}" in
else *"microsoft"* )
error "KVM acceleration not available $KVM_ERR, this will cause a major loss of performance." error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
error "See the FAQ on how to diagnose the cause, or continue without KVM by setting KVM=N (not recommended)." *"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
error "KVM acceleration is not available $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ for possible causes, or continue without it by adding KVM: \"N\" (not recommended)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88 [[ "$DEBUG" != [Yy1]* ]] && exit 88
fi fi
fi fi
fi
fi fi

View file

@ -1,38 +0,0 @@
#!/usr/bin/env bash
set -Eeuo pipefail
escape () {
local s
s=${1//&/\&amp;}
s=${s//</\&lt;}
s=${s//>/\&gt;}
s=${s//'"'/\&quot;}
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

View file

@ -1,212 +0,0 @@
#!/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
: "${RAM_CHECK:="Y"}" # Check available RAM
: "${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)
CORES=$(grep -c '^processor' /proc/cpuinfo)
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
CPU=$(lscpu | grep -m 1 -i 'model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
# 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_SPARE=500000000
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/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_CHECK" != [Nn]* ]]; then
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
error "Your configured RAM_SIZE of $WANTED_GB GB is too high for the $AVAIL_GB GB of memory available, please set a lower value."
exit 17
fi
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//&/\&amp;}
s=${s//</\&lt;}
s=${s//>/\&gt;}
s=${s//'"'/\&quot;}
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
}
hasDisk() {
[ -b "/disk1" ] && return 0
[ -b "/dev/disk1" ] && return 0
[ -b "${DEVICE:-}" ] && return 0
[ -s "$STORAGE/data.img" ] && return 0
[ -s "$STORAGE/data.qcow2" ] && return 0
return 1
}
# Start webserver
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
return 0