Make-initrd development discussion
 help / color / mirror / Atom feed
* Re: [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature
  @ 2021-10-26 18:58 ` Alexey Gladkov
  2021-10-26 19:54   ` Leonid Krivoshein
  2021-10-26 19:03 ` Alexey Gladkov
  1 sibling, 1 reply; 5+ messages in thread
From: Alexey Gladkov @ 2021-10-26 18:58 UTC (permalink / raw)
  To: make-initrd

On Sun, Oct 24, 2021 at 08:22:54PM +0300, Leonid Krivoshein wrote:
> 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@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=$?

Вот этим eval ты запросто можешь выполнить любой произвольный код из
$text или другой переменной из аргументов. На самом деле тебе этот eval не
нужен, если аккуратно заполнить свои же аргументы:

	set -- $IM_WIDGET_ARGS \
		${NOLINES:+--ascii-lines} \
		${IM_BACKTITLE:+--backtitle "$IM_BACKTITLE"} \
		--title "[ Please choose... ]" \
		--no-tags --menu "\n$text" \
		$height $width $items \
		"$@"

	text="$(dialog "$@" 2>&1 1>&3)" || rc=$?

Ну а можно и как ниже вообще без set сразу dialog передавать аргументы.

> +	exec 3>&-
> +
> +	[ -z "$CONSOLE" ] ||
> +		reset
> +	[ $rc -eq 0 ] ||
> +		return $rc
> +	eval "$varname=\"$text\""

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=$?

Тоже самое что и в IM_choice.

> +	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\""

eval "$varname=\"\$rc\""
                 ^

$ echo $HOME
/home/legion
$ text='$HOME'
$ eval "v=\"$text\""
$ echo $v
/home/legion
$ eval "v=\"\$text\""
$ echo $v
$HOME

> +		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"

А тут зачем eval если это IM_gauge "$1" "$2" ?

> +}
> +
> +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
> 

> _______________________________________________
> Make-initrd mailing list
> Make-initrd@lists.altlinux.org
> https://lists.altlinux.org/mailman/listinfo/make-initrd


-- 
Rgrds, legion



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature
    2021-10-26 18:58 ` [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature Alexey Gladkov
@ 2021-10-26 19:03 ` Alexey Gladkov
  2021-10-26 20:07   ` Leonid Krivoshein
  1 sibling, 1 reply; 5+ messages in thread
From: Alexey Gladkov @ 2021-10-26 19:03 UTC (permalink / raw)
  To: make-initrd

On Sun, Oct 24, 2021 at 08:22:54PM +0300, Leonid Krivoshein wrote:
> This feature adds the ability to use text dialogs in
> the initramfs scripts. See README.md for more details.

Я не понимаю, как эта фича будет работать с остальными. Например с luks
или fsck, которая может захотеть что-нибудь спросить и которая ничего из
этого не использует. Ты же не останавливаешь очень udev в ueventd. Поэтому
может вылезти любой диалог.

> Signed-off-by: Leonid Krivoshein <klark.devel@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
> 

> _______________________________________________
> Make-initrd mailing list
> Make-initrd@lists.altlinux.org
> https://lists.altlinux.org/mailman/listinfo/make-initrd


-- 
Rgrds, legion



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature
  2021-10-26 18:58 ` [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature Alexey Gladkov
@ 2021-10-26 19:54   ` Leonid Krivoshein
  2021-10-26 20:25     ` Leonid Krivoshein
  0 siblings, 1 reply; 5+ messages in thread
From: Leonid Krivoshein @ 2021-10-26 19:54 UTC (permalink / raw)
  To: make-initrd



26.10.2021 21:58, Alexey Gladkov пишет:
> On Sun, Oct 24, 2021 at 08:22:54PM +0300, Leonid Krivoshein wrote:
>> 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@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=$?
> Вот этим eval ты запросто можешь выполнить любой произвольный код из
> $text или другой переменной из аргументов.

По идее, в $text, да и в других переменных тем более, не может быть 
произвольного кода, так как это всё формируется в коде bootchain не на 
основе вводимых данных. Однако, ...


> На самом деле тебе этот eval не
> нужен, если аккуратно заполнить свои же аргументы:
>
> 	set -- $IM_WIDGET_ARGS \
> 		${NOLINES:+--ascii-lines} \
> 		${IM_BACKTITLE:+--backtitle "$IM_BACKTITLE"} \
> 		--title "[ Please choose... ]" \
> 		--no-tags --menu "\n$text" \
> 		$height $width $items \
> 		"$@"
>
> 	text="$(dialog "$@" 2>&1 1>&3)" || rc=$?

...эта идея мне очень нравится, так и переделаю. Будет меньше 
"мета-программирования".


> Ну а можно и как ниже вообще без set сразу dialog передавать аргументы.
>
>> +	exec 3>&-
>> +
>> +	[ -z "$CONSOLE" ] ||
>> +		reset
>> +	[ $rc -eq 0 ] ||
>> +		return $rc
>> +	eval "$varname=\"$text\""
> 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=$?
> Тоже самое что и в IM_choice.

Ну да, там тогда почти во всём data/lib/IM-widgets/ это надо будет 
переделать.


>> +	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\""
> eval "$varname=\"\$rc\""
>                   ^
>
> $ echo $HOME
> /home/legion
> $ text='$HOME'
> $ eval "v=\"$text\""
> $ echo $v
> /home/legion
> $ eval "v=\"\$text\""
> $ echo $v
> $HOME

Да, эту идею я уже понял. Но, может, лучше с set?

$ text="..'; cat /etc/passwd; '.."
$ eval "v='$x'"
(уф, не буду показывать)!..

Это я к тому, что разве \" гарантирует невыполнение произвольного кода?
Я же мог и двойные кавычки использовать вместо одинарных:

$ text="..\"; cat /etc/passwd; \".."
$ eval "v=\"$x\""
(уф, тоже не буду показывать)!..


>> +		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"
> А тут зачем eval если это IM_gauge "$1" "$2" ?
>

В общем, да. Надо переделать.


>> +}
>> +
>> +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
>>
>> _______________________________________________
>> Make-initrd mailing list
>> Make-initrd@lists.altlinux.org
>> https://lists.altlinux.org/mailman/listinfo/make-initrd
>

-- 
Best regards,
Leonid Krivoshein.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature
  2021-10-26 19:03 ` Alexey Gladkov
@ 2021-10-26 20:07   ` Leonid Krivoshein
  0 siblings, 0 replies; 5+ messages in thread
From: Leonid Krivoshein @ 2021-10-26 20:07 UTC (permalink / raw)
  To: make-initrd


26.10.2021 22:03, Alexey Gladkov пишет:
> On Sun, Oct 24, 2021 at 08:22:54PM +0300, Leonid Krivoshein wrote:
>> This feature adds the ability to use text dialogs in
>> the initramfs scripts. See README.md for more details.
> Я не понимаю, как эта фича будет работать с остальными. Например с luks
> или fsck, которая может захотеть что-нибудь спросить и которая ничего из
> этого не использует. Ты же не останавливаешь очень udev в ueventd. Поэтому
> может вылезти любой диалог.

На текущий момент bootchain-interactive можно использовать какой-то 
одной фичей make-initrd, например, модулями bootchain. Именно по этой 
причине префикс bootchain пришлось пока оставить.

Очень хотелось бы эту ситуацию изменить, перетащить фичу на уровень 
выше, сделать её расшаренной между остальными фичами. Мы хотели обсудить 
воочию именно этот вопрос с тобой и Антоном. Если бы я знал, как это 
сделать, я бы давно сделал.

Потом, я уже говорил: эта "библиотека виджетов", равно как и вся 
концепция "IM", была сделана наспех только ради того, чтобы было просто 
и удобно рисовать виджеты в altboot. Тут есть что обсуждать и, наверное, 
переделывать в плане улучшения.


>> Signed-off-by: Leonid Krivoshein <klark.devel@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
>>
>> _______________________________________________
>> Make-initrd mailing list
>> Make-initrd@lists.altlinux.org
>> https://lists.altlinux.org/mailman/listinfo/make-initrd
>

-- 
Best regards,
Leonid Krivoshein.



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature
  2021-10-26 19:54   ` Leonid Krivoshein
@ 2021-10-26 20:25     ` Leonid Krivoshein
  0 siblings, 0 replies; 5+ messages in thread
From: Leonid Krivoshein @ 2021-10-26 20:25 UTC (permalink / raw)
  To: make-initrd


26.10.2021 22:54, Leonid Krivoshein пишет:
>> $ echo $HOME
>> /home/legion
>> $ text='$HOME'
>> $ eval "v=\"$text\""
>> $ echo $v
>> /home/legion
>> $ eval "v=\"\$text\""
>> $ echo $v
>> $HOME
>
> Да, эту идею я уже понял. Но, может, лучше с set?
>
> $ text="..'; cat /etc/passwd; '.."
> $ eval "v='$x'"
> (уф, не буду показывать)!..
>
> Это я к тому, что разве \" гарантирует невыполнение произвольного кода?
> Я же мог и двойные кавычки использовать вместо одинарных:
>
> $ text="..\"; cat /etc/passwd; \".."
> $ eval "v=\"$x\""
> (уф, тоже не буду показывать)!..

Не, вот так всё нормально:

$ text="..\"; cat /etc/passwd; \".."
$ eval "v=\"\$text\""

Это я ошибся.


-- 
Best regards,
Leonid Krivoshein.



^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2021-10-26 20:25 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-26 18:58 ` [make-initrd] [PATCH v6 18/22] bootchain-interactive: initial feature Alexey Gladkov
2021-10-26 19:54   ` Leonid Krivoshein
2021-10-26 20:25     ` Leonid Krivoshein
2021-10-26 19:03 ` Alexey Gladkov
2021-10-26 20:07   ` Leonid Krivoshein

Make-initrd development discussion

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://lore.altlinux.org/make-initrd/0 make-initrd/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 make-initrd make-initrd/ http://lore.altlinux.org/make-initrd \
		make-initrd@lists.altlinux.org make-initrd@lists.altlinux.ru make-initrd@lists.altlinux.com
	public-inbox-index make-initrd

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://lore.altlinux.org/org.altlinux.lists.make-initrd


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git