[2/4] installer: extract attended UI to installer_ui.sh

Message ID 20260427063957.603123-3-kasturi.shekar@siemens.com
State New
Headers show
Series installer: separate installer into backend, UI, and flow modules | expand

Commit Message

Kasturi shekar April 27, 2026, 6:39 a.m. UTC
From: "kasturi.shekar" <kasturi.shekar@siemens.com>

Move attended dialog interactions into installer_ui.sh
The new UI module provides:
 - image selection menu
 - target device selection menu
 - install and overwrite confirmation dialogs
 - error and info dialogs
 - attended progress gauge hooks

Signed-off-by: kasturi.shekar <kasturi.shekar@siemens.com>
---
 .../files/usr/bin/installer_ui.sh             | 217 +++++++++++++-----
 1 file changed, 161 insertions(+), 56 deletions(-)
 mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh

Patch

diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh
old mode 100755
new mode 100644
index 33685c6f..9d3ba5de
--- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh
+++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh
@@ -1,86 +1,191 @@ 
 #!/usr/bin/env bash
+# This software is a part of ISAR.
+# Copyright (C) Siemens AG, 2026
 #
-# installer_ui.sh — Attended installer frontend
-# ------------------------------------------------
+# SPDX-License-Identifier: MIT
 
-SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
-INSTALL_DATA=${INSTALL_DATA:-./install}
+#--------------------------------------------------------------------------
+# installer_ui.sh - Frontend/UI helpers for isar installer.
+#
+# This file is intentionally UI-only:
+# - dialog menus and message boxes
+# - attended confirmations
+# - user abort countdown handling
+#--------------------------------------------------------------------------
+UI_GAUGE_PID=""
+
+#--------------------------------------------------------------------------
+# ui_show_error <message>
+#
+# Displays an error dialog in attended mode.
+#--------------------------------------------------------------------------
+ui_show_error() {
+    local message="$1"
+    dialog --msgbox "$message" 6 60
+}
 
-# Backend APIs
-. "$SCRIPT_DIR/sys_api.sh"
+#--------------------------------------------------------------------------
+# ui_show_info <message>
+#
+# Displays an informational dialog in attended mode.
+#--------------------------------------------------------------------------
+ui_show_info() {
+    local message="$1"
+    dialog --msgbox "$message" 6 60
+}
+
+#--------------------------------------------------------------------------
+# ui_countdown_allow_attended_switch <seconds> <abort_file>
+#
+# In unattended mode, this gives users a chance to switch to attended
+# mode by pressing any key. Returns 0 when attended mode should be
+# used, and 1 otherwise.
+#--------------------------------------------------------------------------
+ui_countdown_allow_attended_switch() {
+    local timeout="$1"
+    local abort_file="$2"
+    local i
+
+    # Countdown loop prints a message once per second and accepts a key press.
+    # If any key is pressed, create the abort trigger file for the caller.
+    for ((i=timeout; i>0; i--)); do
+        echo -ne "\rUnattended installation will start in $i seconds. Press any key to switch to attended mode..."
+
+        if [ -f "$abort_file" ] || read -n 1 -t 1; then
+            touch "$abort_file"
+            echo
+            return 0
+        fi
+    done
 
-# ------------------------------------------------
-# Helpers
-# ------------------------------------------------
-die() {
-    dialog --msgbox "$1" 6 60
-    exit 1
+    echo
+    return 1
 }
 
-# ------------------------------------------------
-# UI: Select image
-# ------------------------------------------------
-ui_select_image() {
-    local images json list=()
+#--------------------------------------------------------------------------
+# ui_select_image_menu <install_data_dir>
+#
+# Uses sys_list_installable_entries backend API and returns selected
+# relative image path on stdout.
+#--------------------------------------------------------------------------
+ui_select_image_menu() {
+    local install_data_dir="$1"
+    local list=()
+    local entry
+    local selected
 
-    # On failure, show error dialog and exit
-    json=$(sys_locate_disk_images search_path="$INSTALL_DATA") || \
-        die "No installable images found in $INSTALL_DATA"
+    while IFS= read -r entry; do
+        [ -n "$entry" ] || continue
+        list+=("$entry" "$entry")
+    done < <(sys_list_installable_entries "$install_data_dir")
 
-    # Extract image paths from JSON
-    images=$(echo "$json" | sed -n 's/.*"images":\[\(.*\)\].*/\1/p' | tr -d '"' | tr ',' '\n')
+    if [ "${#list[@]}" -eq 0 ]; then
+        return 1
+    fi
 
-    # Building dialog menu entries
-    for img in $images; do
-        base=$(basename "$img")
-        list+=("$img" "$base")
-    done
+    selected=$(dialog --no-tags \
+        --menu "Select image to be installed" 12 70 6 \
+        "${list[@]}" --output-fd 1) || return 2
 
-    # Display menu and capture selection
-    INSTALL_IMAGE=$(dialog --no-tags \
-        --menu "Select image to install" 10 70 5 \
-        "${list[@]}" \
-        --output-fd 1) || exit 0
+    echo "$selected"
+    return 0
 }
 
-# ------------------------------------------------
-# UI: Select target device
-# ------------------------------------------------
-ui_select_target_device() {
+#--------------------------------------------------------------------------
+# ui_select_target_device_menu <device...>
+#
+# Displays candidate target devices and returns selected /dev path.
+#--------------------------------------------------------------------------
+ui_select_target_device_menu() {
     local list=()
+    local target
+    local target_size
+    local state
+    local selected
 
-    devices=$(sys_list_valid_target_devices) || \
-        die "No valid target devices found"
+    for target in "$@"; do
+        [ -b "$target" ] || continue
 
-    for dev in $devices; do
-        [ -b "$dev" ] || continue
+        target_size=$(sys_device_size "$target")
+        [ -n "$target_size" ] || target_size="unknown"
 
-        size=$(lsblk --nodeps --noheadings -o SIZE "$dev" 2>/dev/null | tr -d " ")
-        [ -z "$size" ] && size="unknown"
-
-        if cmp /dev/zero "$dev" -n 1M >/dev/null 2>&1; then
+        # Indicate whether the selected device is already empty, to help users
+        # avoid accidental overwrite of data.
+        if sys_device_is_empty "$target"; then
             state="empty"
         else
             state="contains data"
         fi
 
-        list+=("$dev" "$dev ($size, $state)")
+        list+=("$target" "$target ($target_size, $state)")
     done
 
-    if [ "${#list[@]}" -lt 2 ]; then
-        die "no installable target devices available"
+    if [ "${#list[@]}" -eq 0 ]; then
+        return 1
     fi
 
-    TARGET_DEVICE=$(dialog --no-tags \
-        --menu "Select target device" 10 70 6 \
-        "${list[@]}" \
-        --output-fd 1) || exit 0
+    selected=$(dialog --no-tags \
+        --menu "Select device to install image to" 12 70 6 \
+        "${list[@]}" --output-fd 1) || return 2
+
+    echo "$selected"
+    return 0
 }
 
-run_interactive_installer() {
-    clear
-    ui_select_image
-    ui_select_target_device
+#--------------------------------------------------------------------------
+# ui_confirm_install <image_path> <target_device> <target_size>
+#
+# Returns:
+#   0 when user confirms, 1 when canceled.
+#--------------------------------------------------------------------------
+ui_confirm_install() {
+    local image_path="$1"
+    local target_device="$2"
+    local target_size="$3"
+
+    dialog --yes-label Ok --no-label Cancel \
+        --yesno "Start installing\n'$image_path'\nto $target_device (capacity: $target_size)" 8 70
+}
+
+#--------------------------------------------------------------------------
+# ui_confirm_overwrite
+#
+# Returns:
+#   0 when user accepts overwrite, 1 when canceled.
+#--------------------------------------------------------------------------
+ui_confirm_overwrite() {
+    dialog --defaultno --yesno "WARNING: Target device is not empty! Continue anyway?" 8 70
+}
+
+#--------------------------------------------------------------------------
+# ui_start_progress_gauge <pipe_path>
+#
+# Opens a dialog gauge and updates it from bmaptool psplash pipe.
+#--------------------------------------------------------------------------
+ui_start_progress_gauge() {
+    local pipe_path="$1"
+
+    (
+        while true; do
+            if read -r line < "$pipe_path"; then
+                percentage=$(echo "$line" | awk '{ print $2 }')
+                echo "$percentage"
+            fi
+        done
+    ) | dialog --gauge "Flashing image, please wait..." 10 70 0 &
+
+    UI_GAUGE_PID=$!
+}
+
+#--------------------------------------------------------------------------
+# ui_stop_progress_gauge
+#
+# Best-effort termination of the active progress gauge process.
+#--------------------------------------------------------------------------
+ui_stop_progress_gauge() {
+    if [ -n "$UI_GAUGE_PID" ]; then
+        kill "$UI_GAUGE_PID" 2>/dev/null || true
+        UI_GAUGE_PID=""
+    fi
 }
 
-run_interactive_installer