Compare commits

..

No commits in common. "master" and "v7.45" have entirely different histories.

22 changed files with 243 additions and 570 deletions

6
.devcontainer.json Normal file
View File

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

View File

@ -1,15 +0,0 @@
{
"name": "Virtual DSM",
"service": "dsm",
"forwardPorts": [5000],
"portsAttributes": {
"5000": {
"label": "Web",
"onAutoForward": "notify"
}
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
},
"dockerComposeFile": "codespaces.yml"
}

View File

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

View File

@ -1,9 +1,9 @@
services: services:
dsm: dsm:
container_name: dsm container_name: dsm
image: ghcr.io/vdsm/virtual-dsm image: vdsm/virtual-dsm
environment: environment:
RAM_SIZE: "half" RAM_SIZE: "max"
DISK_SIZE: "max" DISK_SIZE: "max"
CPU_CORES: "max" CPU_CORES: "max"
devices: devices:

View File

@ -22,7 +22,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v5
with: with:
fetch-depth: 0 fetch-depth: 0
- -

View File

@ -9,7 +9,7 @@ jobs:
steps: steps:
- -
name: Checkout name: Checkout
uses: actions/checkout@v6 uses: actions/checkout@v5
- -
name: Run ShellCheck name: Run ShellCheck
uses: ludeeus/action-shellcheck@master uses: ludeeus/action-shellcheck@master
@ -22,8 +22,3 @@ jobs:
dockerfile: Dockerfile dockerfile: Dockerfile
ignore: DL3008,DL3003,DL3006,DL3013 ignore: DL3008,DL3003,DL3006,DL3013
failure-threshold: warning failure-threshold: warning
-
name: Validate JSON and YML files
uses: GrantBirki/json-yaml-validate@v4
with:
yaml_exclude_regex: ".*\\kubernetes\\.yml$"

View File

@ -12,15 +12,13 @@ jobs:
dockerHubDescription: dockerHubDescription:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- - uses: actions/checkout@v5
name: Checkout repo -
uses: actions/checkout@v6 name: Docker Hub Description
- uses: peter-evans/dockerhub-description@v5
name: Docker Hub Description with:
uses: peter-evans/dockerhub-description@v5 username: ${{ secrets.DOCKERHUB_USERNAME }}
with: password: ${{ secrets.DOCKERHUB_TOKEN }}
username: ${{ secrets.DOCKERHUB_USERNAME }} repository: ${{ secrets.DOCKERHUB_REPO }}
password: ${{ secrets.DOCKERHUB_TOKEN }} short-description: ${{ github.event.repository.description }}
repository: ${{ secrets.DOCKERHUB_REPO }} readme-filepath: ./readme.md
short-description: ${{ github.event.repository.description }}
readme-filepath: ./readme.md

View File

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

View File

@ -47,7 +47,7 @@ services:
##### Via Docker CLI: ##### Via Docker CLI:
```bash ```bash
docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 docker.io/vdsm/virtual-dsm docker run -it --rm --name dsm -e "DISK_SIZE=256G" -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm
``` ```
##### Via Kubernetes: ##### Via Kubernetes:
@ -124,7 +124,7 @@ kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/m
### How do I change the amount of CPU or RAM? ### How do I change the amount of CPU or RAM?
By default, Virtual DSM will be allowed to use 2 CPU cores and 2 GB of RAM. By default, the container will be allowed to use a maximum of 2 CPU cores and 2 GB of RAM.
If you want to adjust this, you can specify the desired amount using the following environment variables: If you want to adjust this, you can specify the desired amount using the following environment variables:

View File

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

View File

@ -17,16 +17,8 @@ SYSTEM="$STORAGE/$BASE.system.img"
[ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81 [ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82 [ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
if ! setOwner "$BOOT"; then
error "Failed to set the owner for \"$BOOT\" !"
fi
if ! setOwner "$SYSTEM"; then
error "Failed to set the owner for \"$SYSTEM\" !"
fi
fmt2ext() { fmt2ext() {
local DISK_FMT="$1" local DISK_FMT=$1
case "${DISK_FMT,,}" in case "${DISK_FMT,,}" in
qcow2) qcow2)
@ -42,7 +34,7 @@ fmt2ext() {
} }
ext2fmt() { ext2fmt() {
local DISK_EXT="$1" local DISK_EXT=$1
case "${DISK_EXT,,}" in case "${DISK_EXT,,}" in
qcow2) qcow2)
@ -58,7 +50,7 @@ ext2fmt() {
} }
getSize() { getSize() {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_EXT DISK_FMT local DISK_EXT DISK_FMT
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//') DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
@ -78,7 +70,7 @@ getSize() {
} }
isCow() { isCow() {
local FS="$1" local FS=$1
if [[ "${FS,,}" == "btrfs" ]]; then if [[ "${FS,,}" == "btrfs" ]]; then
return 0 return 0
@ -88,7 +80,7 @@ isCow() {
} }
supportsDirect() { supportsDirect() {
local FS="$1" local FS=$1
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
return 1 return 1
@ -99,11 +91,11 @@ supportsDirect() {
createDisk() { createDisk() {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_SPACE="$2" local DISK_SPACE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DISK_FMT="$4" local DISK_FMT=$4
local FS="$5" local FS=$5
local DATA_SIZE DIR SPACE GB FA local DATA_SIZE DIR SPACE GB FA
rm -f "$DISK_FILE" rm -f "$DISK_FILE"
@ -121,7 +113,6 @@ createDisk() {
error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..." error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76 error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
fi fi
fi fi
html "Creating a $DISK_DESC image..." html "Creating a $DISK_DESC image..."
@ -186,16 +177,16 @@ createDisk() {
resizeDisk() { resizeDisk() {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_SPACE="$2" local DISK_SPACE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DISK_FMT="$4" local DISK_FMT=$4
local FS="$5" local FS=$5
local CUR_SIZE DATA_SIZE DIR SPACE GB local CUR_SIZE DATA_SIZE DIR SPACE GB
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE") DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
local REQ=$(( DATA_SIZE - CUR_SIZE )) local REQ=$((DATA_SIZE-CUR_SIZE))
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71 (( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
if [[ "$ALLOCATE" != [Nn]* ]]; then if [[ "$ALLOCATE" != [Nn]* ]]; then
@ -209,7 +200,6 @@ resizeDisk() {
error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.." error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74 error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
fi fi
fi fi
GB=$(formatBytes "$CUR_SIZE") GB=$(formatBytes "$CUR_SIZE")
@ -255,13 +245,13 @@ resizeDisk() {
convertDisk() { convertDisk() {
local SOURCE_FILE="$1" local SOURCE_FILE=$1
local SOURCE_FMT="$2" local SOURCE_FMT=$2
local DST_FILE="$3" local DST_FILE=$3
local DST_FMT="$4" local DST_FMT=$4
local DISK_BASE="$5" local DISK_BASE=$5
local DISK_DESC="$6" local DISK_DESC=$6
local FS="$7" local FS=$7
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79 [ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79 [ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
@ -283,7 +273,6 @@ convertDisk() {
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..." error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..."
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76 error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
fi fi
fi fi
local msg="Converting $DISK_DESC to $DST_FMT" local msg="Converting $DISK_DESC to $DST_FMT"
@ -338,31 +327,31 @@ convertDisk() {
checkFS () { checkFS () {
local FS="$1" local FS=$1
local DISK_FILE="$2" local DISK_FILE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DIR FA local DIR FA
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then if [[ "${FS,,}" == "overlay"* ]]; then
warn "the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!" info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
if [[ "${FS,,}" == "fuse"* ]]; then if [[ "${FS,,}" == "fuse"* ]]; then
warn "the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!" info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
fi fi
if ! supportsDirect "$FS"; then if ! supportsDirect "$FS"; then
warn "the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..." info "Warning: the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi fi
if isCow "$FS"; then if isCow "$FS"; then
if [ -f "$DISK_FILE" ]; then if [ -f "$DISK_FILE" ]; then
FA=$(lsattr "$DISK_FILE") FA=$(lsattr "$DISK_FILE")
if [[ "$FA" != *"C"* ]]; then if [[ "$FA" != *"C"* ]]; then
warn "COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!" info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
fi fi
fi fi
fi fi
@ -372,15 +361,15 @@ checkFS () {
createDevice () { createDevice () {
local DISK_FILE="$1" local DISK_FILE=$1
local DISK_TYPE="$2" local DISK_TYPE=$2
local DISK_INDEX="$3" local DISK_INDEX=$3
local DISK_ADDRESS="$4" local DISK_ADDRESS=$4
local DISK_FMT="$5" local DISK_FMT=$5
local DISK_IO="$6" local DISK_IO=$6
local DISK_CACHE="$7" local DISK_CACHE=$7
local DISK_SERIAL="$8" local DISK_SERIAL=$8
local DISK_SECTORS="$9" local DISK_SECTORS=$9
local DISK_ID="data$DISK_INDEX" local DISK_ID="data$DISK_INDEX"
local index="" local index=""
@ -426,16 +415,16 @@ createDevice () {
addDisk () { addDisk () {
local DISK_BASE="$1" local DISK_BASE=$1
local DISK_TYPE="$2" local DISK_TYPE=$2
local DISK_DESC="$3" local DISK_DESC=$3
local DISK_SPACE="$4" local DISK_SPACE=$4
local DISK_INDEX="$5" local DISK_INDEX=$5
local DISK_ADDRESS="$6" local DISK_ADDRESS=$6
local DISK_FMT="$7" local DISK_FMT=$7
local DISK_IO="$8" local DISK_IO=$8
local DISK_CACHE="$9" local DISK_CACHE=$9
local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE LEFT FREE USED local DISK_EXT DIR SPACE GB DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DISK_EXT=$(fmt2ext "$DISK_FMT") DISK_EXT=$(fmt2ext "$DISK_FMT")
local DISK_FILE="$DISK_BASE.$DISK_EXT" local DISK_FILE="$DISK_BASE.$DISK_EXT"
@ -443,25 +432,18 @@ addDisk () {
DIR=$(dirname "$DISK_FILE") DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0 [ ! -d "$DIR" ] && return 0
if [[ "${DISK_SPACE,,}" == "max" || "${DISK_SPACE,,}" == "half" ]]; then if [[ "${DISK_SPACE,,}" == "max" ]]; then
local SPARE=1073741824 local SPARE=536870912
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1) SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
(( SPACE < SPARE )) && SPACE="$SPARE" || SPACE=$((SPACE-SPARE))
if [[ "${DISK_SPACE,,}" == "max" ]]; then GB=$(( SPACE/1073741825 ))
FREE=$(( FREE - SPARE ))
else
FREE=$(( FREE / 2 ))
fi
(( FREE < SPARE )) && FREE="$SPARE"
GB=$(( FREE / 1073741825 ))
DISK_SPACE="${GB}G" DISK_SPACE="${GB}G"
fi fi
SPACE="${DISK_SPACE// /}" SPACE="${DISK_SPACE// /}"
[ -z "$SPACE" ] && SPACE="256G" [ -z "$SPACE" ] && SPACE="16G"
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G" [ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g') SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
@ -472,7 +454,7 @@ addDisk () {
DATA_SIZE=$(numfmt --from=iec "$SPACE") DATA_SIZE=$(numfmt --from=iec "$SPACE")
if (( DATA_SIZE < 6442450944 )); then if (( DATA_SIZE < 6442450944 )); then
error "Please increase the ${DISK_DESC^^}_SIZE variable to at least 6 GB." && exit 73 error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi fi
FS=$(stat -f -c %T "$DIR") FS=$(stat -f -c %T "$DIR")
@ -483,7 +465,7 @@ addDisk () {
DISK_CACHE="writeback" DISK_CACHE="writeback"
fi fi
if [ ! -s "$DISK_FILE" ] ; then if ! [ -s "$DISK_FILE" ] ; then
if [[ "${DISK_FMT,,}" != "raw" ]]; then if [[ "${DISK_FMT,,}" != "raw" ]]; then
PREV_FMT="raw" PREV_FMT="raw"
@ -496,7 +478,6 @@ addDisk () {
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $? convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
fi fi
fi fi
if [ -s "$DISK_FILE" ]; then if [ -s "$DISK_FILE" ]; then
@ -504,18 +485,7 @@ addDisk () {
CUR_SIZE=$(getSize "$DISK_FILE") CUR_SIZE=$(getSize "$DISK_FILE")
if (( DATA_SIZE > CUR_SIZE )); then if (( DATA_SIZE > CUR_SIZE )); then
resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $? resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
else
if (( DATA_SIZE < CUR_SIZE )); then
if [[ "${DISK_SPACE,,}" != "max" && "${DISK_SPACE,,}" != "half" ]]; then
info "You decreased the ${DISK_DESC^^}_SIZE variable to ${DISK_SPACE/G/ GB} but shrinking disks is not supported, will be ignored..."
fi
fi
fi fi
else else
@ -524,39 +494,6 @@ addDisk () {
fi fi
if [ -f "$DISK_FILE" ] && [[ "$ALLOCATE" == [Nn]* ]]; then
CUR_SIZE=$(getSize "$DISK_FILE")
USED=$(du -sB 1 "$DISK_FILE" | cut -f1)
FREE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
LEFT=$(( CUR_SIZE - USED - FREE ))
if (( LEFT > 0 )); then
GB=$(formatBytes "$FREE")
LEFT=$(formatBytes "$LEFT")
CUR_SIZE=$(formatBytes "$CUR_SIZE")
msg="the virtual size of the ${DISK_DESC,,} is $CUR_SIZE"
if [[ "$USED" == "0" ]]; then
msg+=","
else
USED=$(formatBytes "$USED")
msg+=" (of which $USED is used),"
fi
warn "$msg but there is only $GB of free space left in $DIR, make at least $LEFT more room available!"
fi
fi
if [ -f "$DISK_FILE" ]; then
if ! setOwner "$DISK_FILE"; then
error "Failed to set the owner for \"$DISK_FILE\" !"
fi
fi
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "") DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "")
return 0 return 0
@ -564,34 +501,27 @@ addDisk () {
addDevice () { addDevice () {
local DISK_DEV="$1" local DISK_DEV=$1
local DISK_TYPE="$2" local DISK_TYPE=$2
local DISK_INDEX="$3" local DISK_INDEX=$3
local DISK_ADDRESS="$4" local DISK_ADDRESS=$4
[ -z "$DISK_DEV" ] && return 0 [ -z "$DISK_DEV" ] && return 0
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55 [ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
local sectors="" local sectors=""
local dev_type="" local result logical physical
dev_type=$(lsblk -no TYPE "$DISK_DEV" 2>/dev/null | head -n1) result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}"
# Only detect and apply sector sizes for partitions, not whole disks if [ -n "$physical" ]; then
# Whole disk passthrough with explicit sector sizes causes DSM not to recognize the disk if [[ "$physical" != "512" ]]; then
if [[ "$dev_type" == "part" ]]; then sectors=",logical_block_size=$logical,physical_block_size=$physical"
local result logical physical
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}"
if [ -n "$physical" ]; then
if [[ "$physical" != "512" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
else
warn "Failed to determine the sector size for $DISK_DEV"
fi fi
else
warn "Failed to determine the sector size for $DISK_DEV"
fi fi
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors") DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")

View File

@ -18,9 +18,7 @@ cd /run
. proc.sh # Initialize processor . proc.sh # Initialize processor
. serial.sh # Initialize serialport . serial.sh # Initialize serialport
. power.sh # Configure shutdown . power.sh # Configure shutdown
. memory.sh # Check available memory
. config.sh # Configure arguments . config.sh # Configure arguments
. finish.sh # Finish initialization
trap - ERR trap - ERR

View File

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

View File

@ -31,6 +31,7 @@ if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
BASE=$(basename "$URL" .pat) BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }"
printf -v BASE '%b' "${BASE//%/\\x}" printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}" BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
@ -65,6 +66,7 @@ fi
if [ ! -s "$FILE" ]; then if [ ! -s "$FILE" ]; then
BASE=$(basename "${URL%%\?*}" .pat) BASE=$(basename "${URL%%\?*}" .pat)
BASE="${BASE//+/ }"
printf -v BASE '%b' "${BASE//%/\\x}" printf -v BASE '%b' "${BASE//%/\\x}"
BASE="${BASE//[!A-Za-z0-9._-]/_}" BASE="${BASE//[!A-Za-z0-9._-]/_}"
fi fi
@ -80,16 +82,16 @@ rm -f "$STORAGE/$BASE.system.img"
# Check filesystem # Check filesystem
FS=$(stat -f -c %T "$STORAGE") FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "overlay"* && "${ENGINE,,}" == "docker" ]]; then if [[ "${FS,,}" == "overlay"* ]]; then
warn "the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!" info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
fi fi
if [[ "${FS,,}" == "fuse"* ]]; then if [[ "${FS,,}" == "fuse"* ]]; then
warn "the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!" info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
fi fi
if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then if [[ "${FS,,}" == "ecryptfs" || "${FS,,}" == "tmpfs" ]]; then
warn "the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..." info "Warning: the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi fi
if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then
@ -98,10 +100,6 @@ fi
if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then
TMP="$STORAGE/tmp" TMP="$STORAGE/tmp"
rm -rf "$TMP"
if ! makeDir "$TMP"; then
error "Failed to create directory \"$TMP\" !" && exit 93
fi
else else
TMP="/tmp/dsm" TMP="/tmp/dsm"
TMP_SPACE=2147483648 TMP_SPACE=2147483648
@ -110,9 +108,10 @@ else
if (( TMP_SPACE > SPACE )); then if (( TMP_SPACE > SPACE )); then
error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93 error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93
fi fi
rm -rf "$TMP" && mkdir -p "$TMP"
fi fi
rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace # Check free diskspace
ROOT_SPACE=536870912 ROOT_SPACE=536870912
SPACE=$(df --output=avail -B 1 / | tail -n 1) SPACE=$(df --output=avail -B 1 / | tail -n 1)
@ -225,8 +224,6 @@ if ! touch "$SYSTEM"; then
error "Could not create file $SYSTEM for the system disk." && exit 98 error "Could not create file $SYSTEM for the system disk." && exit 98
fi fi
! setOwner "$SYSTEM" && error "Failed to set the owner for \"$SYSTEM\" !"
if [[ "${FS,,}" == "btrfs" ]]; then if [[ "${FS,,}" == "btrfs" ]]; then
{ chattr +C "$SYSTEM"; } || : { chattr +C "$SYSTEM"; } || :
FA=$(lsattr "$SYSTEM") FA=$(lsattr "$SYSTEM")
@ -259,11 +256,7 @@ PART="$TMP/partition.fdisk"
sfdisk -q "$SYSTEM" < "$PART" sfdisk -q "$SYSTEM" < "$PART"
MOUNT="$TMP/system" MOUNT="$TMP/system"
rm -rf "$MOUNT" rm -rf "$MOUNT" && mkdir -p "$MOUNT"
if ! makeDir "$MOUNT"; then
error "Failed to create directory \"$MOUNT\" !" && exit 93
fi
MSG="Extracting system partition..." MSG="Extracting system partition..."
info "Install: $MSG" && html "$MSG" info "Install: $MSG" && html "$MSG"
@ -298,7 +291,6 @@ fakeroot -- bash -c "set -Eeu;\
rm -rf "$MOUNT" rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE/dsm.ver" echo "$BASE" > "$STORAGE/dsm.ver"
! setOwner "$STORAGE/dsm.ver" && error "Failed to set the owner for \"$STORAGE/dsm.ver\" !"
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$PAT" rm -f "$PAT"
@ -306,13 +298,7 @@ else
mv -f "$PAT" "$STORAGE/$BASE.pat" mv -f "$PAT" "$STORAGE/$BASE.pat"
fi fi
if [ -f "$STORAGE/$BASE.pat" ]; then
! setOwner "$STORAGE/$BASE.pat" && error "Failed to set the owner for \"$STORAGE/$BASE.pat\" !"
fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img" mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
! setOwner "$STORAGE/$BASE.boot.img" && error "Failed to set the owner for \"$STORAGE/$BASE.boot.img\" !"
rm -rf "$TMP" rm -rf "$TMP"
return 0 return 0

View File

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

View File

@ -19,16 +19,14 @@ set -Eeuo pipefail
: "${VM_NET_HOST:="VirtualDSM"}" : "${VM_NET_HOST:="VirtualDSM"}"
: "${VM_NET_MASK:="255.255.255.0"}" : "${VM_NET_MASK:="255.255.255.0"}"
: "${PASST:="/run/passt"}" : "${PASST:="passt"}"
: "${PASST_MTU:=""}" : "${PASST_MTU:=""}"
: "${PASST_OPTS:=""}" : "${PASST_OPTS:=""}"
: "${PASST_DEBUG:=""}" : "${PASST_DEBUG:=""}"
: "${PASST_PID:="/var/run/passt.pid"}"
: "${DNSMASQ_OPTS:=""}" : "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ_DEBUG:=""}" : "${DNSMASQ_DEBUG:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}" : "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_PID:="/var/run/dnsmasq.pid"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}" : "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
ADD_ERR="Please add the following setting to your container:" ADD_ERR="Please add the following setting to your container:"
@ -123,63 +121,57 @@ configureDNS() {
local host="$4" local host="$4"
local mask="$5" local mask="$5"
local gateway="$6" local gateway="$6"
local arguments="$DNSMASQ_OPTS"
echo "$gateway" > /run/shm/qemu.gw echo "$gateway" > /run/shm/qemu.gw
[[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0 [[ "${DNSMASQ_DISABLE:-}" == [Yy1]* ]] && return 0
[[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..." [[ "$DEBUG" == [Yy1]* ]] && echo "Starting dnsmasq daemon..."
[ -s "$DNSMASQ_PID" ] && pKill "$(<"$DNSMASQ_PID")" local log="/var/log/dnsmasq.log"
rm -f "$DNSMASQ_PID" rm -f "$log"
case "${NETWORK,,}" in case "${NETWORK,,}" in
"tap" | "tun" | "tuntap" | "y" ) "nat" | "tap" | "tun" | "tuntap" | "y" )
# Create lease file for faster resolve # Create lease file for faster resolve
echo "0 $mac $ip $host 01:$mac" > /var/lib/misc/dnsmasq.leases echo "0 $mac $ip $host 01:$mac" > /var/lib/misc/dnsmasq.leases
chmod 644 /var/lib/misc/dnsmasq.leases chmod 644 /var/lib/misc/dnsmasq.leases
# dnsmasq configuration: # dnsmasq configuration:
arguments+=" --dhcp-authoritative" DNSMASQ_OPTS+=" --dhcp-authoritative"
# Set DHCP range and host # Set DHCP range and host
arguments+=" --dhcp-range=$ip,$ip" DNSMASQ_OPTS+=" --dhcp-range=$ip,$ip"
arguments+=" --dhcp-host=$mac,,$ip,$host,infinite" DNSMASQ_OPTS+=" --dhcp-host=$mac,,$ip,$host,infinite"
# Set DNS server and gateway # Set DNS server and gateway
arguments+=" --dhcp-option=option:netmask,$mask" DNSMASQ_OPTS+=" --dhcp-option=option:netmask,$mask"
arguments+=" --dhcp-option=option:router,$gateway" DNSMASQ_OPTS+=" --dhcp-option=option:router,$gateway"
arguments+=" --dhcp-option=option:dns-server,$gateway" DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,$gateway"
esac esac
# Set interfaces # Set interfaces
arguments+=" --interface=$if" DNSMASQ_OPTS+=" --interface=$if"
arguments+=" --bind-interfaces" DNSMASQ_OPTS+=" --bind-interfaces"
# Add DNS entry for container # Add DNS entry for container
arguments+=" --address=/host.lan/$gateway" DNSMASQ_OPTS+=" --address=/host.lan/$gateway"
# Set local dns resolver to dnsmasq when needed # Set local dns resolver to dnsmasq when needed
[ -f /etc/resolv.dnsmasq ] && arguments+=" --resolv-file=/etc/resolv.dnsmasq" [ -f /etc/resolv.dnsmasq ] && DNSMASQ_OPTS+=" --resolv-file=/etc/resolv.dnsmasq"
# Enable logging to file # Enable logging to file
local log="/var/log/dnsmasq.log" DNSMASQ_OPTS+=" --log-facility=$log"
rm -f "$log"
arguments+=" --log-facility=$log"
arguments=$(echo "$arguments" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${arguments// -/$'\n-'}" [[ "$DEBUG" == [Yy1]* ]] && printf "Dnsmasq arguments:\n\n%s\n\n" "${DNSMASQ_OPTS// -/$'\n-'}"
if ! $DNSMASQ ${arguments:+ $arguments}; then if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
local msg="Failed to start Dnsmasq, reason: $?" local msg="Failed to start Dnsmasq, reason: $?"
[ -f "$log" ] && cat "$log"
if [[ "${NETWORK,,}" == "slirp" || "${NETWORK,,}" == "passt" || "$ROOTLESS" != [Yy1]* || "$DEBUG" == [Yy1]* ]]; then error "$msg"
[ -f "$log" ] && [ -s "$log" ] && cat "$log"
error "$msg"
fi
return 1 return 1
fi fi
@ -226,14 +218,11 @@ getUserPorts() {
for hostport in ${exclude//,/ }; do for hostport in ${exclude//,/ }; do
local port="${hostport///tcp}" local val="${hostport///tcp}"
port="${port///udp}"
if [[ "$num" == "$port" ]]; then if [[ "$num" == "${val///udp}" ]]; then
num="" num=""
if [[ "$port" != "$WEB_PORT" ]]; then warn "Could not assign port ${val///udp} to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!"
warn "Could not assign port $port to \"USER_PORTS\" because it is already in \"HOST_PORTS\"!"
fi
fi fi
done done
@ -282,7 +271,6 @@ getSlirp() {
configureSlirp() { configureSlirp() {
NETWORK="slirp"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..." [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring slirp networking..."
local ip="$IP" local ip="$IP"
@ -314,12 +302,14 @@ configureSlirp() {
configurePasst() { configurePasst() {
NETWORK="passt"
[[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..." [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring user-mode networking..."
local log="/tmp/passt.log" local log="/var/log/passt.log"
rm -f "$log" rm -f "$log"
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
local ip="$IP" local ip="$IP"
[ -n "$VM_NET_IP" ] && ip="$VM_NET_IP" [ -n "$VM_NET_IP" ] && ip="$VM_NET_IP"
@ -351,7 +341,7 @@ configurePasst() {
PASST_OPTS+=" -H $VM_NET_HOST" PASST_OPTS+=" -H $VM_NET_HOST"
PASST_OPTS+=" -M $GATEWAY_MAC" PASST_OPTS+=" -M $GATEWAY_MAC"
PASST_OPTS+=" -P $PASST_PID" PASST_OPTS+=" -P /var/run/passt.pid"
PASST_OPTS+=" -l $log" PASST_OPTS+=" -l $log"
PASST_OPTS+=" -q" PASST_OPTS+=" -q"
@ -363,8 +353,6 @@ configurePasst() {
PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//') PASST_OPTS=$(echo "$PASST_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}" [[ "$DEBUG" == [Yy1]* ]] && printf "Passt arguments:\n\n%s\n\n" "${PASST_OPTS// -/$'\n-'}"
[ ! -f "$PASST" ] && cp /usr/bin/passt* /run
if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then if ! $PASST ${PASST_OPTS:+ $PASST_OPTS} >/dev/null 2>&1; then
rm -f "$log" rm -f "$log"
@ -372,7 +360,7 @@ configurePasst() {
{ $PASST ${PASST_OPTS:+ $PASST_OPTS}; rc=$?; } || : { $PASST ${PASST_OPTS:+ $PASST_OPTS}; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
[ -f "$log" ] && [ -s "$log" ] && cat "$log" [ -f "$log" ] && cat "$log"
warn "failed to start passt ($rc), falling back to slirp networking!" warn "failed to start passt ($rc), falling back to slirp networking!"
configureSlirp && return 0 || return 1 configureSlirp && return 0 || return 1
fi fi
@ -383,7 +371,7 @@ configurePasst() {
tail -fn +0 "$log" --pid=$$ & tail -fn +0 "$log" --pid=$$ &
else else
if [[ "$DEBUG" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
[ -f "$log" ] && [ -s "$log" ] && cat "$log" && echo "" [ -f "$log" ] && cat "$log" && echo ""
fi fi
fi fi
@ -404,6 +392,7 @@ configureNAT() {
# Create the necessary file structure for /dev/net/tun # Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
[[ "$PODMAN" == [Yy1]* ]] && return 1
[ ! -d /dev/net ] && mkdir -m 755 /dev/net [ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun chmod 666 /dev/net/tun
@ -411,7 +400,6 @@ configureNAT() {
fi fi
if [ ! -c /dev/net/tun ]; then if [ ! -c /dev/net/tun ]; then
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
warn "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
@ -419,7 +407,6 @@ configureNAT() {
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || : { sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" warn "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
return 1 return 1
fi fi
@ -446,7 +433,6 @@ configureNAT() {
{ ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || : { ip link add dev "$VM_NET_BRIDGE" type bridge ; rc=$?; } || :
if (( rc != 0 )); then if (( rc != 0 )); then
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1 warn "failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi fi
@ -461,7 +447,6 @@ configureNAT() {
# QEMU Works with taps, set tap to the bridge created # QEMU Works with taps, set tap to the bridge created
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1
warn "$tuntap" && return 1 warn "$tuntap" && return 1
fi fi
@ -502,11 +487,8 @@ configureNAT() {
fi fi
fi fi
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE > /dev/null 2>&1; then if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
[[ "$ROOTLESS" == [Yy1]* && "$DEBUG" != [Yy1]* ]] && return 1 warn "$tables" && return 1
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
warn "$tables" && return 1
fi
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
@ -540,11 +522,13 @@ configureNAT() {
closeBridge() { closeBridge() {
[ -s "$PASST_PID" ] && pKill "$(<"$PASST_PID")" local pid="/var/run/dnsmasq.pid"
rm -f "$PASST_PID" [ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
[ -s "$DNSMASQ_PID" ] && pKill "$(<"$DNSMASQ_PID")" pid="/var/run/passt.pid"
rm -f "$DNSMASQ_PID" [ -s "$pid" ] && pKill "$(<"$pid")"
rm -f "$pid"
case "${NETWORK,,}" in case "${NETWORK,,}" in
"user"* | "passt" | "slirp" ) return 0 ;; "user"* | "passt" | "slirp" ) return 0 ;;
@ -600,9 +584,9 @@ closeNetwork() {
cleanUp() { cleanUp() {
# Clean up old files # Clean up old files
rm -f "$PASST_PID"
rm -f "$DNSMASQ_PID"
rm -f /etc/resolv.dnsmasq rm -f /etc/resolv.dnsmasq
rm -f /var/run/passt.pid
rm -f /var/run/dnsmasq.pid
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
info "Lingering interface will be removed..." info "Lingering interface will be removed..."
@ -642,7 +626,7 @@ getInfo() {
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1" [ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2" [ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3" [ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
# Automatically detect the default network interface # 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=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0" [ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
fi fi
@ -711,7 +695,7 @@ getInfo() {
[ -z "$MTU" ] && MTU="0" [ -z "$MTU" ] && MTU="0"
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
if [[ "$MTU" != "0" ]] && [ "$MTU" -lt "1500" ]; then if [[ "$MTU" != "0" && "$MTU" != "1500" ]]; then
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0" warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
fi fi
fi fi
@ -724,7 +708,6 @@ getInfo() {
# Generate MAC address based on Docker container ID in hostname # Generate MAC address based on Docker container ID in hostname
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/') VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
echo "${VM_NET_MAC^^}" > "$file" echo "${VM_NET_MAC^^}" > "$file"
! setOwner "$file" && error "Failed to set the owner for \"$file\" !"
fi fi
fi fi
@ -742,6 +725,13 @@ getInfo() {
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/') GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
if [[ "$PODMAN" == [Yy1]* && "$DHCP" != [Yy1]* ]]; then
if [ -z "$NETWORK" ] || [[ "${NETWORK^^}" == "Y" ]]; then
# By default Podman has no permissions for NAT networking
NETWORK="user"
fi
fi
if [[ "$DEBUG" == [Yy1]* ]]; then if [[ "$DEBUG" == [Yy1]* ]]; then
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu" line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)" [[ "$MTU" != "0" && "$MTU" != "$mtu" ]] && line+=" ($MTU)"
@ -795,28 +785,26 @@ else
fi fi
case "${NETWORK,,}" in case "${NETWORK,,}" in
"passt" | "slirp" | "user"* ) ;; "user"* | "passt" | "slirp" ) ;;
"tap" | "tun" | "tuntap" | "y" | "" ) "nat" | "tap" | "tun" | "tuntap" | "y" )
# Configure tap interface # Configure tap interface
if ! configureNAT; then if ! configureNAT; then
closeBridge closeBridge
NETWORK="user" NETWORK="user"
msg="falling back to user-mode networking!"
if [[ "$ROOTLESS" != [Yy1]* || "$DEBUG" == [Yy1]* ]]; then msg="failed to setup NAT networking, $msg"
msg="falling back to user-mode networking!"
msg="failed to setup NAT networking, $msg"
warn "$msg"
fi
fi ;; fi ;;
esac esac
[[ "${NETWORK,,}" == "user"* ]] && NETWORK="passt"
case "${NETWORK,,}" in case "${NETWORK,,}" in
"tap" | "tun" | "tuntap" | "y" | "" ) ;; "nat" | "tap" | "tun" | "tuntap" | "y" ) ;;
"passt" | "user"* ) "passt" )
# Configure for user-mode networking (passt) # Configure for user-mode networking (passt)
if ! configurePasst; then if ! configurePasst; then
@ -840,7 +828,7 @@ else
"passt" | "slirp" ) "passt" | "slirp" )
if [ -z "$USER_PORTS" ]; then if [ -z "$USER_PORTS" ]; then
info "Notice: because user-mode networking is active, when you need to forward custom ports to DSM, add them to the \"USER_PORTS\" variable." info "Notice: because user-mode networking is active, if you need to expose ports, add them to the \"USER_PORTS\" variable."
fi ;; fi ;;
esac esac

View File

@ -33,7 +33,6 @@ _trap() {
finish() { finish() {
local pid local pid
local cnt=0
local reason=$1 local reason=$1
touch "$QEMU_END" touch "$QEMU_END"
@ -41,24 +40,14 @@ finish() {
if [ -s "$QEMU_PID" ]; then if [ -s "$QEMU_PID" ]; then
pid=$(<"$QEMU_PID") pid=$(<"$QEMU_PID")
echo && error "Forcefully terminating Virtual DSM, reason: $reason..." echo && error "Forcefully terminating QEMU process, reason: $reason..."
{ kill -15 "$pid" || true; } 2>/dev/null { kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do while isAlive "$pid"; do
sleep 1 sleep 1
cnt=$((cnt+1))
# Workaround for zombie pid # Workaround for zombie pid
[ ! -s "$QEMU_PID" ] && break [ ! -s "$QEMU_PID" ] && break
if [ "$cnt" == "5" ]; then
echo && error "QEMU did not terminate itself, forcefully killing process..."
{ kill -9 "$pid" || true; } 2>/dev/null
fi
done done
fi fi
fKill "print.sh" fKill "print.sh"

View File

@ -33,8 +33,9 @@ if [[ "$KVM" != [Nn]* ]]; then
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard" KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
if ! grep -qw "sse4_2" <<< "$flags"; then if ! grep -qw "sse4_2" <<< "$flags"; then
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!" info "Your CPU does not have the SSE4 instruction set that Virtual DSM requires, it will be emulated..."
[[ "$DEBUG" != [Yy1]* ]] && exit 88 [ -z "$CPU_MODEL" ] && CPU_MODEL="qemu64"
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi fi
if [ -z "$CPU_MODEL" ]; then if [ -z "$CPU_MODEL" ]; then

View File

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

View File

@ -24,40 +24,19 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
# Helper variables # Helper variables
ROOTLESS="N" PODMAN="N"
PRIVILEGED="N"
ENGINE="Docker" ENGINE="Docker"
PROCESS="${APP,,}" PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}" PROCESS="${PROCESS// /-}"
if [ -f "/run/.containerenv" ]; then if [ -f "/run/.containerenv" ]; then
ENGINE="${container:-}" PODMAN="Y"
if [[ "${ENGINE,,}" == *"podman"* ]]; then ENGINE="Podman"
ROOTLESS="Y"
ENGINE="Podman"
else
[ -z "$ENGINE" ] && ENGINE="Kubernetes"
fi
fi fi
echo " Starting $APP for $ENGINE v$(</run/version)..." echo " Starting $APP for $ENGINE v$(</run/version)..."
echo " For support visit $SUPPORT" echo " For support visit $SUPPORT"
# Get the capability bounding set
CAP_BND=$(grep '^CapBnd:' /proc/$$/status | awk '{print $2}')
CAP_BND=$(printf "%d" "0x${CAP_BND}")
# Get the last capability number
LAST_CAP=$(cat /proc/sys/kernel/cap_last_cap)
# Calculate the maximum capability value
MAX_CAP=$(((1 << (LAST_CAP + 1)) - 1))
if [ "${CAP_BND}" -eq "${MAX_CAP}" ]; then
ROOTLESS="N"
PRIVILEGED="Y"
fi
INFO="/run/shm/msg.html" INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html" PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html" TEMPLATE="/var/www/index.html"
@ -80,8 +59,6 @@ fi
CPU_CORES="${CPU_CORES// /}" CPU_CORES="${CPU_CORES// /}"
[[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES" [[ "${CPU_CORES,,}" == "max" ]] && CPU_CORES="$CORES"
[[ "${CPU_CORES,,}" == "half" ]] && CPU_CORES=$(( CORES / 2 ))
[[ "${CPU_CORES,,}" == "0" ]] && CPU_CORES="1"
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15 [ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
if [ "$CPU_CORES" -gt "$CORES" ]; then if [ "$CPU_CORES" -gt "$CORES" ]; then
@ -99,7 +76,8 @@ fi
# Check folder # Check folder
if [[ "${STORAGE,,}" != "/storage" ]]; then if [[ "${COMMIT:-}" == [Yy1]* ]]; then
STORAGE="/local"
mkdir -p "$STORAGE" mkdir -p "$STORAGE"
fi fi
@ -108,9 +86,7 @@ if [ ! -d "$STORAGE" ]; then
fi fi
if [ ! -w "$STORAGE" ]; then if [ ! -w "$STORAGE" ]; then
msg="Storage folder ($STORAGE) is not writeable!" error "Storage folder ($STORAGE) is not writeable!" && exit 13
msg+=" If SELinux is active, you need to add the \":Z\" flag to the bind mount."
error "$msg" && exit 13
fi fi
# Check filesystem # Check filesystem
@ -129,19 +105,21 @@ RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
RAM_SIZE="${RAM_SIZE// /}" RAM_SIZE="${RAM_SIZE// /}"
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16 [ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
if [[ "${RAM_SIZE,,}" != "max" && "${RAM_SIZE,,}" != "half" ]]; then if [[ "${RAM_SIZE,,}" == "max" ]]; then
RAM_WANTED=$(( RAM_AVAIL - RAM_SPARE - RAM_SPARE ))
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then RAM_WANTED=$(( RAM_WANTED / 1073741825 ))
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M" RAM_SIZE="${RAM_WANTED}G"
fi
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
fi fi
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 # Print system info
SYS="${SYS/-generic/}" SYS="${SYS/-generic/}"
FS="${FS/UNKNOWN //}" FS="${FS/UNKNOWN //}"
@ -155,6 +133,15 @@ TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
echo " CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..." echo " CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
echo echo
# Check 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
# Check KVM support # Check KVM support
if [[ "${PLATFORM,,}" == "x64" ]]; then if [[ "${PLATFORM,,}" == "x64" ]]; then
@ -187,10 +174,6 @@ if [[ "$KVM" != [Nn]* ]]; then
if ! grep -qw "vmx\|svm" <<< "$flags"; then if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)" KVM_ERR="(not enabled in BIOS)"
fi fi
if ! grep -qw "sse4_2" <<< "$flags"; then
error "Your CPU does not have the SSE4 instruction set that Virtual DSM requires!"
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi fi
fi fi
fi fi

View File

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

View File

@ -67,37 +67,6 @@ fKill() {
return 0 return 0
} }
setOwner() {
local file="$1"
local dir uid gid
[ ! -f "$file" ] && return 1
dir=$(dirname -- "$file")
uid=$(stat -c '%u' "$dir")
gid=$(stat -c '%g' "$dir")
! chown "$uid:$gid" "$file" && return 1
return 0
}
makeDir() {
local path="$1"
local dir uid gid
[ -d "$path" ] && return 0
! mkdir -p "$path" && return 1
dir=$(dirname -- "$path")
uid=$(stat -c '%u' "$dir")
gid=$(stat -c '%g' "$dir")
! chown "$uid:$gid" "$path" && return 1
return 0
}
escape () { escape () {
local s local s
s=${1//&/\&amp;} s=${1//&/\&amp;}
@ -154,11 +123,11 @@ cpu() {
fi fi
cpu="${cpu// CPU/}" cpu="${cpu// CPU/}"
cpu="${cpu// [0-9][0-9][0-9] Core}"
cpu="${cpu// [0-9][0-9] Core}"
cpu="${cpu// [0-9] Core}" cpu="${cpu// [0-9] Core}"
cpu="${cpu//[0-9][0-9]th Gen }" cpu="${cpu// [0-9][0-9] Core}"
cpu="${cpu// [0-9][0-9][0-9] Core}"
cpu="${cpu//[0-9]th Gen }" cpu="${cpu//[0-9]th Gen }"
cpu="${cpu//[0-9][0-9]th Gen }"
cpu="${cpu// Processor/}" cpu="${cpu// Processor/}"
cpu="${cpu// Quad core/}" cpu="${cpu// Quad core/}"
cpu="${cpu// Dual core/}" cpu="${cpu// Dual core/}"