Jelajahi Sumber

fix(core): stop prompts looping forever when no input is available (#5042)

* fix(core): stop prompts looping forever when no input is available

fn_prompt_yn read the answer in an endless loop. When stdin is not a
terminal (cron, piped input, a container started without -it), read hits
EOF, the answer stays empty and "Please answer yes or no." is printed
forever at full CPU. Running ./vhserver install with stdin from /dev/null
wrote over 3 million lines (83 MB) in 60 seconds at the server directory
prompt.

Without a terminal bash also ignores read -e, -i and -p, so the prefilled
default is never applied and taking the default is not an option. Several
prompts default to Y on actions that must not happen unattended (accept
EULA, enable stats, delete all modules, overwrite the fastdl directory,
remove a mod), and treating no input as "no" would exit 0 on an aborted
install.

Prompts now check the read return code. If read fails with no answer,
the command prints an error and exits with code 2. Scripted answers such
as "yes |" or "printf 'y\n' |" keep working, including an answer with no
trailing newline. The same check is added to:

- fn_prompt_message, which returns 1, and command_send, which exits with
  an error because core_exit.sh inside $() only ends the subshell.
- the mods-install and mods-remove selection loops, which looped forever
  on "is not a valid addon/mod" in the same way.

* fix(core): document shared prompt exit codes for ShellCheck
Daniel Gibbs 3 hari lalu
induk
melakukan
8ef06ff6ac

+ 7 - 1
lgsm/modules/command_mods_install.sh

@@ -64,7 +64,13 @@ echo -e ""
 
 
 while :; do
 while :; do
 	echo -en "Enter an ${cyan}addon/mod${default} to ${green}install${default} (or exit to abort): "
 	echo -en "Enter an ${cyan}addon/mod${default} to ${green}install${default} (or exit to abort): "
-	read -r usermodselect
+	if ! read -r usermodselect && [ -z "${usermodselect}" ]; then
+		echo -e ""
+		fn_print_error_nl "Unable to read an addon/mod: no input available"
+		fn_script_log_error "Unable to read an addon/mod: no input available"
+		exitcode=2
+		core_exit.sh
+	fi
 	# Exit if user says exit or abort.
 	# Exit if user says exit or abort.
 	if [ "${usermodselect}" == "exit" ] || [ "${usermodselect}" == "abort" ]; then
 	if [ "${usermodselect}" == "exit" ] || [ "${usermodselect}" == "abort" ]; then
 		core_exit.sh
 		core_exit.sh

+ 7 - 1
lgsm/modules/command_mods_remove.sh

@@ -35,7 +35,13 @@ echo -e ""
 
 
 while :; do
 while :; do
 	echo -en "Enter an ${cyan}addon/mod${default} to ${red}remove${default} (or exit to abort): "
 	echo -en "Enter an ${cyan}addon/mod${default} to ${red}remove${default} (or exit to abort): "
-	read -r usermodselect
+	if ! read -r usermodselect && [ -z "${usermodselect}" ]; then
+		echo -e ""
+		fn_print_error_nl "Unable to read an addon/mod: no input available"
+		fn_script_log_error "Unable to read an addon/mod: no input available"
+		exitcode=2
+		core_exit.sh
+	fi
 	# Exit if user says exit or abort.
 	# Exit if user says exit or abort.
 	if [ "${usermodselect}" == "exit" ] || [ "${usermodselect}" == "abort" ]; then
 	if [ "${usermodselect}" == "exit" ] || [ "${usermodselect}" == "abort" ]; then
 		core_exit.sh
 		core_exit.sh

+ 8 - 1
lgsm/modules/command_send.sh

@@ -22,7 +22,14 @@ if [ "${status}" != "0" ]; then
 		commandtosend="${userinput2}"
 		commandtosend="${userinput2}"
 	else
 	else
 		echo ""
 		echo ""
-		commandtosend=$(fn_prompt_message "send: ")
+		if ! commandtosend=$(fn_prompt_message "send: "); then
+			echo -e ""
+			fn_print_error_nl "Unable to read a command to send: no input available"
+			fn_script_log_error "Unable to read a command to send: no input available"
+			# shellcheck disable=SC2034 # Used by core_exit.sh.
+			exitcode=2
+			core_exit.sh
+		fi
 	fi
 	fi
 	echo ""
 	echo ""
 	fn_print_dots "Sending command to console: \"${commandtosend}\""
 	fn_print_dots "Sending command to console: \"${commandtosend}\""

+ 11 - 2
lgsm/modules/core_messages.sh

@@ -320,7 +320,14 @@ fn_prompt_yn() {
 	fi
 	fi
 
 
 	while true; do
 	while true; do
-		read -e -i "${initial}" -p "${prompt}" -r yn
+		if ! read -e -i "${initial}" -p "${prompt}" -r yn && [ -z "${yn}" ]; then
+			echo -e ""
+			fn_print_error_nl "Unable to read an answer: no input available"
+			fn_script_log_error "Unable to read an answer to \"${1}\": no input available"
+			# shellcheck disable=SC2034 # Used by core_exit.sh.
+			exitcode=2
+			core_exit.sh
+		fi
 		case "${yn}" in
 		case "${yn}" in
 			[Yy] | [Yy][Ee][Ss]) return 0 ;;
 			[Yy] | [Yy][Ee][Ss]) return 0 ;;
 			[Nn] | [Nn][Oo]) return 1 ;;
 			[Nn] | [Nn][Oo]) return 1 ;;
@@ -334,7 +341,9 @@ fn_prompt_message() {
 	while true; do
 	while true; do
 		unset prompt
 		unset prompt
 		local prompt="$1"
 		local prompt="$1"
-		read -e -p "${prompt}" -r answer
+		if ! read -e -p "${prompt}" -r answer && [ -z "${answer}" ]; then
+			return 1
+		fi
 		if fn_prompt_yn "Continue" Y; then
 		if fn_prompt_yn "Continue" Y; then
 			break
 			break
 		fi
 		fi