diff --git a/docker/rascsi-web/Dockerfile b/docker/rascsi-web/Dockerfile index 3937b3c0..f3f4c367 100644 --- a/docker/rascsi-web/Dockerfile +++ b/docker/rascsi-web/Dockerfile @@ -19,7 +19,10 @@ RUN apt-get update \ RUN groupadd pi RUN useradd --create-home --shell /bin/bash -g pi pi RUN echo "pi ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers -RUN echo "pi:rascsi" | chpasswd +RUN echo "pi:rascsi" | chpasswd + +# Allows custom PATH for mock commands to work when executing with sudo +RUN sed -i 's/^Defaults\tsecure_path/#Defaults\tsecure_path./' /etc/sudoers RUN mkdir /home/pi/shared_files RUN touch /etc/dhcpcd.conf diff --git a/docker/rascsi-web/start.sh b/docker/rascsi-web/start.sh index c282b6d0..19bc0399 100644 --- a/docker/rascsi-web/start.sh +++ b/docker/rascsi-web/start.sh @@ -11,6 +11,9 @@ fi # Start Nginx service nginx +# Use mock commands +export PATH="/home/pi/RASCSI/python/web/mock/bin:$PATH" + # Pass args to web UI start script if [[ $RASCSI_PASSWORD ]]; then /home/pi/RASCSI/python/web/start.sh "$@" --password=$RASCSI_PASSWORD diff --git a/easyinstall.sh b/easyinstall.sh index 52256b5b..2b960a7f 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -626,11 +626,13 @@ function installHfdisk() { # Fetch HFS drivers that the Web Interface uses function fetchHardDiskDrivers() { - if [ ! -d "$BASE/mac-hard-disk-drivers" ]; then + DRIVER_ARCHIVE="mac-hard-disk-drivers" + if [ ! -d "$BASE/$DRIVER_ARCHIVE" ]; then cd "$BASE" || exit 1 - wget -r https://www.dropbox.com/s/gcs4v5pcmk7rxtb/mac-hard-disk-drivers.zip?dl=0 - unzip -d mac-hard-disk-drivers mac-hard-disk-drivers.zip - rm mac-hard-disk-drivers.zip + # -N option overwrites if downloaded file is newer than existing file + wget -N "https://www.dropbox.com/s/gcs4v5pcmk7rxtb/$DRIVER_ARCHIVE.zip?dl=1" -O "$DRIVER_ARCHIVE.zip" + unzip -d "$DRIVER_ARCHIVE" "$DRIVER_ARCHIVE.zip" + rm "$DRIVER_ARCHIVE.zip" fi } diff --git a/python/common/src/rascsi/sys_cmds.py b/python/common/src/rascsi/sys_cmds.py index 0eb5ee64..c2609a43 100644 --- a/python/common/src/rascsi/sys_cmds.py +++ b/python/common/src/rascsi/sys_cmds.py @@ -3,11 +3,12 @@ Module with methods that interact with the Pi system """ import subprocess import logging -from subprocess import run +from subprocess import run, CalledProcessError from shutil import disk_usage from re import findall, match from socket import socket, gethostname, AF_INET, SOCK_DGRAM from pathlib import Path +from platform import uname from rascsi.common_settings import SHELL_ERROR @@ -37,20 +38,6 @@ class SysCmds: logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8")) ra_git_version = "" - try: - os_version = ( - subprocess.run( - ["uname", "--kernel-name", "--kernel-release", "--machine"], - capture_output=True, - check=True, - ) - .stdout.decode("utf-8") - .strip() - ) - except subprocess.CalledProcessError as error: - logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8")) - os_version = "Unknown OS" - PROC_MODEL_PATH = "/proc/device-tree/model" SYS_VENDOR_PATH = "/sys/devices/virtual/dmi/id/sys_vendor" SYS_PROD_PATH = "/sys/devices/virtual/dmi/id/product_name" @@ -77,7 +64,11 @@ class SysCmds: else: hardware = "Unknown Device" - return {"git": ra_git_version, "env": f"{hardware}, {os_version}" } + env = uname() + return { + "git": ra_git_version, + "env": f"{hardware}, {env.system} {env.release} {env.machine}", + } @staticmethod def running_proc(daemon): @@ -172,6 +163,42 @@ class SysCmds: sock.close() return ip_addr, host + @staticmethod + def get_pretty_host(): + """ + Returns either the pretty hostname if set, or the regular hostname as fallback. + """ + try: + process = run( + ["hostnamectl", "status", "--pretty"], + capture_output=True, + check=True, + ) + pretty_hostname = process.stdout.decode("utf-8").rstrip() + if pretty_hostname: + return pretty_hostname + except CalledProcessError as error: + logging.error(str(error)) + + return gethostname() + + @staticmethod + def set_pretty_host(name): + """ + Set the pretty hostname for the system + """ + try: + process = run( + ["sudo", "hostnamectl", "set-hostname", "--pretty", name], + capture_output=False, + check=True, + ) + except CalledProcessError as error: + logging.error(str(error)) + return False + + return True + @staticmethod def get_logs(lines, scope): """ diff --git a/python/web/mock/bin/hostnamectl b/python/web/mock/bin/hostnamectl new file mode 100755 index 00000000..4cb249dd --- /dev/null +++ b/python/web/mock/bin/hostnamectl @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +TMP_FILE="/tmp/hostnamectl_pretty.tmp" + +if [[ "$1" == "set-hostname" && "$2" == "--pretty" ]]; then + if [[ -z "$3" ]]; then + rm "$TMP_FILE" 2>/dev/null || true + else + echo "$3" > $TMP_FILE + fi + + exit 0 +fi + +if [[ "$1" == "status" ]]; then + cat "$TMP_FILE" 2>/dev/null + exit 0 +fi + +echo "Mock does not recognize: $0 $@" +exit 1 diff --git a/python/web/src/static/themes/modern/style.css b/python/web/src/static/themes/modern/style.css index dbc53dca..301463c3 100644 --- a/python/web/src/static/themes/modern/style.css +++ b/python/web/src/static/themes/modern/style.css @@ -208,12 +208,12 @@ select { */ div.header { display: flex; + align-items: center; } div.header div.title { order: 1; text-align: left; - flex-grow: 1; } div.header div.title h1 { @@ -227,7 +227,18 @@ div.header div.title a { } div.header div.hostname { - display: none; + color: #ccc; + padding: 0 0.5rem; + order: 2; + flex-grow: 1; +} + +div.header div.hostname span { + display: inline-block; + border: 1px solid #ccc; + border-radius: 1rem; + padding: 0.125rem 0.5rem; + font-size: 0.75rem; } div.header div.login-status { @@ -253,9 +264,10 @@ div.header div.authentication-disabled { padding: 0 0.5rem; } -@media (max-width: 820px) { +@media (max-width: 900px) { div.header { min-height: 3.5rem; /* Safari 14 iOS and iPad OS */ + background: var(--dark); } body:not(.logged-in) div.header { @@ -263,10 +275,6 @@ div.header div.authentication-disabled { min-height: 8.875rem; /* Safari 14 iOS and iPad OS */ } - div.header div.title { - background: var(--dark); - } - div.header div.title a { display: block; background: url("/static/logo.png") no-repeat; @@ -326,7 +334,7 @@ div.header div.authentication-disabled { } } -@media (min-width: 821px) { +@media (min-width: 901px) { div.header { background: var(--dark); align-items: center; @@ -509,7 +517,7 @@ section > details ul { border-radius: 0.5rem; } -@media (max-width: 820px) { +@media (max-width: 900px) { section > details summary { font-size: 0.9rem; } @@ -578,7 +586,7 @@ table#attached-devices tr.reserved td { background-color: #ffe9e9; } -@media (max-width: 820px) { +@media (max-width: 900px) { table#attached-devices th.product, table#attached-devices td.product { display: none; @@ -597,7 +605,7 @@ table#attached-devices tr.reserved td { } } -@media (min-width: 821px) { +@media (min-width: 901px) { section#current-config form#config-actions { float: left; height: 2.75rem; @@ -670,7 +678,7 @@ section#files p { margin-top: 1rem; } -@media (max-width: 820px) { +@media (max-width: 900px) { section#files table#images tr th:nth-child(2), section#files table#images tr td:nth-child(2) { display: none; @@ -684,7 +692,7 @@ section#files p { } } -@media (min-width: 821px) { +@media (min-width: 901px) { section#files table#images form.file-copy input[type="submit"], section#files table#images form.file-rename input[type="submit"], section#files table#images form.file-delete input[type="submit"], @@ -740,7 +748,7 @@ section#attach-devices form { display: block; } -@media (max-width: 820px) { +@media (max-width: 900px) { section#attach-devices table tr th:nth-child(2), section#attach-devices table tr td:nth-child(2) { display: none; @@ -779,8 +787,12 @@ section#logging div:first-of-type { Index > Section: System ------------------------------------------------------------------------------ */ -@media (min-width: 821px) { - section#system input[type="submit"] { +section#system div.power-control { + margin-top: 1rem; +} + +@media (min-width: 901px) { + section#system div.power-control input[type="submit"] { background: var(--danger); border-color: var(--danger); color: #fff; diff --git a/python/web/src/templates/base.html b/python/web/src/templates/base.html index 09249238..3a31c9ef 100644 --- a/python/web/src/templates/base.html +++ b/python/web/src/templates/base.html @@ -77,8 +77,7 @@