Compare commits
26 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9546b11150 | ||
![]() |
ffc4bb39c2 | ||
![]() |
41da658176 | ||
![]() |
caa7bc311d | ||
![]() |
72738ce2a3 | ||
![]() |
d5cb0b8c71 | ||
![]() |
038c4c6e14 | ||
![]() |
f89cb831c4 | ||
![]() |
a100af1fc5 | ||
![]() |
56cb411a23 | ||
![]() |
7622677489 | ||
![]() |
18dee3c07d | ||
![]() |
105db123af | ||
![]() |
846c232bd0 | ||
![]() |
0e5cca6a1e | ||
![]() |
cd018a0cca | ||
![]() |
be36019608 | ||
![]() |
8f70833e1f | ||
![]() |
29adf083b7 | ||
![]() |
95848f01a0 | ||
![]() |
469875fed4 | ||
![]() |
5659b0245d | ||
![]() |
4177dcd59b | ||
![]() |
ca650b2ffc | ||
![]() |
61a0db36b4 | ||
![]() |
ad2458ea42 |
17 changed files with 85 additions and 2198 deletions
2
.github/ISSUE_TEMPLATE/1-issue.yml
vendored
2
.github/ISSUE_TEMPLATE/1-issue.yml
vendored
|
@ -21,6 +21,7 @@ body:
|
|||
attributes:
|
||||
label: Docker compose
|
||||
description: The compose file (or otherwise the `docker run` command used).
|
||||
render: yaml
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
@ -28,6 +29,7 @@ body:
|
|||
attributes:
|
||||
label: Docker log
|
||||
description: The logfile of the container (as shown by `docker logs qemu`).
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
|
2
.github/ISSUE_TEMPLATE/3-bug.yml
vendored
2
.github/ISSUE_TEMPLATE/3-bug.yml
vendored
|
@ -23,6 +23,7 @@ body:
|
|||
attributes:
|
||||
label: Docker compose
|
||||
description: The compose file (or otherwise the `docker run` command used).
|
||||
render: yaml
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
@ -30,6 +31,7 @@ body:
|
|||
attributes:
|
||||
label: Docker log
|
||||
description: The logfile of the container (as shown by `docker logs qemu`).
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -46,8 +46,8 @@ jobs:
|
|||
with:
|
||||
context: git
|
||||
images: |
|
||||
${{ secrets.DOCKERHUB_REPO }}
|
||||
ghcr.io/${{ github.repository }}
|
||||
${{ secrets.DOCKERHUB_REPO }}
|
||||
tags: |
|
||||
type=raw,value=latest,priority=100
|
||||
type=raw,value=${{ vars.MAJOR }}.${{ vars.MINOR }}
|
||||
|
|
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
|
@ -14,7 +14,7 @@ jobs:
|
|||
name: Run ShellCheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
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
|
||||
uses: hadolint/hadolint-action@v3.1.0
|
||||
|
|
25
Dockerfile
25
Dockerfile
|
@ -1,3 +1,6 @@
|
|||
ARG VERSION_ARG="latest"
|
||||
|
||||
FROM qemux/qemu:${VERSION_ARG} AS src
|
||||
FROM debian:trixie-slim
|
||||
|
||||
ARG VERSION_ARG="0.0"
|
||||
|
@ -16,6 +19,7 @@ RUN set -eu && \
|
|||
wget \
|
||||
7zip \
|
||||
curl \
|
||||
fdisk \
|
||||
nginx \
|
||||
procps \
|
||||
seabios \
|
||||
|
@ -30,12 +34,12 @@ RUN set -eu && \
|
|||
iputils-ping \
|
||||
genisoimage \
|
||||
ca-certificates \
|
||||
netcat-openbsd \
|
||||
qemu-system-arm \
|
||||
qemu-efi-aarch64 && \
|
||||
qemu-system-arm && \
|
||||
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 && \
|
||||
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/ && \
|
||||
|
@ -46,22 +50,19 @@ RUN set -eu && \
|
|||
echo "$VERSION_ARG" > /run/version && \
|
||||
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/master/web/index.html /var/www/index.html
|
||||
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu/master/web/js/script.js /var/www/js/script.js
|
||||
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu/master/web/css/style.css /var/www/css/style.css
|
||||
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu/master/web/img/favicon.svg /var/www/img/favicon.svg
|
||||
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu/master/web/conf/defaults.json /usr/share/novnc
|
||||
ADD --chmod=664 https://raw.githubusercontent.com/qemus/qemu/master/web/conf/mandatory.json /usr/share/novnc
|
||||
ADD --chmod=744 https://raw.githubusercontent.com/qemus/qemu/master/web/conf/nginx.conf /etc/nginx/sites-enabled/web.conf
|
||||
COPY --chmod=755 ./src /run/
|
||||
|
||||
VOLUME /storage
|
||||
EXPOSE 22 5900 8006
|
||||
|
||||
ENV BOOT="alpine"
|
||||
ENV CPU_CORES="2"
|
||||
ENV RAM_SIZE="2G"
|
||||
ENV DISK_SIZE="16G"
|
||||
ENV BOOT="http://example.com/image.iso"
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]
|
||||
|
|
34
readme.md
34
readme.md
|
@ -49,7 +49,7 @@ services:
|
|||
##### Via Docker CLI:
|
||||
|
||||
```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 --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:
|
||||
|
@ -60,16 +60,7 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
|||
|
||||
##### Via Github Codespaces:
|
||||
|
||||
[`Click here to launch this container in the cloud!`](https://github.com/codespaces/new?skip_quickstart=true&machine=basicLinux32gb&repo=626637620&ref=master&devcontainer_path=.devcontainer.json)
|
||||
|
||||
## Compatibility ⚙️
|
||||
|
||||
| **Product** | **Platform** | |
|
||||
|---|---|---|
|
||||
| Docker Engine | Linux | ✅ |
|
||||
| Docker Desktop | Linux | ❌ |
|
||||
| Docker Desktop | macOS | ❌ |
|
||||
| Github Codespaces | cloud | ✅ |
|
||||
[](https://codespaces.new/qemus/qemu)
|
||||
|
||||
## FAQ 💬
|
||||
|
||||
|
@ -206,9 +197,16 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
|||
|
||||
### How do I verify if my system supports KVM?
|
||||
|
||||
Only Linux and Windows 11 support KVM virtualization, macOS and Windows 10 do not unfortunately.
|
||||
First check if your software is compatible using this chart:
|
||||
|
||||
You can run the following commands in Linux to check your system:
|
||||
| **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
|
||||
sudo apt install cpu-checker
|
||||
|
@ -223,11 +221,7 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
|||
|
||||
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's.
|
||||
|
||||
If you do not receive any error from `kvm-ok` but the container still complains about KVM, please check whether:
|
||||
|
||||
- you are not using "Docker Desktop for Linux" as it does not support KVM, instead make use of Docker Engine directly.
|
||||
|
||||
- it could help to add `privileged: true` to your compose file (or `sudo` to your `docker run` command), to rule out any permission issue.
|
||||
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?
|
||||
|
||||
|
@ -307,12 +301,12 @@ kubectl apply -f https://raw.githubusercontent.com/qemus/qemu-arm/refs/heads/mas
|
|||
|
||||
### 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
|
||||
devices:
|
||||
- /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.
|
||||
|
|
22
src/boot.sh
22
src/boot.sh
|
@ -75,26 +75,42 @@ 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"
|
||||
[[ "${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'" ;;
|
||||
"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
|
||||
|
|
|
@ -3,7 +3,7 @@ set -Eeuo pipefail
|
|||
|
||||
: "${UUID:=""}"
|
||||
: "${SERIAL:="mon:stdio"}"
|
||||
: "${USB:="qemu-xhci,id=xhci"}"
|
||||
: "${USB:="qemu-xhci,id=xhci,p2=7,p3=7"}"
|
||||
: "${MONITOR:="telnet:localhost:7100,server,nowait,nodelay"}"
|
||||
: "${SMP:="$CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"}"
|
||||
|
||||
|
@ -12,19 +12,24 @@ SERIAL_OPTS="-serial $SERIAL"
|
|||
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')
|
||||
MON_OPTS="-monitor $MONITOR -name $PROCESS,process=$PROCESS,debug-threads=on"
|
||||
[ -n "$USB" ] && [[ "${USB,,}" != "no"* ]] && USB_OPTS="-device $USB -device usb-kbd -device usb-tablet"
|
||||
MAC_OPTS="-machine type=${MACHINE},secure=${SECURE},dump-guest-core=off${KVM_OPTS}"
|
||||
[ -n "$UUID" ] && MAC_OPTS="$MAC_OPTS -uuid $UUID"
|
||||
|
||||
[ -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"
|
||||
if [ -d "/shared" ]; then
|
||||
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
|
||||
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 ' ')
|
||||
|
@ -67,7 +72,7 @@ if [[ "$RAM_CHECK" != [Nn]* ]]; then
|
|||
fi
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
printf "Arguments:\n\n%s" "${ARGS// -/$'\n-'}" && echo
|
||||
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
|
||||
fi
|
||||
|
||||
return 0
|
||||
|
|
224
src/define.sh
224
src/define.sh
|
@ -1,224 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
pipe() {
|
||||
local code="99"
|
||||
msg="Failed to connect to $1, reason:"
|
||||
|
||||
curl --disable --silent --max-time 10 --fail --location "${1}" || {
|
||||
code="$?"
|
||||
}
|
||||
|
||||
case "${code,,}" in
|
||||
"6" ) error "$msg could not resolve host!" ;;
|
||||
"7" ) error "$msg no internet connection available!" ;;
|
||||
"28" ) error "$msg connection timed out!" ;;
|
||||
"99" ) return 0 ;;
|
||||
*) error "$msg $code" ;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
getURL() {
|
||||
local id="${1/ /}"
|
||||
local ret="$2"
|
||||
local url=""
|
||||
local arm=""
|
||||
local name=""
|
||||
local body=""
|
||||
local version=""
|
||||
|
||||
case "${id,,}" in
|
||||
"alma" | "almalinux" | "alma-linux" )
|
||||
name="AlmaLinux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://repo.almalinux.org/almalinux/9/live/x86_64/AlmaLinux-9-latest-x86_64-Live-GNOME.iso"
|
||||
arm="https://repo.almalinux.org/almalinux/9/live/aarch64/AlmaLinux-9-latest-aarch64-Live-GNOME.iso"
|
||||
fi ;;
|
||||
"alpine" | "alpinelinux" | "alpine-linux" )
|
||||
name="Alpine Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/latest-releases.yaml") || exit 65
|
||||
version=$(echo "$body" | awk '/"Xen"/{found=0} {if(found) print} /"Virtual"/{found=1}' | grep 'version:' | awk '{print $2}')
|
||||
url="https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/alpine-virt-$version-x86_64.iso"
|
||||
arm="https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/aarch64/alpine-virt-$version-aarch64.iso"
|
||||
fi ;;
|
||||
"arch" | "archlinux" | "arch-linux" )
|
||||
name="Arch Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso"
|
||||
fi ;;
|
||||
"cachy" | "cachyos" )
|
||||
name="CachyOS"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://cachyos.org/download/") || exit 65
|
||||
url=$(echo "$body" | tr '&' '\n' | grep "ISO/desktop" | grep -v 'iso.sha' | grep -v 'iso.sig' | cut -d';' -f2)
|
||||
arm=$(echo "$body" | tr '&' '\n' | grep "ISO/handheld" | grep -v 'iso.sha' | grep -v 'iso.sig' | cut -d';' -f2)
|
||||
fi ;;
|
||||
"centos" | "centosstream" | "centos-stream" )
|
||||
name="CentOS Stream"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://linuxsoft.cern.ch/centos-stream/") || exit 65
|
||||
version=$(echo "$body" | grep "\-stream" | cut -d'"' -f 6 | cut -d'-' -f 1 | head -n 1)
|
||||
url="https://mirrors.xtom.de/centos-stream/$version-stream/BaseOS/x86_64/iso/CentOS-Stream-$version-latest-x86_64-dvd1.iso"
|
||||
arm="https://mirrors.xtom.de/centos-stream/$version-stream/BaseOS/aarch64/iso/CentOS-Stream-$version-latest-aarch64-dvd1.iso"
|
||||
fi ;;
|
||||
"debian" )
|
||||
name="Debian"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://cdimage.debian.org/debian-cd/") || exit 65
|
||||
version=$(echo "$body" | grep '\.[0-9]/' | cut -d'>' -f 9 | cut -d'/' -f 1)
|
||||
url="https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid/debian-live-$version-amd64-standard.iso"
|
||||
arm="https://cdimage.debian.org/debian-cd/current/arm64/iso-dvd/debian-$version-arm64-DVD-1.iso"
|
||||
fi ;;
|
||||
"fedora" | "fedoralinux" | "fedora-linux" )
|
||||
name="Fedora Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://getfedora.org/releases.json") || exit 65
|
||||
version=$(echo "$body" | jq -r 'map(.version) | unique | .[]' | sed 's/ /_/g' | sort -r | head -n 1)
|
||||
url=$(echo "$body" | jq -r "map(select(.arch==\"x86_64\" and .version==\"${version}\" and .variant==\"Workstation\" and .subvariant==\"Workstation\" )) | .[].link")
|
||||
arm=$(echo "$body" | jq -r "map(select(.arch==\"aarch64\" and .version==\"${version}\" and .variant==\"Workstation\" and .subvariant==\"Workstation\" )) | .[].link")
|
||||
fi ;;
|
||||
"gentoo" | "gentoolinux" | "gentoo-linux" )
|
||||
name="Gentoo Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
if [[ "${ARCH,,}" != "arm64" ]]; then
|
||||
body=$(pipe "https://mirror.bytemark.co.uk/gentoo/releases/amd64/autobuilds/latest-iso.txt") || exit 65
|
||||
version=$(echo "$body" | grep livegui | cut -d' ' -f1)
|
||||
url="https://distfiles.gentoo.org/releases/amd64/autobuilds/$version"
|
||||
else
|
||||
body=$(pipe "https://mirror.bytemark.co.uk/gentoo/releases/arm64/autobuilds/latest-qcow2.txt") || exit 65
|
||||
version=$(echo "$body" | grep cloudinit | cut -d' ' -f1)
|
||||
arm="https://distfiles.gentoo.org/releases/arm64/autobuilds/$version"
|
||||
fi
|
||||
fi ;;
|
||||
"kali" | "kalilinux" | "kali-linux" )
|
||||
name="Kali Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://cdimage.kali.org/current/?C=M;O=D") || exit 65
|
||||
version=$(echo "$body" | grep -o ">kali-linux-.*-live-amd64.iso" | head -n 1 | cut -c 2-)
|
||||
url="https://cdimage.kali.org/current/$version"
|
||||
version=$(echo "$body" | grep -o ">kali-linux-.*-live-arm64.iso" | head -n 1 | cut -c 2-)
|
||||
arm="https://cdimage.kali.org/current/$version"
|
||||
fi ;;
|
||||
"kubuntu" )
|
||||
name="Kubuntu"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://cdimage.ubuntu.com/kubuntu/releases/24.10/release/kubuntu-24.10-desktop-amd64.iso"
|
||||
fi ;;
|
||||
"lmde" )
|
||||
name="Linux Mint Debian Edition"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://mirror.rackspace.com/linuxmint/iso/debian/lmde-6-cinnamon-64bit.iso"
|
||||
fi ;;
|
||||
"macos" | "osx" )
|
||||
name="macOS"
|
||||
error "To install $name use: https://github.com/dockur/macos" && return 1 ;;
|
||||
"mint" | "linuxmint" | "linux-mint" )
|
||||
name="Linux Mint"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://mirrors.layeronline.com/linuxmint/stable/22.1/linuxmint-22.1-cinnamon-64bit.iso"
|
||||
fi ;;
|
||||
"manjaro" )
|
||||
name="Manjaro"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://gitlab.manjaro.org/web/iso-info/-/raw/master/file-info.json") || exit 65
|
||||
url=$(echo "$body" | jq -r .official.plasma.image)
|
||||
fi ;;
|
||||
"mx" | "mxlinux" | "mx-linux" )
|
||||
name="MX Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
version=$(curl --disable -Ils "https://sourceforge.net/projects/mx-linux/files/latest/download" | grep -i 'location:' | cut -d? -f1 | cut -d_ -f1 | cut -d- -f3) || exit 65
|
||||
url="https://mirror.umd.edu/mxlinux-iso/MX/Final/Xfce/MX-${version}_x64.iso"
|
||||
fi ;;
|
||||
"nixos" )
|
||||
name="NixOS"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://nix-channels.s3.amazonaws.com/?delimiter=/") || exit 65
|
||||
version=$(echo "$body" | grep -o -E 'nixos-[[:digit:]]+\.[[:digit:]]+' | cut -d- -f2 | sort -nru | head -n 1)
|
||||
url="https://channels.nixos.org/nixos-$version/latest-nixos-gnome-x86_64-linux.iso"
|
||||
arm="https://channels.nixos.org/nixos-$version/latest-nixos-gnome-aarch64-linux.iso"
|
||||
fi ;;
|
||||
"opensuse" | "open-suse" | "suse" )
|
||||
name="OpenSUSE"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://download.opensuse.org/distribution/leap/") || exit 65
|
||||
version=$(echo "$body" | grep 'class="name"' | cut -d '/' -f2 | grep -v 42 | sort -r | head -n 1)
|
||||
url="https://download.opensuse.org/distribution/leap/$version/installer/iso/agama-installer-Leap.x86_64-Leap.iso"
|
||||
arm="https://download.opensuse.org/distribution/leap/$version/installer/iso/agama-installer-Leap.aarch64-Leap.iso"
|
||||
fi ;;
|
||||
"oracle" | "oraclelinux" | "oracle-linux" )
|
||||
name="Oracle Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://yum.oracle.com/ISOS/OracleLinux/OL9/u5/x86_64/OracleLinux-R9-U5-x86_64-boot.iso"
|
||||
arm="https://yum.oracle.com/ISOS/OracleLinux/OL9/u5/aarch64/OracleLinux-R9-U5-aarch64-boot-uek.iso"
|
||||
fi ;;
|
||||
"rocky" | "rockylinux" | "rocky-linux" )
|
||||
name="Rocky Linux"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://dl.rockylinux.org/pub/rocky/9/live/x86_64/Rocky-9-Workstation-x86_64-latest.iso"
|
||||
arm="https://dl.rockylinux.org/pub/rocky/9/live/aarch64/Rocky-9-Workstation-aarch64-latest.iso"
|
||||
fi ;;
|
||||
"slack" | "slackware" )
|
||||
name="Slackware"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://slackware.nl/slackware-live/slackware64-current-live/slackware64-live-current.iso"
|
||||
fi ;;
|
||||
"tails" )
|
||||
name="Tails"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
body=$(pipe "https://tails.net/install/v2/Tails/amd64/stable/latest.json") || exit 65
|
||||
url=$(echo "$body" | jq -r '.installations[0]."installation-paths"[]|select(.type=="iso")|."target-files"[0].url')
|
||||
fi ;;
|
||||
"ubuntu" | "ubuntu-desktop" )
|
||||
name="Ubuntu Desktop"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://releases.ubuntu.com/24.04.2/ubuntu-24.04.2-desktop-amd64.iso"
|
||||
arm="https://cdimage.ubuntu.com/ubuntu/releases/24.10/release/ubuntu-24.10-desktop-arm64.iso"
|
||||
fi ;;
|
||||
"ubuntus" | "ubuntu-server")
|
||||
name="Ubuntu Server"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://releases.ubuntu.com/24.04.2/ubuntu-24.04.2-live-server-amd64.iso"
|
||||
arm="https://cdimage.ubuntu.com/releases/24.04/release/ubuntu-24.04.2-live-server-arm64.iso"
|
||||
fi ;;
|
||||
"windows" )
|
||||
name="Windows"
|
||||
error "To install $name use: https://github.com/dockur/windows" && return 1 ;;
|
||||
"xubuntu" )
|
||||
name="Xubuntu"
|
||||
if [[ "$ret" == "url" ]]; then
|
||||
url="https://mirror.us.leaseweb.net/ubuntu-cdimage/xubuntu/releases/24.04/release/xubuntu-24.04.2-desktop-amd64.iso"
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
case "${ret,,}" in
|
||||
"name" )
|
||||
echo "$name"
|
||||
;;
|
||||
"url" )
|
||||
|
||||
if [[ "${ARCH,,}" != "arm64" ]]; then
|
||||
if [ -n "$name" ] && [ -z "$url" ]; then
|
||||
error "No image for $name available!"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
if [ -n "$name" ] && [ -z "$arm" ]; then
|
||||
error "No image for $name is available for ARM64 yet! "
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${ARCH,,}" != "arm64" ]]; then
|
||||
echo "$url"
|
||||
else
|
||||
echo "$arm"
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
683
src/disk.sh
683
src/disk.sh
|
@ -1,683 +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_uring'
|
||||
: "${DISK_FMT:=""}" # Disk file format, can be set to "raw" (default) or "qcow2"
|
||||
: "${DISK_TYPE:=""}" # Device type to be used, "sata", "nvme", "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 GB 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
|
||||
GB=$(formatBytes "$SPACE")
|
||||
error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..."
|
||||
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||
fi
|
||||
fi
|
||||
|
||||
html "Creating a $DISK_DESC image..."
|
||||
info "Creating a ${DISK_SPACE/G/ GB} $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/G/ GB} ($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" &>/dev/null; then
|
||||
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
|
||||
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
||||
rm -f "$DISK_FILE"
|
||||
error "$FAIL" && exit 77
|
||||
fi
|
||||
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 GB
|
||||
|
||||
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
|
||||
GB=$(formatBytes "$SPACE")
|
||||
error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.."
|
||||
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
|
||||
fi
|
||||
fi
|
||||
|
||||
GB=$(formatBytes "$CUR_SIZE")
|
||||
MSG="Resizing $DISK_DESC from $GB to ${DISK_SPACE/G/ GB}..."
|
||||
info "$MSG" && html "$MSG"
|
||||
|
||||
local FAIL="Could not resize the $DISK_STYLE $DISK_FMT $DISK_DESC image from ${GB} to ${DISK_SPACE/G/ GB} ($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" &>/dev/null; then
|
||||
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
|
||||
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
|
||||
error "$FAIL" && exit 75
|
||||
fi
|
||||
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 GB
|
||||
|
||||
# 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
|
||||
GB=$(formatBytes "$SPACE")
|
||||
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..."
|
||||
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
|
||||
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" &>/dev/null; then
|
||||
if ! fallocate -l -x "$CUR_SIZE" "$TMP_FILE"; then
|
||||
error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
|
||||
fi
|
||||
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
|
||||
"none" ) ;;
|
||||
"auto" )
|
||||
echo "$result"
|
||||
;;
|
||||
"usb" )
|
||||
result+=",if=none \
|
||||
-device usb-storage,drive=${DISK_ID}${index}"
|
||||
echo "$result"
|
||||
;;
|
||||
"nvme" )
|
||||
result+=",if=none \
|
||||
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}"
|
||||
echo "$result"
|
||||
;;
|
||||
"ide" | "sata" )
|
||||
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},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_INDEX=$3
|
||||
local DISK_ADDRESS=$4
|
||||
|
||||
local index=""
|
||||
local DISK_ID="cdrom$DISK_INDEX"
|
||||
[ -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
|
||||
"none" ) ;;
|
||||
"auto" )
|
||||
echo "$result"
|
||||
;;
|
||||
"usb" )
|
||||
result+=",if=none \
|
||||
-device usb-storage,drive=${DISK_ID}${index},removable=on"
|
||||
echo "$result"
|
||||
;;
|
||||
"nvme" )
|
||||
result+=",if=none \
|
||||
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}"
|
||||
echo "$result"
|
||||
;;
|
||||
"ide" | "sata" )
|
||||
result+=",if=none \
|
||||
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \
|
||||
-device ide-cd,drive=${DISK_ID},bus=ahci${DISK_INDEX}.0${index}"
|
||||
echo "$result"
|
||||
;;
|
||||
"blk" | "virtio-blk" )
|
||||
result+=",if=none \
|
||||
-device virtio-blk-pci,drive=${DISK_ID},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 SPACE 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
|
||||
|
||||
SPACE="${DISK_SPACE// /}"
|
||||
[ -z "$SPACE" ] && SPACE="16G"
|
||||
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
|
||||
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||
|
||||
if ! numfmt --from=iec "$SPACE" &>/dev/null; then
|
||||
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
|
||||
fi
|
||||
|
||||
DATA_SIZE=$(numfmt --from=iec "$SPACE")
|
||||
|
||||
if (( DATA_SIZE < 104857600 )); then
|
||||
error "Please increase ${DISK_DESC^^}_SIZE to at least 100 MB." && 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" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
createDisk "$DISK_FILE" "$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"
|
||||
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
|
||||
|
||||
case "${DISK_TYPE,,}" in
|
||||
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
||||
* ) error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is not recognized!" && exit 80 ;;
|
||||
esac
|
||||
|
||||
if [[ "${MACHINE,,}" != "virt" ]]; then
|
||||
FALLBACK="ide"
|
||||
else
|
||||
FALLBACK="usb"
|
||||
fi
|
||||
|
||||
[[ "${BOOT_MODE:-}" == "windows_legacy" ]] && FALLBACK="auto"
|
||||
|
||||
if [ -z "${MEDIA_TYPE:-}" ]; then
|
||||
if [[ "${BOOT_MODE:-}" != "windows"* ]]; then
|
||||
if [[ "${DISK_TYPE,,}" == "blk" ]]; then
|
||||
MEDIA_TYPE="$FALLBACK"
|
||||
else
|
||||
MEDIA_TYPE="$DISK_TYPE"
|
||||
fi
|
||||
else
|
||||
MEDIA_TYPE="$FALLBACK"
|
||||
fi
|
||||
fi
|
||||
|
||||
case "${MEDIA_TYPE,,}" in
|
||||
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
|
||||
* ) error "Invalid MEDIA_TYPE specified, value \"$MEDIA_TYPE\" is not recognized!" && exit 80 ;;
|
||||
esac
|
||||
|
||||
if [ -f "$BOOT" ] && [ -s "$BOOT" ]; then
|
||||
case "${BOOT,,}" in
|
||||
*".iso" )
|
||||
DISK_OPTS+=$(addMedia "$BOOT" "$MEDIA_TYPE" "$BOOT_INDEX" "0x5") ;;
|
||||
*".img" | *".raw" )
|
||||
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "$BOOT_INDEX" "0x5" "raw" "$DISK_IO" "$DISK_CACHE") ;;
|
||||
*".qcow2" )
|
||||
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "$BOOT_INDEX" "0x5" "qcow2" "$DISK_IO" "$DISK_CACHE") ;;
|
||||
* )
|
||||
error "Invalid BOOT image specified, extension \".${BOOT/*./}\" is not recognized!" && exit 80 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
DRIVERS="/drivers.iso"
|
||||
[ ! -f "$DRIVERS" ] || [ ! -s "$DRIVERS" ] && DRIVERS="$STORAGE/drivers.iso"
|
||||
|
||||
if [ -f "$DRIVERS" ] && [ -s "$DRIVERS" ]; then
|
||||
DISK_OPTS+=$(addMedia "$DRIVERS" "$FALLBACK" "" "0x6")
|
||||
fi
|
||||
|
||||
RESCUE="/start.iso"
|
||||
[ ! -f "$RESCUE" ] || [ ! -s "$RESCUE" ] && RESCUE="$STORAGE/start.iso"
|
||||
|
||||
if [ -f "$RESCUE" ] && [ -s "$RESCUE" ]; then
|
||||
DISK_OPTS+=$(addMedia "$RESCUE" "$FALLBACK" "1" "0x6")
|
||||
fi
|
||||
|
||||
DISK1_FILE="$STORAGE/${DISK_NAME}"
|
||||
DISK2_FILE="/storage2/${DISK_NAME}2"
|
||||
DISK3_FILE="/storage3/${DISK_NAME}3"
|
||||
DISK4_FILE="/storage4/${DISK_NAME}4"
|
||||
|
||||
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
|
|
@ -2,6 +2,8 @@
|
|||
set -Eeuo pipefail
|
||||
|
||||
: "${APP:="QEMU"}"
|
||||
: "${MACHINE:="virt"}"
|
||||
: "${PLATFORM:="arm64"}"
|
||||
: "${SUPPORT:="https://github.com/qemus/qemu-arm"}"
|
||||
|
||||
cd /run
|
||||
|
|
344
src/install.sh
344
src/install.sh
|
@ -1,344 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
moveFile() {
|
||||
|
||||
local file="$1"
|
||||
local ext="${file##*.}"
|
||||
local dest="$STORAGE/boot.$ext"
|
||||
|
||||
if [[ "$file" == "$dest" ]] || [[ "$file" == "/boot.$ext" ]]; then
|
||||
BOOT="$file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if ! mv -f "$file" "$dest"; then
|
||||
error "Failed to move $file to $dest !"
|
||||
return 1
|
||||
fi
|
||||
|
||||
BOOT="$dest"
|
||||
return 0
|
||||
}
|
||||
|
||||
detectType() {
|
||||
|
||||
local dir=""
|
||||
local file="$1"
|
||||
|
||||
[ ! -f "$file" ] && return 1
|
||||
[ ! -s "$file" ] && return 1
|
||||
|
||||
case "${file,,}" in
|
||||
*".iso" | *".img" | *".raw" | *".qcow2" ) ;;
|
||||
* ) return 1 ;;
|
||||
esac
|
||||
|
||||
if [ -n "$BOOT_MODE" ] || [[ "${file,,}" != *".iso" ]]; then
|
||||
! moveFile "$file" && return 1
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Automaticly detect UEFI-compatible ISO's
|
||||
dir=$(isoinfo -f -i "$file")
|
||||
|
||||
if [ -n "$dir" ]; then
|
||||
dir=$(echo "${dir^^}" | grep "^/EFI")
|
||||
[ -z "$dir" ] && BOOT_MODE="legacy"
|
||||
else
|
||||
error "Failed to read ISO file, invalid format!"
|
||||
fi
|
||||
|
||||
! moveFile "$file" && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
downloadFile() {
|
||||
|
||||
local url="$1"
|
||||
local base="$2"
|
||||
local name="$3"
|
||||
local msg rc total size progress
|
||||
|
||||
local dest="$STORAGE/$base.tmp"
|
||||
rm -f "$dest"
|
||||
|
||||
# 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
|
||||
|
||||
if [ -z "$name" ]; then
|
||||
name="$base"
|
||||
msg="Downloading image"
|
||||
else
|
||||
msg="Downloading $name"
|
||||
fi
|
||||
|
||||
info "Downloading $name..."
|
||||
html "$msg..."
|
||||
|
||||
/run/progress.sh "$dest" "0" "$msg ([P])..." &
|
||||
|
||||
{ wget "$url" -O "$dest" -q --timeout=30 --no-http-keep-alive --show-progress "$progress"; rc=$?; } || :
|
||||
|
||||
fKill "progress.sh"
|
||||
|
||||
if (( rc == 0 )) && [ -f "$dest" ]; then
|
||||
total=$(stat -c%s "$dest")
|
||||
size=$(formatBytes "$total")
|
||||
if [ "$total" -lt 100000 ]; then
|
||||
error "Invalid image file: is only $size ?" && return 1
|
||||
fi
|
||||
html "Download finished successfully..."
|
||||
mv -f "$dest" "$STORAGE/$base"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg="Failed to download $url"
|
||||
(( rc == 3 )) && error "$msg , cannot write file (disk full?)" && return 1
|
||||
(( rc == 4 )) && error "$msg , network failure!" && return 1
|
||||
(( rc == 8 )) && error "$msg , server issued an error response!" && return 1
|
||||
|
||||
error "$msg , reason: $rc"
|
||||
return 1
|
||||
}
|
||||
|
||||
convertImage() {
|
||||
|
||||
local source_file=$1
|
||||
local source_fmt=$2
|
||||
local dst_file=$3
|
||||
local dst_fmt=$4
|
||||
local dir base fs fa space space_gb
|
||||
local cur_size cur_gb src_size disk_param
|
||||
|
||||
[ -f "$dst_file" ] && error "Conversion failed, destination file $dst_file already exists?" && return 1
|
||||
[ ! -f "$source_file" ] && error "Conversion failed, source file $source_file does not exists?" && return 1
|
||||
|
||||
if [[ "${source_fmt,,}" == "${dst_fmt,,}" ]]; then
|
||||
mv -f "$source_file" "$dst_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local tmp_file="$dst_file.tmp"
|
||||
dir=$(dirname "$tmp_file")
|
||||
|
||||
rm -f "$tmp_file"
|
||||
|
||||
if [ -n "$ALLOCATE" ] && [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||
|
||||
# Check free diskspace
|
||||
src_size=$(qemu-img info "$source_file" -f "$source_fmt" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/')
|
||||
space=$(df --output=avail -B 1 "$dir" | tail -n 1)
|
||||
|
||||
if (( src_size > space )); then
|
||||
space_gb=$(formatBytes "$space")
|
||||
error "Not enough free space to convert image in $dir, it has only $space_gb available..." && return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
base=$(basename "$source_file")
|
||||
info "Converting $base..."
|
||||
html "Converting image..."
|
||||
|
||||
local conv_flags="-p"
|
||||
|
||||
if [ -z "$ALLOCATE" ] || [[ "$ALLOCATE" == [Nn]* ]]; then
|
||||
disk_param="preallocation=off"
|
||||
else
|
||||
disk_param="preallocation=falloc"
|
||||
fi
|
||||
|
||||
fs=$(stat -f -c %T "$dir")
|
||||
[[ "${fs,,}" == "btrfs" ]] && disk_param+=",nocow=on"
|
||||
|
||||
if [[ "$dst_fmt" != "raw" ]]; then
|
||||
if [ -z "$ALLOCATE" ] || [[ "$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 image in $dir, is there enough space available?" && return 1
|
||||
fi
|
||||
|
||||
if [[ "$dst_fmt" == "raw" ]]; then
|
||||
if [ -n "$ALLOCATE" ] && [[ "$ALLOCATE" != [Nn]* ]]; then
|
||||
# Work around qemu-img bug
|
||||
cur_size=$(stat -c%s "$tmp_file")
|
||||
cur_gb=$(formatBytes "$cur_size")
|
||||
if ! fallocate -l "$cur_size" "$tmp_file" &>/dev/null; then
|
||||
if ! fallocate -l -x "$cur_size" "$tmp_file"; then
|
||||
error "Failed to allocate $cur_gb for image!"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$source_file"
|
||||
mv "$tmp_file" "$dst_file"
|
||||
|
||||
if [[ "${fs,,}" == "btrfs" ]]; then
|
||||
fa=$(lsattr "$dst_file")
|
||||
if [[ "$fa" != *"C"* ]]; then
|
||||
error "Failed to disable COW for image on ${fs^^} filesystem!"
|
||||
fi
|
||||
fi
|
||||
|
||||
html "Conversion completed..."
|
||||
return 0
|
||||
}
|
||||
|
||||
findFile() {
|
||||
|
||||
local file
|
||||
local ext="$1"
|
||||
local fname="boot.$ext"
|
||||
|
||||
if [ -d "/$fname" ]; then
|
||||
warn "The file /$fname has an invalid path!"
|
||||
fi
|
||||
|
||||
file=$(find / -maxdepth 1 -type f -iname "$fname" | head -n 1)
|
||||
[ ! -s "$file" ] && file=$(find "$STORAGE" -maxdepth 1 -type f -iname "$fname" | head -n 1)
|
||||
detectType "$file" && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
findFile "iso" && return 0
|
||||
findFile "img" && return 0
|
||||
findFile "raw" && return 0
|
||||
findFile "qcow2" && return 0
|
||||
|
||||
if [ -z "$BOOT" ] || [[ "$BOOT" == *"example.com/image.iso" ]]; then
|
||||
hasDisk && return 0
|
||||
error "No value specified for the BOOT variable." && exit 64
|
||||
fi
|
||||
|
||||
url=$(getURL "$BOOT" "url") || exit 34
|
||||
name=$(getURL "$BOOT" "name") || exit 34
|
||||
|
||||
[ -n "$url" ] && BOOT="$url"
|
||||
|
||||
if [[ "$BOOT" != *"."* ]]; then
|
||||
error "Invalid BOOT value specified, shortcut \"$BOOT\" is not recognized!" && exit 64
|
||||
fi
|
||||
|
||||
if [[ "${BOOT,,}" != "http"* ]]; then
|
||||
error "Invalid BOOT value specified, \"$BOOT\" is not a valid URL!" && exit 64
|
||||
fi
|
||||
|
||||
base=$(basename "${BOOT%%\?*}")
|
||||
: "${base//+/ }"; printf -v base '%b' "${_//%/\\x}"
|
||||
base=$(echo "$base" | sed -e 's/[^A-Za-z0-9._-]/_/g')
|
||||
|
||||
case "${base,,}" in
|
||||
|
||||
*".iso" | *".img" | *".raw" | *".qcow2" )
|
||||
|
||||
detectType "$STORAGE/$base" && return 0 ;;
|
||||
|
||||
*".vdi" | *".vmdk" | *".vhd" | *".vhdx" )
|
||||
|
||||
detectType "$STORAGE/${base%.*}.img" && return 0
|
||||
detectType "$STORAGE/${base%.*}.qcow2" && return 0 ;;
|
||||
|
||||
*".gz" | *".gzip" | *".xz" | *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
|
||||
|
||||
case "${base%.*}" in
|
||||
|
||||
*".iso" | *".img" | *".raw" | *".qcow2" )
|
||||
|
||||
detectType "$STORAGE/${base%.*}" && return 0 ;;
|
||||
|
||||
*".vdi" | *".vmdk" | *".vhd" | *".vhdx" )
|
||||
|
||||
find="${base%.*}"
|
||||
|
||||
detectType "$STORAGE/${find%.*}.img" && return 0
|
||||
detectType "$STORAGE/${find%.*}.qcow2" && return 0 ;;
|
||||
|
||||
esac ;;
|
||||
|
||||
* ) error "Unknown file extension, type \".${base/*./}\" is not recognized!" && exit 33 ;;
|
||||
esac
|
||||
|
||||
if ! downloadFile "$BOOT" "$base" "$name"; then
|
||||
rm -f "$STORAGE/$base.tmp" && exit 60
|
||||
fi
|
||||
|
||||
case "${base,,}" in
|
||||
*".gz" | *".gzip" | *".xz" | *".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
|
||||
info "Extracting $base..."
|
||||
html "Extracting image..." ;;
|
||||
esac
|
||||
|
||||
case "${base,,}" in
|
||||
*".gz" | *".gzip" )
|
||||
|
||||
gzip -dc "$STORAGE/$base" > "$STORAGE/${base%.*}"
|
||||
rm -f "$STORAGE/$base"
|
||||
base="${base%.*}"
|
||||
|
||||
;;
|
||||
*".xz" )
|
||||
|
||||
xz -dc "$STORAGE/$base" > "$STORAGE/${base%.*}"
|
||||
rm -f "$STORAGE/$base"
|
||||
base="${base%.*}"
|
||||
|
||||
;;
|
||||
*".7z" | *".zip" | *".rar" | *".lzma" | *".bz" | *".bz2" )
|
||||
|
||||
tmp="$STORAGE/extract"
|
||||
rm -rf "$tmp"
|
||||
mkdir -p "$tmp"
|
||||
7z x "$STORAGE/$base" -o"$tmp" > /dev/null
|
||||
|
||||
rm -f "$STORAGE/$base"
|
||||
base="${base%.*}"
|
||||
|
||||
if [ ! -s "$tmp/$base" ]; then
|
||||
rm -rf "$tmp"
|
||||
error "Cannot find file \"${base}\" in .${BOOT/*./} archive!" && exit 32
|
||||
fi
|
||||
|
||||
mv "$tmp/$base" "$STORAGE/$base"
|
||||
rm -rf "$tmp"
|
||||
|
||||
;;
|
||||
esac
|
||||
|
||||
case "${base,,}" in
|
||||
*".iso" | *".img" | *".raw" | *".qcow2" )
|
||||
detectType "$STORAGE/$base" && return 0
|
||||
error "Cannot read file \"${base}\"" && exit 63 ;;
|
||||
esac
|
||||
|
||||
target_ext="img"
|
||||
target_fmt="${DISK_FMT:-}"
|
||||
[ -z "$target_fmt" ] && target_fmt="raw"
|
||||
[[ "$target_fmt" != "raw" ]] && target_ext="qcow2"
|
||||
|
||||
case "${base,,}" in
|
||||
*".vdi" ) source_fmt="vdi" ;;
|
||||
*".vhd" ) source_fmt="vpc" ;;
|
||||
*".vhdx" ) source_fmt="vpc" ;;
|
||||
*".vmdk" ) source_fmt="vmdk" ;;
|
||||
* ) error "Unknown file extension, type \".${base/*./}\" is not recognized!" && exit 33 ;;
|
||||
esac
|
||||
|
||||
dst="$STORAGE/${base%.*}.$target_ext"
|
||||
|
||||
! convertImage "$STORAGE/$base" "$source_fmt" "$dst" "$target_fmt" && exit 35
|
||||
|
||||
base=$(basename "$dst")
|
||||
detectType "$STORAGE/$base" && return 0
|
||||
error "Cannot convert file \"${base}\"" && exit 36
|
508
src/network.sh
508
src/network.sh
|
@ -1,508 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Docker environment variables
|
||||
|
||||
: "${MAC:=""}"
|
||||
: "${MTU:=""}"
|
||||
: "${DHCP:="N"}"
|
||||
: "${NETWORK:="Y"}"
|
||||
: "${USER_PORTS:=""}"
|
||||
: "${HOST_PORTS:=""}"
|
||||
: "${ADAPTER:="virtio-net-pci"}"
|
||||
|
||||
: "${VM_NET_DEV:=""}"
|
||||
: "${VM_NET_TAP:="qemu"}"
|
||||
: "${VM_NET_MAC:="$MAC"}"
|
||||
: "${VM_NET_HOST:="$APP"}"
|
||||
: "${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
|
||||
{ msg=$(ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge 2>&1); rc=$?; } || :
|
||||
|
||||
case "$msg" in
|
||||
"RTNETLINK answers: File exists"* )
|
||||
while ! ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge; do
|
||||
info "Waiting for macvtap interface to become available.."
|
||||
sleep 5
|
||||
done ;;
|
||||
"RTNETLINK answers: Invalid argument"* )
|
||||
error "Cannot create macvtap interface. Please make sure that the network type of the container is 'macvlan' and not 'ipvlan'."
|
||||
return 1 ;;
|
||||
"RTNETLINK answers: Operation not permitted"* )
|
||||
error "No permission to create macvtap interface. Please make sure that your host kernel supports it and that the NET_ADMIN capability is set."
|
||||
return 1 ;;
|
||||
*)
|
||||
[ -n "$msg" ] && echo "$msg" >&2
|
||||
if (( rc != 0 )); then
|
||||
error "Cannot create macvtap interface."
|
||||
return 1
|
||||
fi ;;
|
||||
esac
|
||||
|
||||
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
|
||||
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
||||
warn "Failed to set MTU size.."
|
||||
fi
|
||||
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"
|
||||
|
||||
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 [[ "${DEBUG_DNS:-}" == [Yy1]* ]]; then
|
||||
DNSMASQ_OPTS+=" -d"
|
||||
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
|
||||
return 0
|
||||
fi
|
||||
|
||||
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() {
|
||||
|
||||
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
|
||||
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
|
||||
|
||||
# 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 "$tuntap" && 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
|
||||
|
||||
# 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 pool!" && 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
|
||||
|
||||
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
|
||||
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
|
||||
warn "Failed to set MTU size.."
|
||||
fi
|
||||
fi
|
||||
|
||||
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
|
||||
|
||||
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
|
||||
warn "Failed to set gateway MAC address.."
|
||||
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
|
||||
}
|
||||
|
||||
closeBridge() {
|
||||
|
||||
local pid="/var/run/dnsmasq.pid"
|
||||
[ -s "$pid" ] && pKill "$(<"$pid")"
|
||||
|
||||
[[ "${NETWORK,,}" == "user"* ]] && return 0
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
closeBridge
|
||||
return 0
|
||||
|
||||
fi
|
||||
|
||||
ip link set "$VM_NET_TAP" down || true
|
||||
ip link delete "$VM_NET_TAP" || true
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
checkOS() {
|
||||
|
||||
local name
|
||||
local os=""
|
||||
local if="macvlan"
|
||||
name=$(uname -a)
|
||||
|
||||
[[ "${name,,}" == *"darwin"* ]] && os="Docker Desktop for macOS"
|
||||
[[ "${name,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows"
|
||||
|
||||
if [[ "$DHCP" == [Yy1]* ]]; then
|
||||
if="macvtap"
|
||||
[[ "${name,,}" == *"synology"* ]] && os="Synology Container Manager"
|
||||
fi
|
||||
|
||||
if [ -n "$os" ]; then
|
||||
warn "you are using $os which does not support $if, 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 26
|
||||
fi
|
||||
|
||||
BASE_IP="${VM_NET_IP%.*}."
|
||||
|
||||
if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then
|
||||
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27
|
||||
fi
|
||||
|
||||
if [ -z "$MTU" ]; then
|
||||
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
|
||||
fi
|
||||
|
||||
if [ "$MTU" -gt "1500" ]; then
|
||||
info "MTU size is too large: $MTU, ignoring..." && MTU="0"
|
||||
fi
|
||||
|
||||
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
|
||||
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
|
||||
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${BOOT_MODE:-}" == "windows_legacy" ]]; then
|
||||
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
|
||||
warn "MTU size is $MTU, but cannot be set for legacy Windows versions!" && MTU="0"
|
||||
fi
|
||||
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}' | head -n 1)
|
||||
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
|
||||
|
||||
IP6=""
|
||||
# shellcheck disable=SC2143
|
||||
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
||||
IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
|
||||
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# ######################################
|
||||
# Configure Network
|
||||
# ######################################
|
||||
|
||||
if [[ "$NETWORK" == [Nn]* ]]; then
|
||||
NET_OPTS=""
|
||||
return 0
|
||||
fi
|
||||
|
||||
getInfo
|
||||
html "Initializing network..."
|
||||
|
||||
if [[ "$DEBUG" == [Yy1]* ]]; then
|
||||
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
|
||||
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
|
||||
[[ "$MTU" != "0" ]] && [[ "$MTU" != "$mtu" ]] && line+=" ($MTU)"
|
||||
info "$line"
|
||||
if [ -f /etc/resolv.conf ]; then
|
||||
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
|
||||
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
|
||||
fi
|
||||
echo
|
||||
fi
|
||||
|
||||
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
|
||||
info "Lingering interface will be removed..."
|
||||
ip link delete "$VM_NET_TAP" || true
|
||||
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
|
||||
|
||||
closeBridge
|
||||
NETWORK="user"
|
||||
warn "falling back to user-mode networking! Performance will be bad and port mapping will not work."
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
if [[ "${NETWORK,,}" == "user"* ]]; then
|
||||
|
||||
# Configure for user-mode networking (slirp)
|
||||
configureUser || exit 24
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
|
||||
[[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
|
||||
|
||||
html "Initialized network successfully..."
|
||||
return 0
|
16
src/proc.sh
16
src/proc.sh
|
@ -34,10 +34,10 @@ if [[ "$KVM" != [Nn]* ]]; then
|
|||
KVM_ERR=""
|
||||
|
||||
if [ ! -e /dev/kvm ]; then
|
||||
KVM_ERR="(device file missing)"
|
||||
KVM_ERR="(/dev/kvm is missing)"
|
||||
else
|
||||
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
|
||||
KVM_ERR="(no write access)"
|
||||
KVM_ERR="(/dev/kvm is unwriteable)"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -46,8 +46,16 @@ if [[ "$KVM" != [Nn]* ]]; then
|
|||
if [[ "$OSTYPE" =~ ^darwin ]]; then
|
||||
warn "you are using macOS which has no KVM support, this will cause a major loss of performance."
|
||||
else
|
||||
error "KVM acceleration not available $KVM_ERR, this will cause a major loss of performance."
|
||||
error "See the FAQ on how to diagnose the cause, or continue without KVM by setting KVM=N (not recommended)."
|
||||
kernel=$(uname -a)
|
||||
case "${kernel,,}" in
|
||||
*"microsoft"* )
|
||||
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
|
||||
*"synology"* )
|
||||
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
|
||||
*)
|
||||
error "KVM acceleration is not available $KVM_ERR, this will cause a major loss of performance."
|
||||
error "See the FAQ for possible causes, or continue without it by adding KVM: \"N\" (not recommended)." ;;
|
||||
esac
|
||||
[[ "$DEBUG" != [Yy1]* ]] && exit 88
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
#!/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="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
|
||||
size="$size%"
|
||||
fi
|
||||
echo "${body//(\[P\])/($size)}"> "$info"
|
||||
fi
|
||||
fi
|
||||
sleep 1 & wait $!
|
||||
done
|
175
src/reset.sh
175
src/reset.sh
|
@ -1,175 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
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:="2"}" # Amount of CPU cores
|
||||
: "${RAM_SIZE:="2G"}" # Maximum RAM amount
|
||||
: "${RAM_CHECK:="Y"}" # Check available RAM
|
||||
: "${DISK_SIZE:="16G"}" # Initial data disk size
|
||||
: "${BOOT_MODE:=""}" # Boot system with UEFI
|
||||
: "${BOOT_INDEX:="9"}" # Boot index of CD drive
|
||||
: "${STORAGE:="/storage"}" # Storage folder location
|
||||
|
||||
# Helper variables
|
||||
|
||||
PROCESS="${APP,,}"
|
||||
PROCESS="${PROCESS// /-}"
|
||||
|
||||
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>"
|
||||
|
||||
CPU=$(cpu)
|
||||
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)
|
||||
|
||||
if ! grep -qi "socket(s)" <<< "$(lscpu)"; then
|
||||
SOCKETS=1
|
||||
else
|
||||
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
|
||||
fi
|
||||
|
||||
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
|
||||
|
||||
# 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 [[ "${COMMIT:-}" == [Yy1]* ]]; then
|
||||
STORAGE="/local"
|
||||
mkdir -p "$STORAGE"
|
||||
fi
|
||||
|
||||
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="${RAM_SIZE// /}"
|
||||
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
|
||||
|
||||
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
|
||||
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
|
||||
fi
|
||||
|
||||
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
|
||||
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
|
||||
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
|
||||
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
|
||||
|
||||
# Print system info
|
||||
SYS="${SYS/-generic/}"
|
||||
FS=$(stat -f -c %T "$STORAGE")
|
||||
FS="${FS/UNKNOWN //}"
|
||||
FS="${FS/ext2\/ext3/ext4}"
|
||||
FS=$(echo "$FS" | sed 's/[)(]//g')
|
||||
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
|
||||
SPACE_GB=$(formatBytes "$SPACE" "down")
|
||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL" "down")
|
||||
TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
|
||||
|
||||
echo "❯ CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
|
||||
echo
|
||||
|
||||
# Check compatibilty
|
||||
|
||||
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
|
||||
DISK_IO="threads"
|
||||
DISK_CACHE="writeback"
|
||||
fi
|
||||
|
||||
if [[ "${BOOT_MODE:-}" == "windows"* ]]; then
|
||||
if [[ "${FS,,}" == "btrfs" ]]; then
|
||||
warn "you are using the BTRFS filesystem for /storage, this might introduce issues with Windows Setup!"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check available memory
|
||||
|
||||
if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
|
||||
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
|
||||
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
|
||||
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
|
||||
info "$msg"
|
||||
fi
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
user="admin"
|
||||
[ -n "${USER:-}" ] && user="${USER:-}"
|
||||
|
||||
if [ -n "${PASS:-}" ]; then
|
||||
|
||||
sed -i "s/auth_basic off/auth_basic \"NoVNC\"/g" /etc/nginx/sites-enabled/web.conf
|
||||
|
||||
else
|
||||
|
||||
sed -i "s/auth_basic \"NoVNC\"/auth_basic off/g" /etc/nginx/sites-enabled/web.conf
|
||||
|
||||
fi
|
||||
|
||||
# Set password
|
||||
echo "$user:{PLAIN}${PASS:-}" > /etc/nginx/.htpasswd
|
||||
|
||||
# shellcheck disable=SC2143
|
||||
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
|
||||
|
||||
sed -i "s/listen 8006 default_server;/listen [::]:8006 default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
|
||||
|
||||
else
|
||||
|
||||
sed -i "s/listen [::]:8006 default_server ipv6only=off;/listen 8006 default_server;/g" /etc/nginx/sites-enabled/web.conf
|
||||
|
||||
fi
|
||||
|
||||
# Start webserver
|
||||
cp -r /var/www/* /run/shm
|
||||
html "Starting $APP for Docker..."
|
||||
nginx -e stderr
|
||||
|
||||
return 0
|
171
src/utils.sh
171
src/utils.sh
|
@ -1,171 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
|
||||
# Helper functions
|
||||
|
||||
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; }
|
||||
|
||||
formatBytes() {
|
||||
local result
|
||||
result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;')
|
||||
local unit="${result//[0-9. ]}"
|
||||
result="${result//[a-zA-Z ]/}"
|
||||
if [[ "${2:-}" == "up" ]]; then
|
||||
if [[ "$result" == *"."* ]]; then
|
||||
result="${result%%.*}"
|
||||
result=$((result+1))
|
||||
fi
|
||||
else
|
||||
if [[ "${2:-}" == "down" ]]; then
|
||||
result="${result%%.*}"
|
||||
fi
|
||||
fi
|
||||
echo "$result $unit"
|
||||
return 0
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
cpu() {
|
||||
local ret
|
||||
local cpu=""
|
||||
|
||||
ret=$(lscpu)
|
||||
|
||||
if grep -qi "model name" <<< "$ret"; then
|
||||
cpu=$(echo "$ret" | grep -m 1 -i 'model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
||||
fi
|
||||
|
||||
if [ -z "${cpu// /}" ] && grep -qi "model:" <<< "$ret"; then
|
||||
cpu=$(echo "$ret" | grep -m 1 -i 'model:' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
|
||||
fi
|
||||
|
||||
cpu="${cpu// CPU/}"
|
||||
cpu="${cpu// 4 Core/}"
|
||||
cpu="${cpu// 6 Core/}"
|
||||
cpu="${cpu// 8 Core/}"
|
||||
cpu="${cpu// 10 Core/}"
|
||||
cpu="${cpu// 12 Core/}"
|
||||
cpu="${cpu// 16 Core/}"
|
||||
cpu="${cpu// 32 Core/}"
|
||||
cpu="${cpu// 64 Core/}"
|
||||
cpu="${cpu//7th Gen /}"
|
||||
cpu="${cpu//8th Gen /}"
|
||||
cpu="${cpu//9th Gen /}"
|
||||
cpu="${cpu//10th Gen /}"
|
||||
cpu="${cpu//11th Gen /}"
|
||||
cpu="${cpu//12th Gen /}"
|
||||
cpu="${cpu//13th Gen /}"
|
||||
cpu="${cpu//14th Gen /}"
|
||||
cpu="${cpu//15th Gen /}"
|
||||
cpu="${cpu// Processor/}"
|
||||
cpu="${cpu// Quad core/}"
|
||||
cpu="${cpu// Dual core/}"
|
||||
cpu="${cpu// Octa core/}"
|
||||
cpu="${cpu// Core TM/ Core}"
|
||||
cpu="${cpu// with Radeon Graphics/}"
|
||||
cpu="${cpu// with Radeon Vega Graphics/}"
|
||||
|
||||
[ -z "${cpu// /}" ] && cpu="Unknown"
|
||||
|
||||
echo "$cpu"
|
||||
return 0
|
||||
}
|
||||
|
||||
hasDisk() {
|
||||
|
||||
[ -b "/disk" ] && return 0
|
||||
[ -b "/disk1" ] && return 0
|
||||
[ -b "/dev/disk1" ] && return 0
|
||||
[ -b "${DEVICE:-}" ] && return 0
|
||||
|
||||
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
|
||||
[ -s "$STORAGE/$DISK_NAME.img" ] && return 0
|
||||
[ -s "$STORAGE/$DISK_NAME.qcow2" ] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
Loading…
Reference in a new issue