* [PATCH 0/4] installer: separate installer into backend, UI, and flow modules
@ 2026-04-27 6:39 'Kasturi shekar' via isar-users
2026-04-27 6:39 ` [PATCH 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users
` (3 more replies)
0 siblings, 4 replies; 18+ messages in thread
From: 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 UTC (permalink / raw)
To: isar-users; +Cc: kasturi.shekar
From: "kasturi.shekar" <kasturi.shekar@siemens.com>
This series refactors the installer by separating backend logic,
attended UI, and high-level flow into dedicated modules.
The main objectives are:
- improve readability and maintainability
- keep behavior and user-visible flow unchanged
Patch-1: introduces a backend API layer (sys_api.sh) that provides
installer functionality via structured shell functions.
Patch-2: adapts the existing dialog-based installer frontend to consume
these APIs, without changing behavior.
Patch-3: high-level orchestration and keep deploy-image-wic.sh as a thin launcher.
This keeps the externally used script name stable while making the flow easier
to review and maintain.
patch-4: update deploy-image recipe to ship the newly split installer modules
kasturi.shekar (4):
installer: extract backend helpers to sys_api.sh
installer: extract attended UI to installer_ui.sh
installer: add installer flow module and entrypoint
installer: install seprated installer modules in recipe
.../deploy-image/deploy-image_0.1.bb | 6 +-
.../files/usr/bin/deploy-image-wic.sh | 475 +++++++-----------
.../files/usr/bin/installer_ui.sh | 217 +++++---
.../deploy-image/files/usr/bin/sys_api.sh | 397 ++++++++++++---
4 files changed, 688 insertions(+), 407 deletions(-)
mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh
mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh
--
2.47.3
--
You received this message because you are subscribed to the Google Groups "isar-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260427063957.603123-1-kasturi.shekar%40siemens.com.
^ permalink raw reply [flat|nested] 18+ messages in thread* [PATCH 1/4] installer: extract backend helpers to sys_api.sh 2026-04-27 6:39 [PATCH 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 ` 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users ` (2 subsequent siblings) 3 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 UTC (permalink / raw) To: isar-users; +Cc: kasturi.shekar From: "kasturi.shekar" <kasturi.shekar@siemens.com> Move backend and system-facing installer logic into sys_api.sh This patch introduces backend helper APIs for: - install image discovery - target device discovery and filtering - root/live-device exclusion - RAID member and inactive md filtering - block device state and size helpers - bmap copy execution with locking and progress pipe support Signed-off-by: kasturi.shekar <kasturi.shekar@siemens.com> --- .../deploy-image/files/usr/bin/sys_api.sh | 400 +++++++++++++++--- 1 file changed, 333 insertions(+), 67 deletions(-) diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh index b144419e..9c9c5840 100644 --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh @@ -1,84 +1,350 @@ #!/usr/bin/env bash +# This software is a part of ISAR. +# Copyright (C) Siemens AG, 2026 # -# sys_api.sh — Ported installer system APIs -# ------------------------------------------------------------------- +# SPDX-License-Identifier: MIT -# Load shared framework utilities -source ./func.sh || { - echo "Error: func.sh not found or not readable." >&2 - exit 1 +#-------------------------------------------------------------------------- +# sys_api.sh - Backend APIs for deploy-image-wic installer. +# +# This file intentionally contains only system/backend logic: +# - image discovery +# - target device discovery and filtering +# - low-level install/copy flow +# +# UI concerns (dialog boxes, menus, confirmations) are implemented in +# installer_ui.sh and called by the main orchestrator script. +#-------------------------------------------------------------------------- + +# Globals populated by sys_discover_target_devices(). +SYS_CURRENT_ROOT_DEV="" +SYS_TARGET_DEVICES=() + +#-------------------------------------------------------------------------- +# sys_first_default_image_name <install_data_dir> +# +# Returns: +# basename of matching *.wic* image (excluding *.wic.bmap), or empty. +#-------------------------------------------------------------------------- +sys_first_default_image_name() { + local install_data_dir="$1" + + find "$install_data_dir" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" \ + -exec basename {} \; | head -n 1 +} + +#-------------------------------------------------------------------------- +# sys_list_installable_entries <install_data_dir> +# +# Returns: +# relative file paths under install_data_dir, one per line. +# Files ending in *.wic.bmap are excluded. +#-------------------------------------------------------------------------- +sys_list_installable_entries() { + local install_data_dir="$1" + + ( + cd "$install_data_dir" || return 1 + find . -type f ! -iname "*.wic.bmap" -print | sed 's#^./##' + ) } -# ------------------------------------------------------------------- -# API: locate_disk_images -# Description: -# Find disk image files (.wic, .wic.bz2) under given path. -# Usage: -# sys_locate_disk_images search_path=/install -# Returns JSON: -# {"error":"", "retval":"0", "images":["/install/image.wic", ...]} -# ------------------------------------------------------------------- -sys_locate_disk_images() { - local -A ARGS - local required=(search_path) - api_args ARGS required[@] "$@" || { - pack_return_data error "$_args_error" retval "1" - return 1 - } - - local fn="${FUNCNAME[0]}" - local path="${ARGS[search_path]}" - - log_info "$fn" "Searching for disk images in '$path'" - - local images - images=$(find "$path" -type f -iname "*.wic*" ! -iname "*.wic.bmap" 2>/dev/null) - - # when no images found in /install - if [[ -z "$images" ]]; then - log_warn "$fn" "No images found." - pack_return_data error "No images found" retval "1" - return 1 +#-------------------------------------------------------------------------- +# sys_resolve_image_path <install_data_dir> <image_or_path> +# +# Arguments: +# install_data_dir - base directory where installable images are stored +# image_or_path - either a relative image filename under install_data_dir +# or an explicit absolute/relative filesystem path +# +# Returns: +# absolute image path if resolvable, else non-zero return code. +#-------------------------------------------------------------------------- +sys_resolve_image_path() { + local install_data_dir="$1" + local image_or_path="$2" + + if [ -f "$image_or_path" ]; then + echo "$image_or_path" + return 0 + fi + + if [ -f "$install_data_dir/$image_or_path" ]; then + echo "$install_data_dir/$image_or_path" + return 0 fi - # Convert newline-separated paths into JSON array elements - local json_images - json_images=$(printf '"%s",' $images | sed 's/,$//') - # final JSON response on stdout - echo "{ \"error\":\"\", \"retval\":\"0\", \"images\":[${json_images}] }" + return 1 } +#-------------------------------------------------------------------------- +# sys_discover_target_devices +# +# Populates globals: +# SYS_CURRENT_ROOT_DEV - current live root device identifier +# SYS_TARGET_DEVICES - array with valid target block devices (/dev/*) +# +# Filtering rules match legacy deploy-image-wic behavior, including: +# - skip installer/live root device and backing devices +# - skip loop/dm/ram/sr/mtd/zram +# - skip inactive md arrays and md members +#-------------------------------------------------------------------------- +sys_discover_target_devices() { + local current_root_dev_type + local current_root_dev + local root_source + local root_source_resolved + local base_no_part + local slave + local slave_dev + local slave_base + local devices + local mmc_devices + local device + local is_raid_member + local holder_path + local holder_name + local state + local skip_device + local ex + + local exclude_list=() + + SYS_TARGET_DEVICES=() + + # Determine the live root device so we do not offer the current system disk + # as an install target. For NFS roots use a special sentinel value. + + current_root_dev_type=$(findmnt / -o fstype -n) + if [ "$current_root_dev_type" = "nfs" ]; then + current_root_dev="nfs" + exclude_list+=("nfs") + else + root_source=$(findmnt / -o source -n) + root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") + current_root_dev=${root_source_resolved#/dev/} + + exclude_list+=("$current_root_dev") + + # Strip partition suffix for common disk names so the base device + # (e.g. /dev/sda or /dev/mmcblk0) also does not become selectable. + if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then + base_no_part="${current_root_dev%p[0-9]*}" + else + base_no_part="${current_root_dev%%[0-9]*}" + fi + + if [ -n "$base_no_part" ]; then + exclude_list+=("$base_no_part") + fi + + # Exclude backing devices for the live root, such as multipath or RAID + # underlying devices exposed via /sys/block/<root>/slaves. + if [ -d "/sys/block/$current_root_dev/slaves" ]; then + for slave in /sys/block/"$current_root_dev"/slaves/*; do + [ -e "$slave" ] || continue + slave_dev=$(basename "$slave") + exclude_list+=("$slave_dev") + slave_base=${slave_dev%%[0-9]*} + [ -n "$slave_base" ] && exclude_list+=("$slave_base") + done + fi + fi + + SYS_CURRENT_ROOT_DEV="$current_root_dev" + + # Gather block devices from /sys/block while preserving mmcblk devices that + # would otherwise be filtered by the main find expression. + devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true + mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true + devices="$devices $mmc_devices" + + for device in $devices; do + is_raid_member=0 + + if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then + for holder_path in /sys/block/$device/holders/*; do + holder_name=$(basename "$holder_path") + case "$holder_name" in + md[0-9]*) + is_raid_member=1 + break + ;; + esac + done + fi + + if [ "$is_raid_member" -eq 1 ]; then + # Skip RAID member devices, because we only want whole block devices + # available for standalone image installation. + continue + fi + + if [[ "$device" == md* ]]; then + # Accept only active or clean md arrays, reject degraded/inactive RAID sets. + if [ -f "/sys/block/$device/md/array_state" ]; then + state=$(cat /sys/block/$device/md/array_state | tr -d '\n' | tr -d ' ') + if [ "$state" != "active" ] && [ "$state" != "clean" ]; then + echo "Skipping RAID device $device: state='$state'" >&2 + continue + fi + echo "Found RAID device $device: state='$state'" >&2 + else + echo "Skipping RAID device $device: no array_state file" >&2 + continue + fi + fi -# ------------------------------------------------------------------- -# API: sys_list_valid_target_devices -# Description: -# Shell-friendly variant of sys_get_valid_target_devices. -# Prints one valid device per line (absolute path). -# Usage: -# sys_list_valid_target_devices -# Output: -# /dev/sdb -# /dev/sdc -# ------------------------------------------------------------------- -sys_list_valid_target_devices() { - local dev - - for dev in /sys/block/*; do - dev=$(basename "$dev") - - case "$dev" in - loop*|ram*|sr*|mtd*) + case "$device" in + dm-*|loop*|mtd*|sr*|ram*|zram*) continue ;; esac - # skip inactive md devices - if [[ "$dev" == md* ]]; then - [ -f "/sys/block/$dev/md/array_state" ] || continue - state=$(cat /sys/block/$dev/md/array_state) - [ "$state" = "active" ] || [ "$state" = "clean" ] || continue - fi + skip_device=0 + for ex in "${exclude_list[@]}"; do + if [[ "$device" == "$ex"* ]]; then + skip_device=1 + break + fi + done - echo "/dev/$dev" + if [ "$skip_device" -eq 0 ]; then + SYS_TARGET_DEVICES+=("/dev/$device") + fi done } + +#-------------------------------------------------------------------------- +# sys_device_size <device> +# +# Returns: +# human-readable size string from lsblk, or empty if unavailable. +#-------------------------------------------------------------------------- +sys_device_size() { + local device="$1" + lsblk --nodeps --noheadings -o SIZE "$device" 2>/dev/null | tr -d " " +} + +#-------------------------------------------------------------------------- +# sys_device_is_empty <device> +# +# Returns: +# 0 when first 1 MiB is zero-filled, 1 otherwise. +#-------------------------------------------------------------------------- +sys_device_is_empty() { + local device="$1" + cmp /dev/zero "$device" -n 1M >/dev/null 2>&1 +} + +#-------------------------------------------------------------------------- +# sys_version_ge <current> <required> +# +# Returns: +# 0 if current >= required, else 1. +#-------------------------------------------------------------------------- +sys_version_ge() { + local current="$1" + local required="$2" + + if [ "$(printf '%s\n' "$current"X "$required" | sort -V | head -n 1)" != "$current"X ]; then + return 0 + fi + return 1 +} + +#-------------------------------------------------------------------------- +# sys_bmap_options_for_image <image_path> +# +# Returns: +# bmaptool options string ("--nobmap" when no sidecar bmap is present). +#-------------------------------------------------------------------------- +sys_bmap_options_for_image() { + local image_path="$1" + local disk_bmap + + disk_bmap="${image_path%.wic*}.wic.bmap" + if [ -f "$disk_bmap" ]; then + # If a .wic.bmap sidecar exists, return an empty option string so + # bmaptool uses the bundled map automatically. + echo "" + else + echo "--nobmap" + fi +} + +#-------------------------------------------------------------------------- +# sys_install_image_with_lock <image_path> <target_device> +# +# Performs the actual bmaptool copy, serializing concurrent installer +# consoles via flock. When UI hook functions are available, this API +# uses them to present a progress gauge in attended mode. +# +# Optional UI hooks (if defined): +# ui_start_progress_gauge <pipe_path> +# ui_stop_progress_gauge +#-------------------------------------------------------------------------- +sys_install_image_with_lock() { + local image_path="$1" + local target_device="$2" + local lockfile="/run/installer.lock" + local progress_pipe="/run/installer.fifo" + local bmap_options + local bmap_version + local quiet_flag="" + + bmap_options=$(sys_bmap_options_for_image "$image_path") + + exec 9>"$lockfile" + if flock -n 9; then + bmap_version=$(bmaptool --version | awk '{ print $NF }') + + if sys_version_ge "$bmap_version" "3.6"; then + if [ -p "$progress_pipe" ]; then + rm -f "$progress_pipe" + fi + + if ! mkfifo "$progress_pipe"; then + echo "Error: Failed to create named pipe $progress_pipe" + return 1 + fi + + bmap_options="$bmap_options --psplash-pipe=$progress_pipe" + quiet_flag="-q" + + if declare -F ui_start_progress_gauge >/dev/null; then + ui_start_progress_gauge "$progress_pipe" + fi + fi + + # Run the actual copying step under the lock to avoid concurrent writes + # from multiple installer consoles or sessions. + if ! bmaptool $quiet_flag copy $bmap_options "$image_path" "$target_device"; then + if declare -F ui_stop_progress_gauge >/dev/null; then + ui_stop_progress_gauge + fi + return 1 + fi + + if declare -F ui_stop_progress_gauge >/dev/null; then + ui_stop_progress_gauge + fi + else + echo "Installation already running in another console." + sleep 5 + + if [ -e "$progress_pipe" ]; then + echo "Installation progress..." + tail -f "$progress_pipe" | while read -r line; do + printf "\r%s%%" "$line" + done + else + echo "Waiting for installation to finish..." + while pgrep -x "bmaptool" >/dev/null; do + sleep 5 + done + fi + fi + + return 0 +} -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260427063957.603123-2-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 2/4] installer: extract attended UI to installer_ui.sh 2026-04-27 6:39 [PATCH 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 ` 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH " 'Kasturi shekar' via isar-users 3 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 UTC (permalink / raw) To: isar-users; +Cc: kasturi.shekar 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 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 -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260427063957.603123-3-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 3/4] installer: add installer flow module and entrypoint 2026-04-27 6:39 [PATCH 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 ` 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH " 'Kasturi shekar' via isar-users 3 siblings, 1 reply; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 UTC (permalink / raw) To: isar-users; +Cc: kasturi.shekar From: "kasturi.shekar" <kasturi.shekar@siemens.com> high-level orchestration and keep deploy-image-wic.sh as a thin launcher. This keeps the externally used script name stable while making the flow easier to review and maintain by separating: - backend APIs - UI frontend - mode/orchestration flow Signed-off-by: kasturi.shekar <kasturi.shekar@siemens.com> --- .../files/usr/bin/deploy-image-wic.sh | 475 +++++++----------- 1 file changed, 192 insertions(+), 283 deletions(-) mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh old mode 100755 new mode 100644 index 60944e58..d128a9b8 --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # This software is a part of Isar. -# Copyright (C) Siemens AG, 2024 +# Copyright (C) Siemens AG, 2026 # # SPDX-License-Identifier: MIT @@ -9,293 +9,202 @@ installdata=${INSTALL_DATA:-/install} SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; ) . "${SCRIPT_DIR}/../lib/deploy-image-wic/handle-config.sh" +. "${SCRIPT_DIR}/sys_api.sh" +. "${SCRIPT_DIR}/installer_ui.sh" -if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then - abort_file=/tmp/attended_mode_trigger - for ((i=$installer_unattended_abort_timeout; i>0; i--)); do - echo -ne "\rUnattended installation will start in $i seconds. Press any key to switch to attended mode..." +#-------------------------------------------------------------------------- +# This module contains high-level installer flow logic +# while keeping low-level backend APIs in sys_api.sh +# and user-facing dialogs in installer_ui.sh. +#-------------------------------------------------------------------------- - # Switch to attended mode if the abort file exists or any key pressed during countdown - # Create abort file to notify all other console instances to abort - if [ -f "$abort_file" ] || read -n 1 -t 1; then - installer_unattended=false - touch "$abort_file" - break - fi - done -fi - -if ! $installer_unattended; then - installer_image_uri=$(find "$installdata" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" -exec basename {} \;) - if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then - pushd "$installdata" - for f in $(find . -type f ! -iname "*.wic.bmap"); do - array+=("$f" "$f") - done - popd - if [ ${#array[@]} -gt 0 ]; then - if ! installer_image_uri=$(dialog --no-tags \ - --menu "Select image to be installed" 10 60 3 \ - "${array[@]}" --output-fd 1); then - exit 0 - fi - fi - fi - - if [ ! -f "$installdata/$installer_image_uri" ]; then - dialog --msgbox "Could not find an image to install. Installation aborted." 6 60 - exit 1 - fi - - # inspired by poky/meta/recipes-core/initrdscripts/files/install-efi.sh - target_device_list="" - current_root_dev_type=$(findmnt / -o fstype -n) - exclude_list=() - - if [ "$current_root_dev_type" = "nfs" ]; then - current_root_dev="nfs" - exclude_list+=("nfs") - else - # For normal or immutable systems, get the backing device of '/' - root_source=$(findmnt / -o source -n) - root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") - current_root_dev=${root_source_resolved#/dev/} - - # Always exclude the exact device mounted as / - exclude_list+=("$current_root_dev") - - if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then - base_no_part="${current_root_dev%p[0-9]*}" - else - base_no_part="${current_root_dev%%[0-9]*}" - fi - if [ -n "$base_no_part" ]; then - exclude_list+=("$base_no_part") - fi - - # If root is coming through a dm-* device (e.g., dm-verity), - # the actual physical devices appear under /sys/block/<dm>/slaves/. - # We must exclude those slaves as well, otherwise the installer - # might show the live USB stick as a valid target. - if [ -d "/sys/block/$current_root_dev/slaves" ]; then - for slave in /sys/block/"$current_root_dev"/slaves/*; do - [ -e "$slave" ] || continue - slave_dev=$(basename "$slave") - exclude_list+=("$slave_dev") - slave_base=${slave_dev%%[0-9]*} - [ -n "$slave_base" ] && exclude_list+=("$slave_base") - done - fi - fi - - echo "Searching for target device..." - - devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true - mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true - devices="$devices $mmc_devices" - - for device in $devices; do - is_raid_member=0 - - if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then - for holder_path in /sys/block/$device/holders/*; do - holder_name=$(basename "$holder_path") - case "$holder_name" in - md[0-9]*) - is_raid_member=1 - break - ;; - esac - done - fi - - if [ "$is_raid_member" -eq 1 ]; then - continue # Skip RAID member disks - fi - - if [[ "$device" == md* ]]; then - if [ -f "/sys/block/$device/md/array_state" ]; then - state=$(cat /sys/block/$device/md/array_state) - if [ "$state" != "active" ] && [ "$state" != "clean" ]; then - continue - fi - else - continue - fi - fi - - case $device in - dm-*) - # skip device-mapper device - ;; - loop*) - # skip loop device - ;; - mtd*) - ;; - sr*) - # skip CDROM device - ;; - ram*) - # skip ram device - ;; - zram*) - # skip zram device - ;; - *) - #skip any excluded devices (root and its slaves) - skip_device=0 - for ex in "${exclude_list[@]}"; do - if [[ "$device" == "$ex"* ]]; then - skip_device=1 - break - fi - done - - if [ "$skip_device" -eq 0 ]; then - target_device_list="$target_device_list $device" - fi - ;; - esac - done - - if [ -z "${target_device_list}" ]; then - dialog --msgbox "You need another device (besides the live device /dev/${current_root_dev}) to install the image. Installation aborted." 7 60 - exit 1 - fi - - if [ "$(echo "$target_device_list" | wc -w)" -gt 1 ]; then - array=() - for target in $(echo "$target_device_list" | xargs -n1 | sort); do - target_size=$(lsblk --nodeps --noheadings -o SIZE /dev/"$target" | tr -d " ") - if cmp /dev/zero /dev/"$target" -n 1M; then - state="empty" - else - state="contains data" - fi - array+=("/dev/$target" "/dev/$target ($target_size, $state)") - done - if ! installer_target_dev=$(dialog --no-tags \ - --menu "Select device to install image to" 10 60 3 \ - "${array[@]}" --output-fd 1); then - exit 0 - fi - else - installer_target_dev="/dev/$(echo "$target_device_list" | tr -d " ")" - fi - TARGET_DEVICE_SIZE=$(lsblk --nodeps --noheadings -o SIZE "$installer_target_dev" | tr -d " ") - if ! dialog --yes-label Ok --no-label Cancel \ - --yesno "Start installing\n'$installer_image_uri'\nto $installer_target_dev (capacity: $TARGET_DEVICE_SIZE)" 7 60; then - exit 0 - fi - - # set absolute paths to be compatible with unattended mode - installer_image_uri="$installdata/$installer_image_uri" -fi - -if ! cmp /dev/zero "$installer_target_dev" -n 1M; then - if ! $installer_unattended; then - if ! dialog --defaultno \ - --yesno "WARNING: Target device is not empty! Continue anyway?" 5 60; then - exit 0 - fi - else - if [ "$installer_target_overwrite" != "OVERWRITE" ]; then - echo "Target device is not empty! -> Abort" - echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." - - exit 1 - fi - fi -fi - -bmap_options="" - -# bmap file is expected to be next to the installer image -DISK_BMAP="${installer_image_uri%.wic*}.wic.bmap" - -if [ ! -f "$DISK_BMAP" ]; then - bmap_options="--nobmap" -fi - -if ! $installer_unattended; then - clear -fi - -# Function to compare version numbers -version_ge() { - if [ "$(printf '%s\n' "$1"X "$2" | sort -V | head -n 1)" != "$1"X ]; then - return 0 - else - return 1 - fi +#-------------------------------------------------------------------------- +# flow_run_attended +# +# Handles all attended-mode interactions and assigns: +# installer_image_uri +# installer_target_dev +# +# Returns: +# 0 on success +# 1 on hard error +# 2 on user cancel +#-------------------------------------------------------------------------- +flow_run_attended() { + local default_image + local selected_image + local selected_target + local target_device_size + + default_image=$(sys_first_default_image_name "$installdata") + if [ -n "$default_image" ] && [ -f "$installdata/$default_image" ]; then + installer_image_uri="$default_image" + fi + + if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then + selected_image=$(ui_select_image_menu "$installdata") + case $? in + 0) + installer_image_uri="$selected_image" + ;; + 1) + ui_show_error "Could not find an image to install. Installation aborted." + return 1 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac + fi + + if [ ! -f "$installdata/$installer_image_uri" ]; then + ui_show_error "Could not find an image to install. Installation aborted." + return 1 + fi + + echo "Searching for target device..." + sys_discover_target_devices + if [ "${#SYS_TARGET_DEVICES[@]}" -eq 0 ]; then + ui_show_error "You need another device (besides the live device /dev/${SYS_CURRENT_ROOT_DEV}) to install the image. Installation aborted." + return 1 + fi + + if [ "${#SYS_TARGET_DEVICES[@]}" -gt 1 ]; then + selected_target=$(ui_select_target_device_menu "${SYS_TARGET_DEVICES[@]}") + case $? in + 0) + installer_target_dev="$selected_target" + ;; + 1) + ui_show_error "No installable target devices available. Installation aborted." + return 1 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac + else + installer_target_dev="${SYS_TARGET_DEVICES[0]}" + fi + + target_device_size=$(sys_device_size "$installer_target_dev") + if ! ui_confirm_install "$installer_image_uri" "$installer_target_dev" "$target_device_size"; then + return 2 + fi + + installer_image_uri="$installdata/$installer_image_uri" + return 0 } -lockfile="/run/installer.lock" -progress_pipe="/run/installer.fifo" - -exec 9>"$lockfile" -if flock -n 9; then - # Get bmap-tools version - bmap_version=$(bmaptool --version | awk '{ print $NF }') - - if version_ge "$bmap_version" "3.6"; then - if ! mkfifo "$progress_pipe"; then - echo "Error: Failed to create named pipe $progress_pipe" - exit 1 - fi - - # Add psplash pipe to bmap_options - bmap_options="$bmap_options --psplash-pipe=$progress_pipe" - quiet_flag="-q" - - # Initialize the dialog gauge and update it dynamically - ( - while true; do - if read -r line < "$progress_pipe"; then - percentage=$(echo "$line" | awk '{ print $2 }') - echo "$percentage" - fi - done - ) | dialog --gauge "Flashing image, please wait..." 10 70 0 & - - gauge_pid=$! - fi - - if ! bmaptool $quiet_flag copy $bmap_options "$installer_image_uri" "$installer_target_dev"; then - kill "$gauge_pid" - exit 1 - fi - - # Attempt to terminate the gauge process if still running. - # Errors are ignored since the process may already have exited. - kill "$gauge_pid" 2>/dev/null -else - echo "Installation already running in another console." +#-------------------------------------------------------------------------- +# flow_validate_target_overwrite_policy +# +# Enforces overwrite policy for non-empty targets in both attended and +# unattended modes. +# +# Returns: +# 0 when policy permits installation +# 1 when policy rejects installation +# 2 when user cancels in attended mode +#-------------------------------------------------------------------------- +flow_validate_target_overwrite_policy() { + if sys_device_is_empty "$installer_target_dev"; then + return 0 + fi + + if ! $installer_unattended; then + if ! ui_confirm_overwrite; then + return 2 + fi + else + if [ "$installer_target_overwrite" != "OVERWRITE" ]; then + echo "Target device is not empty! -> Abort" + echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." + return 1 + fi + fi + + return 0 +} - # Wait for running console to create the progress pipe - sleep 5 +#-------------------------------------------------------------------------- +# flow_maybe_switch_to_attended +# +# If unattended abort-by-key is enabled, this function offers a timeout +# window to switch to attended mode. +#-------------------------------------------------------------------------- +flow_maybe_switch_to_attended() { + local abort_file + + if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then + abort_file=/tmp/attended_mode_trigger + if ui_countdown_allow_attended_switch "$installer_unattended_abort_timeout" "$abort_file"; then + installer_unattended=false + fi + fi +} - # Check if progress pipe exists and has content - if [ -e "$progress_pipe" ]; then - echo "Installation progress..." - tail -f "$progress_pipe" | while read line; do - printf "\r%s%%" "$line" - done - else - # Periodically check if bmaptool is still running every 5 seconds - echo "Waiting for installation to finish..." - while pgrep -x "bmaptool" > /dev/null; do - sleep 5 - done - fi -fi +#-------------------------------------------------------------------------- +# flow_run +# +# Main installer flow combining attended/unattended execution paths. +#-------------------------------------------------------------------------- +flow_run() { + flow_maybe_switch_to_attended + + if ! $installer_unattended; then + flow_run_attended + case $? in + 0) + ;; + 2) + return 0 + ;; + *) + return 1 + ;; + esac + fi + + flow_validate_target_overwrite_policy + case $? in + 0) + ;; + 2) + return 0 + ;; + *) + return 1 + ;; + esac + + if ! $installer_unattended; then + clear + fi + + if ! sys_install_image_with_lock "$installer_image_uri" "$installer_target_dev"; then + if ! $installer_unattended; then + ui_show_error "Installation failed." + fi + return 1 + fi + + if ! $installer_unattended; then + ui_show_info "Installation was successful. System will be rebooted. Please remove the USB stick." + else + echo "Installation was successful." + fi + + return 0 +} -if ! $installer_unattended; then - dialog --title "Reboot" \ - --msgbox "Installation was successful. System will be rebooted. Please remove the USB stick." 6 60 -else - echo "Installation was successful." -fi +# Entrypoint: run the installer flow and propagate status. +flow_run +exit $? -exit 0 -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260427063957.603123-4-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 2026-04-27 6:39 ` [PATCH 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 ` 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users ` (3 more replies) 0 siblings, 4 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> This series refactors the installer by separating backend logic, attended UI, and high-level flow into dedicated modules. The main objectives are: - improve readability and maintainability - keep behavior and user-visible flow unchanged Patch-1: introduces a backend API layer (sys_api.sh) that provides installer functionality via structured shell functions. Patch-2: adapts the existing dialog-based installer frontend to consume these APIs, without changing behavior. Patch-3: high-level orchestration and keep deploy-image-wic.sh as a thin launcher. This keeps the externally used script name stable while making the flow easier to review and maintain. patch-4: update deploy-image recipe to ship the newly split installer modules Kasturi Shekar (4): installer: extract backend helpers to sys_api.sh installer: extract attended UI to installer_ui.sh installer: add installer flow module and entrypoint installer: install seprated installer modules in recipe .../deploy-image/deploy-image_0.1.bb | 6 +- .../files/usr/bin/deploy-image-wic.sh | 475 +++++++----------- .../files/usr/bin/installer_ui.sh | 217 +++++--- .../deploy-image/files/usr/bin/sys_api.sh | 400 ++++++++++++--- 4 files changed, 691 insertions(+), 407 deletions(-) mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260514091951.1572682-1-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 1/4] installer: extract backend helpers to sys_api.sh 2026-05-14 9:19 ` [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 ` 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users ` (2 subsequent siblings) 3 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> Move backend and system-facing installer logic into sys_api.sh This patch introduces backend helper APIs for: - install image discovery - target device discovery and filtering - root/live-device exclusion - RAID member and inactive md filtering - block device state and size helpers - bmap copy execution with locking and progress pipe support Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> --- .../deploy-image/files/usr/bin/sys_api.sh | 400 +++++++++++++++--- 1 file changed, 333 insertions(+), 67 deletions(-) diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh index b144419e..9c9c5840 100644 --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh @@ -1,84 +1,350 @@ #!/usr/bin/env bash +# This software is a part of ISAR. +# Copyright (C) Siemens AG, 2026 # -# sys_api.sh — Ported installer system APIs -# ------------------------------------------------------------------- +# SPDX-License-Identifier: MIT -# Load shared framework utilities -source ./func.sh || { - echo "Error: func.sh not found or not readable." >&2 - exit 1 +#-------------------------------------------------------------------------- +# sys_api.sh - Backend APIs for deploy-image-wic installer. +# +# This file intentionally contains only system/backend logic: +# - image discovery +# - target device discovery and filtering +# - low-level install/copy flow +# +# UI concerns (dialog boxes, menus, confirmations) are implemented in +# installer_ui.sh and called by the main orchestrator script. +#-------------------------------------------------------------------------- + +# Globals populated by sys_discover_target_devices(). +SYS_CURRENT_ROOT_DEV="" +SYS_TARGET_DEVICES=() + +#-------------------------------------------------------------------------- +# sys_first_default_image_name <install_data_dir> +# +# Returns: +# basename of matching *.wic* image (excluding *.wic.bmap), or empty. +#-------------------------------------------------------------------------- +sys_first_default_image_name() { + local install_data_dir="$1" + + find "$install_data_dir" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" \ + -exec basename {} \; | head -n 1 +} + +#-------------------------------------------------------------------------- +# sys_list_installable_entries <install_data_dir> +# +# Returns: +# relative file paths under install_data_dir, one per line. +# Files ending in *.wic.bmap are excluded. +#-------------------------------------------------------------------------- +sys_list_installable_entries() { + local install_data_dir="$1" + + ( + cd "$install_data_dir" || return 1 + find . -type f ! -iname "*.wic.bmap" -print | sed 's#^./##' + ) } -# ------------------------------------------------------------------- -# API: locate_disk_images -# Description: -# Find disk image files (.wic, .wic.bz2) under given path. -# Usage: -# sys_locate_disk_images search_path=/install -# Returns JSON: -# {"error":"", "retval":"0", "images":["/install/image.wic", ...]} -# ------------------------------------------------------------------- -sys_locate_disk_images() { - local -A ARGS - local required=(search_path) - api_args ARGS required[@] "$@" || { - pack_return_data error "$_args_error" retval "1" - return 1 - } - - local fn="${FUNCNAME[0]}" - local path="${ARGS[search_path]}" - - log_info "$fn" "Searching for disk images in '$path'" - - local images - images=$(find "$path" -type f -iname "*.wic*" ! -iname "*.wic.bmap" 2>/dev/null) - - # when no images found in /install - if [[ -z "$images" ]]; then - log_warn "$fn" "No images found." - pack_return_data error "No images found" retval "1" - return 1 +#-------------------------------------------------------------------------- +# sys_resolve_image_path <install_data_dir> <image_or_path> +# +# Arguments: +# install_data_dir - base directory where installable images are stored +# image_or_path - either a relative image filename under install_data_dir +# or an explicit absolute/relative filesystem path +# +# Returns: +# absolute image path if resolvable, else non-zero return code. +#-------------------------------------------------------------------------- +sys_resolve_image_path() { + local install_data_dir="$1" + local image_or_path="$2" + + if [ -f "$image_or_path" ]; then + echo "$image_or_path" + return 0 + fi + + if [ -f "$install_data_dir/$image_or_path" ]; then + echo "$install_data_dir/$image_or_path" + return 0 fi - # Convert newline-separated paths into JSON array elements - local json_images - json_images=$(printf '"%s",' $images | sed 's/,$//') - # final JSON response on stdout - echo "{ \"error\":\"\", \"retval\":\"0\", \"images\":[${json_images}] }" + return 1 } +#-------------------------------------------------------------------------- +# sys_discover_target_devices +# +# Populates globals: +# SYS_CURRENT_ROOT_DEV - current live root device identifier +# SYS_TARGET_DEVICES - array with valid target block devices (/dev/*) +# +# Filtering rules match legacy deploy-image-wic behavior, including: +# - skip installer/live root device and backing devices +# - skip loop/dm/ram/sr/mtd/zram +# - skip inactive md arrays and md members +#-------------------------------------------------------------------------- +sys_discover_target_devices() { + local current_root_dev_type + local current_root_dev + local root_source + local root_source_resolved + local base_no_part + local slave + local slave_dev + local slave_base + local devices + local mmc_devices + local device + local is_raid_member + local holder_path + local holder_name + local state + local skip_device + local ex + + local exclude_list=() + + SYS_TARGET_DEVICES=() + + # Determine the live root device so we do not offer the current system disk + # as an install target. For NFS roots use a special sentinel value. + + current_root_dev_type=$(findmnt / -o fstype -n) + if [ "$current_root_dev_type" = "nfs" ]; then + current_root_dev="nfs" + exclude_list+=("nfs") + else + root_source=$(findmnt / -o source -n) + root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") + current_root_dev=${root_source_resolved#/dev/} + + exclude_list+=("$current_root_dev") + + # Strip partition suffix for common disk names so the base device + # (e.g. /dev/sda or /dev/mmcblk0) also does not become selectable. + if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then + base_no_part="${current_root_dev%p[0-9]*}" + else + base_no_part="${current_root_dev%%[0-9]*}" + fi + + if [ -n "$base_no_part" ]; then + exclude_list+=("$base_no_part") + fi + + # Exclude backing devices for the live root, such as multipath or RAID + # underlying devices exposed via /sys/block/<root>/slaves. + if [ -d "/sys/block/$current_root_dev/slaves" ]; then + for slave in /sys/block/"$current_root_dev"/slaves/*; do + [ -e "$slave" ] || continue + slave_dev=$(basename "$slave") + exclude_list+=("$slave_dev") + slave_base=${slave_dev%%[0-9]*} + [ -n "$slave_base" ] && exclude_list+=("$slave_base") + done + fi + fi + + SYS_CURRENT_ROOT_DEV="$current_root_dev" + + # Gather block devices from /sys/block while preserving mmcblk devices that + # would otherwise be filtered by the main find expression. + devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true + mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true + devices="$devices $mmc_devices" + + for device in $devices; do + is_raid_member=0 + + if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then + for holder_path in /sys/block/$device/holders/*; do + holder_name=$(basename "$holder_path") + case "$holder_name" in + md[0-9]*) + is_raid_member=1 + break + ;; + esac + done + fi + + if [ "$is_raid_member" -eq 1 ]; then + # Skip RAID member devices, because we only want whole block devices + # available for standalone image installation. + continue + fi + + if [[ "$device" == md* ]]; then + # Accept only active or clean md arrays, reject degraded/inactive RAID sets. + if [ -f "/sys/block/$device/md/array_state" ]; then + state=$(cat /sys/block/$device/md/array_state | tr -d '\n' | tr -d ' ') + if [ "$state" != "active" ] && [ "$state" != "clean" ]; then + echo "Skipping RAID device $device: state='$state'" >&2 + continue + fi + echo "Found RAID device $device: state='$state'" >&2 + else + echo "Skipping RAID device $device: no array_state file" >&2 + continue + fi + fi -# ------------------------------------------------------------------- -# API: sys_list_valid_target_devices -# Description: -# Shell-friendly variant of sys_get_valid_target_devices. -# Prints one valid device per line (absolute path). -# Usage: -# sys_list_valid_target_devices -# Output: -# /dev/sdb -# /dev/sdc -# ------------------------------------------------------------------- -sys_list_valid_target_devices() { - local dev - - for dev in /sys/block/*; do - dev=$(basename "$dev") - - case "$dev" in - loop*|ram*|sr*|mtd*) + case "$device" in + dm-*|loop*|mtd*|sr*|ram*|zram*) continue ;; esac - # skip inactive md devices - if [[ "$dev" == md* ]]; then - [ -f "/sys/block/$dev/md/array_state" ] || continue - state=$(cat /sys/block/$dev/md/array_state) - [ "$state" = "active" ] || [ "$state" = "clean" ] || continue - fi + skip_device=0 + for ex in "${exclude_list[@]}"; do + if [[ "$device" == "$ex"* ]]; then + skip_device=1 + break + fi + done - echo "/dev/$dev" + if [ "$skip_device" -eq 0 ]; then + SYS_TARGET_DEVICES+=("/dev/$device") + fi done } + +#-------------------------------------------------------------------------- +# sys_device_size <device> +# +# Returns: +# human-readable size string from lsblk, or empty if unavailable. +#-------------------------------------------------------------------------- +sys_device_size() { + local device="$1" + lsblk --nodeps --noheadings -o SIZE "$device" 2>/dev/null | tr -d " " +} + +#-------------------------------------------------------------------------- +# sys_device_is_empty <device> +# +# Returns: +# 0 when first 1 MiB is zero-filled, 1 otherwise. +#-------------------------------------------------------------------------- +sys_device_is_empty() { + local device="$1" + cmp /dev/zero "$device" -n 1M >/dev/null 2>&1 +} + +#-------------------------------------------------------------------------- +# sys_version_ge <current> <required> +# +# Returns: +# 0 if current >= required, else 1. +#-------------------------------------------------------------------------- +sys_version_ge() { + local current="$1" + local required="$2" + + if [ "$(printf '%s\n' "$current"X "$required" | sort -V | head -n 1)" != "$current"X ]; then + return 0 + fi + return 1 +} + +#-------------------------------------------------------------------------- +# sys_bmap_options_for_image <image_path> +# +# Returns: +# bmaptool options string ("--nobmap" when no sidecar bmap is present). +#-------------------------------------------------------------------------- +sys_bmap_options_for_image() { + local image_path="$1" + local disk_bmap + + disk_bmap="${image_path%.wic*}.wic.bmap" + if [ -f "$disk_bmap" ]; then + # If a .wic.bmap sidecar exists, return an empty option string so + # bmaptool uses the bundled map automatically. + echo "" + else + echo "--nobmap" + fi +} + +#-------------------------------------------------------------------------- +# sys_install_image_with_lock <image_path> <target_device> +# +# Performs the actual bmaptool copy, serializing concurrent installer +# consoles via flock. When UI hook functions are available, this API +# uses them to present a progress gauge in attended mode. +# +# Optional UI hooks (if defined): +# ui_start_progress_gauge <pipe_path> +# ui_stop_progress_gauge +#-------------------------------------------------------------------------- +sys_install_image_with_lock() { + local image_path="$1" + local target_device="$2" + local lockfile="/run/installer.lock" + local progress_pipe="/run/installer.fifo" + local bmap_options + local bmap_version + local quiet_flag="" + + bmap_options=$(sys_bmap_options_for_image "$image_path") + + exec 9>"$lockfile" + if flock -n 9; then + bmap_version=$(bmaptool --version | awk '{ print $NF }') + + if sys_version_ge "$bmap_version" "3.6"; then + if [ -p "$progress_pipe" ]; then + rm -f "$progress_pipe" + fi + + if ! mkfifo "$progress_pipe"; then + echo "Error: Failed to create named pipe $progress_pipe" + return 1 + fi + + bmap_options="$bmap_options --psplash-pipe=$progress_pipe" + quiet_flag="-q" + + if declare -F ui_start_progress_gauge >/dev/null; then + ui_start_progress_gauge "$progress_pipe" + fi + fi + + # Run the actual copying step under the lock to avoid concurrent writes + # from multiple installer consoles or sessions. + if ! bmaptool $quiet_flag copy $bmap_options "$image_path" "$target_device"; then + if declare -F ui_stop_progress_gauge >/dev/null; then + ui_stop_progress_gauge + fi + return 1 + fi + + if declare -F ui_stop_progress_gauge >/dev/null; then + ui_stop_progress_gauge + fi + else + echo "Installation already running in another console." + sleep 5 + + if [ -e "$progress_pipe" ]; then + echo "Installation progress..." + tail -f "$progress_pipe" | while read -r line; do + printf "\r%s%%" "$line" + done + else + echo "Waiting for installation to finish..." + while pgrep -x "bmaptool" >/dev/null; do + sleep 5 + done + fi + fi + + return 0 +} -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260514091951.1572682-2-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 2/4] installer: extract attended UI to installer_ui.sh 2026-05-14 9:19 ` [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 ` 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 4/4] installer: install seprated installer modules in recipe 'Kasturi shekar' via isar-users 3 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar 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 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 -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260514091951.1572682-3-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 3/4] installer: add installer flow module and entrypoint 2026-05-14 9:19 ` [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 ` 'Kasturi shekar' via isar-users 2026-06-04 12:56 ` Anton Mikanovich 2026-05-14 9:19 ` [PATCH v2 4/4] installer: install seprated installer modules in recipe 'Kasturi shekar' via isar-users 3 siblings, 1 reply; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> high-level orchestration and keep deploy-image-wic.sh as a thin launcher. This keeps the externally used script name stable while making the flow easier to review and maintain by separating: - backend APIs - UI frontend - mode/orchestration flow Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> --- .../files/usr/bin/deploy-image-wic.sh | 475 +++++++----------- 1 file changed, 192 insertions(+), 283 deletions(-) mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh old mode 100755 new mode 100644 index 9a4102b2..a692b679 --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # This software is a part of Isar. -# Copyright (C) Siemens AG, 2024 +# Copyright (C) Siemens AG, 2026 # # SPDX-License-Identifier: MIT @@ -9,293 +9,202 @@ installdata=${INSTALL_DATA:-/install} SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; ) . "${SCRIPT_DIR}/../lib/deploy-image-wic/handle-config.sh" +. "${SCRIPT_DIR}/sys_api.sh" +. "${SCRIPT_DIR}/installer_ui.sh" -if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then - abort_file=/run/attended_mode_trigger - for ((i=$installer_unattended_abort_timeout; i>0; i--)); do - echo -ne "\rUnattended installation will start in $i seconds. Press any key to switch to attended mode..." +#-------------------------------------------------------------------------- +# This module contains high-level installer flow logic +# while keeping low-level backend APIs in sys_api.sh +# and user-facing dialogs in installer_ui.sh. +#-------------------------------------------------------------------------- - # Switch to attended mode if the abort file exists or any key pressed during countdown - # Create abort file to notify all other console instances to abort - if [ -f "$abort_file" ] || read -n 1 -t 1; then - installer_unattended=false - touch "$abort_file" - break - fi - done -fi - -if ! $installer_unattended; then - installer_image_uri=$(find "$installdata" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" -exec basename {} \;) - if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then - pushd "$installdata" - for f in $(find . -type f ! -iname "*.wic.bmap"); do - array+=("$f" "$f") - done - popd - if [ ${#array[@]} -gt 0 ]; then - if ! installer_image_uri=$(dialog --no-tags \ - --menu "Select image to be installed" 10 60 3 \ - "${array[@]}" --output-fd 1); then - exit 0 - fi - fi - fi - - if [ ! -f "$installdata/$installer_image_uri" ]; then - dialog --msgbox "Could not find an image to install. Installation aborted." 6 60 - exit 1 - fi - - # inspired by poky/meta/recipes-core/initrdscripts/files/install-efi.sh - target_device_list="" - current_root_dev_type=$(findmnt / -o fstype -n) - exclude_list=() - - if [ "$current_root_dev_type" = "nfs" ]; then - current_root_dev="nfs" - exclude_list+=("nfs") - else - # For normal or immutable systems, get the backing device of '/' - root_source=$(findmnt / -o source -n) - root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") - current_root_dev=${root_source_resolved#/dev/} - - # Always exclude the exact device mounted as / - exclude_list+=("$current_root_dev") - - if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then - base_no_part="${current_root_dev%p[0-9]*}" - else - base_no_part="${current_root_dev%%[0-9]*}" - fi - if [ -n "$base_no_part" ]; then - exclude_list+=("$base_no_part") - fi - - # If root is coming through a dm-* device (e.g., dm-verity), - # the actual physical devices appear under /sys/block/<dm>/slaves/. - # We must exclude those slaves as well, otherwise the installer - # might show the live USB stick as a valid target. - if [ -d "/sys/block/$current_root_dev/slaves" ]; then - for slave in /sys/block/"$current_root_dev"/slaves/*; do - [ -e "$slave" ] || continue - slave_dev=$(basename "$slave") - exclude_list+=("$slave_dev") - slave_base=${slave_dev%%[0-9]*} - [ -n "$slave_base" ] && exclude_list+=("$slave_base") - done - fi - fi - - echo "Searching for target device..." - - devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true - mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true - devices="$devices $mmc_devices" - - for device in $devices; do - is_raid_member=0 - - if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then - for holder_path in /sys/block/$device/holders/*; do - holder_name=$(basename "$holder_path") - case "$holder_name" in - md[0-9]*) - is_raid_member=1 - break - ;; - esac - done - fi - - if [ "$is_raid_member" -eq 1 ]; then - continue # Skip RAID member disks - fi - - if [[ "$device" == md* ]]; then - if [ -f "/sys/block/$device/md/array_state" ]; then - state=$(cat /sys/block/$device/md/array_state) - if [ "$state" != "active" ] && [ "$state" != "clean" ]; then - continue - fi - else - continue - fi - fi - - case $device in - dm-*) - # skip device-mapper device - ;; - loop*) - # skip loop device - ;; - mtd*) - ;; - sr*) - # skip CDROM device - ;; - ram*) - # skip ram device - ;; - zram*) - # skip zram device - ;; - *) - #skip any excluded devices (root and its slaves) - skip_device=0 - for ex in "${exclude_list[@]}"; do - if [[ "$device" == "$ex"* ]]; then - skip_device=1 - break - fi - done - - if [ "$skip_device" -eq 0 ]; then - target_device_list="$target_device_list $device" - fi - ;; - esac - done - - if [ -z "${target_device_list}" ]; then - dialog --msgbox "You need another device (besides the live device /dev/${current_root_dev}) to install the image. Installation aborted." 7 60 - exit 1 - fi - - if [ "$(echo "$target_device_list" | wc -w)" -gt 1 ]; then - array=() - for target in $(echo "$target_device_list" | xargs -n1 | sort); do - target_size=$(lsblk --nodeps --noheadings -o SIZE /dev/"$target" | tr -d " ") - if cmp /dev/zero /dev/"$target" -n 1M; then - state="empty" - else - state="contains data" - fi - array+=("/dev/$target" "/dev/$target ($target_size, $state)") - done - if ! installer_target_dev=$(dialog --no-tags \ - --menu "Select device to install image to" 10 60 3 \ - "${array[@]}" --output-fd 1); then - exit 0 - fi - else - installer_target_dev="/dev/$(echo "$target_device_list" | tr -d " ")" - fi - TARGET_DEVICE_SIZE=$(lsblk --nodeps --noheadings -o SIZE "$installer_target_dev" | tr -d " ") - if ! dialog --yes-label Ok --no-label Cancel \ - --yesno "Start installing\n'$installer_image_uri'\nto $installer_target_dev (capacity: $TARGET_DEVICE_SIZE)" 7 60; then - exit 0 - fi - - # set absolute paths to be compatible with unattended mode - installer_image_uri="$installdata/$installer_image_uri" -fi - -if ! cmp /dev/zero "$installer_target_dev" -n 1M; then - if ! $installer_unattended; then - if ! dialog --defaultno \ - --yesno "WARNING: Target device is not empty! Continue anyway?" 5 60; then - exit 0 - fi - else - if [ "$installer_target_overwrite" != "OVERWRITE" ]; then - echo "Target device is not empty! -> Abort" - echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." - - exit 1 - fi - fi -fi - -bmap_options="" - -# bmap file is expected to be next to the installer image -DISK_BMAP="${installer_image_uri%.wic*}.wic.bmap" - -if [ ! -f "$DISK_BMAP" ]; then - bmap_options="--nobmap" -fi - -if ! $installer_unattended; then - clear -fi - -# Function to compare version numbers -version_ge() { - if [ "$(printf '%s\n' "$1"X "$2" | sort -V | head -n 1)" != "$1"X ]; then - return 0 - else - return 1 - fi +#-------------------------------------------------------------------------- +# flow_run_attended +# +# Handles all attended-mode interactions and assigns: +# installer_image_uri +# installer_target_dev +# +# Returns: +# 0 on success +# 1 on hard error +# 2 on user cancel +#-------------------------------------------------------------------------- +flow_run_attended() { + local default_image + local selected_image + local selected_target + local target_device_size + + default_image=$(sys_first_default_image_name "$installdata") + if [ -n "$default_image" ] && [ -f "$installdata/$default_image" ]; then + installer_image_uri="$default_image" + fi + + if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then + selected_image=$(ui_select_image_menu "$installdata") + case $? in + 0) + installer_image_uri="$selected_image" + ;; + 1) + ui_show_error "Could not find an image to install. Installation aborted." + return 1 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac + fi + + if [ ! -f "$installdata/$installer_image_uri" ]; then + ui_show_error "Could not find an image to install. Installation aborted." + return 1 + fi + + echo "Searching for target device..." + sys_discover_target_devices + if [ "${#SYS_TARGET_DEVICES[@]}" -eq 0 ]; then + ui_show_error "You need another device (besides the live device /dev/${SYS_CURRENT_ROOT_DEV}) to install the image. Installation aborted." + return 1 + fi + + if [ "${#SYS_TARGET_DEVICES[@]}" -gt 1 ]; then + selected_target=$(ui_select_target_device_menu "${SYS_TARGET_DEVICES[@]}") + case $? in + 0) + installer_target_dev="$selected_target" + ;; + 1) + ui_show_error "No installable target devices available. Installation aborted." + return 1 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac + else + installer_target_dev="${SYS_TARGET_DEVICES[0]}" + fi + + target_device_size=$(sys_device_size "$installer_target_dev") + if ! ui_confirm_install "$installer_image_uri" "$installer_target_dev" "$target_device_size"; then + return 2 + fi + + installer_image_uri="$installdata/$installer_image_uri" + return 0 } -lockfile="/run/installer.lock" -progress_pipe="/run/installer.fifo" - -exec 9>"$lockfile" -if flock -n 9; then - # Get bmap-tools version - bmap_version=$(bmaptool --version | awk '{ print $NF }') - - if version_ge "$bmap_version" "3.6"; then - if ! mkfifo "$progress_pipe"; then - echo "Error: Failed to create named pipe $progress_pipe" - exit 1 - fi - - # Add psplash pipe to bmap_options - bmap_options="$bmap_options --psplash-pipe=$progress_pipe" - quiet_flag="-q" - - # Initialize the dialog gauge and update it dynamically - ( - while true; do - if read -r line < "$progress_pipe"; then - percentage=$(echo "$line" | awk '{ print $2 }') - echo "$percentage" - fi - done - ) | dialog --gauge "Flashing image, please wait..." 10 70 0 & - - gauge_pid=$! - fi - - if ! bmaptool $quiet_flag copy $bmap_options "$installer_image_uri" "$installer_target_dev"; then - kill "$gauge_pid" - exit 1 - fi - - # Attempt to terminate the gauge process if still running. - # Errors are ignored since the process may already have exited. - kill "$gauge_pid" 2>/dev/null -else - echo "Installation already running in another console." +#-------------------------------------------------------------------------- +# flow_validate_target_overwrite_policy +# +# Enforces overwrite policy for non-empty targets in both attended and +# unattended modes. +# +# Returns: +# 0 when policy permits installation +# 1 when policy rejects installation +# 2 when user cancels in attended mode +#-------------------------------------------------------------------------- +flow_validate_target_overwrite_policy() { + if sys_device_is_empty "$installer_target_dev"; then + return 0 + fi + + if ! $installer_unattended; then + if ! ui_confirm_overwrite; then + return 2 + fi + else + if [ "$installer_target_overwrite" != "OVERWRITE" ]; then + echo "Target device is not empty! -> Abort" + echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." + return 1 + fi + fi + + return 0 +} - # Wait for running console to create the progress pipe - sleep 5 +#-------------------------------------------------------------------------- +# flow_maybe_switch_to_attended +# +# If unattended abort-by-key is enabled, this function offers a timeout +# window to switch to attended mode. +#-------------------------------------------------------------------------- +flow_maybe_switch_to_attended() { + local abort_file + + if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then + abort_file=/run/attended_mode_trigger + if ui_countdown_allow_attended_switch "$installer_unattended_abort_timeout" "$abort_file"; then + installer_unattended=false + fi + fi +} - # Check if progress pipe exists and has content - if [ -e "$progress_pipe" ]; then - echo "Installation progress..." - tail -f "$progress_pipe" | while read line; do - printf "\r%s%%" "$line" - done - else - # Periodically check if bmaptool is still running every 5 seconds - echo "Waiting for installation to finish..." - while pgrep -x "bmaptool" > /dev/null; do - sleep 5 - done - fi -fi +#-------------------------------------------------------------------------- +# flow_run +# +# Main installer flow combining attended/unattended execution paths. +#-------------------------------------------------------------------------- +flow_run() { + flow_maybe_switch_to_attended + + if ! $installer_unattended; then + flow_run_attended + case $? in + 0) + ;; + 2) + return 0 + ;; + *) + return 1 + ;; + esac + fi + + flow_validate_target_overwrite_policy + case $? in + 0) + ;; + 2) + return 0 + ;; + *) + return 1 + ;; + esac + + if ! $installer_unattended; then + clear + fi + + if ! sys_install_image_with_lock "$installer_image_uri" "$installer_target_dev"; then + if ! $installer_unattended; then + ui_show_error "Installation failed." + fi + return 1 + fi + + if ! $installer_unattended; then + ui_show_info "Installation was successful. System will be rebooted. Please remove the USB stick." + else + echo "Installation was successful." + fi + + return 0 +} -if ! $installer_unattended; then - dialog --title "Reboot" \ - --msgbox "Installation was successful. System will be rebooted. Please remove the USB stick." 6 60 -else - echo "Installation was successful." -fi +# Entrypoint: run the installer flow and propagate status. +flow_run +exit $? -exit 0 -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260514091951.1572682-4-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v2 3/4] installer: add installer flow module and entrypoint 2026-05-14 9:19 ` [PATCH v2 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users @ 2026-06-04 12:56 ` Anton Mikanovich 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 0 siblings, 1 reply; 18+ messages in thread From: Anton Mikanovich @ 2026-06-04 12:56 UTC (permalink / raw) To: Kasturi shekar, isar-users Hello Kasturi, 14.05.2026 12:19, 'Kasturi shekar' via isar-users wrote: > From: Kasturi Shekar <kasturi.shekar@siemens.com> > > high-level orchestration and keep deploy-image-wic.sh as a thin launcher. > > This keeps the externally used script name stable while making the flow easier > to review and maintain by separating: > - backend APIs > - UI frontend > - mode/orchestration flow > > Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> > --- > .../files/usr/bin/deploy-image-wic.sh | 475 +++++++----------- > 1 file changed, 192 insertions(+), 283 deletions(-) > mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh > > diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh > old mode 100755 > new mode 100644 > index 9a4102b2..a692b679 > --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh > +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh > @@ -1,6 +1,6 @@ > #!/usr/bin/env bash > # This software is a part of Isar. > -# Copyright (C) Siemens AG, 2024 > +# Copyright (C) Siemens AG, 2026 > # > # SPDX-License-Identifier: MIT > > @@ -9,293 +9,202 @@ installdata=${INSTALL_DATA:-/install} > SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; ) > > . "${SCRIPT_DIR}/../lib/deploy-image-wic/handle-config.sh" > +. "${SCRIPT_DIR}/sys_api.sh" > +. "${SCRIPT_DIR}/installer_ui.sh" > > -if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then > - abort_file=/run/attended_mode_trigger > - for ((i=$installer_unattended_abort_timeout; i>0; i--)); do > - echo -ne "\rUnattended installation will start in $i seconds. Press any key to switch to attended mode..." > +#-------------------------------------------------------------------------- > +# This module contains high-level installer flow logic > +# while keeping low-level backend APIs in sys_api.sh > +# and user-facing dialogs in installer_ui.sh. > +#-------------------------------------------------------------------------- > > - # Switch to attended mode if the abort file exists or any key pressed during countdown > - # Create abort file to notify all other console instances to abort > - if [ -f "$abort_file" ] || read -n 1 -t 1; then > - installer_unattended=false > - touch "$abort_file" > - break > - fi > - done > -fi > - > -if ! $installer_unattended; then > - installer_image_uri=$(find "$installdata" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" -exec basename {} \;) > - if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then > - pushd "$installdata" > - for f in $(find . -type f ! -iname "*.wic.bmap"); do > - array+=("$f" "$f") > - done > - popd > - if [ ${#array[@]} -gt 0 ]; then > - if ! installer_image_uri=$(dialog --no-tags \ > - --menu "Select image to be installed" 10 60 3 \ > - "${array[@]}" --output-fd 1); then > - exit 0 > - fi > - fi > - fi > - > - if [ ! -f "$installdata/$installer_image_uri" ]; then > - dialog --msgbox "Could not find an image to install. Installation aborted." 6 60 > - exit 1 > - fi > - > - # inspired by poky/meta/recipes-core/initrdscripts/files/install-efi.sh > - target_device_list="" > - current_root_dev_type=$(findmnt / -o fstype -n) > - exclude_list=() > - > - if [ "$current_root_dev_type" = "nfs" ]; then > - current_root_dev="nfs" > - exclude_list+=("nfs") > - else > - # For normal or immutable systems, get the backing device of '/' > - root_source=$(findmnt / -o source -n) > - root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") > - current_root_dev=${root_source_resolved#/dev/} > - > - # Always exclude the exact device mounted as / > - exclude_list+=("$current_root_dev") > - > - if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then > - base_no_part="${current_root_dev%p[0-9]*}" > - else > - base_no_part="${current_root_dev%%[0-9]*}" > - fi > - if [ -n "$base_no_part" ]; then > - exclude_list+=("$base_no_part") > - fi > - > - # If root is coming through a dm-* device (e.g., dm-verity), > - # the actual physical devices appear under /sys/block/<dm>/slaves/. > - # We must exclude those slaves as well, otherwise the installer > - # might show the live USB stick as a valid target. > - if [ -d "/sys/block/$current_root_dev/slaves" ]; then > - for slave in /sys/block/"$current_root_dev"/slaves/*; do > - [ -e "$slave" ] || continue > - slave_dev=$(basename "$slave") > - exclude_list+=("$slave_dev") > - slave_base=${slave_dev%%[0-9]*} > - [ -n "$slave_base" ] && exclude_list+=("$slave_base") > - done > - fi > - fi > - > - echo "Searching for target device..." > - > - devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true > - mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true > - devices="$devices $mmc_devices" > - > - for device in $devices; do > - is_raid_member=0 > - > - if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then > - for holder_path in /sys/block/$device/holders/*; do > - holder_name=$(basename "$holder_path") > - case "$holder_name" in > - md[0-9]*) > - is_raid_member=1 > - break > - ;; > - esac > - done > - fi > - > - if [ "$is_raid_member" -eq 1 ]; then > - continue # Skip RAID member disks > - fi > - > - if [[ "$device" == md* ]]; then > - if [ -f "/sys/block/$device/md/array_state" ]; then > - state=$(cat /sys/block/$device/md/array_state) > - if [ "$state" != "active" ] && [ "$state" != "clean" ]; then > - continue > - fi > - else > - continue > - fi > - fi > - > - case $device in > - dm-*) > - # skip device-mapper device > - ;; > - loop*) > - # skip loop device > - ;; > - mtd*) > - ;; > - sr*) > - # skip CDROM device > - ;; > - ram*) > - # skip ram device > - ;; > - zram*) > - # skip zram device > - ;; > - *) > - #skip any excluded devices (root and its slaves) > - skip_device=0 > - for ex in "${exclude_list[@]}"; do > - if [[ "$device" == "$ex"* ]]; then > - skip_device=1 > - break > - fi > - done > - > - if [ "$skip_device" -eq 0 ]; then > - target_device_list="$target_device_list $device" > - fi > - ;; > - esac > - done > - > - if [ -z "${target_device_list}" ]; then > - dialog --msgbox "You need another device (besides the live device /dev/${current_root_dev}) to install the image. Installation aborted." 7 60 > - exit 1 > - fi > - > - if [ "$(echo "$target_device_list" | wc -w)" -gt 1 ]; then > - array=() > - for target in $(echo "$target_device_list" | xargs -n1 | sort); do > - target_size=$(lsblk --nodeps --noheadings -o SIZE /dev/"$target" | tr -d " ") > - if cmp /dev/zero /dev/"$target" -n 1M; then > - state="empty" > - else > - state="contains data" > - fi > - array+=("/dev/$target" "/dev/$target ($target_size, $state)") > - done > - if ! installer_target_dev=$(dialog --no-tags \ > - --menu "Select device to install image to" 10 60 3 \ > - "${array[@]}" --output-fd 1); then > - exit 0 > - fi > - else > - installer_target_dev="/dev/$(echo "$target_device_list" | tr -d " ")" > - fi > - TARGET_DEVICE_SIZE=$(lsblk --nodeps --noheadings -o SIZE "$installer_target_dev" | tr -d " ") > - if ! dialog --yes-label Ok --no-label Cancel \ > - --yesno "Start installing\n'$installer_image_uri'\nto $installer_target_dev (capacity: $TARGET_DEVICE_SIZE)" 7 60; then > - exit 0 > - fi > - > - # set absolute paths to be compatible with unattended mode > - installer_image_uri="$installdata/$installer_image_uri" > -fi > - > -if ! cmp /dev/zero "$installer_target_dev" -n 1M; then > - if ! $installer_unattended; then > - if ! dialog --defaultno \ > - --yesno "WARNING: Target device is not empty! Continue anyway?" 5 60; then > - exit 0 > - fi > - else > - if [ "$installer_target_overwrite" != "OVERWRITE" ]; then > - echo "Target device is not empty! -> Abort" > - echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." > - > - exit 1 > - fi > - fi > -fi > - > -bmap_options="" > - > -# bmap file is expected to be next to the installer image > -DISK_BMAP="${installer_image_uri%.wic*}.wic.bmap" > - > -if [ ! -f "$DISK_BMAP" ]; then > - bmap_options="--nobmap" > -fi > - > -if ! $installer_unattended; then > - clear > -fi > - > -# Function to compare version numbers > -version_ge() { > - if [ "$(printf '%s\n' "$1"X "$2" | sort -V | head -n 1)" != "$1"X ]; then > - return 0 > - else > - return 1 > - fi > +#-------------------------------------------------------------------------- > +# flow_run_attended > +# > +# Handles all attended-mode interactions and assigns: > +# installer_image_uri > +# installer_target_dev > +# > +# Returns: > +# 0 on success > +# 1 on hard error > +# 2 on user cancel > +#-------------------------------------------------------------------------- > +flow_run_attended() { > + local default_image > + local selected_image > + local selected_target > + local target_device_size > + > + default_image=$(sys_first_default_image_name "$installdata") > + if [ -n "$default_image" ] && [ -f "$installdata/$default_image" ]; then > + installer_image_uri="$default_image" > + fi > + > + if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then This check is never hit because installer_image_uri is always set after calling sys_first_default_image_name. It results in skipping image selection dialog and always installing the first one. > + selected_image=$(ui_select_image_menu "$installdata") > + case $? in > + 0) > + installer_image_uri="$selected_image" > + ;; > + 1) > + ui_show_error "Could not find an image to install. Installation aborted." > + return 1 > + ;; > + 2) > + return 2 > + ;; > + *) > + return 1 > + ;; > + esac > + fi > + > + if [ ! -f "$installdata/$installer_image_uri" ]; then > + ui_show_error "Could not find an image to install. Installation aborted." > + return 1 > + fi > + > + echo "Searching for target device..." > + sys_discover_target_devices > + if [ "${#SYS_TARGET_DEVICES[@]}" -eq 0 ]; then > + ui_show_error "You need another device (besides the live device /dev/${SYS_CURRENT_ROOT_DEV}) to install the image. Installation aborted." > + return 1 > + fi > + > + if [ "${#SYS_TARGET_DEVICES[@]}" -gt 1 ]; then > + selected_target=$(ui_select_target_device_menu "${SYS_TARGET_DEVICES[@]}") > + case $? in > + 0) > + installer_target_dev="$selected_target" > + ;; > + 1) > + ui_show_error "No installable target devices available. Installation aborted." > + return 1 > + ;; > + 2) > + return 2 > + ;; > + *) > + return 1 > + ;; > + esac > + else > + installer_target_dev="${SYS_TARGET_DEVICES[0]}" > + fi > + > + target_device_size=$(sys_device_size "$installer_target_dev") > + if ! ui_confirm_install "$installer_image_uri" "$installer_target_dev" "$target_device_size"; then > + return 2 > + fi > + > + installer_image_uri="$installdata/$installer_image_uri" > + return 0 > } > > -lockfile="/run/installer.lock" > -progress_pipe="/run/installer.fifo" > - > -exec 9>"$lockfile" > -if flock -n 9; then > - # Get bmap-tools version > - bmap_version=$(bmaptool --version | awk '{ print $NF }') > - > - if version_ge "$bmap_version" "3.6"; then > - if ! mkfifo "$progress_pipe"; then > - echo "Error: Failed to create named pipe $progress_pipe" > - exit 1 > - fi > - > - # Add psplash pipe to bmap_options > - bmap_options="$bmap_options --psplash-pipe=$progress_pipe" > - quiet_flag="-q" > - > - # Initialize the dialog gauge and update it dynamically > - ( > - while true; do > - if read -r line < "$progress_pipe"; then > - percentage=$(echo "$line" | awk '{ print $2 }') > - echo "$percentage" > - fi > - done > - ) | dialog --gauge "Flashing image, please wait..." 10 70 0 & > - > - gauge_pid=$! > - fi > - > - if ! bmaptool $quiet_flag copy $bmap_options "$installer_image_uri" "$installer_target_dev"; then > - kill "$gauge_pid" > - exit 1 > - fi > - > - # Attempt to terminate the gauge process if still running. > - # Errors are ignored since the process may already have exited. > - kill "$gauge_pid" 2>/dev/null > -else > - echo "Installation already running in another console." > +#-------------------------------------------------------------------------- > +# flow_validate_target_overwrite_policy > +# > +# Enforces overwrite policy for non-empty targets in both attended and > +# unattended modes. > +# > +# Returns: > +# 0 when policy permits installation > +# 1 when policy rejects installation > +# 2 when user cancels in attended mode > +#-------------------------------------------------------------------------- > +flow_validate_target_overwrite_policy() { > + if sys_device_is_empty "$installer_target_dev"; then > + return 0 > + fi > + > + if ! $installer_unattended; then > + if ! ui_confirm_overwrite; then > + return 2 > + fi > + else > + if [ "$installer_target_overwrite" != "OVERWRITE" ]; then > + echo "Target device is not empty! -> Abort" > + echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." > + return 1 > + fi > + fi > + > + return 0 > +} > > - # Wait for running console to create the progress pipe > - sleep 5 > +#-------------------------------------------------------------------------- > +# flow_maybe_switch_to_attended > +# > +# If unattended abort-by-key is enabled, this function offers a timeout > +# window to switch to attended mode. > +#-------------------------------------------------------------------------- > +flow_maybe_switch_to_attended() { > + local abort_file > + > + if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then > + abort_file=/run/attended_mode_trigger > + if ui_countdown_allow_attended_switch "$installer_unattended_abort_timeout" "$abort_file"; then > + installer_unattended=false > + fi > + fi > +} > > - # Check if progress pipe exists and has content > - if [ -e "$progress_pipe" ]; then > - echo "Installation progress..." > - tail -f "$progress_pipe" | while read line; do > - printf "\r%s%%" "$line" > - done > - else > - # Periodically check if bmaptool is still running every 5 seconds > - echo "Waiting for installation to finish..." > - while pgrep -x "bmaptool" > /dev/null; do > - sleep 5 > - done > - fi > -fi > +#-------------------------------------------------------------------------- > +# flow_run > +# > +# Main installer flow combining attended/unattended execution paths. > +#-------------------------------------------------------------------------- > +flow_run() { > + flow_maybe_switch_to_attended > + > + if ! $installer_unattended; then > + flow_run_attended > + case $? in > + 0) > + ;; > + 2) > + return 0 > + ;; > + *) > + return 1 > + ;; > + esac > + fi > + > + flow_validate_target_overwrite_policy > + case $? in > + 0) > + ;; > + 2) > + return 0 > + ;; > + *) > + return 1 > + ;; > + esac > + > + if ! $installer_unattended; then > + clear > + fi > + > + if ! sys_install_image_with_lock "$installer_image_uri" "$installer_target_dev"; then > + if ! $installer_unattended; then > + ui_show_error "Installation failed." > + fi > + return 1 > + fi > + > + if ! $installer_unattended; then > + ui_show_info "Installation was successful. System will be rebooted. Please remove the USB stick." > + else > + echo "Installation was successful." > + fi > + > + return 0 > +} > > -if ! $installer_unattended; then > - dialog --title "Reboot" \ > - --msgbox "Installation was successful. System will be rebooted. Please remove the USB stick." 6 60 > -else > - echo "Installation was successful." > -fi > +# Entrypoint: run the installer flow and propagate status. > +flow_run > +exit $? > > -exit 0 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/8b658081-ce7b-4ffc-b108-1691f5a8ceaf%40ilbers.de. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 2026-06-04 12:56 ` Anton Mikanovich @ 2026-06-08 5:17 ` 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users ` (4 more replies) 0 siblings, 5 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> This series refactors the installer by separating backend logic, attended UI, and high-level flow into dedicated modules. The main objectives are: - improve readability and maintainability - keep behavior and user-visible flow unchanged Patch-1: introduces a backend API layer (sys_api.sh) that provides installer functionality via structured shell functions. Patch-2: adapts the existing dialog-based installer frontend to consume these APIs, without changing behavior. Patch-3: high-level orchestration and keep deploy-image-wic.sh as a thin launcher. This keeps the externally used script name stable while making the flow easier to review and maintain. patch-4: update deploy-image recipe to ship the newly split installer modules .../deploy-image/deploy-image_0.1.bb | 6 +- .../files/usr/bin/deploy-image-wic.sh | 472 +++++++----------- .../files/usr/bin/installer_ui.sh | 217 +++++--- .../deploy-image/files/usr/bin/sys_api.sh | 400 ++++++++++++--- 4 files changed, 688 insertions(+), 407 deletions(-) mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260608051737.946413-1-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 1/4] installer: extract backend helpers to sys_api.sh 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 ` 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users ` (3 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> Move backend and system-facing installer logic into sys_api.sh This patch introduces backend helper APIs for: - install image discovery - target device discovery and filtering - root/live-device exclusion - RAID member and inactive md filtering - block device state and size helpers - bmap copy execution with locking and progress pipe support Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> --- .../deploy-image/files/usr/bin/sys_api.sh | 400 +++++++++++++++--- 1 file changed, 333 insertions(+), 67 deletions(-) diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh index b144419e..9c9c5840 100644 --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/sys_api.sh @@ -1,84 +1,350 @@ #!/usr/bin/env bash +# This software is a part of ISAR. +# Copyright (C) Siemens AG, 2026 # -# sys_api.sh — Ported installer system APIs -# ------------------------------------------------------------------- +# SPDX-License-Identifier: MIT -# Load shared framework utilities -source ./func.sh || { - echo "Error: func.sh not found or not readable." >&2 - exit 1 +#-------------------------------------------------------------------------- +# sys_api.sh - Backend APIs for deploy-image-wic installer. +# +# This file intentionally contains only system/backend logic: +# - image discovery +# - target device discovery and filtering +# - low-level install/copy flow +# +# UI concerns (dialog boxes, menus, confirmations) are implemented in +# installer_ui.sh and called by the main orchestrator script. +#-------------------------------------------------------------------------- + +# Globals populated by sys_discover_target_devices(). +SYS_CURRENT_ROOT_DEV="" +SYS_TARGET_DEVICES=() + +#-------------------------------------------------------------------------- +# sys_first_default_image_name <install_data_dir> +# +# Returns: +# basename of matching *.wic* image (excluding *.wic.bmap), or empty. +#-------------------------------------------------------------------------- +sys_first_default_image_name() { + local install_data_dir="$1" + + find "$install_data_dir" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" \ + -exec basename {} \; | head -n 1 +} + +#-------------------------------------------------------------------------- +# sys_list_installable_entries <install_data_dir> +# +# Returns: +# relative file paths under install_data_dir, one per line. +# Files ending in *.wic.bmap are excluded. +#-------------------------------------------------------------------------- +sys_list_installable_entries() { + local install_data_dir="$1" + + ( + cd "$install_data_dir" || return 1 + find . -type f ! -iname "*.wic.bmap" -print | sed 's#^./##' + ) } -# ------------------------------------------------------------------- -# API: locate_disk_images -# Description: -# Find disk image files (.wic, .wic.bz2) under given path. -# Usage: -# sys_locate_disk_images search_path=/install -# Returns JSON: -# {"error":"", "retval":"0", "images":["/install/image.wic", ...]} -# ------------------------------------------------------------------- -sys_locate_disk_images() { - local -A ARGS - local required=(search_path) - api_args ARGS required[@] "$@" || { - pack_return_data error "$_args_error" retval "1" - return 1 - } - - local fn="${FUNCNAME[0]}" - local path="${ARGS[search_path]}" - - log_info "$fn" "Searching for disk images in '$path'" - - local images - images=$(find "$path" -type f -iname "*.wic*" ! -iname "*.wic.bmap" 2>/dev/null) - - # when no images found in /install - if [[ -z "$images" ]]; then - log_warn "$fn" "No images found." - pack_return_data error "No images found" retval "1" - return 1 +#-------------------------------------------------------------------------- +# sys_resolve_image_path <install_data_dir> <image_or_path> +# +# Arguments: +# install_data_dir - base directory where installable images are stored +# image_or_path - either a relative image filename under install_data_dir +# or an explicit absolute/relative filesystem path +# +# Returns: +# absolute image path if resolvable, else non-zero return code. +#-------------------------------------------------------------------------- +sys_resolve_image_path() { + local install_data_dir="$1" + local image_or_path="$2" + + if [ -f "$image_or_path" ]; then + echo "$image_or_path" + return 0 + fi + + if [ -f "$install_data_dir/$image_or_path" ]; then + echo "$install_data_dir/$image_or_path" + return 0 fi - # Convert newline-separated paths into JSON array elements - local json_images - json_images=$(printf '"%s",' $images | sed 's/,$//') - # final JSON response on stdout - echo "{ \"error\":\"\", \"retval\":\"0\", \"images\":[${json_images}] }" + return 1 } +#-------------------------------------------------------------------------- +# sys_discover_target_devices +# +# Populates globals: +# SYS_CURRENT_ROOT_DEV - current live root device identifier +# SYS_TARGET_DEVICES - array with valid target block devices (/dev/*) +# +# Filtering rules match legacy deploy-image-wic behavior, including: +# - skip installer/live root device and backing devices +# - skip loop/dm/ram/sr/mtd/zram +# - skip inactive md arrays and md members +#-------------------------------------------------------------------------- +sys_discover_target_devices() { + local current_root_dev_type + local current_root_dev + local root_source + local root_source_resolved + local base_no_part + local slave + local slave_dev + local slave_base + local devices + local mmc_devices + local device + local is_raid_member + local holder_path + local holder_name + local state + local skip_device + local ex + + local exclude_list=() + + SYS_TARGET_DEVICES=() + + # Determine the live root device so we do not offer the current system disk + # as an install target. For NFS roots use a special sentinel value. + + current_root_dev_type=$(findmnt / -o fstype -n) + if [ "$current_root_dev_type" = "nfs" ]; then + current_root_dev="nfs" + exclude_list+=("nfs") + else + root_source=$(findmnt / -o source -n) + root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") + current_root_dev=${root_source_resolved#/dev/} + + exclude_list+=("$current_root_dev") + + # Strip partition suffix for common disk names so the base device + # (e.g. /dev/sda or /dev/mmcblk0) also does not become selectable. + if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then + base_no_part="${current_root_dev%p[0-9]*}" + else + base_no_part="${current_root_dev%%[0-9]*}" + fi + + if [ -n "$base_no_part" ]; then + exclude_list+=("$base_no_part") + fi + + # Exclude backing devices for the live root, such as multipath or RAID + # underlying devices exposed via /sys/block/<root>/slaves. + if [ -d "/sys/block/$current_root_dev/slaves" ]; then + for slave in /sys/block/"$current_root_dev"/slaves/*; do + [ -e "$slave" ] || continue + slave_dev=$(basename "$slave") + exclude_list+=("$slave_dev") + slave_base=${slave_dev%%[0-9]*} + [ -n "$slave_base" ] && exclude_list+=("$slave_base") + done + fi + fi + + SYS_CURRENT_ROOT_DEV="$current_root_dev" + + # Gather block devices from /sys/block while preserving mmcblk devices that + # would otherwise be filtered by the main find expression. + devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true + mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true + devices="$devices $mmc_devices" + + for device in $devices; do + is_raid_member=0 + + if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then + for holder_path in /sys/block/$device/holders/*; do + holder_name=$(basename "$holder_path") + case "$holder_name" in + md[0-9]*) + is_raid_member=1 + break + ;; + esac + done + fi + + if [ "$is_raid_member" -eq 1 ]; then + # Skip RAID member devices, because we only want whole block devices + # available for standalone image installation. + continue + fi + + if [[ "$device" == md* ]]; then + # Accept only active or clean md arrays, reject degraded/inactive RAID sets. + if [ -f "/sys/block/$device/md/array_state" ]; then + state=$(cat /sys/block/$device/md/array_state | tr -d '\n' | tr -d ' ') + if [ "$state" != "active" ] && [ "$state" != "clean" ]; then + echo "Skipping RAID device $device: state='$state'" >&2 + continue + fi + echo "Found RAID device $device: state='$state'" >&2 + else + echo "Skipping RAID device $device: no array_state file" >&2 + continue + fi + fi -# ------------------------------------------------------------------- -# API: sys_list_valid_target_devices -# Description: -# Shell-friendly variant of sys_get_valid_target_devices. -# Prints one valid device per line (absolute path). -# Usage: -# sys_list_valid_target_devices -# Output: -# /dev/sdb -# /dev/sdc -# ------------------------------------------------------------------- -sys_list_valid_target_devices() { - local dev - - for dev in /sys/block/*; do - dev=$(basename "$dev") - - case "$dev" in - loop*|ram*|sr*|mtd*) + case "$device" in + dm-*|loop*|mtd*|sr*|ram*|zram*) continue ;; esac - # skip inactive md devices - if [[ "$dev" == md* ]]; then - [ -f "/sys/block/$dev/md/array_state" ] || continue - state=$(cat /sys/block/$dev/md/array_state) - [ "$state" = "active" ] || [ "$state" = "clean" ] || continue - fi + skip_device=0 + for ex in "${exclude_list[@]}"; do + if [[ "$device" == "$ex"* ]]; then + skip_device=1 + break + fi + done - echo "/dev/$dev" + if [ "$skip_device" -eq 0 ]; then + SYS_TARGET_DEVICES+=("/dev/$device") + fi done } + +#-------------------------------------------------------------------------- +# sys_device_size <device> +# +# Returns: +# human-readable size string from lsblk, or empty if unavailable. +#-------------------------------------------------------------------------- +sys_device_size() { + local device="$1" + lsblk --nodeps --noheadings -o SIZE "$device" 2>/dev/null | tr -d " " +} + +#-------------------------------------------------------------------------- +# sys_device_is_empty <device> +# +# Returns: +# 0 when first 1 MiB is zero-filled, 1 otherwise. +#-------------------------------------------------------------------------- +sys_device_is_empty() { + local device="$1" + cmp /dev/zero "$device" -n 1M >/dev/null 2>&1 +} + +#-------------------------------------------------------------------------- +# sys_version_ge <current> <required> +# +# Returns: +# 0 if current >= required, else 1. +#-------------------------------------------------------------------------- +sys_version_ge() { + local current="$1" + local required="$2" + + if [ "$(printf '%s\n' "$current"X "$required" | sort -V | head -n 1)" != "$current"X ]; then + return 0 + fi + return 1 +} + +#-------------------------------------------------------------------------- +# sys_bmap_options_for_image <image_path> +# +# Returns: +# bmaptool options string ("--nobmap" when no sidecar bmap is present). +#-------------------------------------------------------------------------- +sys_bmap_options_for_image() { + local image_path="$1" + local disk_bmap + + disk_bmap="${image_path%.wic*}.wic.bmap" + if [ -f "$disk_bmap" ]; then + # If a .wic.bmap sidecar exists, return an empty option string so + # bmaptool uses the bundled map automatically. + echo "" + else + echo "--nobmap" + fi +} + +#-------------------------------------------------------------------------- +# sys_install_image_with_lock <image_path> <target_device> +# +# Performs the actual bmaptool copy, serializing concurrent installer +# consoles via flock. When UI hook functions are available, this API +# uses them to present a progress gauge in attended mode. +# +# Optional UI hooks (if defined): +# ui_start_progress_gauge <pipe_path> +# ui_stop_progress_gauge +#-------------------------------------------------------------------------- +sys_install_image_with_lock() { + local image_path="$1" + local target_device="$2" + local lockfile="/run/installer.lock" + local progress_pipe="/run/installer.fifo" + local bmap_options + local bmap_version + local quiet_flag="" + + bmap_options=$(sys_bmap_options_for_image "$image_path") + + exec 9>"$lockfile" + if flock -n 9; then + bmap_version=$(bmaptool --version | awk '{ print $NF }') + + if sys_version_ge "$bmap_version" "3.6"; then + if [ -p "$progress_pipe" ]; then + rm -f "$progress_pipe" + fi + + if ! mkfifo "$progress_pipe"; then + echo "Error: Failed to create named pipe $progress_pipe" + return 1 + fi + + bmap_options="$bmap_options --psplash-pipe=$progress_pipe" + quiet_flag="-q" + + if declare -F ui_start_progress_gauge >/dev/null; then + ui_start_progress_gauge "$progress_pipe" + fi + fi + + # Run the actual copying step under the lock to avoid concurrent writes + # from multiple installer consoles or sessions. + if ! bmaptool $quiet_flag copy $bmap_options "$image_path" "$target_device"; then + if declare -F ui_stop_progress_gauge >/dev/null; then + ui_stop_progress_gauge + fi + return 1 + fi + + if declare -F ui_stop_progress_gauge >/dev/null; then + ui_stop_progress_gauge + fi + else + echo "Installation already running in another console." + sleep 5 + + if [ -e "$progress_pipe" ]; then + echo "Installation progress..." + tail -f "$progress_pipe" | while read -r line; do + printf "\r%s%%" "$line" + done + else + echo "Waiting for installation to finish..." + while pgrep -x "bmaptool" >/dev/null; do + sleep 5 + done + fi + fi + + return 0 +} -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260608051737.946413-2-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 2/4] installer: extract attended UI to installer_ui.sh 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 ` 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users ` (2 subsequent siblings) 4 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar 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 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 -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260608051737.946413-3-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 3/4] installer: add installer flow module and entrypoint 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 ` 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 4/4] installer: install seprated installer modules in recipe 'Kasturi shekar' via isar-users 2026-06-12 10:50 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules Anton Mikanovich 4 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> high-level orchestration and keep deploy-image-wic.sh as a thin launcher. This keeps the externally used script name stable while making the flow easier to review and maintain by separating: - backend APIs - UI frontend - mode/orchestration flow Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> --- .../files/usr/bin/deploy-image-wic.sh | 472 +++++++----------- 1 file changed, 189 insertions(+), 283 deletions(-) mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh diff --git a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh old mode 100755 new mode 100644 index 9a4102b2..42107d16 --- a/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh +++ b/meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # This software is a part of Isar. -# Copyright (C) Siemens AG, 2024 +# Copyright (C) Siemens AG, 2026 # # SPDX-License-Identifier: MIT @@ -9,293 +9,199 @@ installdata=${INSTALL_DATA:-/install} SCRIPT_DIR=$( dirname -- "$( readlink -f -- "$0"; )"; ) . "${SCRIPT_DIR}/../lib/deploy-image-wic/handle-config.sh" +. "${SCRIPT_DIR}/sys_api.sh" +. "${SCRIPT_DIR}/installer_ui.sh" -if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then - abort_file=/run/attended_mode_trigger - for ((i=$installer_unattended_abort_timeout; i>0; i--)); do - echo -ne "\rUnattended installation will start in $i seconds. Press any key to switch to attended mode..." +#-------------------------------------------------------------------------- +# This module contains high-level installer flow logic +# while keeping low-level backend APIs in sys_api.sh +# and user-facing dialogs in installer_ui.sh. +#-------------------------------------------------------------------------- - # Switch to attended mode if the abort file exists or any key pressed during countdown - # Create abort file to notify all other console instances to abort - if [ -f "$abort_file" ] || read -n 1 -t 1; then - installer_unattended=false - touch "$abort_file" - break - fi - done -fi - -if ! $installer_unattended; then - installer_image_uri=$(find "$installdata" -type f -iname "*.wic*" -a -not -iname "*.wic.bmap" -exec basename {} \;) - if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then - pushd "$installdata" - for f in $(find . -type f ! -iname "*.wic.bmap"); do - array+=("$f" "$f") - done - popd - if [ ${#array[@]} -gt 0 ]; then - if ! installer_image_uri=$(dialog --no-tags \ - --menu "Select image to be installed" 10 60 3 \ - "${array[@]}" --output-fd 1); then - exit 0 - fi - fi - fi - - if [ ! -f "$installdata/$installer_image_uri" ]; then - dialog --msgbox "Could not find an image to install. Installation aborted." 6 60 - exit 1 - fi - - # inspired by poky/meta/recipes-core/initrdscripts/files/install-efi.sh - target_device_list="" - current_root_dev_type=$(findmnt / -o fstype -n) - exclude_list=() - - if [ "$current_root_dev_type" = "nfs" ]; then - current_root_dev="nfs" - exclude_list+=("nfs") - else - # For normal or immutable systems, get the backing device of '/' - root_source=$(findmnt / -o source -n) - root_source_resolved=$(readlink -f "$root_source" 2>/dev/null || echo "$root_source") - current_root_dev=${root_source_resolved#/dev/} - - # Always exclude the exact device mounted as / - exclude_list+=("$current_root_dev") - - if [[ "$current_root_dev" =~ ^(mmcblk|nvme) ]]; then - base_no_part="${current_root_dev%p[0-9]*}" - else - base_no_part="${current_root_dev%%[0-9]*}" - fi - if [ -n "$base_no_part" ]; then - exclude_list+=("$base_no_part") - fi - - # If root is coming through a dm-* device (e.g., dm-verity), - # the actual physical devices appear under /sys/block/<dm>/slaves/. - # We must exclude those slaves as well, otherwise the installer - # might show the live USB stick as a valid target. - if [ -d "/sys/block/$current_root_dev/slaves" ]; then - for slave in /sys/block/"$current_root_dev"/slaves/*; do - [ -e "$slave" ] || continue - slave_dev=$(basename "$slave") - exclude_list+=("$slave_dev") - slave_base=${slave_dev%%[0-9]*} - [ -n "$slave_base" ] && exclude_list+=("$slave_base") - done - fi - fi - - echo "Searching for target device..." - - devices=$(find /sys/block/ -type b,c,f,l -not -iname "mmcblk*" -printf "%f\n") || true - mmc_devices=$(find /sys/block/ -type b,c,f,l -iname "mmcblk[0-9]" -printf "%f\n") || true - devices="$devices $mmc_devices" - - for device in $devices; do - is_raid_member=0 - - if [ -d "/sys/block/$device/holders" ] && [ ! -d "/sys/block/$device/md" ]; then - for holder_path in /sys/block/$device/holders/*; do - holder_name=$(basename "$holder_path") - case "$holder_name" in - md[0-9]*) - is_raid_member=1 - break - ;; - esac - done - fi - - if [ "$is_raid_member" -eq 1 ]; then - continue # Skip RAID member disks - fi - - if [[ "$device" == md* ]]; then - if [ -f "/sys/block/$device/md/array_state" ]; then - state=$(cat /sys/block/$device/md/array_state) - if [ "$state" != "active" ] && [ "$state" != "clean" ]; then - continue - fi - else - continue - fi - fi - - case $device in - dm-*) - # skip device-mapper device - ;; - loop*) - # skip loop device - ;; - mtd*) - ;; - sr*) - # skip CDROM device - ;; - ram*) - # skip ram device - ;; - zram*) - # skip zram device - ;; - *) - #skip any excluded devices (root and its slaves) - skip_device=0 - for ex in "${exclude_list[@]}"; do - if [[ "$device" == "$ex"* ]]; then - skip_device=1 - break - fi - done - - if [ "$skip_device" -eq 0 ]; then - target_device_list="$target_device_list $device" - fi - ;; - esac - done - - if [ -z "${target_device_list}" ]; then - dialog --msgbox "You need another device (besides the live device /dev/${current_root_dev}) to install the image. Installation aborted." 7 60 - exit 1 - fi - - if [ "$(echo "$target_device_list" | wc -w)" -gt 1 ]; then - array=() - for target in $(echo "$target_device_list" | xargs -n1 | sort); do - target_size=$(lsblk --nodeps --noheadings -o SIZE /dev/"$target" | tr -d " ") - if cmp /dev/zero /dev/"$target" -n 1M; then - state="empty" - else - state="contains data" - fi - array+=("/dev/$target" "/dev/$target ($target_size, $state)") - done - if ! installer_target_dev=$(dialog --no-tags \ - --menu "Select device to install image to" 10 60 3 \ - "${array[@]}" --output-fd 1); then - exit 0 - fi - else - installer_target_dev="/dev/$(echo "$target_device_list" | tr -d " ")" - fi - TARGET_DEVICE_SIZE=$(lsblk --nodeps --noheadings -o SIZE "$installer_target_dev" | tr -d " ") - if ! dialog --yes-label Ok --no-label Cancel \ - --yesno "Start installing\n'$installer_image_uri'\nto $installer_target_dev (capacity: $TARGET_DEVICE_SIZE)" 7 60; then - exit 0 - fi - - # set absolute paths to be compatible with unattended mode - installer_image_uri="$installdata/$installer_image_uri" -fi - -if ! cmp /dev/zero "$installer_target_dev" -n 1M; then - if ! $installer_unattended; then - if ! dialog --defaultno \ - --yesno "WARNING: Target device is not empty! Continue anyway?" 5 60; then - exit 0 - fi - else - if [ "$installer_target_overwrite" != "OVERWRITE" ]; then - echo "Target device is not empty! -> Abort" - echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." - - exit 1 - fi - fi -fi - -bmap_options="" - -# bmap file is expected to be next to the installer image -DISK_BMAP="${installer_image_uri%.wic*}.wic.bmap" - -if [ ! -f "$DISK_BMAP" ]; then - bmap_options="--nobmap" -fi - -if ! $installer_unattended; then - clear -fi - -# Function to compare version numbers -version_ge() { - if [ "$(printf '%s\n' "$1"X "$2" | sort -V | head -n 1)" != "$1"X ]; then - return 0 - else - return 1 - fi +#-------------------------------------------------------------------------- +# flow_run_attended +# +# Handles all attended-mode interactions and assigns: +# installer_image_uri +# installer_target_dev +# +# Returns: +# 0 on success +# 1 on hard error +# 2 on user cancel +#-------------------------------------------------------------------------- +flow_run_attended() { + local selected_image + local selected_target + local target_device_size + + # In attended mode, prefer to ask the user for the image selection. + # Only use installer_image_uri when it was explicitly configured. + + if [ -z "$installer_image_uri" ] || [ ! -f "$installdata/$installer_image_uri" ]; then + selected_image=$(ui_select_image_menu "$installdata") + case $? in + 0) + installer_image_uri="$selected_image" + ;; + 1) + ui_show_error "Could not find an image to install. Installation aborted." + return 1 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac + fi + + if [ ! -f "$installdata/$installer_image_uri" ]; then + ui_show_error "Could not find an image to install. Installation aborted." + return 1 + fi + + echo "Searching for target device..." + sys_discover_target_devices + if [ "${#SYS_TARGET_DEVICES[@]}" -eq 0 ]; then + ui_show_error "You need another device (besides the live device /dev/${SYS_CURRENT_ROOT_DEV}) to install the image. Installation aborted." + return 1 + fi + + if [ "${#SYS_TARGET_DEVICES[@]}" -gt 1 ]; then + selected_target=$(ui_select_target_device_menu "${SYS_TARGET_DEVICES[@]}") + case $? in + 0) + installer_target_dev="$selected_target" + ;; + 1) + ui_show_error "No installable target devices available. Installation aborted." + return 1 + ;; + 2) + return 2 + ;; + *) + return 1 + ;; + esac + else + installer_target_dev="${SYS_TARGET_DEVICES[0]}" + fi + + target_device_size=$(sys_device_size "$installer_target_dev") + if ! ui_confirm_install "$installer_image_uri" "$installer_target_dev" "$target_device_size"; then + return 2 + fi + + installer_image_uri="$installdata/$installer_image_uri" + return 0 } -lockfile="/run/installer.lock" -progress_pipe="/run/installer.fifo" - -exec 9>"$lockfile" -if flock -n 9; then - # Get bmap-tools version - bmap_version=$(bmaptool --version | awk '{ print $NF }') - - if version_ge "$bmap_version" "3.6"; then - if ! mkfifo "$progress_pipe"; then - echo "Error: Failed to create named pipe $progress_pipe" - exit 1 - fi - - # Add psplash pipe to bmap_options - bmap_options="$bmap_options --psplash-pipe=$progress_pipe" - quiet_flag="-q" - - # Initialize the dialog gauge and update it dynamically - ( - while true; do - if read -r line < "$progress_pipe"; then - percentage=$(echo "$line" | awk '{ print $2 }') - echo "$percentage" - fi - done - ) | dialog --gauge "Flashing image, please wait..." 10 70 0 & - - gauge_pid=$! - fi - - if ! bmaptool $quiet_flag copy $bmap_options "$installer_image_uri" "$installer_target_dev"; then - kill "$gauge_pid" - exit 1 - fi - - # Attempt to terminate the gauge process if still running. - # Errors are ignored since the process may already have exited. - kill "$gauge_pid" 2>/dev/null -else - echo "Installation already running in another console." +#-------------------------------------------------------------------------- +# flow_validate_target_overwrite_policy +# +# Enforces overwrite policy for non-empty targets in both attended and +# unattended modes. +# +# Returns: +# 0 when policy permits installation +# 1 when policy rejects installation +# 2 when user cancels in attended mode +#-------------------------------------------------------------------------- +flow_validate_target_overwrite_policy() { + if sys_device_is_empty "$installer_target_dev"; then + return 0 + fi + + if ! $installer_unattended; then + if ! ui_confirm_overwrite; then + return 2 + fi + else + if [ "$installer_target_overwrite" != "OVERWRITE" ]; then + echo "Target device is not empty! -> Abort" + echo "If you want to override existing data set \"installer.target.overwrite=OVERWRITE\" on your kernel cmdline or edit your \"auto.install\" file accordingly." + return 1 + fi + fi + + return 0 +} - # Wait for running console to create the progress pipe - sleep 5 +#-------------------------------------------------------------------------- +# flow_maybe_switch_to_attended +# +# If unattended abort-by-key is enabled, this function offers a timeout +# window to switch to attended mode. +#-------------------------------------------------------------------------- +flow_maybe_switch_to_attended() { + local abort_file + + if [ "$installer_unattended" = true ] && [ "$installer_unattended_abort_enable" = true ]; then + abort_file=/run/attended_mode_trigger + if ui_countdown_allow_attended_switch "$installer_unattended_abort_timeout" "$abort_file"; then + installer_unattended=false + fi + fi +} - # Check if progress pipe exists and has content - if [ -e "$progress_pipe" ]; then - echo "Installation progress..." - tail -f "$progress_pipe" | while read line; do - printf "\r%s%%" "$line" - done - else - # Periodically check if bmaptool is still running every 5 seconds - echo "Waiting for installation to finish..." - while pgrep -x "bmaptool" > /dev/null; do - sleep 5 - done - fi -fi +#-------------------------------------------------------------------------- +# flow_run +# +# Main installer flow combining attended/unattended execution paths. +#-------------------------------------------------------------------------- +flow_run() { + flow_maybe_switch_to_attended + + if ! $installer_unattended; then + flow_run_attended + case $? in + 0) + ;; + 2) + return 0 + ;; + *) + return 1 + ;; + esac + fi + + flow_validate_target_overwrite_policy + case $? in + 0) + ;; + 2) + return 0 + ;; + *) + return 1 + ;; + esac + + if ! $installer_unattended; then + clear + fi + + if ! sys_install_image_with_lock "$installer_image_uri" "$installer_target_dev"; then + if ! $installer_unattended; then + ui_show_error "Installation failed." + fi + return 1 + fi + + if ! $installer_unattended; then + ui_show_info "Installation was successful. System will be rebooted. Please remove the USB stick." + else + echo "Installation was successful." + fi + + return 0 +} -if ! $installer_unattended; then - dialog --title "Reboot" \ - --msgbox "Installation was successful. System will be rebooted. Please remove the USB stick." 6 60 -else - echo "Installation was successful." -fi +# Entrypoint: run the installer flow and propagate status. +flow_run +exit $? -exit 0 -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260608051737.946413-4-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 4/4] installer: install seprated installer modules in recipe 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users ` (2 preceding siblings ...) 2026-06-08 5:17 ` [PATCH v3 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 ` 'Kasturi shekar' via isar-users 2026-06-12 10:50 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules Anton Mikanovich 4 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-06-08 5:17 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> Update deploy-image recipe to ship the newly split installer modules. Add the following files to SRC_URI and do_install: - sys_api.sh - installer_ui.sh Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> --- .../recipes-installer/deploy-image/deploy-image_0.1.bb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb index 44b96c27..dde92eb4 100644 --- a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb +++ b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb @@ -1,5 +1,5 @@ # This software is a part of Isar. -# Copyright (C) Siemens AG, 2024 +# Copyright (C) Siemens AG, 2024-2026 # # SPDX-License-Identifier: MIT @@ -9,6 +9,8 @@ MAINTAINER = "isar-users <isar-users@googlegroups.com>" inherit dpkg-raw SRC_URI = "file://usr/bin/deploy-image-wic.sh \ + file://usr/bin/sys_api.sh \ + file://usr/bin/installer_ui.sh \ file://usr/lib/deploy-image-wic/handle-config.sh \ " @@ -19,5 +21,7 @@ do_install[cleandirs] = "${D}/usr/bin/ \ " do_install() { install -m 0755 ${WORKDIR}/usr/bin/deploy-image-wic.sh ${D}/usr/bin/deploy-image-wic.sh + install -m 0755 ${WORKDIR}/usr/bin/sys_api.sh ${D}/usr/bin/sys_api.sh + install -m 0755 ${WORKDIR}/usr/bin/installer_ui.sh ${D}/usr/bin/installer_ui.sh install -m 0755 ${WORKDIR}/usr/lib/deploy-image-wic/handle-config.sh ${D}/usr/lib/deploy-image-wic/handle-config.sh } -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260608051737.946413-5-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users ` (3 preceding siblings ...) 2026-06-08 5:17 ` [PATCH v3 4/4] installer: install seprated installer modules in recipe 'Kasturi shekar' via isar-users @ 2026-06-12 10:50 ` Anton Mikanovich 4 siblings, 0 replies; 18+ messages in thread From: Anton Mikanovich @ 2026-06-12 10:50 UTC (permalink / raw) To: Kasturi shekar, isar-users On 08/06/2026 08:17, 'Kasturi shekar' via isar-users wrote: > From: Kasturi Shekar <kasturi.shekar@siemens.com> > > This series refactors the installer by separating backend logic, > attended UI, and high-level flow into dedicated modules. > The main objectives are: > - improve readability and maintainability > - keep behavior and user-visible flow unchanged > > Patch-1: introduces a backend API layer (sys_api.sh) that provides > installer functionality via structured shell functions. > > Patch-2: adapts the existing dialog-based installer frontend to consume > these APIs, without changing behavior. > > Patch-3: high-level orchestration and keep deploy-image-wic.sh as a thin launcher. > This keeps the externally used script name stable while making the flow easier > to review and maintain. > > patch-4: update deploy-image recipe to ship the newly split installer modules > > .../deploy-image/deploy-image_0.1.bb | 6 +- > .../files/usr/bin/deploy-image-wic.sh | 472 +++++++----------- > .../files/usr/bin/installer_ui.sh | 217 +++++--- > .../deploy-image/files/usr/bin/sys_api.sh | 400 ++++++++++++--- > 4 files changed, 688 insertions(+), 407 deletions(-) > mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/deploy-image-wic.sh > mode change 100755 => 100644 meta-isar/recipes-installer/deploy-image/files/usr/bin/installer_ui.sh > Applied to next, thanks. -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/cac65768-1c06-4953-8397-8b7e69cf45fd%40ilbers.de. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 4/4] installer: install seprated installer modules in recipe 2026-05-14 9:19 ` [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users ` (2 preceding siblings ...) 2026-05-14 9:19 ` [PATCH v2 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 ` 'Kasturi shekar' via isar-users 3 siblings, 0 replies; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-05-14 9:19 UTC (permalink / raw) To: isar-users; +Cc: Kasturi Shekar From: Kasturi Shekar <kasturi.shekar@siemens.com> Update deploy-image recipe to ship the newly split installer modules. Add the following files to SRC_URI and do_install: - sys_api.sh - installer_ui.sh Signed-off-by: Kasturi Shekar <kasturi.shekar@siemens.com> --- .../recipes-installer/deploy-image/deploy-image_0.1.bb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb index 44b96c27..dde92eb4 100644 --- a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb +++ b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb @@ -1,5 +1,5 @@ # This software is a part of Isar. -# Copyright (C) Siemens AG, 2024 +# Copyright (C) Siemens AG, 2024-2026 # # SPDX-License-Identifier: MIT @@ -9,6 +9,8 @@ MAINTAINER = "isar-users <isar-users@googlegroups.com>" inherit dpkg-raw SRC_URI = "file://usr/bin/deploy-image-wic.sh \ + file://usr/bin/sys_api.sh \ + file://usr/bin/installer_ui.sh \ file://usr/lib/deploy-image-wic/handle-config.sh \ " @@ -19,5 +21,7 @@ do_install[cleandirs] = "${D}/usr/bin/ \ " do_install() { install -m 0755 ${WORKDIR}/usr/bin/deploy-image-wic.sh ${D}/usr/bin/deploy-image-wic.sh + install -m 0755 ${WORKDIR}/usr/bin/sys_api.sh ${D}/usr/bin/sys_api.sh + install -m 0755 ${WORKDIR}/usr/bin/installer_ui.sh ${D}/usr/bin/installer_ui.sh install -m 0755 ${WORKDIR}/usr/lib/deploy-image-wic/handle-config.sh ${D}/usr/lib/deploy-image-wic/handle-config.sh } -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260514091951.1572682-5-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH 4/4] installer: install seprated installer modules in recipe 2026-04-27 6:39 [PATCH 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users ` (2 preceding siblings ...) 2026-04-27 6:39 ` [PATCH 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 ` 'Kasturi shekar' via isar-users 2026-05-14 6:20 ` Anton Mikanovich 3 siblings, 1 reply; 18+ messages in thread From: 'Kasturi shekar' via isar-users @ 2026-04-27 6:39 UTC (permalink / raw) To: isar-users; +Cc: kasturi.shekar From: "kasturi.shekar" <kasturi.shekar@siemens.com> Update deploy-image recipe to ship the newly split installer modules. Add the following files to SRC_URI and do_install: - sys_api.sh - installer_ui.sh This finalizes packaging for the refactored installer layout. Signed-off-by: kasturi.shekar <kasturi.shekar@siemens.com> --- .../recipes-installer/deploy-image/deploy-image_0.1.bb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb index 03b082a9..ef161ebd 100644 --- a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb +++ b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb @@ -1,5 +1,5 @@ # This software is a part of Isar. -# Copyright (C) Siemens AG, 2024 +# Copyright (C) Siemens AG, 2026 # # SPDX-License-Identifier: MIT @@ -9,6 +9,8 @@ MAINTAINER = "isar-users <isar-users@googlegroups.com>" inherit dpkg-raw SRC_URI = "file://usr/bin/deploy-image-wic.sh \ + file://usr/bin/sys_api.sh \ + file://usr/bin/installer_ui.sh \ file://usr/lib/deploy-image-wic/handle-config.sh \ " @@ -20,5 +22,7 @@ do_install[cleandirs] = "${D}/usr/bin/ \ " do_install() { install -m 0755 ${WORKDIR}/usr/bin/deploy-image-wic.sh ${D}/usr/bin/deploy-image-wic.sh + install -m 0755 ${WORKDIR}/usr/bin/sys_api.sh ${D}/usr/bin/sys_api.sh + install -m 0755 ${WORKDIR}/usr/bin/installer_ui.sh ${D}/usr/bin/installer_ui.sh install -m 0755 ${WORKDIR}/usr/lib/deploy-image-wic/handle-config.sh ${D}/usr/lib/deploy-image-wic/handle-config.sh } -- 2.47.3 -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/20260427063957.603123-5-kasturi.shekar%40siemens.com. ^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH 4/4] installer: install seprated installer modules in recipe 2026-04-27 6:39 ` [PATCH " 'Kasturi shekar' via isar-users @ 2026-05-14 6:20 ` Anton Mikanovich 0 siblings, 0 replies; 18+ messages in thread From: Anton Mikanovich @ 2026-05-14 6:20 UTC (permalink / raw) To: isar-users, Kasturi Shekar 27.04.2026 09:39, 'Kasturi shekar' via isar-users wrote: > From: "kasturi.shekar" <kasturi.shekar@siemens.com> > > Update deploy-image recipe to ship the newly split installer modules. > Add the following files to SRC_URI and do_install: > - sys_api.sh > - installer_ui.sh > > This finalizes packaging for the refactored installer layout. > > Signed-off-by: kasturi.shekar <kasturi.shekar@siemens.com> Just to keep things clean, can you use "Name Surname" format for From and Signed-off-by sections? > --- > .../recipes-installer/deploy-image/deploy-image_0.1.bb | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb > index 03b082a9..ef161ebd 100644 > --- a/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb > +++ b/meta-isar/recipes-installer/deploy-image/deploy-image_0.1.bb > @@ -1,5 +1,5 @@ > # This software is a part of Isar. > -# Copyright (C) Siemens AG, 2024 > +# Copyright (C) Siemens AG, 2026 This should probably be look like "Copyright (C) Siemens AG, 2024-2026", because you've just changed some lines, not the whole file. The same for patch 3. > # > # SPDX-License-Identifier: MIT > > @@ -9,6 +9,8 @@ MAINTAINER = "isar-users <isar-users@googlegroups.com>" > inherit dpkg-raw > > SRC_URI = "file://usr/bin/deploy-image-wic.sh \ > + file://usr/bin/sys_api.sh \ > + file://usr/bin/installer_ui.sh \ > file://usr/lib/deploy-image-wic/handle-config.sh \ > " > > @@ -20,5 +22,7 @@ do_install[cleandirs] = "${D}/usr/bin/ \ > " > do_install() { > install -m 0755 ${WORKDIR}/usr/bin/deploy-image-wic.sh ${D}/usr/bin/deploy-image-wic.sh > + install -m 0755 ${WORKDIR}/usr/bin/sys_api.sh ${D}/usr/bin/sys_api.sh > + install -m 0755 ${WORKDIR}/usr/bin/installer_ui.sh ${D}/usr/bin/installer_ui.sh > install -m 0755 ${WORKDIR}/usr/lib/deploy-image-wic/handle-config.sh ${D}/usr/lib/deploy-image-wic/handle-config.sh > } -- You received this message because you are subscribed to the Google Groups "isar-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to isar-users+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/isar-users/1aaf8140-e8c4-48c7-8a23-0c2bbc6c0433%40ilbers.de. ^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-06-12 10:50 UTC | newest] Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-04-27 6:39 [PATCH 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users 2026-05-14 9:19 ` [PATCH v2 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users 2026-06-04 12:56 ` Anton Mikanovich 2026-06-08 5:17 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 1/4] installer: extract backend helpers to sys_api.sh 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 2/4] installer: extract attended UI to installer_ui.sh 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 3/4] installer: add installer flow module and entrypoint 'Kasturi shekar' via isar-users 2026-06-08 5:17 ` [PATCH v3 4/4] installer: install seprated installer modules in recipe 'Kasturi shekar' via isar-users 2026-06-12 10:50 ` [PATCH v3 0/4] installer: separate installer into backend, UI, and flow modules Anton Mikanovich 2026-05-14 9:19 ` [PATCH v2 4/4] installer: install seprated installer modules in recipe 'Kasturi shekar' via isar-users 2026-04-27 6:39 ` [PATCH " 'Kasturi shekar' via isar-users 2026-05-14 6:20 ` Anton Mikanovich
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox