[make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature

Leonid Krivoshein klark.devel at gmail.com
Sun Oct 24 20:22:53 MSK 2021


This feature adds the ability to use text dialogs in
the initramfs scripts. See README.md for more details.

Signed-off-by: Leonid Krivoshein <klark.devel at gmail.com>
---
 features/bootchain-interactive/README.md      | 184 +++++++++++++
 features/bootchain-interactive/config.mk      |   5 +
 .../data/bin/activate-interactive-vt          |  27 ++
 .../data/bin/interactive-sh-functions         | 247 ++++++++++++++++++
 .../initrd/cmdline.d/bootchain-interactive    |   3 +
 .../data/lib/IM-widgets/choice                |  60 +++++
 .../data/lib/IM-widgets/dlgmsg                |  25 ++
 .../data/lib/IM-widgets/errmsg                |  29 ++
 .../data/lib/IM-widgets/form                  |  70 +++++
 .../data/lib/IM-widgets/gauge                 |  31 +++
 .../data/lib/IM-widgets/ponder                |  67 +++++
 features/bootchain-interactive/rules.mk       |   2 +
 12 files changed, 750 insertions(+)
 create mode 100644 features/bootchain-interactive/README.md
 create mode 100644 features/bootchain-interactive/config.mk
 create mode 100755 features/bootchain-interactive/data/bin/activate-interactive-vt
 create mode 100644 features/bootchain-interactive/data/bin/interactive-sh-functions
 create mode 100644 features/bootchain-interactive/data/etc/initrd/cmdline.d/bootchain-interactive
 create mode 100644 features/bootchain-interactive/data/lib/IM-widgets/choice
 create mode 100644 features/bootchain-interactive/data/lib/IM-widgets/dlgmsg
 create mode 100644 features/bootchain-interactive/data/lib/IM-widgets/errmsg
 create mode 100644 features/bootchain-interactive/data/lib/IM-widgets/form
 create mode 100644 features/bootchain-interactive/data/lib/IM-widgets/gauge
 create mode 100644 features/bootchain-interactive/data/lib/IM-widgets/ponder
 create mode 100644 features/bootchain-interactive/rules.mk

diff --git a/features/bootchain-interactive/README.md b/features/bootchain-interactive/README.md
new file mode 100644
index 0000000..adb3de5
--- /dev/null
+++ b/features/bootchain-interactive/README.md
@@ -0,0 +1,184 @@
+# Feature: bootchain-interactive
+
+Feature adds the ability to use text dialogs in the initramfs scripts.
+
+## Boot parameters
+
+- `console=...` - Disable to switch TTY's, it useful for network/serial console.
+- `noaskuser` - Disable all dialogs, it useful for console without user.
+- `nolines` - Disable pseudo-graphics line drawing, it useful if not supported.
+
+## Synopsis
+```
+. interactive-sh-functions
+```
+
+## Global variables
+
+- `$IM_BACKTITLE` - Back title for all input and output dialogs.
+- `$IM_WIDGET_ARGS` - Additional arguments for `dialog` command.
+- `$CONSOLE` - Non-empty value, if switching between TTY's are disabled.
+- `$NOASKUSER` - Non-empty value, if all dialogs are disabled.
+- `$NOLINES` - Non-empty value, if pseudo-graphics line drawing are disabled.
+
+## Briefly API
+
+- `IM_is_active()` - Returns 0, if interactive mode already activated.
+- `IM_exec()` - Re-execute specified process on the foreground (tty2 by
+  default).
+- `IM_activate()` - Request to immediately or delayed activation of the
+  interactive mode.
+- `IM_load_widgets()` - Load specified widgets from the library.
+- `IM_load_all()` - Load all available widgets from the library.
+- `IM_start_output()` - Notify `interactive` feature about starting output.
+- `IM_start_input()` - Notify `interactive` feature about starting intput.
+- `IM_show_bootsplash()` - Show bootsplash such as plymoth and start the
+  progress bar.
+- `IM_hide_bootsplash()` - Hide bootsplash such as plymoth and stop the
+  progress bar.
+- `IM_update_bootsplash()` - Notify bootsplash such as plymoth about boot
+  state changes.
+
+## Widgets library
+
+Library is a scripts set, located in /lib/IM-widgets directory inside intitramfs
+iamge. The base set can be extended. Before use input widgets, `IM_start_input()`
+must be called, and `IM_start_output()` in otherwise.
+
+### choice (input)
+
+Display menu with one or more items, labels before items not displayed.
+On success returns 0 and write choosen label to specified variable. Based
+on `dialog --menu`.
+
+Syntax:
+```
+IM_choice <varname> <text> <label1> <item1> [<label2> <item2>…]
+```
+
+Example:
+```
+text="Please choose the installation method."
+
+while ! IM_choice method "$text" \
+    nfs   "NFS server"      \
+    ftp   "FTP server"      \
+    http  "HTTP server"     \
+    cifs  "SAMBA server"    \
+    cdrom "CD-ROM Drive"    \
+    disk  "Hard Disk Drive" \
+    #
+do
+    sleep 0.5
+done
+
+case "$method" in
+nfs)
+…
+esac
+```
+
+### dlgmsg (input)
+
+Display text message. Always returns 0. Based on `dialog --msgbox`.
+
+Syntax:
+```
+IM_dlgmsg <title> <text>
+```
+
+Example:
+```
+IM_dlgmsg "Live is success!" "$text"
+```
+
+### errmsg (input)
+
+Display error message. Always returns 0. Based on `dialog --msgbox`.
+
+Syntax:
+```
+IM_errmsg <text>
+```
+
+Example:
+```
+IM_errmsg "Disk read error, try again!"
+```
+
+### form (input)
+
+Display mixed data form. Input one or more text fields and store values
+to specified varibales. Some variables associated with private data,
+such as password, this input field characters outputs as asterics (`*`).
+On success returns 0 and fill all variables by the entered values.
+Based on `dialog --mixedform`.
+
+Syntax:
+```
+IM_form <title> <text> <text-height> \
+    <varname1> <fldlen1> <caption1>  \
+    [<varname2> <fldlen2> <caption2>…]
+```
+
+Example:
+```
+IM_form "$title" "$text" 5      \
+    server     64 "HTTP-server" \
+    directory 128 "Directory"   \
+    ||
+    continue
+[ -n "$server" ] && [ -n "$directory" ] ||
+    continue
+```
+
+### gauge (output)
+
+Display gauge (progress bar). Integer value from 0 to 100 must be sent
+via stdin to specify displayed percent of the process passed. This is work
+in conjuction with pv command. Always returns 0. Based on `dialog --gauge`.
+
+Note for `netconsole` usage: after process will finish, don't forget reset
+the terminal, otherwise keyboard input will be lost.
+
+Syntax:
+```
+echo <integer> | IM_gauge <title> [<text>]
+```
+
+Example:
+```
+( for i in $(seq 1 10); do
+    echo "${i}0"
+    sleep 1
+  done
+) | IM_gauge "[ Loading... ]"
+
+[ -z "$CONSOLE" ] ||
+    reset
+```
+
+### ponder (output)
+
+Displays the <waiting…> widget, which displays the undefined time of the
+ongoing process, works independently of the main program code. The parameters
+<delay> and <step> at startup determine by how many percent the thermometer
+will automatically advance after a given time, i.e. set the frequency and
+speed of the widget refresh. Always returns 0. Based on the `gauge` widget.
+
+Syntax:
+```
+IM_ponder_start <title> [[[<text>] <delay>] <step>]
+…
+IM_ponder_stop
+```
+
+Example:
+```
+IM_ponder_start "[ Scanning disk... ]" \
+    "Searching random bits on the disk for fill CRNG entropy..."
+find / -type f -print0 |
+    xargs -0 grep "Linus Torvalds" >/tmp/Linus.txt 2>/dev/null
+rm -f /tmp/Linus.txt
+IM_ponder_stop
+```
diff --git a/features/bootchain-interactive/config.mk b/features/bootchain-interactive/config.mk
new file mode 100644
index 0000000..69c8756
--- /dev/null
+++ b/features/bootchain-interactive/config.mk
@@ -0,0 +1,5 @@
+$(call feature-requires,depmod-image)
+
+BOOTCHAIN_INTERACTIVE_DATADIR = $(FEATURESDIR)/bootchain-interactive/data
+
+BOOTCHAIN_INTERACTIVE_PROGS = chvt dialog openvt pv
diff --git a/features/bootchain-interactive/data/bin/activate-interactive-vt b/features/bootchain-interactive/data/bin/activate-interactive-vt
new file mode 100755
index 0000000..90ae329
--- /dev/null
+++ b/features/bootchain-interactive/data/bin/activate-interactive-vt
@@ -0,0 +1,27 @@
+#!/bin/bash -efu
+
+. interactive-sh-functions
+
+delay="${1-}"
+
+IM_is_active ||
+	fatal "interactive mode required"
+exec </dev/null >/dev/null 2>&1
+
+if [ -n "$delay" ]; then
+	while [ ! -f "${_IM_activated}" ]; do
+		[ "$delay" -gt 0 ] ||
+			break
+		delay=$(( $delay - 1 ))
+		sleep 1
+	done
+	sleep 1
+fi
+
+if [ ! -f "${_IM_activated}" ] && IM_is_active; then
+	:> "${_IM_activated}"
+	IM_hide_bootsplash
+	[ -n "$CONSOLE" ] ||
+		chvt "${_IM_VT_number}"
+	rootdelay_pause
+fi
diff --git a/features/bootchain-interactive/data/bin/interactive-sh-functions b/features/bootchain-interactive/data/bin/interactive-sh-functions
new file mode 100644
index 0000000..9baf3df
--- /dev/null
+++ b/features/bootchain-interactive/data/bin/interactive-sh-functions
@@ -0,0 +1,247 @@
+#!/bin/bash -efu
+
+if [ -z "${__interactive_sh_functions-}" ]; then
+__interactive_sh_functions=1
+
+. /.initrd/initenv
+. initrd-sh-functions
+
+. shell-signal
+
+message_time=1
+
+# Public
+IM_BACKTITLE=
+IM_WIDGET_ARGS=
+CONSOLE="${CONSOLE-}"
+NOASKUSER="${NOASKUSER-}"
+NOLINES="${NOLINES-}"
+
+# Internal
+_IM_max_width=
+_IM_widgetsdir=/lib/IM-widgets
+_IM_flag=/.initrd/interactive-mode
+_IM_unsplashed="${_IM_flag}/BOOTSPLASH-STOPPED"
+_IM_activated="${_IM_flag}/VT-ACTIVATED"
+_IM_VT_number="${_IM_VT_number:-2}"
+
+# Standart "reboot message"
+IM_RBMSG="Press ENTER to reboot the computer..."
+
+
+IM_is_active()
+{
+	[ -d "${_IM_flag}" ] ||
+		return 1
+}
+
+IM_ponder_stop()
+{
+	: # Base implementation overrided in /lib/IM-widgets/ponder
+}
+
+_IM_exit_handler()
+{
+	local rc=$?
+
+	trap - EXIT
+
+	if IM_is_active; then
+		IM_ponder_stop
+		rootdelay_unpause
+		if [ -z "$CONSOLE" ] && [ -z "$NOASKUSER" ]; then
+			clear
+			chvt 1
+		fi
+		IM_show_bootsplash
+		rm -rf -- "${_IM_flag}"
+	fi
+
+	exit $rc
+}
+
+IM_exec()
+{
+	local now=
+
+	if [ "${1-}" = "--now" ]; then
+		now=-s
+		shift
+	fi
+
+	! IM_is_active ||
+		fatal "already in interactive mode"
+
+	if [ -n "$CONSOLE" ] || [ -n "$NOASKUSER" ]; then
+		exec "$@"
+	else
+		[ -e "/dev/tty${_IM_VT_number}" ] ||
+			mknod "/dev/tty${_IM_VT_number}" c 4 ${_IM_VT_number}
+		exec openvt -f -w $now -c${_IM_VT_number} -- "$@"
+	fi
+
+	fatal "exec failed in IM_exec()"
+}
+
+# shellcheck disable=SC2120
+IM_activate()
+{
+	local delay="${1-}"
+	local logfile="${2:-/var/log/IM.log}"
+
+	! IM_is_active ||
+		fatal "already in interactive mode"
+	set_cleanup_handler _IM_exit_handler
+
+	if [ -n "$NOASKUSER" ]; then
+		exec </dev/null >/dev/null 2>>"$logfile"
+	elif [ -n "$CONSOLE" ]; then
+		exec </dev/console >/dev/console 2>>"$logfile"
+	else
+		exec <"/dev/tty${_IM_VT_number}" >"/dev/tty${_IM_VT_number}" 2>>"$logfile"
+	fi
+
+	mkdir -p -- "${_IM_flag}"
+
+	export TERM="${TERM:-linux}"
+	export DIALOG_TTY=1
+	export LC_ALL=C
+	export LANG=C
+
+	# Determinating maximum width
+	if [ -n "$NOASKUSER" ]; then
+		_IM_max_width=80
+		printf '%s\n' "${_IM_max_width}" >"${_IM_flag}/MAX-WIDTH"
+	elif [ -z "${_IM_max_width}" ]; then
+		local esc cols rows
+
+		# The snippet above by Oleg Nesterov (C) was modified for IM, see:
+		# https://lists.altlinux.org/pipermail/make-initrd/2021-June/000458.html
+		#
+		echo -ne "\e[s\e[1000;1000H\e[6n\e[u"
+		# shellcheck disable=SC2162
+		IFS=';[' read -s -t2 -dR esc rows cols || {
+			rows=24
+			cols=80
+		}
+		_IM_max_width=$(( $cols - 6 ))
+		stty rows "$rows" cols "$cols" 2>/dev/null ||:
+		printf '%s\n' "${_IM_max_width}" >"${_IM_flag}/MAX-WIDTH"
+	fi
+
+	# Activating IM VT
+	if [ -n "$NOASKUSER" ]; then
+		message "TTY's not used, dialogs are disabled"
+	elif [ -n "$CONSOLE" ]; then
+		activate-interactive-vt
+		message "TTY's not available, using current system console"
+	elif [ -z "$delay" ]; then
+		activate-interactive-vt
+		message "TTY${_IM_VT_number} now active"
+	else
+		activate-interactive-vt "$delay" &
+		message "TTY${_IM_VT_number} will be activated after $((1 + $delay)) seconds"
+	fi
+
+	# Warm up: back title do not displayed only with the first widget
+	# after openvt(), single dialog exec strangely solve this problem.
+	#
+	if [ -z "$CONSOLE" ] && [ -z "$NOASKUSER" ]; then
+		dialog	${NOLINES:+--ascii-lines}	\
+			--backtitle "WARM UP"		\
+			--title "[ Loading widgets ]"	\
+			--pause "" 7 40 0		\
+		||:
+	fi
+
+	# Also we need to load and to check all widgets before using them
+	IM_load_all
+}
+
+IM_load_widgets()
+{
+	local widget loaded
+
+	for widget in "$@" _; do
+		[ -s "${_IM_widgetsdir}/$widget" ] ||
+			continue
+		eval "loaded=\"\${__IM_${widget}_loaded-}\""
+
+		if [ -z "$loaded" ]; then
+			eval "__IM_${widget}_loaded=1"
+			. "${_IM_widgetsdir}/$widget"
+		fi
+	done
+}
+
+IM_load_all()
+{
+	local widget
+
+	# shellcheck disable=SC2045
+	for widget in $(ls -- "${_IM_widgetsdir}/"); do
+		IM_load_widgets "$widget"
+	done
+}
+
+IM_start_output()
+{
+	# shellcheck disable=SC2119
+	IM_is_active ||
+		IM_activate
+	[ -n "${_IM_max_width}" ] ||
+		read -r _IM_max_width <"${_IM_flag}/MAX-WIDTH" ||
+			_IM_max_width=66
+	IM_load_widgets "$@"
+}
+
+IM_start_input()
+{
+	[ -z "$NOASKUSER" ] ||
+		fatal "input widgets not allowed, dialogs are disabled"
+	IM_start_output "$@"
+	[ -f "${_IM_activated}" ] ||
+		activate-interactive-vt
+}
+
+IM_show_bootsplash()
+{
+	local cmd=plymouth
+
+	if IM_is_active &&
+		[ -f "${_IM_unsplashed}" ] &&
+		command -v $cmd >/dev/null &&
+		$cmd --ping >/dev/null 2>&1
+	then
+		$cmd unpause-progress --show-splash ||:
+		rm -f -- "${_IM_unsplashed}"
+	fi
+}
+
+IM_hide_bootsplash()
+{
+	local cmd=plymouth
+
+	if IM_is_active &&
+		[ ! -f "${_IM_unsplashed}" ] &&
+		command -v $cmd >/dev/null &&
+		$cmd --ping >/dev/null 2>&1
+	then
+		$cmd pause-progress --hide-splash ||:
+		:> "${_IM_unsplashed}"
+	fi
+}
+
+IM_update_bootsplash()
+{
+	local cmd=plymouth
+
+	if IM_is_active &&
+		command -v $cmd >/dev/null &&
+		$cmd --ping >/dev/null 2>&1
+	then
+		$cmd update --status="$1" ||:
+	fi
+}
+
+fi # __interactive_sh_functions
diff --git a/features/bootchain-interactive/data/etc/initrd/cmdline.d/bootchain-interactive b/features/bootchain-interactive/data/etc/initrd/cmdline.d/bootchain-interactive
new file mode 100644
index 0000000..8676770
--- /dev/null
+++ b/features/bootchain-interactive/data/etc/initrd/cmdline.d/bootchain-interactive
@@ -0,0 +1,3 @@
+register_parameter string CONSOLE
+register_parameter bool NOASKUSER
+register_parameter bool NOLINES
diff --git a/features/bootchain-interactive/data/lib/IM-widgets/choice b/features/bootchain-interactive/data/lib/IM-widgets/choice
new file mode 100644
index 0000000..23517e0
--- /dev/null
+++ b/features/bootchain-interactive/data/lib/IM-widgets/choice
@@ -0,0 +1,60 @@
+#!/bin/bash -efu
+
+IM_choice()
+{
+	IM_start_input
+
+	local varname="$1" text="${2:-\n}"; shift 2
+	local height=1 width=$(( 4 + ${#text} ))
+	local rc=0 items=$(( $# / 2 ))
+
+	_calculate_items_width()
+	{
+		local label iw i=0
+
+		while [ $i -lt $items ]; do
+			label="$2"; shift 2
+			iw=$(( 4 + ${#label} ))
+			[ $iw -le $width ] ||
+				width=$iw
+			i=$((1 + $i))
+		done
+	}
+
+	[ $items -gt 0 ] ||
+		return 1
+	[ $width -gt "${_IM_max_width}" ] ||
+		_calculate_items_width "$@"
+	if [ $width -lt 40 ]; then
+		width=40
+	elif [ $width -gt ${_IM_max_width} ]; then
+		height=$(( $width / ${_IM_max_width} + 1 ))
+		width=${_IM_max_width}
+	fi
+	if [ $items -gt 7 ]; then
+		height=$((14 + $height))
+	else
+		height=$((7 + $height + $items))
+	fi
+
+	local dlgcmd="dialog $IM_WIDGET_ARGS ${NOLINES:+--ascii-lines}"
+	dlgcmd="$dlgcmd ${IM_BACKTITLE:+--backtitle \"$IM_BACKTITLE\"}"
+	dlgcmd="$dlgcmd --title \"[ Please choose... ]\""
+	dlgcmd="$dlgcmd --no-tags --menu \"\n$text\""
+	dlgcmd="$dlgcmd $height $width $items"
+
+	while [ $# -ge 2 ]; do
+		dlgcmd="$dlgcmd \"$1\" \"$2\""
+		shift 2
+	done
+
+	exec 3>&1
+	text="$(eval "$dlgcmd" 2>&1 1>&3)" || rc=$?
+	exec 3>&-
+
+	[ -z "$CONSOLE" ] ||
+		reset
+	[ $rc -eq 0 ] ||
+		return $rc
+	eval "$varname=\"$text\""
+}
diff --git a/features/bootchain-interactive/data/lib/IM-widgets/dlgmsg b/features/bootchain-interactive/data/lib/IM-widgets/dlgmsg
new file mode 100644
index 0000000..74e1eba
--- /dev/null
+++ b/features/bootchain-interactive/data/lib/IM-widgets/dlgmsg
@@ -0,0 +1,25 @@
+#!/bin/bash -efu
+
+IM_dlgmsg()
+{
+	IM_start_input
+
+	local title="$1" text="$2" height=2
+	local width=$(( 4 + ${#text} ))
+
+	if [ $width -lt 40 ]; then
+		width=40
+	elif [ $width -gt ${_IM_max_width} ]; then
+		height=$(( $width / ${_IM_max_width} + 2 ))
+		width=${_IM_max_width}
+	fi
+
+	dialog	$IM_WIDGET_ARGS					\
+		${NOLINES:+--ascii-lines}			\
+		${IM_BACKTITLE:+--backtitle "$IM_BACKTITLE"}	\
+		--title "$title"				\
+		--msgbox "\n$text"				\
+		$((4 + $height)) $width ||:
+	[ -z "$CONSOLE" ] ||
+		reset
+}
diff --git a/features/bootchain-interactive/data/lib/IM-widgets/errmsg b/features/bootchain-interactive/data/lib/IM-widgets/errmsg
new file mode 100644
index 0000000..e5f05db
--- /dev/null
+++ b/features/bootchain-interactive/data/lib/IM-widgets/errmsg
@@ -0,0 +1,29 @@
+#!/bin/bash -efu
+
+IM_errmsg()
+{
+	IM_start_input
+
+	local text="$1" height=2
+	local width=$(( 4 + ${#text} ))
+
+	if [ $width -lt 40 ]; then
+		width=40
+	elif [ $width -gt ${_IM_max_width} ]; then
+		height=$(( $width / ${_IM_max_width} + 2 ))
+		width=${_IM_max_width}
+	fi
+
+	[ ! -s /etc/dialogrc.error ] ||
+		export DIALOGRC=/etc/dialogrc.error
+	dialog	$IM_WIDGET_ARGS					\
+		${NOLINES:+--ascii-lines}			\
+		${IM_BACKTITLE:+--backtitle "$IM_BACKTITLE"}	\
+		--title "[ Error! ]"				\
+		--msgbox "\n$text"				\
+		$((4 + $height)) $width ||:
+	[ -z "$CONSOLE" ] ||
+		reset
+	[ ! -s /etc/dialogrc.error ] ||
+		export DIALOGRC=
+}
diff --git a/features/bootchain-interactive/data/lib/IM-widgets/form b/features/bootchain-interactive/data/lib/IM-widgets/form
new file mode 100644
index 0000000..0c8c98e
--- /dev/null
+++ b/features/bootchain-interactive/data/lib/IM-widgets/form
@@ -0,0 +1,70 @@
+#!/bin/bash -efu
+
+IM_form()
+{
+	IM_start_input
+
+	local i=0 lw=0 formHeight=$(( $# / 3 - 1 ))
+	local title="$1" text="$2" textHeight="$3"
+	local label varname ilen itype; shift 3
+
+	_calculate_labels_width()
+	{
+		while [ $i -lt $formHeight ]; do
+			label="$3"; shift 3
+			[ ${#label} -le $lw ] ||
+				lw=${#label}
+			i=$((1 + $i))
+		done
+	}
+
+	[ $formHeight -gt 0 ] ||
+		return 1
+	[ -n "$title" ] ||
+		title="[ Please fill entries... ]"
+	_calculate_labels_width "$@"
+	lw=$((4 + $lw)); i=1
+
+	local width=60 rc=0 vars="" values=""
+	local height=$((7 + $textHeight + $formHeight))
+	local fieldWidth=$(( $width - $lw - 6 ))
+
+	local dlgcmd="dialog $IM_WIDGET_ARGS ${NOLINES:+--ascii-lines}"
+	dlgcmd="$dlgcmd ${IM_BACKTITLE:+--backtitle \"$IM_BACKTITLE\"}"
+	dlgcmd="$dlgcmd --insecure --title \"$title\""
+	dlgcmd="$dlgcmd --mixedform \"\n$text\""
+	dlgcmd="$dlgcmd $height $width $formHeight"
+
+	while [ $i -le $formHeight ]; do
+		varname="$1"
+		ilen="$2"
+		label="$3"
+		shift 3
+		itype=0
+		case "$varname" in
+		password*|passwd*|pass|pass1|pass2)
+			itype=1
+			;;
+		esac
+		vars="${vars}${varname} "
+		dlgcmd="$dlgcmd \"$label:\" $i 1 \"\${$varname}\""
+		dlgcmd="$dlgcmd $i $lw $fieldWidth $ilen $itype"
+		i=$((1 + $i))
+	done
+
+	exec 3>&1
+	values=$(eval "$dlgcmd" 2>&1 1>&3) || rc=$?
+	exec 3>&-
+
+	[ -z "$CONSOLE" ] ||
+		reset
+	[ "$rc" = 0 ] ||
+		return $rc
+	i=1
+	while [ "$i" -le "$formHeight" ]; do
+		varname="$(echo "$vars" |cut -f$i -d ' ')"
+		rc="$(echo "$values" |sed -n -r ${i}p)"
+		eval "$varname=\"$rc\""
+		i=$((1 + $i))
+	done
+}
diff --git a/features/bootchain-interactive/data/lib/IM-widgets/gauge b/features/bootchain-interactive/data/lib/IM-widgets/gauge
new file mode 100644
index 0000000..baf7ac5
--- /dev/null
+++ b/features/bootchain-interactive/data/lib/IM-widgets/gauge
@@ -0,0 +1,31 @@
+#!/bin/bash -efu
+
+IM_gauge()
+{
+	IM_start_output
+
+	local title="$1" text="${2-}"
+	local height=1 width=$(( 4 + ${#text} ))
+
+	if [ $width -gt ${_IM_max_width} ]; then
+		height=$(( $width / ${_IM_max_width} + 1 ))
+		width=${_IM_max_width}
+	elif [ $width -lt 40 ]; then
+		[ $width -ne 4 ] ||
+			height=0
+		width=40
+	fi
+
+	if [ -n "$text" ]; then
+		height=$((1 + $height))
+		text="\n$text"
+	fi
+
+	dialog	$IM_WIDGET_ARGS					\
+		${NOLINES:+--ascii-lines}			\
+		${IM_BACKTITLE:+--backtitle "$IM_BACKTITLE"}	\
+		--title "$title"				\
+		--gauge "$text"					\
+		$((5 + $height)) $width 2>/dev/null		\
+	||:
+}
diff --git a/features/bootchain-interactive/data/lib/IM-widgets/ponder b/features/bootchain-interactive/data/lib/IM-widgets/ponder
new file mode 100644
index 0000000..c6d4600
--- /dev/null
+++ b/features/bootchain-interactive/data/lib/IM-widgets/ponder
@@ -0,0 +1,67 @@
+#!/bin/bash -efu
+
+# Internal
+_IM_ponder_pid=
+_IM_ponder_finished="${_IM_flag}/PONDER-FINISHED"
+
+_IM_ponder_bg()
+{
+	local dlgcmd="IM_gauge \"$1\" \"$2\""
+	local delay="$3" step="$4" percent=0 forward=1
+
+	( while [ ! -f "${_IM_ponder_finished}" ]; do
+		echo "$percent"
+
+		if [ $forward -ne 0 ]; then
+			if [ $percent -lt 100 ]; then
+				percent=$(( $percent + $step ))
+			else
+				percent=$(( $percent - $step ))
+				forward=0
+			fi
+		else
+			if [ $percent -gt 0 ]; then
+				percent=$(( $percent - $step ))
+			else
+				percent=$(( $percent + $step ))
+				forward=1
+			fi
+		fi
+
+		[ $percent -le 100 ] ||
+			percent=100
+		[ $percent -ge 0 ] ||
+			percent=0
+		sleep "$delay"
+	  done
+
+	  echo "100"
+	) |eval "$dlgcmd"
+}
+
+IM_ponder_start()
+{
+	IM_start_output gauge
+
+	local title="$1" text="${2-}"
+	local delay="${3:-0.5}"
+	local step="${4:-10}"
+
+	[ -z "${_IM_ponder_pid}" ] ||
+		return 0
+	rm -f -- "${_IM_ponder_finished}"
+	_IM_ponder_bg "$title" "$text" "$delay" "$step" &
+	_IM_ponder_pid=$!
+}
+
+IM_ponder_stop()
+{
+	[ -n "${_IM_ponder_pid}" ] ||
+		return 0
+	:> "${_IM_ponder_finished}"
+	wait "${_IM_ponder_pid}" 2>/dev/null ||:
+	rm -f -- "${_IM_ponder_finished}"
+	[ -z "$CONSOLE" ] ||
+		reset
+	_IM_ponder_pid=
+}
diff --git a/features/bootchain-interactive/rules.mk b/features/bootchain-interactive/rules.mk
new file mode 100644
index 0000000..b647caf
--- /dev/null
+++ b/features/bootchain-interactive/rules.mk
@@ -0,0 +1,2 @@
+PUT_FEATURE_DIRS  += $(BOOTCHAIN_INTERACTIVE_DATADIR)
+PUT_FEATURE_PROGS += $(BOOTCHAIN_INTERACTIVE_PROGS)
-- 
2.24.1



More information about the Make-initrd mailing list