* [PATCH 2/3] deploy-image: report the result of the console holding the lock
2026-10-27 11:05 [PATCH 1/3] installer: add reusable progress gauge srinuvasan.a via isar-users
@ 2026-10-27 11:06 ` srinuvasan.a via isar-users
2026-10-27 11:06 ` [PATCH 3/3] deploy-image: re-encrypt LUKS volumes with new master keys srinuvasan.a via isar-users
2026-10-30 16:16 ` [PATCH 1/3] installer: add reusable progress gauge Zhihang Wei
2 siblings, 0 replies; 4+ messages in thread
From: srinuvasan.a via isar-users @ 2026-10-27 11:06 UTC (permalink / raw)
To: isar-users; +Cc: cedric.hombourger, Srinuvasan A
From: Srinuvasan A <srinuvasan.a@siemens.com>
When several installer consoles run, only the one holding the
installation lock copies the image. Consoles that do not get the lock
waited for it to finish and then always reported success, even if the
installation had failed.
Wait for the lock holder with flock and let it record its outcome in
/run/installer.result. Consoles that waited return success only if the
holder succeeded.
Signed-off-by: Srinuvasan A <srinuvasan.a@siemens.com>
Suggested-by: Cedric Hombourger <cedric.hombourger@siemens.com>
---
.../deploy-image/files/usr/bin/sys_api.sh | 27 +++++++++----------
1 file changed, 12 insertions(+), 15 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 5f591745..26b39108 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
@@ -278,7 +278,9 @@ sys_bmap_options_for_image() {
#
# 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.
+# uses them to present a progress gauge in attended mode. Consoles that
+# do not get the lock wait for its holder and report its result, which is
+# kept in /run/installer.result.
#
# Optional UI hooks (if defined):
# ui_start_progress_gauge <pipe_path>
@@ -289,6 +291,7 @@ sys_install_image_with_lock() {
local target_device="$2"
local lockfile="/run/installer.lock"
local progress_pipe="/run/installer.fifo"
+ local result_file="/run/installer.result"
local bmap_options
local bmap_version
local quiet_flag=""
@@ -297,6 +300,7 @@ sys_install_image_with_lock() {
exec 9>"$lockfile"
if flock -n 9; then
+ rm -f "$result_file"
bmap_version=$(bmaptool --version | awk '{ print $NF }')
if sys_version_ge "$bmap_version" "3.6"; then
@@ -323,27 +327,20 @@ sys_install_image_with_lock() {
if declare -F ui_stop_progress_gauge >/dev/null; then
ui_stop_progress_gauge
fi
+ echo failed > "$result_file"
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
+ echo ok > "$result_file"
+ else
+ echo "Installation already running in another console. Waiting for it to finish..."
+ flock 9
+ # The other console owns the result; only report success if it did.
+ [ "$(cat "$result_file" 2>/dev/null)" = ok ] || return 1
fi
return 0
--
2.34.1
--
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/20261027110601.1197504-2-srinuvasan.a%40siemens.com.
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH 3/3] deploy-image: re-encrypt LUKS volumes with new master keys
2026-10-27 11:05 [PATCH 1/3] installer: add reusable progress gauge srinuvasan.a via isar-users
2026-10-27 11:06 ` [PATCH 2/3] deploy-image: report the result of the console holding the lock srinuvasan.a via isar-users
@ 2026-10-27 11:06 ` srinuvasan.a via isar-users
2026-10-30 16:16 ` [PATCH 1/3] installer: add reusable progress gauge Zhihang Wei
2 siblings, 0 replies; 4+ messages in thread
From: srinuvasan.a via isar-users @ 2026-10-27 11:06 UTC (permalink / raw)
To: isar-users; +Cc: cedric.hombourger, Srinuvasan A
From: Srinuvasan A <srinuvasan.a@siemens.com>
Raw image installation clones the LUKS volume keys of the source image
to every installed device. Add sys_api functions that rotate them on the
offline target before the installation lock is released:
- sys_luks_list_volumes: verify the target is idle and every LUKS2
volume is a pristine provisioning container
- sys_luks_reencrypt_volume: reencrypt one volume (no resilience
journal, as a failure requires reinstalling) emitting percentages
- sys_luks_rotate_keys: orchestrate the above, one progress gauge per
volume through the optional ui_reencrypt_volume_gauge hook, which
uses ui_progress_gauge
The initial passphrase defaults to empty and can be supplied through the
optional sys_luks_passphrase_hook callback. cryptsetup and jq are only
required when an encrypted volume is found; when missing, key rotation
is skipped with a warning.
A failed rotation is recorded as the installation result, so that other
consoles report it.
Signed-off-by: Srinuvasan A <srinuvasan.a@siemens.com>
Suggested-by: Cedric Hombourger <cedric.hombourger@siemens.com>
---
.../files/usr/bin/installer_ui.sh | 14 ++
.../deploy-image/files/usr/bin/sys_api.sh | 212 +++++++++++++++++-
2 files changed, 222 insertions(+), 4 deletions(-)
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
index aa906221..6f2433e6 100644
--- 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
@@ -209,6 +209,20 @@ ui_progress_gauge() {
return "$status"
}
+#--------------------------------------------------------------------------
+# ui_reencrypt_volume_gauge <device> <index> <count>
+#
+# Shows a gauge for one encrypted volume, fed with percentages on stdin
+# (see sys_luks_reencrypt_volume).
+#--------------------------------------------------------------------------
+ui_reencrypt_volume_gauge() {
+ local device="$1"
+ local index="$2"
+ local count="$3"
+
+ ui_progress_gauge "Securing volume $index/$count ($device) with a new encryption key.\nDo not power off."
+}
+
#--------------------------------------------------------------------------
# ui_stop_progress_gauge
#
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 26b39108..4f246697 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
@@ -273,18 +273,215 @@ sys_bmap_options_for_image() {
fi
}
+#--------------------------------------------------------------------------
+# sys_luks_list_volumes <target_device>
+#
+# Prints the LUKS2 volumes found on target_device (and its partitions),
+# one per line, once the target has been verified to be offline and every
+# volume to be a pristine provisioning container: a single keyslot, no
+# tokens and no pending reencryption. Unencrypted targets yield no output
+# and need neither cryptsetup nor jq; encrypted ones yield no output (with a
+# warning) when either is missing, so that key rotation is skipped.
+#
+# Returns:
+# 0 on success (also when tools are missing, see above), 1 when the
+# target is busy or a volume is unsupported (reason on stderr).
+#--------------------------------------------------------------------------
+sys_luks_list_volumes() {
+ local target_device="$1"
+ local devices device mounts fstype metadata probe_status
+ local holders
+ local volumes=()
+
+ partprobe "$target_device" || true
+ ! command -v udevadm >/dev/null || udevadm settle || true
+
+ devices=$(lsblk --list --noheadings --paths --output NAME "$target_device") || return 1
+ while IFS= read -r device; do
+ mounts=$(lsblk --nodeps --noheadings --output MOUNTPOINTS "$device")
+ if [ -n "${mounts//[[:space:]]/}" ]; then
+ echo "Error: $device is mounted" >&2
+ return 1
+ fi
+ holders=(/sys/class/block/"${device##*/}"/holders/*)
+ if [ -e "${holders[0]}" ]; then
+ echo "Error: $device has active holders" >&2
+ return 1
+ fi
+
+ probe_status=0
+ fstype=$(blkid --probe --output value --match-tag TYPE "$device" 2>/dev/null) || probe_status=$?
+ if [ "$probe_status" != 0 ] && [ "$probe_status" != 2 ]; then
+ echo "Error: cannot probe $device" >&2
+ return 1
+ fi
+ [ "$fstype" = crypto_LUKS ] || continue
+
+ # Optional tools: without them keys are not rotated (see sys_luks_rotate_keys).
+ if ! command -v cryptsetup >/dev/null || ! command -v jq >/dev/null; then
+ echo "Warning: $device is encrypted but cryptsetup/jq are missing, skipping key rotation" >&2
+ return 0
+ fi
+ if ! cryptsetup isLuks --type luks2 "$device"; then
+ echo "Error: $device is not a LUKS2 volume" >&2
+ return 1
+ fi
+ metadata=$(cryptsetup luksDump --dump-json-metadata "$device") || return 1
+ if ! jq -e '
+ (.tokens | length) == 0 and
+ (.keyslots | length) == 1 and
+ all(.keyslots[]; .type == "luks2") and
+ (.segments | length) == 1 and
+ all(.segments[]; .type == "crypt" and ((.flags // []) | length) == 0)
+ ' <<< "$metadata" >/dev/null; then
+ echo "Error: unsupported or interrupted LUKS state on $device" >&2
+ return 1
+ fi
+ volumes+=("$device")
+ done <<< "$devices"
+
+ [ ${#volumes[@]} -eq 0 ] || printf '%s\n' "${volumes[@]}"
+}
+
+#--------------------------------------------------------------------------
+# sys_luks_make_keyfile <device>
+#
+# Creates a private key file holding the initial passphrase of <device> and
+# prints its path. The passphrase is empty unless the optional hook
+# sys_luks_passphrase_hook is defined; it is called with the device and
+# must print the passphrase on stdout (no trailing newline is added).
+#--------------------------------------------------------------------------
+sys_luks_make_keyfile() {
+ local device="$1"
+ local keyfile passphrase=""
+
+ if declare -F sys_luks_passphrase_hook >/dev/null; then
+ passphrase=$(sys_luks_passphrase_hook "$device") || return 1
+ fi
+ keyfile=$(mktemp /run/installer-luks-key.XXXXXX) || return 1
+ printf '%s' "$passphrase" > "$keyfile" || { rm -f -- "$keyfile"; return 1; }
+ echo "$keyfile"
+}
+
+#--------------------------------------------------------------------------
+# sys_luks_reencrypt_volume <device>
+#
+# Generates a new volume key for a provisioning LUKS2 volume, unlocking it
+# with its initial passphrase (see sys_luks_make_keyfile), and emits one
+# integer percentage per line on stdout so callers can drive a progress
+# indicator. The volume is offline and a failed run requires reinstalling,
+# so no resilience journal is kept (this halves the amount of data written).
+#
+# Returns:
+# 0 when the volume was reencrypted and verified, 1 otherwise.
+#--------------------------------------------------------------------------
+sys_luks_reencrypt_volume() {
+ local device="$1"
+ local keyfile log uuid metadata status
+
+ keyfile=$(sys_luks_make_keyfile "$device") || return 1
+ uuid=$(cryptsetup luksUUID "$device") || { rm -f -- "$keyfile"; return 1; }
+ log=$(mktemp /run/installer-luks-reencrypt.XXXXXX) || { rm -f -- "$keyfile"; return 1; }
+
+ # Progress is printed on stdout, errors on stderr (kept for reporting).
+ cryptsetup reencrypt --batch-mode --force-offline-reencrypt \
+ --resilience none --progress-json --progress-frequency 1 \
+ --key-file "$keyfile" "$device" 2> "$log" |
+ jq -R --unbuffered -r '
+ fromjson? | select(.device_size != null) |
+ (((.device_bytes | tonumber) * 100) / (.device_size | tonumber) | floor)'
+ status=${PIPESTATUS[0]}
+ [ "$status" = 0 ] || cat "$log" >&2
+ rm -f -- "$log"
+
+ if [ "$status" = 0 ]; then
+ [ "$(cryptsetup luksUUID "$device")" = "$uuid" ] || status=1
+ fi
+ if [ "$status" = 0 ]; then
+ cryptsetup open --test-passphrase --key-file "$keyfile" "$device" || status=1
+ fi
+ if [ "$status" = 0 ]; then
+ metadata=$(cryptsetup luksDump --dump-json-metadata "$device") || status=1
+ jq -e 'all(.keyslots[]; .type != "reencrypt") and
+ all(.segments[]; .type == "crypt" and ((.flags // []) | length) == 0)' \
+ <<< "$metadata" >/dev/null || status=1
+ fi
+
+ rm -f -- "$keyfile"
+ [ "$status" = 0 ] || echo "Error: reencryption of $device failed" >&2
+ [ "$status" = 0 ]
+}
+
+#--------------------------------------------------------------------------
+# sys_luks_rotate_keys <target_device>
+#
+# Installed images share the volume keys of the image they were built
+# from. Reencrypt every LUKS2 volume of target_device so the installed
+# system uses its own master keys. Must run while the target is offline.
+#
+# Optional hooks (if defined):
+# sys_luks_passphrase_hook <device>
+# prints the initial passphrase of <device> (default: empty)
+# ui_reencrypt_volume_gauge <device> <index> <count>
+# reads percentages from stdin while a volume is reencrypted
+#
+# Returns:
+# 0 when all volumes were rotated, none are encrypted, or rotation was
+# skipped because cryptsetup or jq is missing (warning on stderr);
+# 1 otherwise. On failure the target must not be booted and must be
+# reinstalled.
+#--------------------------------------------------------------------------
+sys_luks_rotate_keys() {
+ local target_device="$1"
+ local volumes=()
+ local device keyfile listing status index=0 count
+
+ listing=$(sys_luks_list_volumes "$target_device") || return 1
+ [ -n "$listing" ] || return 0
+ mapfile -t volumes <<< "$listing"
+ count=${#volumes[@]}
+
+ # Fail before touching any volume when the initial credential is wrong.
+ for device in "${volumes[@]}"; do
+ keyfile=$(sys_luks_make_keyfile "$device") || return 1
+ if ! cryptsetup open --test-passphrase --key-file "$keyfile" "$device"; then
+ echo "Error: initial unlock credential is unavailable for $device" >&2
+ rm -f -- "$keyfile"
+ return 1
+ fi
+ rm -f -- "$keyfile"
+ done
+
+ for device in "${volumes[@]}"; do
+ index=$((index + 1))
+ if declare -F ui_reencrypt_volume_gauge >/dev/null; then
+ sys_luks_reencrypt_volume "$device" | ui_reencrypt_volume_gauge "$device" "$index" "$count"
+ status=${PIPESTATUS[0]}
+ else
+ echo "Reencrypting $device ($index/$count); do not power off."
+ sys_luks_reencrypt_volume "$device" | while read -r percent; do
+ printf '\r%s: %s%%' "$device" "$percent"
+ done
+ status=${PIPESTATUS[0]}
+ echo
+ fi
+ [ "$status" = 0 ] || return 1
+ done
+}
+
#--------------------------------------------------------------------------
# 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. Consoles that
-# do not get the lock wait for its holder and report its result, which is
-# kept in /run/installer.result.
+# consoles via flock, then rotates the LUKS volume keys of the target
+# (see sys_luks_rotate_keys) before the lock is released. When UI hook
+# functions are available, this API uses them to present progress gauges
+# in attended mode.
#
# Optional UI hooks (if defined):
# ui_start_progress_gauge <pipe_path>
# ui_stop_progress_gauge
+# ui_reencrypt_volume_gauge <device> <index> <count>
#--------------------------------------------------------------------------
sys_install_image_with_lock() {
local image_path="$1"
@@ -335,6 +532,13 @@ sys_install_image_with_lock() {
ui_stop_progress_gauge
fi
+ # Still under the lock: give the installed system its own volume keys.
+ if ! sys_luks_rotate_keys "$target_device"; then
+ echo "Volume-key rotation failed. Installation is incomplete; do not boot the target." >&2
+ echo failed > "$result_file"
+ return 1
+ fi
+
echo ok > "$result_file"
else
echo "Installation already running in another console. Waiting for it to finish..."
--
2.34.1
--
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/20261027110601.1197504-3-srinuvasan.a%40siemens.com.
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH 1/3] installer: add reusable progress gauge
2026-10-27 11:05 [PATCH 1/3] installer: add reusable progress gauge srinuvasan.a via isar-users
2026-10-27 11:06 ` [PATCH 2/3] deploy-image: report the result of the console holding the lock srinuvasan.a via isar-users
2026-10-27 11:06 ` [PATCH 3/3] deploy-image: re-encrypt LUKS volumes with new master keys srinuvasan.a via isar-users
@ 2026-10-30 16:16 ` Zhihang Wei
2 siblings, 0 replies; 4+ messages in thread
From: Zhihang Wei @ 2026-10-30 16:16 UTC (permalink / raw)
To: srinuvasan.a, isar-users; +Cc: cedric.hombourger, Shivaschandra K L
Applied to next, thanks.
Zhihang
On 10/27/26 12:05, srinuvasan.a via isar-users wrote:
> From: Srinuvasan A <srinuvasan.a@siemens.com>
>
> Add an independent percentage consumer for operations reporting progress.
> Drain progress if dialog exits early, preserving producer execution.
> Keep existing image-copy and RAID progress handling unchanged.
>
> Signed-off-by: Shivaschandra K L <shivaschandra.k-l@siemens.com>
> Signed-off-by: Srinuvasan A <srinuvasan.a@siemens.com>
> Suggested-by: Cedric Hombourger <cedric.hombourger@siemens.com>
> ---
> .../files/usr/bin/installer_ui.sh | 24 +++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
> 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
> index 133d7c70..aa906221 100644
> --- 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
> @@ -185,6 +185,30 @@ ui_start_progress_gauge() {
> UI_GAUGE_PID=$!
> }
>
> +#--------------------------------------------------------------------------
> +# ui_progress_gauge <message>
> +#
> +# Reads integer percentages (0-100), one per line, from stdin.
> +# No dialog is opened until the first percentage arrives.
> +# If dialog exits early, drain the remaining input so the operation is
> +# not interrupted by SIGPIPE. Callers must check the producer's status
> +# using PIPESTATUS[0]; a completed gauge does not imply operation success.
> +# Returns dialog's status after all progress input has been consumed.
> +#--------------------------------------------------------------------------
> +ui_progress_gauge() {
> + local message="$1"
> + local status=0
> + local percent
> +
> + IFS= read -r percent || return 0
> + dialog --gauge "$message" 10 70 "$percent" || status=$?
> + if [ "$status" -ne 0 ]; then
> + echo "Warning: progress dialog failed; waiting for the operation to finish." >&2
> + fi
> + cat >/dev/null
> + return "$status"
> +}
> +
> #--------------------------------------------------------------------------
> # ui_stop_progress_gauge
> #
--
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/49353192-1f3e-468e-b1ab-4a2b0e86a1a2%40ilbers.de.
^ permalink raw reply [flat|nested] 4+ messages in thread