diff --git a/Dockerfile b/Dockerfile index 7ceea508..cd50964a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,14 +28,16 @@ RUN set -eu && \ ethtool \ iptables \ iproute2 \ - apt-utils \ dnsmasq \ xz-utils \ + apt-utils \ net-tools \ e2fsprogs \ qemu-utils \ + websocketd \ iputils-ping \ genisoimage \ + inotify-tools \ netcat-openbsd \ ca-certificates \ qemu-system-x86 && \ diff --git a/src/display.sh b/src/display.sh index 95579350..ee2558c7 100644 --- a/src/display.sh +++ b/src/display.sh @@ -70,3 +70,5 @@ fi addPackage "xserver-xorg-video-intel" "Intel GPU drivers" addPackage "qemu-system-modules-opengl" "OpenGL module" + +return 0 diff --git a/src/entry.sh b/src/entry.sh index 4f0f249c..e478dcea 100755 --- a/src/entry.sh +++ b/src/entry.sh @@ -7,9 +7,10 @@ set -Eeuo pipefail cd /run -. start.sh # Placeholder +. start.sh # Startup hook . utils.sh # Load functions . reset.sh # Initialize system +. server.sh # Start webserver . define.sh # Define images . install.sh # Download image . disk.sh # Initialize disks diff --git a/src/install.sh b/src/install.sh index 31ddff8e..93340037 100644 --- a/src/install.sh +++ b/src/install.sh @@ -456,3 +456,5 @@ dst="$STORAGE/${base%.*}.$target_ext" base=$(basename "$dst") detectType "$STORAGE/$base" && return 0 error "Cannot convert file \"${base}\"" && exit 36 + +return 0 diff --git a/src/network.sh b/src/network.sh index 57915794..38e0a9f6 100644 --- a/src/network.sh +++ b/src/network.sh @@ -174,7 +174,7 @@ configureDNS() { fi if [[ "$DNSMASQ_DEBUG" == [Yy1]* ]]; then - tail -fn +0 "$log" & + tail -fn +0 "$log" --pid=$$ & fi return 0 @@ -229,6 +229,7 @@ getHostPorts() { if [[ "${WEB:-}" != [Nn]* ]]; then [ -z "$list" ] && list="$WEB_PORT" || list+=",$WEB_PORT" + [ -z "$list" ] && list="$WSD_PORT" || list+=",$WSD_PORT" fi if [[ "${NETWORK,,}" == "passt" ]]; then @@ -369,7 +370,7 @@ configurePasst() { fi if [[ "$PASST_DEBUG" == [Yy1]* ]]; then - tail -fn +0 "$log" & + tail -fn +0 "$log" --pid=$$ & else if [[ "$DEBUG" == [Yy1]* ]]; then [ -f "$log" ] && cat "$log" && echo "" @@ -555,6 +556,11 @@ closeNetwork() { nginx -s stop 2> /dev/null fWait "nginx" + # Shutdown websocket + local pid="/var/run/websocketd.pid" + [ -s "$pid" ] && pKill "$(<"$pid")" + rm -f "$pid" + fi [[ "$NETWORK" == [Nn]* ]] && return 0 diff --git a/src/proc.sh b/src/proc.sh index 8cb47666..a2254628 100644 --- a/src/proc.sh +++ b/src/proc.sh @@ -4,61 +4,14 @@ set -Eeuo pipefail # Docker environment variables : "${HV="Y"}" -: "${KVM:="Y"}" : "${VMX:="N"}" : "${CPU_FLAGS:=""}" : "${CPU_MODEL:=""}" [[ "$DEBUG" == [Yy1]* ]] && echo "Configuring KVM..." -if [[ "$KVM" == [Nn]* ]]; then - warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!" -else - if [[ "${ARCH,,}" != "amd64" ]]; then - KVM="N" - warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, so the machine will run about 10 times slower." - fi -fi - -if [[ "$KVM" != [Nn]* ]]; then - - KVM_ERR="" - - if [ ! -e /dev/kvm ]; then - KVM_ERR="(/dev/kvm is missing)" - else - if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then - KVM_ERR="(/dev/kvm is unwriteable)" - else - flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo) - if ! grep -qw "vmx\|svm" <<< "$flags"; then - KVM_ERR="(not enabled in BIOS)" - fi - fi - fi - - if [ -n "$KVM_ERR" ]; then - KVM="N" - if [[ "$OSTYPE" =~ ^darwin ]]; then - warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower." - else - 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 the machine to run about 10 times slower." - error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;; - esac - [[ "$DEBUG" != [Yy1]* ]] && exit 88 - fi - fi - -fi - vendor=$(lscpu | awk '/Vendor ID/{print $3}') +flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo) if [[ "$KVM" != [Nn]* ]]; then diff --git a/src/progress.sh b/src/progress.sh index 9c7549f0..6f473294 100644 --- a/src/progress.sh +++ b/src/progress.sh @@ -25,7 +25,7 @@ do if [ -s "$file" ]; then bytes=$(du -sb "$file" | cut -f1) if (( bytes > 1000 )); then - if [ -z "$total" ] || [[ "$total" == "0" ]]; then + if [ -z "$total" ] || [[ "$total" == "0" ]] || [ "$bytes" -gt "$total" ]; 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}')" diff --git a/src/reset.sh b/src/reset.sh index a6b0d305..d4ac87ba 100644 --- a/src/reset.sh +++ b/src/reset.sh @@ -9,6 +9,7 @@ trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR # Docker environment variables +: "${KVM:="Y"}" # KVM acceleration : "${BOOT:=""}" # Path of ISO file : "${DEBUG:="N"}" # Disable debugging : "${MACHINE:="q35"}" # Machine selection @@ -148,66 +149,63 @@ if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); th info "$msg" fi -addPackage() { - local pkg=$1 - local desc=$2 +# Check KVM support - 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 -} - -: "${VNC_PORT:="5900"}" # VNC port -: "${MON_PORT:="7100"}" # Monitor port -: "${WEB_PORT:="8006"}" # Webserver port -: "${WSS_PORT:="5700"}" # Websockets port - -if (( VNC_PORT < 5900 )); then - warn "VNC port cannot be set lower than 5900, ignoring value $VNC_PORT." - VNC_PORT="5900" +if [[ "${PLATFORM,,}" == "x64" ]]; then + TARGET="amd64" +else + TARGET="arm64" fi -cp -r /var/www/* /run/shm -html "Starting $APP for $ENGINE..." - -if [[ "${WEB:-}" != [Nn]* ]]; then - - mkdir -p /etc/nginx/sites-enabled - cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf - - user="admin" - [ -n "${USER:-}" ] && user="${USER:-}" - - if [ -n "${PASS:-}" ]; then - - # Set password - echo "$user:{PLAIN}${PASS:-}" > /etc/nginx/.htpasswd - - sed -i "s/auth_basic off/auth_basic \"NoVNC\"/g" /etc/nginx/sites-enabled/web.conf - +if [[ "$KVM" == [Nn]* ]]; then + warn "KVM acceleration is disabled, this will cause the machine to run about 10 times slower!" +else + if [[ "${ARCH,,}" != "$TARGET" ]]; then + KVM="N" + warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for ${PLATFORM^^} instructions, so the machine will run about 10 times slower." fi +fi - sed -i "s/listen 8006 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf - sed -i "s/proxy_pass http:\/\/127.0.0.1:5700\/;/proxy_pass http:\/\/127.0.0.1:$WSS_PORT\/;/g" /etc/nginx/sites-enabled/web.conf - - # shellcheck disable=SC2143 - if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then - - sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf - +if [[ "$KVM" != [Nn]* ]]; then + + KVM_ERR="" + + if [ ! -e /dev/kvm ]; then + KVM_ERR="(/dev/kvm is missing)" + else + if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then + KVM_ERR="(/dev/kvm is unwriteable)" + else + if [[ "${PLATFORM,,}" == "x64" ]]; then + flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo) + if ! grep -qw "vmx\|svm" <<< "$flags"; then + KVM_ERR="(not enabled in BIOS)" + fi + fi + fi fi - # Start webserver - nginx -e stderr + if [ -n "$KVM_ERR" ]; then + KVM="N" + if [[ "$OSTYPE" =~ ^darwin ]]; then + warn "you are using macOS which has no KVM support, so the machine will run about 10 times slower." + else + 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 the machine to run about 10 times slower." + error "See the FAQ for possible causes, or disable acceleration by adding the \"KVM=N\" variable (not recommended)." ;; + esac + [[ "$DEBUG" != [Yy1]* ]] && exit 88 + fi + fi fi +html "Starting $APP for $ENGINE..." + return 0 diff --git a/src/server.sh b/src/server.sh new file mode 100644 index 00000000..3ed85707 --- /dev/null +++ b/src/server.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +: "${VNC_PORT:="5900"}" # VNC port +: "${MON_PORT:="7100"}" # Monitor port +: "${WEB_PORT:="8006"}" # Webserver port +: "${WSD_PORT:="8004"}" # Websockets port +: "${WSS_PORT:="5700"}" # Websockets port + +if (( VNC_PORT < 5900 )); then + warn "VNC port cannot be set lower than 5900, ignoring value $VNC_PORT." + VNC_PORT="5900" +fi + +cp -r /var/www/* /run/shm +rm -f /var/run/websocketd.pid + +if [[ "${WEB:-}" != [Nn]* ]]; then + + mkdir -p /etc/nginx/sites-enabled + cp /etc/nginx/default.conf /etc/nginx/sites-enabled/web.conf + + user="admin" + [ -n "${USER:-}" ] && user="${USER:-}" + + if [ -n "${PASS:-}" ]; then + + # Set password + echo "$user:{PLAIN}${PASS:-}" > /etc/nginx/.htpasswd + + sed -i "s/auth_basic off/auth_basic \"NoVNC\"/g" /etc/nginx/sites-enabled/web.conf + + fi + + sed -i "s/listen 8006 default_server;/listen $WEB_PORT default_server;/g" /etc/nginx/sites-enabled/web.conf + sed -i "s/proxy_pass http:\/\/127.0.0.1:5700\/;/proxy_pass http:\/\/127.0.0.1:$WSS_PORT\/;/g" /etc/nginx/sites-enabled/web.conf + sed -i "s/proxy_pass http:\/\/127.0.0.1:8004\/;/proxy_pass http:\/\/127.0.0.1:$WSD_PORT\/;/g" /etc/nginx/sites-enabled/web.conf + + # shellcheck disable=SC2143 + if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then + + sed -i "s/listen $WEB_PORT default_server;/listen [::]:$WEB_PORT default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf + + fi + + # Start webserver + nginx -e stderr + + # Start websocket server + websocketd --address 127.0.0.1 --port="$WSD_PORT" /run/socket.sh >/var/log/websocketd.log & + echo "$!" > /var/run/websocketd.pid + +fi + +return 0 diff --git a/src/socket.sh b/src/socket.sh new file mode 100644 index 00000000..c095e674 --- /dev/null +++ b/src/socket.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +path="/run/shm/msg.html" + +inotifywait -m "$path" | + while read -r fp event fn; do + case "${event,,}" in + "modify" ) echo -n "s: " && cat "$path" ;; + "delete_self" ) echo "c: vnc" ;; + esac + done diff --git a/src/start.sh b/src/start.sh index 55f68749..1b63c84e 100644 --- a/src/start.sh +++ b/src/start.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash set -Eeuo pipefail -# Override this placeholder file using a Docker bind to execute a script during startup! +# You can override this hook to execute a script before startup! + +return 0 diff --git a/src/utils.sh b/src/utils.sh index e158f555..29535001 100644 --- a/src/utils.sh +++ b/src/utils.sh @@ -159,4 +159,21 @@ hasDisk() { return 1 } +addPackage() { + local pkg=$1 + local desc=$2 + + if apt-mark showinstall | grep -qx "$pkg"; then + return 0 + fi + + local 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 +} + return 0 diff --git a/web/conf/nginx.conf b/web/conf/nginx.conf index ea657f71..fa668087 100644 --- a/web/conf/nginx.conf +++ b/web/conf/nginx.conf @@ -43,6 +43,20 @@ server { } + location /status { + + proxy_http_version 1.1; + + proxy_set_header Connection 'upgrade'; + proxy_set_header Upgrade $http_upgrade; + + proxy_buffering off; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + + proxy_pass http://127.0.0.1:8004/; + } + location /websockify { proxy_http_version 1.1; diff --git a/web/js/script.js b/web/js/script.js index 25a1ee97..3faca263 100644 --- a/web/js/script.js +++ b/web/js/script.js @@ -8,7 +8,7 @@ var webSocketFactory = { ws.addEventListener("open", e => { ws.close(); - document.location.reload(); + window.location.reload(); }); ws.addEventListener("error", e => { @@ -35,12 +35,28 @@ function getInfo() { request.send(); } catch (e) { - var err = "Error: " + e.message; - console.log(err); - setError(err); + setError("Error: " + e.message); } } +function getURL() { + + var protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; + var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, ''); + + return protocol + "//" + window.location.host + path; +} + +function redirect() { + + setInfo("Connecting to VNC", true); + + var wsUrl = getURL() + "/websockify"; + var webSocket = webSocketFactory.connect(wsUrl); + + return true; +} + function processInfo() { try { @@ -51,7 +67,7 @@ function processInfo() { var msg = request.responseText; if (msg == null || msg.length == 0) { setError("Lost connection"); - schedule(); + window.location.reload(); return false; } @@ -62,30 +78,20 @@ function processInfo() { notFound = true; } else { setInfo(msg); - schedule(); return true; } } if (notFound) { - setInfo("Connecting to VNC", true); - - var protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; - var path = window.location.pathname.replace(/[^/]*$/, '').replace(/\/$/, ''); - var wsUrl = protocol + "//" + window.location.host + path + "/websockify"; - var webSocket = webSocketFactory.connect(wsUrl); - + redirect(); return true; } setError("Error: Received statuscode " + request.status); - schedule(); return false; } catch (e) { - var err = "Error: " + e.message; - console.log(err); - setError(err); + setError("Error: " + e.message); return false; } } @@ -126,11 +132,55 @@ function setInfo(msg, loading, error) { } function setError(text) { + console.warn(text); return setInfo(text, false, true); } -function schedule() { - setTimeout(getInfo, interval); +function connect() { + + var wsUrl = getURL() + "/status"; + var ws = new WebSocket(wsUrl); + + ws.onmessage = function(e) { + + var pos = e.data.indexOf(":"); + var cmd = e.data.substring(0, pos); + var msg = e.data.substring(pos + 2); + + switch (cmd) { + case "s": + setInfo(msg); + break; + case "c": + switch (msg) { + case "vnc": + redirect(); + break; + default: + console.warn("Unknown command: " + msg); + break; + } + break; + case "e": + setError(msg); + break; + default: + console.warn("Unknown event: " + cmd); + break; + } + }; + + ws.onclose = function(e) { + setTimeout(function() { + connect(); + }, interval); + }; + + ws.onerror = function(e) { + ws.close(); + window.location.reload(); + }; } -schedule(); +getInfo(); +connect();