From 34a6e45915c05bbebdc3baa5590ce1cd1e6aec95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Iwanicki?= Date: Fri, 24 Oct 2025 15:03:20 +0200 Subject: [PATCH 1/5] wip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Iwanicki --- Makefile | 10 +- include/dts-environment.sh | 2 + include/dts-functions.sh | 66 ++- include/dts-subscription.sh | 22 +- scripts/dasharo-deploy.sh | 12 +- scripts/dts-boot.sh | 10 +- scripts/dts.sh | 95 ++-- tui/dts-tui.yaml | 96 ++++ tui/tui-lib.sh | 648 ++++++++++++++++++++++++ tui/tui_callbacks/dasharo-firmware.sh | 65 +++ tui/tui_callbacks/dpp-keys.sh | 46 ++ tui/tui_callbacks/fuse.sh | 14 + tui/tui_callbacks/hcl-report.sh | 27 + tui/tui_callbacks/poweroff.sh | 6 + tui/tui_callbacks/reboot.sh | 6 + tui/tui_callbacks/restore-firmware.sh | 14 + tui/tui_callbacks/shell.sh | 13 + tui/tui_callbacks/ssh-toggle.sh | 15 + tui/tui_callbacks/toggle-credentials.sh | 13 + tui/tui_callbacks/toggle-logs.sh | 13 + tui/tui_callbacks/transition.sh | 15 + 21 files changed, 1128 insertions(+), 80 deletions(-) create mode 100644 tui/dts-tui.yaml create mode 100644 tui/tui-lib.sh create mode 100644 tui/tui_callbacks/dasharo-firmware.sh create mode 100644 tui/tui_callbacks/dpp-keys.sh create mode 100644 tui/tui_callbacks/fuse.sh create mode 100644 tui/tui_callbacks/hcl-report.sh create mode 100644 tui/tui_callbacks/poweroff.sh create mode 100644 tui/tui_callbacks/reboot.sh create mode 100644 tui/tui_callbacks/restore-firmware.sh create mode 100644 tui/tui_callbacks/shell.sh create mode 100644 tui/tui_callbacks/ssh-toggle.sh create mode 100644 tui/tui_callbacks/toggle-credentials.sh create mode 100644 tui/tui_callbacks/toggle-logs.sh create mode 100644 tui/tui_callbacks/transition.sh diff --git a/Makefile b/Makefile index 02de9112..fb6f7091 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,10 @@ SBINDIR ?= /usr/sbin SYSCONFDIR ?= /etc +LIBDIR ?= /usr/lib install: - install -d $(DESTDIR)$(SBINDIR) + install -d $(DESTDIR)$(SBINDIR)/tui_callbacks install -m 0755 include/dts-environment.sh $(DESTDIR)$(SBINDIR) install -m 0755 include/dts-functions.sh $(DESTDIR)$(SBINDIR) @@ -20,9 +21,14 @@ install: install -m 0755 scripts/dts-boot.sh $(DESTDIR)$(SBINDIR)/dts-boot install -m 0755 scripts/ec_transition.sh $(DESTDIR)$(SBINDIR)/ec_transition install -m 0755 scripts/logging.sh $(DESTDIR)$(SBINDIR)/logging + install -m 0755 tui/tui_callbacks/* $(DESTDIR)$(SBINDIR)/tui_callbacks/ install -m 0755 reports/dasharo-hcl-report.sh $(DESTDIR)$(SBINDIR)/dasharo-hcl-report install -m 0755 reports/touchpad-info.sh $(DESTDIR)$(SBINDIR)/touchpad-info - install -d $(DESTDIR)$(SYSCONFDIR)/profile.d + install -d $(DESTDIR)$(SYSCONFDIR)/profile.d $(DESTDIR)$(SYSCONFDIR)/dts install -m 0755 dts-profile.sh $(DESTDIR)$(SYSCONFDIR)/profile.d + install -m 0644 tui/dts-tui.yaml $(DESTDIR)$(SYSCONFDIR)/dts/dts-tui.yaml + + install -d $(DESTDIR)$(LIBDIR)/dts + install -m 0644 tui/tui-lib.sh $(DESTDIR)$(LIBDIR)/dts/tui-lib.sh diff --git a/include/dts-environment.sh b/include/dts-environment.sh index 53728cf9..7790ebdc 100644 --- a/include/dts-environment.sh +++ b/include/dts-environment.sh @@ -9,6 +9,8 @@ source $DTS_HAL # shellcheck source=../include/dts-functions.sh source $DTS_FUNCS +# shellcheck disable=SC1090 +source "$DTS_TUI_LIB" # Text colors: NORMAL='\033[0m' diff --git a/include/dts-functions.sh b/include/dts-functions.sh index d78fe905..8cac79c7 100644 --- a/include/dts-functions.sh +++ b/include/dts-functions.sh @@ -22,24 +22,24 @@ function echo_yellow() { # print_warning # Print yellow warning print_warning() { - echo_yellow "$1" + tui_echo_yellow "$1" } # print_error # Print red error print_error() { - echo_red "$1" + tui_echo_red "$1" } # print_error # Print green print_ok() { - echo_green "$1" + tui_echo_green "$1" } # Clears the line, usable for carriage returns to make sure no garbage is left. clear_line() { - printf '\r\033[K' + tui_clear_line } check_if_dasharo() { @@ -1122,18 +1122,19 @@ sync_clocks() { } print_disclaimer() { - echo -e \ - "Please note that the report is not anonymous, but we will use it only for\r -backup and future improvement of the Dasharo product. Every log is encrypted\r -and sent over HTTPS, so security is assured.\r -If you still have doubts, you can skip HCL report generation.\r\n -What is inside the HCL report? We gather information about:\r - - PCI, Super I/O, GPIO, EC, audio, and Intel configuration,\r - - MSRs, CMOS NVRAM, CPU info, DIMMs, state of touchpad, SMBIOS and ACPI tables,\r - - Decoded BIOS information, full firmware image backup, kernel dmesg,\r - - IO ports, input bus types, and topology - including I2C and USB,\r -\r -You can find more info about HCL in docs.dasharo.com/glossary\r" + tui_echo_normal \ + "Please note that the report is not anonymous, but we will use it only for +backup and future improvement of the Dasharo product. Every log is encrypted +and sent over HTTPS, so security is assured. +If you still have doubts, you can skip HCL report generation. + +What is inside the HCL report? We gather information about: + - PCI, Super I/O, GPIO, EC, audio, and Intel configuration, + - MSRs, CMOS NVRAM, CPU info, DIMMs, state of touchpad, SMBIOS and ACPI tables, + - Decoded BIOS information, full firmware image backup, kernel dmesg, + - IO ports, input bus types, and topology - including I2C and USB, + +You can find more info about HCL in docs.dasharo.com/glossary" } show_ram_inf() { @@ -1181,7 +1182,7 @@ show_ram_inf() { # Print the extracted values preformatted: for entry in "${memory_devices_array[@]}"; do - echo -e "${BLUE}**${YELLOW} RAM ${entry}" + echo -e "RAM ${entry}" done start_trace_logging } @@ -1232,11 +1233,9 @@ show_ssh_info() { # Display "check your connection" in red color in IP field in case no IPV4 # address is assigned, otherwise display IP/PORT: if [[ -z "$ip" ]]; then - echo -e "${BLUE}**${NORMAL} SSH status: ${GREEN}ON${NORMAL} IP: ${RED}check your connection${NORMAL}" - echo -e "${BLUE}*********************************************************${NORMAL}" + tui_echo_red "check your connection" else - echo -e "${BLUE}**${NORMAL} SSH status: ${GREEN}ON${NORMAL} IP: ${ip}${NORMAL}" - echo -e "${BLUE}*********************************************************${NORMAL}" + echo "${ip}" fi fi } @@ -1951,3 +1950,28 @@ ask_for_choice() { fi done } + +# change_global_state variable value +# after function finishes, state/variable value can be retrieved +# via get_global_state +set_global_state() { + local var="$1" + local value="$2" + ( + flock -x 200 + touch "${DTS_STATE}" + sed -i "/^${var}=/d" "${DTS_STATE}" + echo "${var}=${value}" >>"${DTS_STATE}" + ) 200>"${DTS_STATE_LOCKFILE}" +} + +get_global_state() { + local var="$1" + ( + flock -x 200 + touch "${DTS_STATE}" + # print in first occurrence of = + awk -F '=' -v var="${var}" '$0 ~ "^" var "=" {print $2; exit}' "${DTS_STATE}" + + ) 200>"${DTS_STATE_LOCKFILE}" +} diff --git a/include/dts-subscription.sh b/include/dts-subscription.sh index 1f8a16b8..20b8854e 100644 --- a/include/dts-subscription.sh +++ b/include/dts-subscription.sh @@ -102,10 +102,8 @@ check_dts_extensions_access() { } get_dpp_creds() { - echo "" - read -p "Enter DPP email: " 'DPP_EMAIL' - echo "" - read -p "Enter password: " 'DPP_PASSWORD' + DPP_EMAIL=$(tui_read_prompt "Enter DPP email") + DPP_PASSWORD=$(tui_read_prompt "Enter password") # Export DPP creds to a file for future use. Currently these are being used # for both: MinIO (and its mc CLI) and cloudsend (deprecated, all DPP @@ -113,7 +111,7 @@ get_dpp_creds() { echo ${DPP_EMAIL} >>${DPP_CREDENTIAL_FILE} echo ${DPP_PASSWORD} >>${DPP_CREDENTIAL_FILE} - print_ok "Dasharo DPP credentials have been saved" + tui_echo_green "Dasharo DPP credentials have been saved" } login_to_dpp_server() { @@ -143,10 +141,10 @@ subscription_routine() { if [ -e "${DPP_CREDENTIAL_FILE}" ]; then DPP_EMAIL=$(sed -n '1p' <${DPP_CREDENTIAL_FILE} | tr -d '\n') DPP_PASSWORD=$(sed -n '2p' <${DPP_CREDENTIAL_FILE} | tr -d '\n') - export DPP_IS_LOGGED="true" + set_global_state "DPP_IS_LOGGED" "true" else unset DPP_EMAIL - unset DPP_IS_LOGGED + set_global_state DPP_IS_LOGGED return 1 fi @@ -181,7 +179,7 @@ download_dpp_package() { # Make sure all paths exist: check_dasharo_package_env - echo "Downloading package $package_name..." + tui_echo_normal "Downloading package $package_name..." # Get package link: local download_link @@ -208,7 +206,7 @@ install_dpp_package() { check_dasharo_package_env - echo "Installing package $package_name..." + tui_echo_normal "Installing package $package_name..." update_package_list || return 1 @@ -231,7 +229,7 @@ install_dpp_package() { } install_all_dpp_packages() { - echo "Installing available DTS extensions..." + tui_echo_normal "Installing available DTS extensions..." update_package_list || return 1 @@ -240,7 +238,7 @@ install_all_dpp_packages() { packages_to_download=$(jq -r '.key' "$DPP_AVAIL_PACKAGES_LIST") if [ -z "$packages_to_download" ]; then - echo "No packages to install." + tui_echo_normal "No packages to install." return 1 fi @@ -256,7 +254,7 @@ install_all_dpp_packages() { } check_avail_dpp_packages() { - echo "Checking for available DTS extensions..." + tui_echo_normal "Checking for available DTS extensions..." AVAILABLE_PACKAGES=$(mc find --name "*.rpm" $DPP_SERVER_USER_ALIAS) if [ -z "$AVAILABLE_PACKAGES" ]; then diff --git a/scripts/dasharo-deploy.sh b/scripts/dasharo-deploy.sh index 3d0595ce..f612b3d7 100644 --- a/scripts/dasharo-deploy.sh +++ b/scripts/dasharo-deploy.sh @@ -56,12 +56,12 @@ print_firm_access_warning() { esac # Just a new line: - echo - echo " Dasharo Pro Package version (${_firm_type_print}) is also available." - echo " If you are interested, please visit" - echo " https://shop.3mdeb.com/product-category/dasharo-pro-package/" - # Just a new line: - echo + tui_echo_normal " + Dasharo Pro Package version (${_firm_type_print}) is also available. + If you are interested, please visit + https://shop.3mdeb.com/product-category/dasharo-pro-package/ + +" return 0 } diff --git a/scripts/dts-boot.sh b/scripts/dts-boot.sh index da24c4eb..c7648f51 100644 --- a/scripts/dts-boot.sh +++ b/scripts/dts-boot.sh @@ -4,14 +4,21 @@ # # SPDX-License-Identifier: Apache-2.0 -SBIN_DIR="/usr/sbin" +export SBIN_DIR="/usr/sbin" FUM_EFIVAR="/sys/firmware/efi/efivars/FirmwareUpdateMode-d15b327e-ff2d-4fc1-abf6-c12bd08c1359" +export CONF_DIR="/etc/dts" +export LIB_DIR="/usr/lib/dts" +export RUN_DIR="/var/run/dts" export DTS_FUNCS="$SBIN_DIR/dts-functions.sh" export DTS_ENV="$SBIN_DIR/dts-environment.sh" export DTS_SUBS="$SBIN_DIR/dts-subscription.sh" export DTS_HAL="$SBIN_DIR/dts-hal.sh" export DTS_MOCK_COMMON="$SBIN_DIR/common-mock-func.sh" +export DTS_TUI_LIB="$LIB_DIR/tui-lib.sh" +export DTS_TUI_CONF="$CONF_DIR/dts-tui.yaml" +export DTS_STATE="$RUN_DIR/state.sh" +export DTS_STATE_LOCKFILE="/var/lock/dts-state" export BASH_ENV="$SBIN_DIR/logging" export TMP_LOG_DIR="/tmp/logs" export ERR_LOG_FILE_REALPATH @@ -21,6 +28,7 @@ export ERR_LOG_FILE export SHELLOPTS mkdir -p "$TMP_LOG_DIR" +mkdir -p "$RUN_DIR" # $ERR_LOG_FILE is fd that can only be written to: '>()'. To copy logs # we need underlying file that can be copied ERR_LOG_FILE_REALPATH="/var/local/dts-err_$(basename "$(tty)").log" diff --git a/scripts/dts.sh b/scripts/dts.sh index 018b5dab..0bbc5b12 100644 --- a/scripts/dts.sh +++ b/scripts/dts.sh @@ -3,6 +3,7 @@ # SPDX-FileCopyrightText: 2024 3mdeb # # SPDX-License-Identifier: Apache-2.0 +# shellcheck disable=SC2034 # shellcheck source=../include/dts-environment.sh source $DTS_ENV @@ -11,47 +12,65 @@ source $DTS_FUNCS # shellcheck source=../include/dts-subscription.sh source $DTS_SUBS -trap : 2 -trap : 3 -trap wait_for_input EXIT +# those won't change +DTS_VERSION=$(grep "VERSION_ID" ${OS_VERSION_FILE} | cut -d "=" -f 2-) +RAM_INFO="$(show_ram_inf)" +SHOW_DASHARO_FIRMWARE="true" +if check_if_dasharo; then + SHOW_TRANSITION="true" + SHOW_FUSE="true" +else + SHOW_TRANSITION="false" + SHOW_FALSE="false" +fi -wait_for_input() { - code=$? - if [[ $code -ne 0 ]]; then - read -p "Press Enter to continue." +set_menu_vars() { + DPP_IS_LOGGED=$(get_global_state DPP_IS_LOGGED) + DISPLAY_CREDENTIALS=$(get_global_state DISPLAY_CREDENTIALS) + + if check_if_dasharo; then + DASHARO_FIRMWARE_LABEL="Update Dasharo Firmware" + else + DASHARO_FIRMWARE_LABEL="Install Dasharo Firmware" + fi + if [ "${SYSTEM_VENDOR}" != "QEMU" ] && [ "${SYSTEM_VENDOR}" != "Emulation" ]; then + SHOW_RESTORE_FIRMWARE="true" + else + SHOW_RESTORE_FIRMWARE="false" fi - exit $code -} -while :; do - clear - # Do some subscription routine each time menu is rendered: - subscription_routine - - # Header should always be printed: - show_header - if [ -z "$DPP_SUBMENU_ACTIVE" ]; then - show_hardsoft_inf - show_dpp_credentials - show_ssh_info - show_main_menu - elif [ -n "$DPP_SUBMENU_ACTIVE" ]; then - show_dpp_submenu + if [ "${DPP_IS_LOGGED}" = "true" ]; then + DPP_KEYS_LABEL="Edit your DPP keys" + else + DPP_KEYS_LABEL="Load your DPP keys" + fi + if systemctl is-active sshd &>/dev/null; then + SSH_STATUS="$(tui_echo_green ON)" + SSH_IP="$(show_ssh_info)" + SSH_LABEL="stop SSH server" + SSH_ACTIVE="true" + else + SSH_LABEL="launch SSH server" + SSH_ACTIVE="false" fi - show_footer - - echo - read -n 1 OPTION - echo - - # If OPTION is being matched with smth inside *_options functions the - # functions return 0 and loop start over, if not: next *_options function is - # being checked: - if [ -z "$DPP_SUBMENU_ACTIVE" ]; then - main_menu_options $OPTION && continue - elif [ -n "$DPP_SUBMENU_ACTIVE" ]; then - dpp_submenu_options $OPTION && continue + SEND_LOGS_ACTIVE=$(get_global_state SEND_LOGS_ACTIVE) + if [ "${SEND_LOGS_ACTIVE}" = "true" ]; then + SEND_LOGS_LABEL="disable sending DTS logs" + else + SEND_LOGS_LABEL="enable sending DTS logs" fi - footer_options $OPTION -done + if [ "${DISPLAY_CREDENTIALS}" = "true" ]; then + DPP_EMAIL_DISPLAY=$(mc alias ls premium --json | jq -r '.accessKey') + DPP_PASSWORD_DISPLAY=$(mc alias ls premium --json | jq -r '.secretKey') + DISPLAY_CRED_LABEL="hide DPP credentials" + else + DPP_EMAIL_DISPLAY="***************" + DPP_PASSWORD_DISPLAY="***************" + DISPLAY_CRED_LABEL="display DPP credentials" + fi +} + +tui_register_refresh_callback subscription_routine +tui_register_refresh_callback set_menu_vars +tui_run "$DTS_TUI_CONF" diff --git a/tui/dts-tui.yaml b/tui/dts-tui.yaml new file mode 100644 index 00000000..6b0f04c6 --- /dev/null +++ b/tui/dts-tui.yaml @@ -0,0 +1,96 @@ +--- +header: + title: " Dasharo Tools Suite Script ${DTS_VERSION} " + subtitle: " (c) Dasharo " + link: "https://github.com/Dasharo/dasharo-issues" + +sections: + - label: "HARDWARE INFORMATION" + entries: + - label: "System Inf." + value: "${SYSTEM_VENDOR} ${SYSTEM_MODEL}" + - label: "Baseboard Inf." + value: "${SYSTEM_VENDOR} ${BOARD_MODEL}" + - label: "CPU Inf." + value: "${CPU_VERSION}" + - label: "RAM Virtual" + value: "${RAM_INFO:-Not Specified}" + + - label: "FIRMWARE INFORMATION" + entries: + - label: "BIOS Inf." + value: "${BIOS_VENDOR} ${BIOS_VERSION}" + + - label: "DPP credentials" + condition: "${DPP_IS_LOGGED}" + entries: + - label: "Email" + value: "${DPP_EMAIL_DISPLAY}" + - label: "Password" + value: "${DPP_PASSWORD_DISPLAY}" + + - label: "SSH status: ${SSH_STATUS}" + condition: "${SSH_ACTIVE}" + entries: + - label: "IP" + value: "${SSH_IP}" + +menu: + - key: "1" + label: "Dasharo HCL report" + callback: "${SBIN_DIR}/tui_callbacks/hcl-report.sh" + + - key: "2" + label: "${DASHARO_FIRMWARE_LABEL}" + callback: "${SBIN_DIR}/tui_callbacks/dasharo-firmware.sh" + condition: "${SHOW_DASHARO_FIRMWARE}" + + - key: "3" + label: "Restore firmware from Dasharo HCL report" + callback: "${SBIN_DIR}/tui_callbacks/restore-firmware.sh" + condition: "${SHOW_RESTORE_FIRMWARE}" + + - key: "4" + label: "${DPP_KEYS_LABEL}" + callback: "${SBIN_DIR}/tui_callbacks/dpp-keys.sh" + + - key: "5" + label: "DTS extensions" + callback: "${SBIN_DIR}/tui_callbacks/dts-extensions.sh" + condition: "${SHOW_DTS_EXTENSIONS}" + + - key: "6" + label: "Transition Dasharo Firmware" + callback: "${SBIN_DIR}/tui_callbacks/transition.sh" + condition: "${SHOW_TRANSITION}" + + - key: "7" + label: "Fuse platform" + callback: "${SBIN_DIR}/tui_callbacks/fuse.sh" + condition: "${SHOW_FUSE}" + +footer: + - key: "R" + label: "reboot" + callback: "${SBIN_DIR}/tui_callbacks/reboot.sh" + + - key: "P" + label: "poweroff" + callback: "${SBIN_DIR}/tui_callbacks/poweroff.sh" + + - key: "S" + label: "enter shell" + callback: "${SBIN_DIR}/tui_callbacks/shell.sh" + + - key: "K" + label: "${SSH_LABEL}" + callback: "${SBIN_DIR}/tui_callbacks/ssh-toggle.sh" + + - key: "L" + label: "${SEND_LOGS_LABEL}" + callback: "${SBIN_DIR}/tui_callbacks/toggle-logs.sh" + + - key: "C" + label: "${DISPLAY_CRED_LABEL}" + callback: "${SBIN_DIR}/tui_callbacks/toggle-credentials.sh" + condition: "${DPP_IS_LOGGED}" diff --git a/tui/tui-lib.sh b/tui/tui-lib.sh new file mode 100644 index 00000000..b9f7aa22 --- /dev/null +++ b/tui/tui-lib.sh @@ -0,0 +1,648 @@ +#!/bin/bash +# TUI Library for creating text-based user interfaces +# Requires: yq (YAML processor) + +# ANSI color codes (matching DTS color scheme) +TUI_NORMAL='\033[0m' +TUI_RED='\033[0;31m' +TUI_GREEN='\033[0;32m' +TUI_YELLOW='\033[0;33m' +TUI_BLUE='\033[0;36m' # Cyan, used for borders (matches DTS BLUE) + +# Terminal width configuration +TUI_MAX_WIDTH=60 # Maximum width for borders and footer wrapping + +# Global variables +TUI_RUNNING=true + +# Header variables +TUI_HEADER_TITLE="" +TUI_HEADER_SUBTITLE="" +TUI_HEADER_LINK="" + +# Section arrays (using | as delimiter) +declare -a TUI_SECTIONS_DATA=() # condition|label +declare -a TUI_ENTRIES_DATA=() # section_idx|condition|label|value + +# Menu and footer arrays +declare -a TUI_MENU_DATA=() # key|condition|label|callback +declare -a TUI_FOOTER_DATA=() # key|condition|label|callback + +declare -A TUI_REFRESH_CALLBACKS=() +# used to call callbacks in the same order as they were registered +TUI_REFRESH_CALLBACKS_ORDER=() + +# Terminal control +tui_clear_screen() { + printf '\033[2J\033[H' +} + +tui_hide_cursor() { + printf '\033[?25l' +} + +tui_show_cursor() { + printf '\033[?25h' +} + +tui_clear_line() { + printf '\r\033[K' +} + +# Trap to ensure cursor is shown on exit +trap 'tui_show_cursor' EXIT INT TERM + +# ============================================================================ +# Utility Functions +# ============================================================================ + +# Calculate visible text length (stripping ANSI codes) +# Usage: tui_visible_length "text with ANSI codes" +tui_visible_length() { + local text="$1" + # Remove ANSI escape sequences and count characters + echo -n "$text" | sed 's/\x1b\[[0-9;]*m//g' | wc -c +} + +# Generate a border string of specified length with character +# Usage: tui_generate_border 80 "*" +tui_generate_border() { + local length="$1" + local char="${2:-*}" + printf "%${length}s" | tr ' ' "$char" +} + +# ============================================================================ +# Color Echo Functions (DTS-compatible) +# ============================================================================ + +tui_echo_normal() { + echo -e "${TUI_NORMAL}$1${TUI_NORMAL}" +} + +tui_echo_red() { + echo -e "${TUI_RED}$1${TUI_NORMAL}" +} + +tui_echo_yellow() { + echo -e "${TUI_YELLOW}$1${TUI_NORMAL}" +} + +tui_echo_green() { + echo -e "${TUI_GREEN}$1${TUI_NORMAL}" +} + +tui_echo_blue() { + echo -e "${TUI_BLUE}$1${TUI_NORMAL}" +} + +# ============================================================================ +# Status Message Functions +# ============================================================================ + +tui_print_warning() { + tui_echo_yellow "Warning: $1" +} + +tui_print_error() { + tui_echo_red "Error: $1" +} + +tui_print_success() { + tui_echo_green "$1" +} + +# ============================================================================ +# Border and Layout Functions +# ============================================================================ + +# Print a full-width border line +# Usage: tui_print_border +tui_print_border() { + local border + border=$(tui_generate_border "$TUI_MAX_WIDTH" "*") + echo -e "${TUI_BLUE}${border}${TUI_NORMAL}" +} + +# Print the "**" prefix used in sections and menu items +# Usage: tui_print_border_prefix +tui_print_border_prefix() { + echo -n -e "${TUI_BLUE}**${TUI_NORMAL}" +} + +# ============================================================================ +# Section Rendering Functions +# ============================================================================ + +# Print a section header with borders +# Usage: tui_print_section_header "SECTION LABEL" +tui_print_section_header() { + local label="$1" + tui_print_border + echo -e "${TUI_BLUE}**${TUI_NORMAL} $label ${TUI_NORMAL}" + tui_print_border +} + +# Print a section entry (label: value) +# Usage: tui_print_section_entry "Label" "Value" +tui_print_section_entry() { + local label="$1" + local value="$2" + printf "${TUI_BLUE}**${TUI_YELLOW}%15s: ${TUI_NORMAL}%s\n" "$label" "$value" +} + +# ============================================================================ +# Menu Rendering Functions +# ============================================================================ + +# Print a menu option +# Usage: tui_print_menu_option "1" "Menu label" +tui_print_menu_option() { + local key="$1" + local label="$2" + printf "${TUI_BLUE}**${TUI_YELLOW} %s)${TUI_BLUE} %s${TUI_NORMAL}\n" "$key" "$label" +} + +# ============================================================================ +# Footer Rendering Functions +# ============================================================================ + +# Print a footer action (used internally by tui_render_footer) +# Usage: tui_print_footer_action "K" "label" +tui_print_footer_action() { + local key="$1" + local label="$2" + echo -n -e "${TUI_RED}$key${TUI_NORMAL} to $label" +} + +# ============================================================================ +# Core Functions +# ============================================================================ + +# Expand environment variables in a string +# Usage: tui_expand_vars "string with $VAR" +tui_expand_vars() { + local string="$1" + # Use eval to expand variables, but safely quote the result + eval "printf '%s' \"$string\"" +} + +# Check if a condition evaluates to true +# Conditions can be environment variable checks like: ${VAR} or ${VAR:-default} +# Usage: tui_check_condition "condition" +tui_check_condition() { + local condition="$1" + [[ -z "$condition" ]] && return 0 # No condition means always show + + # Expand and evaluate the condition + local result + result=$(eval echo "$condition" 2>/dev/null || echo "") + + # Check if result is non-empty and not "false" or "0" + [[ -n "$result" && "$result" != "false" && "$result" != "0" ]] +} + +# Load YAML configuration and parse into bash variables +# Usage: tui_load_config "config.yaml" +tui_load_config() { + local config_file="$1" + + if [[ ! -f "$config_file" ]]; then + echo "Error: Config file not found: $config_file" >&2 + return 1 + fi + + if ! command -v yq &>/dev/null; then + echo "Error: yq is required but not installed" >&2 + echo "Install with: pip install yq or brew install yq" >&2 + return 1 + fi + + if ! command -v jq &>/dev/null; then + echo "Error: jq is required but not installed" >&2 + return 1 + fi + + # Convert YAML to JSON once + local json_config + json_config=$(yq eval -o=json "$config_file") + + # Parse header (using @ to safely handle special characters) + TUI_HEADER_TITLE=$(echo "$json_config" | jq -r '.header.title // ""') + TUI_HEADER_SUBTITLE=$(echo "$json_config" | jq -r '.header.subtitle // ""') + TUI_HEADER_LINK=$(echo "$json_config" | jq -r '.header.link // ""') + + # Clear arrays + TUI_SECTIONS_DATA=() + TUI_ENTRIES_DATA=() + TUI_MENU_DATA=() + TUI_FOOTER_DATA=() + + # Parse sections + local section_idx=0 + while IFS='|' read -r condition label; do + if [[ -z "$label" && -z "$condition" ]]; then + continue + fi + TUI_SECTIONS_DATA+=("$condition|$label") + + # Parse entries for this section + while IFS='|' read -r entry_cond entry_label entry_value; do + if [[ -z "$entry_label" && -z "$entry_value" ]]; then + continue + fi + TUI_ENTRIES_DATA+=("$section_idx|$entry_cond|$entry_label|$entry_value") + done < <(echo "$json_config" | jq -r ".sections[$section_idx].entries[]? | \"\(.condition // \"\")|\" + (.label | gsub(\"\\\\|\"; \"\\\\|\" )) + \"|\" + (.value | gsub(\"\\\\|\"; \"\\\\|\"))") + + ((section_idx++)) + done < <(echo "$json_config" | jq -r '.sections[]? | "\(.condition // "")|" + (.label | gsub("\\|"; "\\|"))') + + # Parse menu items + while IFS='|' read -r key condition label callback; do + if [[ -z "$key" ]]; then + continue + fi + TUI_MENU_DATA+=("$key|$condition|$label|$callback") + done < <(echo "$json_config" | jq -r '.menu[]? | .key + "|" + (.condition // "") + "|" + (.label | gsub("\\|"; "\\|")) + "|" + .callback') + + # Parse footer items + while IFS='|' read -r key condition label callback; do + if [[ -z "$key" ]]; then + continue + fi + TUI_FOOTER_DATA+=("$key|$condition|$label|$callback") + done < <(echo "$json_config" | jq -r '.footer[]? | .key + "|" + (.condition // "") + "|" + (.label | gsub("\\|"; "\\|")) + "|" + .callback') +} + +# Render header section +tui_render_header() { + if [[ -n "$TUI_HEADER_TITLE" ]]; then + local title + title=$(tui_expand_vars "$TUI_HEADER_TITLE") + echo -e "${TUI_NORMAL}$title${TUI_NORMAL}" + fi + + if [[ -n "$TUI_HEADER_SUBTITLE" ]]; then + local subtitle + subtitle=$(tui_expand_vars "$TUI_HEADER_SUBTITLE") + echo -e "${TUI_NORMAL}$subtitle${TUI_NORMAL}" + fi + + if [[ -n "$TUI_HEADER_LINK" ]]; then + local link + link=$(tui_expand_vars "$TUI_HEADER_LINK") + echo -e "${TUI_NORMAL}Report issues at: $link${TUI_NORMAL}" + fi +} + +# Render all information sections +tui_render_info_sections() { + local section_idx=0 + local section_data + for section_data in "${TUI_SECTIONS_DATA[@]}"; do + local condition label + IFS='|' read -r condition label <<<"$section_data" + + # Check if section should be displayed + if ! tui_check_condition "$condition"; then + ((section_idx++)) + continue + fi + + label=$(tui_expand_vars "$label") + tui_print_section_header "$label" + + # Render entries for this section + local entry_data + for entry_data in "${TUI_ENTRIES_DATA[@]}"; do + local entry_section_idx entry_condition entry_label entry_value + IFS='|' read -r entry_section_idx entry_condition entry_label entry_value <<<"$entry_data" + + # Only render entries for this section + if [[ "$entry_section_idx" != "$section_idx" ]]; then + continue + fi + + if ! tui_check_condition "$entry_condition"; then + continue + fi + + entry_label=$(tui_expand_vars "$entry_label") + entry_value=$(tui_expand_vars "$entry_value") + tui_print_section_entry "$entry_label" "$entry_value" + done + + ((section_idx++)) + done +} + +# Render main menu options +tui_render_menu() { + if [[ ${#TUI_MENU_DATA[@]} -eq 0 ]]; then + return 0 + fi + + tui_print_border + local menu_item + for menu_item in "${TUI_MENU_DATA[@]}"; do + local key condition label callback + IFS='|' read -r key condition label callback <<<"$menu_item" + + if ! tui_check_condition "$condition"; then + continue + fi + + label=$(tui_expand_vars "$label") + tui_print_menu_option "$key" "$label" + done + tui_print_border +} + +# Render footer actions with auto-wrap +tui_render_footer() { + if [[ ${#TUI_FOOTER_DATA[@]} -eq 0 ]]; then + return 0 + fi + + local footer_parts=() + local footer_item + + # Build footer parts + for footer_item in "${TUI_FOOTER_DATA[@]}"; do + local key condition label callback + IFS='|' read -r key condition label callback <<<"$footer_item" + + if ! tui_check_condition "$condition"; then + continue + fi + + label=$(tui_expand_vars "$label") + footer_parts+=("${TUI_RED}$key${TUI_NORMAL} to $label") + done + + if [[ ${#footer_parts[@]} -gt 0 ]]; then + # Auto-wrap footer to multiple lines if needed + local current_line="" + local current_length=0 + local max_width=$((TUI_MAX_WIDTH - 2)) # Leave 2 chars margin + + for part in "${footer_parts[@]}"; do + # Calculate visible length (strip ANSI codes) + local visible_part + visible_part=$(echo -e "$part" | sed 's/\x1b\[[0-9;]*m//g') + local part_length=${#visible_part} + + # Add separator length if not first item on line + local separator_length=0 + if [[ -n "$current_line" ]]; then + separator_length=2 # " " = 2 spaces + fi + + # Check if adding this part would exceed max width + if [[ -n "$current_line" ]] && ((current_length + separator_length + part_length > max_width)); then + # Print current line and start new line + echo -e "$current_line" + current_line="$part" + current_length=$part_length + else + # Add to current line + if [[ -n "$current_line" ]]; then + current_line+=" $part" + current_length=$((current_length + separator_length + part_length)) + else + current_line="$part" + current_length=$part_length + fi + fi + done + + # Print remaining line + if [[ -n "$current_line" ]]; then + echo -e "$current_line" + fi + fi +} + +# Render complete menu +tui_render() { + tui_clear_screen + tui_render_header + tui_render_info_sections + tui_render_menu + tui_render_footer + echo "" + echo -n -e "${TUI_YELLOW}Enter an option:${TUI_NORMAL}" +} + +# Read a single keypress without waiting for Enter +# Returns the pressed key +tui_read_key() { + local key + read -n 1 -s key + echo "$key" +} + +# Print prompt and read user input +tui_read_prompt() { + local prompt="$1" + local answer + echo -n "${prompt}: " >&2 + read -r answer + echo "${answer}" +} + +# Execute a callback script +# Usage: tui_execute_callback "script_path" +tui_execute_callback() { + local script="$1" + + if [[ ! -f "$script" ]]; then + echo "Error: Callback script not found: $script" >&2 + return 1 + fi + + if [[ ! -x "$script" ]]; then + echo "Error: Callback script is not executable: $script" >&2 + return 1 + fi + + # Clear screen and execute callback + tui_clear_screen + + # Execute the callback script + "$script" + local exit_code=$? + + # Wait for user to press a key before returning to menu + echo "" + echo -n "Press any key to continue..." + tui_read_key + + return $exit_code +} + +# Find menu item by key and return its callback +# Usage: tui_find_menu_callback "key" +tui_find_menu_callback() { + local key="$1" + + for menu_item in "${TUI_MENU_DATA[@]}"; do + IFS='|' read -r menu_key condition label callback <<<"$menu_item" + + if [[ "$menu_key" == "$key" ]]; then + if tui_check_condition "$condition"; then + # Expand environment variables in callback path + callback=$(tui_expand_vars "$callback") + echo "$callback" + return 0 + fi + fi + done + + return 1 +} + +# Find footer item by key and return its callback +# Usage: tui_find_footer_callback "key" +tui_find_footer_callback() { + local key="$1" + + for footer_item in "${TUI_FOOTER_DATA[@]}"; do + IFS='|' read -r footer_key condition label callback <<<"$footer_item" + + if [[ "$footer_key" == "$key" ]]; then + if tui_check_condition "$condition"; then + # Expand environment variables in callback path + callback=$(tui_expand_vars "$callback") + echo "$callback" + return 0 + fi + fi + done + + return 1 +} + +# Handle user input +# Returns: 0 to continue, 1 to exit +tui_handle_input() { + local key + key=$(tui_read_key) + + # Convert to uppercase for case-insensitive matching + key=$(echo "$key" | tr '[:lower:]' '[:upper:]') + + # Hidden option: Q to quit (useful for testing) + if [[ "$key" == "Q" ]]; then + tui_stop + return 0 + fi + + # Try to find callback in menu items + local callback + if callback=$(tui_find_menu_callback "$key"); then + tui_execute_callback "$callback" + return 0 + fi + + # Try to find callback in footer items + if callback=$(tui_find_footer_callback "$key"); then + tui_execute_callback "$callback" + return 0 + fi + + # No matching option found + return 0 +} + +# Main loop +# Usage: tui_run "config.yaml" +tui_run() { + local config_file="$1" + + if ! tui_load_config "$config_file"; then + return 1 + fi + + tui_hide_cursor + TUI_RUNNING=true + + while $TUI_RUNNING; do + for callback in "${TUI_REFRESH_CALLBACKS_ORDER[@]}"; do + eval "${callback}" "${TUI_REFRESH_CALLBACKS["${callback}"]}" + done + tui_render + tui_handle_input + done + + tui_show_cursor + tui_clear_screen +} + +# Stop the TUI loop +tui_stop() { + TUI_RUNNING=false +} + +# register callbacks called during each UI refresh (before showing UI) e.g. +# tui_register_refresh_callback my_callback_func callback_arg_1 callback_arg_2 +tui_register_refresh_callback() { + local callback="$1" + shift + TUI_REFRESH_CALLBACKS["${callback}"]="$*" + TUI_REFRESH_CALLBACKS_ORDER+=("${callback}") +} + +# Export functions for use in other scripts + +# Terminal control +export -f tui_clear_screen +export -f tui_hide_cursor +export -f tui_show_cursor + +# Utility functions +export -f tui_visible_length +export -f tui_generate_border + +# Color echo functions +export -f tui_echo_normal +export -f tui_echo_red +export -f tui_echo_yellow +export -f tui_echo_green +export -f tui_echo_blue + +# Status message functions +export -f tui_print_warning +export -f tui_print_error +export -f tui_print_success + +# Border and layout functions +export -f tui_print_border +export -f tui_print_border_prefix + +# Section rendering functions +export -f tui_print_section_header +export -f tui_print_section_entry + +# Menu rendering functions +export -f tui_print_menu_option + +# Footer rendering functions +export -f tui_print_footer_action + +# Core functions +export -f tui_expand_vars +export -f tui_check_condition +export -f tui_load_config +export -f tui_render_header +export -f tui_render_info_sections +export -f tui_render_menu +export -f tui_render_footer +export -f tui_render +export -f tui_read_key +export -f tui_execute_callback +export -f tui_find_menu_callback +export -f tui_find_footer_callback +export -f tui_handle_input +export -f tui_run +export -f tui_stop diff --git a/tui/tui_callbacks/dasharo-firmware.sh b/tui/tui_callbacks/dasharo-firmware.sh new file mode 100644 index 00000000..d93004f5 --- /dev/null +++ b/tui/tui_callbacks/dasharo-firmware.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +if ! check_if_dasharo; then + # flashrom does not support QEMU, but installation depends on flashrom. + # TODO: this could be handled in a better way: + [ "${SYSTEM_VENDOR}" = "QEMU" ] || [ "${SYSTEM_VENDOR}" = "Emulation" ] && exit 0 + + if wait_for_network_connection; then + tui_echo_normal "Preparing ..." + if [ -z "${LOGS_SENT}" ]; then + export SEND_LOGS="true" + export DEPLOY_REPORT="true" + if ! ${CMD_DASHARO_HCL_REPORT}; then + tui_echo_normal "Unable to connect to dl.dasharo.com for submitting the + \rHCL report. Please recheck your internet connection." + else + LOGS_SENT="1" + fi + fi + fi + + if [ -n "${LOGS_SENT}" ]; then + ${CMD_DASHARO_DEPLOY} install + result=$? + if [ "$result" -ne $OK ] && [ "$result" -ne $CANCEL ]; then + send_dts_logs ask && exit 0 + fi + fi +else + # TODO: This should be placed in dasharo-deploy: + # For NovaCustom TGL laptops with Dasharo version lower than 1.3.0, + # we shall run the ec_transition script instead. See: + # https://docs.dasharo.com/variants/novacustom_nv4x_tgl/releases/#v130-2022-10-18 + if [ "$SYSTEM_VENDOR" = "Notebook" ]; then + case "$SYSTEM_MODEL" in + "NS50_70MU" | "NV4XMB,ME,MZ") + compare_versions $DASHARO_VERSION 1.3.0 + if [ $? -eq 1 ]; then + # For Dasharo version lesser than 1.3.0 + print_warning "Detected NovaCustom hardware with version < 1.3.0" + print_warning "Need to perform EC transition after which the platform will turn off" + print_warning "Then, please power it on and proceed with update again" + print_warning "EC transition procedure will start in 5 seconds" + sleep 5 + ${CMD_EC_TRANSITION} + error_check "Could not perform EC transition" + fi + # Continue with regular update process for Dasharo version + # greater or equal 1.3.0 + ;; + esac + fi + + # Use regular update process for everything else + ${CMD_DASHARO_DEPLOY} update + result=$? + if [ "$result" -ne $OK ] && [ "$result" -ne $CANCEL ]; then + send_dts_logs ask && exit 0 + fi +fi diff --git a/tui/tui_callbacks/dpp-keys.sh b/tui/tui_callbacks/dpp-keys.sh new file mode 100644 index 00000000..5cc330eb --- /dev/null +++ b/tui/tui_callbacks/dpp-keys.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" +# shellcheck source=../../include/dts-subscription.sh +source $DTS_SUBS + +_result= +# Return if there was an issue when asking for credentials: +if ! get_dpp_creds; then + exit 0 +fi + +# Try to log in using available DPP credentials, start loop over if login +# was not successful: +if ! login_to_dpp_server; then + tui_echo_normal "Cannot log in to DPP server." + exit 0 +fi + +# Check for Dasharo Firmware for the current platform, continue to +# packages after checking: +check_for_dasharo_firmware +_result=$? +tui_echo_normal "Your credentials give access to:" +echo -n "Dasharo Pro Package (DPP): " +if [ $_result -eq 0 ]; then + # FIXME: what if credentials have access to + # firmware, but check_for_dasharo_firmware will not detect any platform? + # According to check_for_dasharo_firmware it will return 1 in both + # cases which means that we cannot detect such case. + tui_echo_normal "Dasharo Pro Package (DPP): $(tui_echo_green YES)" +else + tui_echo_normal "Dasharo Pro Package (DPP): NO" +fi + +echo -n "DTS Extensions: " + +if check_dts_extensions_access; then + tui_echo_normal "DTS Extensions: $(tui_echo_green YES)" + check_avail_dpp_packages && install_all_dpp_packages && parse_for_premium_submenu +else + tui_echo_normal "DTS Extensions: NO" +fi diff --git a/tui/tui_callbacks/fuse.sh b/tui/tui_callbacks/fuse.sh new file mode 100644 index 00000000..8429f04e --- /dev/null +++ b/tui/tui_callbacks/fuse.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +check_if_dasharo || exit 0 + +${CMD_DASHARO_DEPLOY} fuse +result=$? +if [ "$result" -ne $OK ] && [ "$result" -ne $CANCEL ]; then + send_dts_logs ask && exit $OK +fi diff --git a/tui/tui_callbacks/hcl-report.sh b/tui/tui_callbacks/hcl-report.sh new file mode 100644 index 00000000..fa2c447d --- /dev/null +++ b/tui/tui_callbacks/hcl-report.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +print_disclaimer +if ask_for_confirmation "Do you want to support Dasharo development by sending us logs with your hardware configuration?"; then + export SEND_LOGS="true" + tui_echo_normal "Thank you for contributing to the Dasharo development!" +else + export SEND_LOGS="false" + tui_echo_normal "Logs will be saved in root directory." + tui_echo_normal "Please consider supporting Dasharo by sending the logs next time." +fi +if [ "${SEND_LOGS}" == "true" ]; then + # DEPLOY_REPORT variable is used in dasharo-hcl-report to determine + # which logs should be printed in the terminal, in the future whole + # dts scripting should get some LOGLEVEL and maybe dumping working + # logs to file + export DEPLOY_REPORT="false" + wait_for_network_connection && ${CMD_DASHARO_HCL_REPORT} +else + export DEPLOY_REPORT="false" + ${CMD_DASHARO_HCL_REPORT} +fi diff --git a/tui/tui_callbacks/poweroff.sh b/tui/tui_callbacks/poweroff.sh new file mode 100644 index 00000000..bf143029 --- /dev/null +++ b/tui/tui_callbacks/poweroff.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" + +$POWEROFF diff --git a/tui/tui_callbacks/reboot.sh b/tui/tui_callbacks/reboot.sh new file mode 100644 index 00000000..79b0b14a --- /dev/null +++ b/tui/tui_callbacks/reboot.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" + +$REBOOT diff --git a/tui/tui_callbacks/restore-firmware.sh b/tui/tui_callbacks/restore-firmware.sh new file mode 100644 index 00000000..d6cc00d5 --- /dev/null +++ b/tui/tui_callbacks/restore-firmware.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +[ "${SYSTEM_VENDOR}" = "QEMU" ] || [ "${SYSTEM_VENDOR}" = "Emulation" ] && exit 0 + +if check_if_dasharo; then + if ! ${CMD_DASHARO_DEPLOY} restore; then + send_dts_logs ask && exit 0 + fi +fi diff --git a/tui/tui_callbacks/shell.sh b/tui/tui_callbacks/shell.sh new file mode 100644 index 00000000..bf0d50ba --- /dev/null +++ b/tui/tui_callbacks/shell.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +tui_echo_normal "Entering shell, to leave type exit and press Enter or press LCtrl+D" +tui_echo_normal "" +send_dts_logs +stop_logging +${CMD_SHELL} +start_logging diff --git a/tui/tui_callbacks/ssh-toggle.sh b/tui/tui_callbacks/ssh-toggle.sh new file mode 100644 index 00000000..b1abcd47 --- /dev/null +++ b/tui/tui_callbacks/ssh-toggle.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# shellcheck disable=SC1090 +source "$DTS_TUI_LIB" + +if systemctl is-active sshd &>/dev/null; then + tui_echo_green "Turning off the SSH server..." + systemctl stop sshd +else + tui_echo_yellow "Starting SSH server!" + tui_echo_yellow "Now you can log in into the system using root account." + tui_echo_yellow "Stopping server will not drop all connected sessions." + systemctl start sshd + tui_echo_green "Listening on IPs: $(ip -br -f inet a show scope global | grep UP | awk '{ print $3 }' | tr '\n' ' ')" +fi diff --git a/tui/tui_callbacks/toggle-credentials.sh b/tui/tui_callbacks/toggle-credentials.sh new file mode 100644 index 00000000..1e8223dc --- /dev/null +++ b/tui/tui_callbacks/toggle-credentials.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +DISPLAY_CREDENTIALS=$(get_global_state DISPLAY_CREDENTIALS) +if [ "${DISPLAY_CREDENTIALS}" = "true" ]; then + set_global_state DISPLAY_CREDENTIALS "false" +else + set_global_state DISPLAY_CREDENTIALS "true" +fi diff --git a/tui/tui_callbacks/toggle-logs.sh b/tui/tui_callbacks/toggle-logs.sh new file mode 100644 index 00000000..d7cd2520 --- /dev/null +++ b/tui/tui_callbacks/toggle-logs.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +SEND_LOGS_ACTIVE=$(get_global_state SEND_LOGS_ACTIVE) +if [ "${SEND_LOGS_ACTIVE}" = "true" ]; then + set_global_state SEND_LOGS_ACTIVE "false" +else + set_global_state SEND_LOGS_ACTIVE "true" +fi diff --git a/tui/tui_callbacks/transition.sh b/tui/tui_callbacks/transition.sh new file mode 100644 index 00000000..013394c8 --- /dev/null +++ b/tui/tui_callbacks/transition.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" +# shellcheck source=../../include/dts-functions.sh +source "$DTS_FUNCS" + +# No transition, if there is no Dasharo firmware installed: +check_if_dasharo || exit 0 + +${CMD_DASHARO_DEPLOY} transition +result=$? +if [ "$result" -ne $OK ] && [ "$result" -ne $CANCEL ]; then + send_dts_logs ask && exit $OK +fi From 304ab046988ef72fb6f1211871a5d4292865b4ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Iwanicki?= Date: Mon, 27 Oct 2025 09:34:20 +0100 Subject: [PATCH 2/5] dts & tui: disable trace logging when rendering UI to fix slowdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Iwanicki --- scripts/dts.sh | 6 ++++-- tui/tui-lib.sh | 27 ++++++++++++++++++++------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/dts.sh b/scripts/dts.sh index 0bbc5b12..eadb460e 100644 --- a/scripts/dts.sh +++ b/scripts/dts.sh @@ -71,6 +71,8 @@ set_menu_vars() { fi } -tui_register_refresh_callback subscription_routine -tui_register_refresh_callback set_menu_vars +tui_register_pre_render_callback subscription_routine +tui_register_pre_render_callback set_menu_vars +tui_register_pre_render_callback stop_trace_logging +tui_register_post_render_callback start_trace_logging tui_run "$DTS_TUI_CONF" diff --git a/tui/tui-lib.sh b/tui/tui-lib.sh index b9f7aa22..3f379f94 100644 --- a/tui/tui-lib.sh +++ b/tui/tui-lib.sh @@ -28,9 +28,11 @@ declare -a TUI_ENTRIES_DATA=() # section_idx|condition|label|value declare -a TUI_MENU_DATA=() # key|condition|label|callback declare -a TUI_FOOTER_DATA=() # key|condition|label|callback -declare -A TUI_REFRESH_CALLBACKS=() +declare -A TUI_PRE_RENDER_CALLBACKS=() +declare -A TUI_POST_RENDER_CALLBACKS=() # used to call callbacks in the same order as they were registered -TUI_REFRESH_CALLBACKS_ORDER=() +TUI_PRE_RENDER_CALLBACKS_ORDER=() +TUI_POST_RENDER_CALLBACKS_ORDER=() # Terminal control tui_clear_screen() { @@ -568,10 +570,13 @@ tui_run() { TUI_RUNNING=true while $TUI_RUNNING; do - for callback in "${TUI_REFRESH_CALLBACKS_ORDER[@]}"; do - eval "${callback}" "${TUI_REFRESH_CALLBACKS["${callback}"]}" + for callback in "${TUI_PRE_RENDER_CALLBACKS_ORDER[@]}"; do + eval "${callback}" "${TUI_PRE_RENDER_CALLBACKS["${callback}"]}" done tui_render + for callback in "${TUI_POST_RENDER_CALLBACKS_ORDER[@]}"; do + eval "${callback}" "${TUI_POST_RENDER_CALLBACKS["${callback}"]}" + done tui_handle_input done @@ -586,11 +591,19 @@ tui_stop() { # register callbacks called during each UI refresh (before showing UI) e.g. # tui_register_refresh_callback my_callback_func callback_arg_1 callback_arg_2 -tui_register_refresh_callback() { +tui_register_pre_render_callback() { + local callback="$1" + shift + TUI_PRE_RENDER_CALLBACKS["${callback}"]="$*" + TUI_PRE_RENDER_CALLBACKS_ORDER+=("${callback}") +} + +# register callbacks called during each UI refresh (after showing UI) +tui_register_post_render_callback() { local callback="$1" shift - TUI_REFRESH_CALLBACKS["${callback}"]="$*" - TUI_REFRESH_CALLBACKS_ORDER+=("${callback}") + TUI_POST_RENDER_CALLBACKS["${callback}"]="$*" + TUI_POST_RENDER_CALLBACKS_ORDER+=("${callback}") } # Export functions for use in other scripts From d25d2d2632fada8c9caa655ef68acd22fc6b046b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Iwanicki?= Date: Mon, 27 Oct 2025 10:08:19 +0100 Subject: [PATCH 3/5] tui: use tui-sh from 3mdeb/tui-sh & wait for key in ssh toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Iwanicki --- Makefile | 4 - scripts/dts-boot.sh | 3 +- tui/tui-lib.sh | 661 -------------------------------- tui/tui_callbacks/ssh-toggle.sh | 2 + 4 files changed, 3 insertions(+), 667 deletions(-) delete mode 100644 tui/tui-lib.sh diff --git a/Makefile b/Makefile index fb6f7091..39e14236 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,6 @@ SBINDIR ?= /usr/sbin SYSCONFDIR ?= /etc -LIBDIR ?= /usr/lib install: install -d $(DESTDIR)$(SBINDIR)/tui_callbacks @@ -29,6 +28,3 @@ install: install -d $(DESTDIR)$(SYSCONFDIR)/profile.d $(DESTDIR)$(SYSCONFDIR)/dts install -m 0755 dts-profile.sh $(DESTDIR)$(SYSCONFDIR)/profile.d install -m 0644 tui/dts-tui.yaml $(DESTDIR)$(SYSCONFDIR)/dts/dts-tui.yaml - - install -d $(DESTDIR)$(LIBDIR)/dts - install -m 0644 tui/tui-lib.sh $(DESTDIR)$(LIBDIR)/dts/tui-lib.sh diff --git a/scripts/dts-boot.sh b/scripts/dts-boot.sh index c7648f51..9d2d417c 100644 --- a/scripts/dts-boot.sh +++ b/scripts/dts-boot.sh @@ -8,14 +8,13 @@ export SBIN_DIR="/usr/sbin" FUM_EFIVAR="/sys/firmware/efi/efivars/FirmwareUpdateMode-d15b327e-ff2d-4fc1-abf6-c12bd08c1359" export CONF_DIR="/etc/dts" -export LIB_DIR="/usr/lib/dts" export RUN_DIR="/var/run/dts" export DTS_FUNCS="$SBIN_DIR/dts-functions.sh" export DTS_ENV="$SBIN_DIR/dts-environment.sh" export DTS_SUBS="$SBIN_DIR/dts-subscription.sh" export DTS_HAL="$SBIN_DIR/dts-hal.sh" export DTS_MOCK_COMMON="$SBIN_DIR/common-mock-func.sh" -export DTS_TUI_LIB="$LIB_DIR/tui-lib.sh" +export DTS_TUI_LIB="/usr/lib/tui/tui-lib.sh" export DTS_TUI_CONF="$CONF_DIR/dts-tui.yaml" export DTS_STATE="$RUN_DIR/state.sh" export DTS_STATE_LOCKFILE="/var/lock/dts-state" diff --git a/tui/tui-lib.sh b/tui/tui-lib.sh deleted file mode 100644 index 3f379f94..00000000 --- a/tui/tui-lib.sh +++ /dev/null @@ -1,661 +0,0 @@ -#!/bin/bash -# TUI Library for creating text-based user interfaces -# Requires: yq (YAML processor) - -# ANSI color codes (matching DTS color scheme) -TUI_NORMAL='\033[0m' -TUI_RED='\033[0;31m' -TUI_GREEN='\033[0;32m' -TUI_YELLOW='\033[0;33m' -TUI_BLUE='\033[0;36m' # Cyan, used for borders (matches DTS BLUE) - -# Terminal width configuration -TUI_MAX_WIDTH=60 # Maximum width for borders and footer wrapping - -# Global variables -TUI_RUNNING=true - -# Header variables -TUI_HEADER_TITLE="" -TUI_HEADER_SUBTITLE="" -TUI_HEADER_LINK="" - -# Section arrays (using | as delimiter) -declare -a TUI_SECTIONS_DATA=() # condition|label -declare -a TUI_ENTRIES_DATA=() # section_idx|condition|label|value - -# Menu and footer arrays -declare -a TUI_MENU_DATA=() # key|condition|label|callback -declare -a TUI_FOOTER_DATA=() # key|condition|label|callback - -declare -A TUI_PRE_RENDER_CALLBACKS=() -declare -A TUI_POST_RENDER_CALLBACKS=() -# used to call callbacks in the same order as they were registered -TUI_PRE_RENDER_CALLBACKS_ORDER=() -TUI_POST_RENDER_CALLBACKS_ORDER=() - -# Terminal control -tui_clear_screen() { - printf '\033[2J\033[H' -} - -tui_hide_cursor() { - printf '\033[?25l' -} - -tui_show_cursor() { - printf '\033[?25h' -} - -tui_clear_line() { - printf '\r\033[K' -} - -# Trap to ensure cursor is shown on exit -trap 'tui_show_cursor' EXIT INT TERM - -# ============================================================================ -# Utility Functions -# ============================================================================ - -# Calculate visible text length (stripping ANSI codes) -# Usage: tui_visible_length "text with ANSI codes" -tui_visible_length() { - local text="$1" - # Remove ANSI escape sequences and count characters - echo -n "$text" | sed 's/\x1b\[[0-9;]*m//g' | wc -c -} - -# Generate a border string of specified length with character -# Usage: tui_generate_border 80 "*" -tui_generate_border() { - local length="$1" - local char="${2:-*}" - printf "%${length}s" | tr ' ' "$char" -} - -# ============================================================================ -# Color Echo Functions (DTS-compatible) -# ============================================================================ - -tui_echo_normal() { - echo -e "${TUI_NORMAL}$1${TUI_NORMAL}" -} - -tui_echo_red() { - echo -e "${TUI_RED}$1${TUI_NORMAL}" -} - -tui_echo_yellow() { - echo -e "${TUI_YELLOW}$1${TUI_NORMAL}" -} - -tui_echo_green() { - echo -e "${TUI_GREEN}$1${TUI_NORMAL}" -} - -tui_echo_blue() { - echo -e "${TUI_BLUE}$1${TUI_NORMAL}" -} - -# ============================================================================ -# Status Message Functions -# ============================================================================ - -tui_print_warning() { - tui_echo_yellow "Warning: $1" -} - -tui_print_error() { - tui_echo_red "Error: $1" -} - -tui_print_success() { - tui_echo_green "$1" -} - -# ============================================================================ -# Border and Layout Functions -# ============================================================================ - -# Print a full-width border line -# Usage: tui_print_border -tui_print_border() { - local border - border=$(tui_generate_border "$TUI_MAX_WIDTH" "*") - echo -e "${TUI_BLUE}${border}${TUI_NORMAL}" -} - -# Print the "**" prefix used in sections and menu items -# Usage: tui_print_border_prefix -tui_print_border_prefix() { - echo -n -e "${TUI_BLUE}**${TUI_NORMAL}" -} - -# ============================================================================ -# Section Rendering Functions -# ============================================================================ - -# Print a section header with borders -# Usage: tui_print_section_header "SECTION LABEL" -tui_print_section_header() { - local label="$1" - tui_print_border - echo -e "${TUI_BLUE}**${TUI_NORMAL} $label ${TUI_NORMAL}" - tui_print_border -} - -# Print a section entry (label: value) -# Usage: tui_print_section_entry "Label" "Value" -tui_print_section_entry() { - local label="$1" - local value="$2" - printf "${TUI_BLUE}**${TUI_YELLOW}%15s: ${TUI_NORMAL}%s\n" "$label" "$value" -} - -# ============================================================================ -# Menu Rendering Functions -# ============================================================================ - -# Print a menu option -# Usage: tui_print_menu_option "1" "Menu label" -tui_print_menu_option() { - local key="$1" - local label="$2" - printf "${TUI_BLUE}**${TUI_YELLOW} %s)${TUI_BLUE} %s${TUI_NORMAL}\n" "$key" "$label" -} - -# ============================================================================ -# Footer Rendering Functions -# ============================================================================ - -# Print a footer action (used internally by tui_render_footer) -# Usage: tui_print_footer_action "K" "label" -tui_print_footer_action() { - local key="$1" - local label="$2" - echo -n -e "${TUI_RED}$key${TUI_NORMAL} to $label" -} - -# ============================================================================ -# Core Functions -# ============================================================================ - -# Expand environment variables in a string -# Usage: tui_expand_vars "string with $VAR" -tui_expand_vars() { - local string="$1" - # Use eval to expand variables, but safely quote the result - eval "printf '%s' \"$string\"" -} - -# Check if a condition evaluates to true -# Conditions can be environment variable checks like: ${VAR} or ${VAR:-default} -# Usage: tui_check_condition "condition" -tui_check_condition() { - local condition="$1" - [[ -z "$condition" ]] && return 0 # No condition means always show - - # Expand and evaluate the condition - local result - result=$(eval echo "$condition" 2>/dev/null || echo "") - - # Check if result is non-empty and not "false" or "0" - [[ -n "$result" && "$result" != "false" && "$result" != "0" ]] -} - -# Load YAML configuration and parse into bash variables -# Usage: tui_load_config "config.yaml" -tui_load_config() { - local config_file="$1" - - if [[ ! -f "$config_file" ]]; then - echo "Error: Config file not found: $config_file" >&2 - return 1 - fi - - if ! command -v yq &>/dev/null; then - echo "Error: yq is required but not installed" >&2 - echo "Install with: pip install yq or brew install yq" >&2 - return 1 - fi - - if ! command -v jq &>/dev/null; then - echo "Error: jq is required but not installed" >&2 - return 1 - fi - - # Convert YAML to JSON once - local json_config - json_config=$(yq eval -o=json "$config_file") - - # Parse header (using @ to safely handle special characters) - TUI_HEADER_TITLE=$(echo "$json_config" | jq -r '.header.title // ""') - TUI_HEADER_SUBTITLE=$(echo "$json_config" | jq -r '.header.subtitle // ""') - TUI_HEADER_LINK=$(echo "$json_config" | jq -r '.header.link // ""') - - # Clear arrays - TUI_SECTIONS_DATA=() - TUI_ENTRIES_DATA=() - TUI_MENU_DATA=() - TUI_FOOTER_DATA=() - - # Parse sections - local section_idx=0 - while IFS='|' read -r condition label; do - if [[ -z "$label" && -z "$condition" ]]; then - continue - fi - TUI_SECTIONS_DATA+=("$condition|$label") - - # Parse entries for this section - while IFS='|' read -r entry_cond entry_label entry_value; do - if [[ -z "$entry_label" && -z "$entry_value" ]]; then - continue - fi - TUI_ENTRIES_DATA+=("$section_idx|$entry_cond|$entry_label|$entry_value") - done < <(echo "$json_config" | jq -r ".sections[$section_idx].entries[]? | \"\(.condition // \"\")|\" + (.label | gsub(\"\\\\|\"; \"\\\\|\" )) + \"|\" + (.value | gsub(\"\\\\|\"; \"\\\\|\"))") - - ((section_idx++)) - done < <(echo "$json_config" | jq -r '.sections[]? | "\(.condition // "")|" + (.label | gsub("\\|"; "\\|"))') - - # Parse menu items - while IFS='|' read -r key condition label callback; do - if [[ -z "$key" ]]; then - continue - fi - TUI_MENU_DATA+=("$key|$condition|$label|$callback") - done < <(echo "$json_config" | jq -r '.menu[]? | .key + "|" + (.condition // "") + "|" + (.label | gsub("\\|"; "\\|")) + "|" + .callback') - - # Parse footer items - while IFS='|' read -r key condition label callback; do - if [[ -z "$key" ]]; then - continue - fi - TUI_FOOTER_DATA+=("$key|$condition|$label|$callback") - done < <(echo "$json_config" | jq -r '.footer[]? | .key + "|" + (.condition // "") + "|" + (.label | gsub("\\|"; "\\|")) + "|" + .callback') -} - -# Render header section -tui_render_header() { - if [[ -n "$TUI_HEADER_TITLE" ]]; then - local title - title=$(tui_expand_vars "$TUI_HEADER_TITLE") - echo -e "${TUI_NORMAL}$title${TUI_NORMAL}" - fi - - if [[ -n "$TUI_HEADER_SUBTITLE" ]]; then - local subtitle - subtitle=$(tui_expand_vars "$TUI_HEADER_SUBTITLE") - echo -e "${TUI_NORMAL}$subtitle${TUI_NORMAL}" - fi - - if [[ -n "$TUI_HEADER_LINK" ]]; then - local link - link=$(tui_expand_vars "$TUI_HEADER_LINK") - echo -e "${TUI_NORMAL}Report issues at: $link${TUI_NORMAL}" - fi -} - -# Render all information sections -tui_render_info_sections() { - local section_idx=0 - local section_data - for section_data in "${TUI_SECTIONS_DATA[@]}"; do - local condition label - IFS='|' read -r condition label <<<"$section_data" - - # Check if section should be displayed - if ! tui_check_condition "$condition"; then - ((section_idx++)) - continue - fi - - label=$(tui_expand_vars "$label") - tui_print_section_header "$label" - - # Render entries for this section - local entry_data - for entry_data in "${TUI_ENTRIES_DATA[@]}"; do - local entry_section_idx entry_condition entry_label entry_value - IFS='|' read -r entry_section_idx entry_condition entry_label entry_value <<<"$entry_data" - - # Only render entries for this section - if [[ "$entry_section_idx" != "$section_idx" ]]; then - continue - fi - - if ! tui_check_condition "$entry_condition"; then - continue - fi - - entry_label=$(tui_expand_vars "$entry_label") - entry_value=$(tui_expand_vars "$entry_value") - tui_print_section_entry "$entry_label" "$entry_value" - done - - ((section_idx++)) - done -} - -# Render main menu options -tui_render_menu() { - if [[ ${#TUI_MENU_DATA[@]} -eq 0 ]]; then - return 0 - fi - - tui_print_border - local menu_item - for menu_item in "${TUI_MENU_DATA[@]}"; do - local key condition label callback - IFS='|' read -r key condition label callback <<<"$menu_item" - - if ! tui_check_condition "$condition"; then - continue - fi - - label=$(tui_expand_vars "$label") - tui_print_menu_option "$key" "$label" - done - tui_print_border -} - -# Render footer actions with auto-wrap -tui_render_footer() { - if [[ ${#TUI_FOOTER_DATA[@]} -eq 0 ]]; then - return 0 - fi - - local footer_parts=() - local footer_item - - # Build footer parts - for footer_item in "${TUI_FOOTER_DATA[@]}"; do - local key condition label callback - IFS='|' read -r key condition label callback <<<"$footer_item" - - if ! tui_check_condition "$condition"; then - continue - fi - - label=$(tui_expand_vars "$label") - footer_parts+=("${TUI_RED}$key${TUI_NORMAL} to $label") - done - - if [[ ${#footer_parts[@]} -gt 0 ]]; then - # Auto-wrap footer to multiple lines if needed - local current_line="" - local current_length=0 - local max_width=$((TUI_MAX_WIDTH - 2)) # Leave 2 chars margin - - for part in "${footer_parts[@]}"; do - # Calculate visible length (strip ANSI codes) - local visible_part - visible_part=$(echo -e "$part" | sed 's/\x1b\[[0-9;]*m//g') - local part_length=${#visible_part} - - # Add separator length if not first item on line - local separator_length=0 - if [[ -n "$current_line" ]]; then - separator_length=2 # " " = 2 spaces - fi - - # Check if adding this part would exceed max width - if [[ -n "$current_line" ]] && ((current_length + separator_length + part_length > max_width)); then - # Print current line and start new line - echo -e "$current_line" - current_line="$part" - current_length=$part_length - else - # Add to current line - if [[ -n "$current_line" ]]; then - current_line+=" $part" - current_length=$((current_length + separator_length + part_length)) - else - current_line="$part" - current_length=$part_length - fi - fi - done - - # Print remaining line - if [[ -n "$current_line" ]]; then - echo -e "$current_line" - fi - fi -} - -# Render complete menu -tui_render() { - tui_clear_screen - tui_render_header - tui_render_info_sections - tui_render_menu - tui_render_footer - echo "" - echo -n -e "${TUI_YELLOW}Enter an option:${TUI_NORMAL}" -} - -# Read a single keypress without waiting for Enter -# Returns the pressed key -tui_read_key() { - local key - read -n 1 -s key - echo "$key" -} - -# Print prompt and read user input -tui_read_prompt() { - local prompt="$1" - local answer - echo -n "${prompt}: " >&2 - read -r answer - echo "${answer}" -} - -# Execute a callback script -# Usage: tui_execute_callback "script_path" -tui_execute_callback() { - local script="$1" - - if [[ ! -f "$script" ]]; then - echo "Error: Callback script not found: $script" >&2 - return 1 - fi - - if [[ ! -x "$script" ]]; then - echo "Error: Callback script is not executable: $script" >&2 - return 1 - fi - - # Clear screen and execute callback - tui_clear_screen - - # Execute the callback script - "$script" - local exit_code=$? - - # Wait for user to press a key before returning to menu - echo "" - echo -n "Press any key to continue..." - tui_read_key - - return $exit_code -} - -# Find menu item by key and return its callback -# Usage: tui_find_menu_callback "key" -tui_find_menu_callback() { - local key="$1" - - for menu_item in "${TUI_MENU_DATA[@]}"; do - IFS='|' read -r menu_key condition label callback <<<"$menu_item" - - if [[ "$menu_key" == "$key" ]]; then - if tui_check_condition "$condition"; then - # Expand environment variables in callback path - callback=$(tui_expand_vars "$callback") - echo "$callback" - return 0 - fi - fi - done - - return 1 -} - -# Find footer item by key and return its callback -# Usage: tui_find_footer_callback "key" -tui_find_footer_callback() { - local key="$1" - - for footer_item in "${TUI_FOOTER_DATA[@]}"; do - IFS='|' read -r footer_key condition label callback <<<"$footer_item" - - if [[ "$footer_key" == "$key" ]]; then - if tui_check_condition "$condition"; then - # Expand environment variables in callback path - callback=$(tui_expand_vars "$callback") - echo "$callback" - return 0 - fi - fi - done - - return 1 -} - -# Handle user input -# Returns: 0 to continue, 1 to exit -tui_handle_input() { - local key - key=$(tui_read_key) - - # Convert to uppercase for case-insensitive matching - key=$(echo "$key" | tr '[:lower:]' '[:upper:]') - - # Hidden option: Q to quit (useful for testing) - if [[ "$key" == "Q" ]]; then - tui_stop - return 0 - fi - - # Try to find callback in menu items - local callback - if callback=$(tui_find_menu_callback "$key"); then - tui_execute_callback "$callback" - return 0 - fi - - # Try to find callback in footer items - if callback=$(tui_find_footer_callback "$key"); then - tui_execute_callback "$callback" - return 0 - fi - - # No matching option found - return 0 -} - -# Main loop -# Usage: tui_run "config.yaml" -tui_run() { - local config_file="$1" - - if ! tui_load_config "$config_file"; then - return 1 - fi - - tui_hide_cursor - TUI_RUNNING=true - - while $TUI_RUNNING; do - for callback in "${TUI_PRE_RENDER_CALLBACKS_ORDER[@]}"; do - eval "${callback}" "${TUI_PRE_RENDER_CALLBACKS["${callback}"]}" - done - tui_render - for callback in "${TUI_POST_RENDER_CALLBACKS_ORDER[@]}"; do - eval "${callback}" "${TUI_POST_RENDER_CALLBACKS["${callback}"]}" - done - tui_handle_input - done - - tui_show_cursor - tui_clear_screen -} - -# Stop the TUI loop -tui_stop() { - TUI_RUNNING=false -} - -# register callbacks called during each UI refresh (before showing UI) e.g. -# tui_register_refresh_callback my_callback_func callback_arg_1 callback_arg_2 -tui_register_pre_render_callback() { - local callback="$1" - shift - TUI_PRE_RENDER_CALLBACKS["${callback}"]="$*" - TUI_PRE_RENDER_CALLBACKS_ORDER+=("${callback}") -} - -# register callbacks called during each UI refresh (after showing UI) -tui_register_post_render_callback() { - local callback="$1" - shift - TUI_POST_RENDER_CALLBACKS["${callback}"]="$*" - TUI_POST_RENDER_CALLBACKS_ORDER+=("${callback}") -} - -# Export functions for use in other scripts - -# Terminal control -export -f tui_clear_screen -export -f tui_hide_cursor -export -f tui_show_cursor - -# Utility functions -export -f tui_visible_length -export -f tui_generate_border - -# Color echo functions -export -f tui_echo_normal -export -f tui_echo_red -export -f tui_echo_yellow -export -f tui_echo_green -export -f tui_echo_blue - -# Status message functions -export -f tui_print_warning -export -f tui_print_error -export -f tui_print_success - -# Border and layout functions -export -f tui_print_border -export -f tui_print_border_prefix - -# Section rendering functions -export -f tui_print_section_header -export -f tui_print_section_entry - -# Menu rendering functions -export -f tui_print_menu_option - -# Footer rendering functions -export -f tui_print_footer_action - -# Core functions -export -f tui_expand_vars -export -f tui_check_condition -export -f tui_load_config -export -f tui_render_header -export -f tui_render_info_sections -export -f tui_render_menu -export -f tui_render_footer -export -f tui_render -export -f tui_read_key -export -f tui_execute_callback -export -f tui_find_menu_callback -export -f tui_find_footer_callback -export -f tui_handle_input -export -f tui_run -export -f tui_stop diff --git a/tui/tui_callbacks/ssh-toggle.sh b/tui/tui_callbacks/ssh-toggle.sh index b1abcd47..8549a55f 100644 --- a/tui/tui_callbacks/ssh-toggle.sh +++ b/tui/tui_callbacks/ssh-toggle.sh @@ -13,3 +13,5 @@ else systemctl start sshd tui_echo_green "Listening on IPs: $(ip -br -f inet a show scope global | grep UP | awk '{ print $3 }' | tr '\n' ' ')" fi + +tui_read_key_to_continue From 0e4c693ad79f3c4600ab6aac7c377ed2f6ca32e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Iwanicki?= Date: Mon, 27 Oct 2025 11:16:16 +0100 Subject: [PATCH 4/5] dts.sh: export 'SEND_LOGS_ACTIVE' and 'DPP_IS_LOGGED' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Iwanicki --- scripts/dts.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/dts.sh b/scripts/dts.sh index eadb460e..fc835c92 100644 --- a/scripts/dts.sh +++ b/scripts/dts.sh @@ -26,6 +26,7 @@ fi set_menu_vars() { DPP_IS_LOGGED=$(get_global_state DPP_IS_LOGGED) + export DPP_IS_LOGGED DISPLAY_CREDENTIALS=$(get_global_state DISPLAY_CREDENTIALS) if check_if_dasharo; then @@ -54,6 +55,7 @@ set_menu_vars() { SSH_ACTIVE="false" fi SEND_LOGS_ACTIVE=$(get_global_state SEND_LOGS_ACTIVE) + export SEND_LOGS_ACTIVE if [ "${SEND_LOGS_ACTIVE}" = "true" ]; then SEND_LOGS_LABEL="disable sending DTS logs" else From 508faf9e4e9f95d8f715adbd7d06866f4d9a8637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Iwanicki?= Date: Tue, 28 Oct 2025 11:03:58 +0100 Subject: [PATCH 5/5] tui: add dts-extensions.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Iwanicki --- Makefile | 2 +- scripts/dts.sh | 3 +++ tui/header.yaml | 5 +++++ tui/tui_callbacks/dasharo-firmware.sh | 0 tui/tui_callbacks/dpp-keys.sh | 0 tui/tui_callbacks/dts-extensions.sh | 16 ++++++++++++++++ tui/tui_callbacks/fuse.sh | 0 tui/tui_callbacks/hcl-report.sh | 0 tui/tui_callbacks/poweroff.sh | 0 tui/tui_callbacks/reboot.sh | 0 tui/tui_callbacks/restore-firmware.sh | 0 tui/tui_callbacks/shell.sh | 0 tui/tui_callbacks/ssh-toggle.sh | 0 tui/tui_callbacks/toggle-credentials.sh | 0 tui/tui_callbacks/toggle-logs.sh | 0 tui/tui_callbacks/transition.sh | 0 16 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tui/header.yaml mode change 100644 => 100755 tui/tui_callbacks/dasharo-firmware.sh mode change 100644 => 100755 tui/tui_callbacks/dpp-keys.sh create mode 100755 tui/tui_callbacks/dts-extensions.sh mode change 100644 => 100755 tui/tui_callbacks/fuse.sh mode change 100644 => 100755 tui/tui_callbacks/hcl-report.sh mode change 100644 => 100755 tui/tui_callbacks/poweroff.sh mode change 100644 => 100755 tui/tui_callbacks/reboot.sh mode change 100644 => 100755 tui/tui_callbacks/restore-firmware.sh mode change 100644 => 100755 tui/tui_callbacks/shell.sh mode change 100644 => 100755 tui/tui_callbacks/ssh-toggle.sh mode change 100644 => 100755 tui/tui_callbacks/toggle-credentials.sh mode change 100644 => 100755 tui/tui_callbacks/toggle-logs.sh mode change 100644 => 100755 tui/tui_callbacks/transition.sh diff --git a/Makefile b/Makefile index 39e14236..7cd79699 100644 --- a/Makefile +++ b/Makefile @@ -27,4 +27,4 @@ install: install -d $(DESTDIR)$(SYSCONFDIR)/profile.d $(DESTDIR)$(SYSCONFDIR)/dts install -m 0755 dts-profile.sh $(DESTDIR)$(SYSCONFDIR)/profile.d - install -m 0644 tui/dts-tui.yaml $(DESTDIR)$(SYSCONFDIR)/dts/dts-tui.yaml + install -m 0644 tui/*.yaml $(DESTDIR)$(SYSCONFDIR)/dts/ diff --git a/scripts/dts.sh b/scripts/dts.sh index fc835c92..63cbdd28 100644 --- a/scripts/dts.sh +++ b/scripts/dts.sh @@ -71,6 +71,9 @@ set_menu_vars() { DPP_PASSWORD_DISPLAY="***************" DISPLAY_CRED_LABEL="display DPP credentials" fi + if [ -f "${DPP_SUBMENU_JSON}" ]; then + SHOW_DTS_EXTENSIONS="true" + fi } tui_register_pre_render_callback subscription_routine diff --git a/tui/header.yaml b/tui/header.yaml new file mode 100644 index 00000000..dc9889fc --- /dev/null +++ b/tui/header.yaml @@ -0,0 +1,5 @@ +--- +header: + title: " Dasharo Tools Suite Script ${DTS_VERSION} " + subtitle: " (c) Dasharo " + link: "https://github.com/Dasharo/dasharo-issues" diff --git a/tui/tui_callbacks/dasharo-firmware.sh b/tui/tui_callbacks/dasharo-firmware.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/dpp-keys.sh b/tui/tui_callbacks/dpp-keys.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/dts-extensions.sh b/tui/tui_callbacks/dts-extensions.sh new file mode 100755 index 00000000..a8db3f6b --- /dev/null +++ b/tui/tui_callbacks/dts-extensions.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# shellcheck source=../../include/dts-environment.sh +source "$DTS_ENV" + +extensions_yaml="/tmp/extensions.yaml" +cat "${CONF_DIR}/header.yaml" >"${extensions_yaml}" +yq -i '. += {"menu": [], "footer": [{"key": "Q", "label": "go back"}]}' "${extensions_yaml}" + +while IFS=$'\t' read -r file_name file_position _; do + export file_name file_position callback + callback="${DPP_PACKAGES_SCRIPTS_PATH}/${file_name}" + yq -i '.menu += {"key": strenv(file_position), "label": strenv(file_name), "callback": strenv(callback)}' "$extensions_yaml" +done < <(yq -p=json '.[] | [.file_name, .file_menu_position] | @tsv' "${DPP_SUBMENU_JSON}") + +tui_run "${extensions_yaml}" diff --git a/tui/tui_callbacks/fuse.sh b/tui/tui_callbacks/fuse.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/hcl-report.sh b/tui/tui_callbacks/hcl-report.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/poweroff.sh b/tui/tui_callbacks/poweroff.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/reboot.sh b/tui/tui_callbacks/reboot.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/restore-firmware.sh b/tui/tui_callbacks/restore-firmware.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/shell.sh b/tui/tui_callbacks/shell.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/ssh-toggle.sh b/tui/tui_callbacks/ssh-toggle.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/toggle-credentials.sh b/tui/tui_callbacks/toggle-credentials.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/toggle-logs.sh b/tui/tui_callbacks/toggle-logs.sh old mode 100644 new mode 100755 diff --git a/tui/tui_callbacks/transition.sh b/tui/tui_callbacks/transition.sh old mode 100644 new mode 100755