initial-setup/Make-sure-the-output-from-custom_getpass-is-serializ.patch
lyn1001 1dccb0d33e Manually sync sp2 to sp3
(cherry picked from commit 0820b709e49d01174606c5d6b2e1664ec75fd44d)
2023-11-29 17:06:32 +08:00

194 lines
8.5 KiB
Diff

From 0634c145af32d17e2e272a30fba4c5421c23761f Mon Sep 17 00:00:00 2001
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Sat, 8 Aug 2020 23:10:59 +0200
Subject: [PATCH 060/116] Make sure the output from custom_getpass() is
serialized after stdout
custom_getpass() writes the prompt to the file descriptor of the console where
the last input came from. At the same time, run() races with it, piping the
sys.stdout data to the same file descriptor (and others).
To make matters worse, the stdout data is buffered and with nobody flushing it
always loses the race, with important stuff being written out only after
custom_getpass() returns and somebody cares to flush it out:
Password:
Password (confirm):
Password:
Password (confirm):
================================================================================
================================================================================
Root password
Please select new root password. You will have to type it twice.
The passwords you entered were different. Please try again.
================================================================================
================================================================================
Root password
Please select new root password. You will have to type it twice.
The password is too short
This patch turns on line buffering, removing the necessity of explicit flushes
from the stdout writer side.
That alone wouldn't be sufficient because the stdout traffic could still be
delayed until the piper thread is awoken; though things wouldn't be mixed up
nearly as severely. To cope with this race the active console traffic is piped
to the piper thread as well and it is now responsible for ordering things.
It is still ugly but hey.
---
initial_setup/tui/tui.py | 90 ++++++++++++++++++++++++++--------------
1 file changed, 59 insertions(+), 31 deletions(-)
diff --git a/initial_setup/tui/tui.py b/initial_setup/tui/tui.py
index 47f876d..347053e 100644
--- a/initial_setup/tui/tui.py
+++ b/initial_setup/tui/tui.py
@@ -34,9 +34,14 @@ class MultipleTTYHandler(object):
self._tui_stdin_fd = tui_stdin_fd
self._tui_stdin = os.fdopen(tui_stdin_fd, "w")
+ self._tui_active_out_fd, active_out_fd = os.pipe()
+ self._tui_active_out = os.fdopen(self._tui_active_out_fd, "r")
+ self._active_out = os.fdopen(active_out_fd, "w")
+
self._shutdown = False
- self._active_console = None
+ self._active_console_in = None
+ self._active_console_out = None
self._console_read_fos = {}
self._console_write_fos = []
@@ -82,6 +87,7 @@ class MultipleTTYHandler(object):
fds = list(self._console_read_fos.keys())
# as well as from the anaconda stdout
fds.append(self._tui_stdout_fd)
+ fds.append(self._tui_active_out_fd)
log.info("multi TTY handler starting")
while True:
# Watch the consoles and IS TUI stdout for data and
@@ -93,41 +99,58 @@ class MultipleTTYHandler(object):
if self._shutdown:
log.info("multi TTY handler shutting down")
break
- for fd in rlist:
- if fd == self._tui_stdout_fd:
- # We need to set the TUI stdout fd to non-blocking,
- # as otherwise reading from it would (predictably) result in
- # the readline() function blocking once it runs out of data.
- os.set_blocking(fd, False)
-
- # The IS TUI wants to write something,
- # read all the lines.
- lines = self._tui_stdout.readlines()
-
- # After we finish reading all the data we need to set
- # the TUI stdout fd to blocking again.
- # Otherwise the fd will not be usable when we try to read from
- # it again for unclear reasons.
- os.set_blocking(fd, True)
-
- lines.append("\n") # seems to get lost somewhere on the way
-
- # Write all the lines IS wrote to stdout to all consoles
- # that we consider usable for the IS TUI.
- for console_fo in self._console_write_fos.values():
- for one_line in lines:
- try:
- console_fo.write(one_line)
- except OSError:
- log.exception("failed to write %s to console %s", one_line, console_fo)
- else:
+ if self._tui_stdout_fd in rlist:
+ # We need to set the TUI stdout fd to non-blocking,
+ # as otherwise reading from it would (predictably) result in
+ # the readline() function blocking once it runs out of data.
+ os.set_blocking(self._tui_stdout_fd, False)
+
+ # The IS TUI wants to write something,
+ # read all the lines.
+ lines = self._tui_stdout.readlines()
+
+ # After we finish reading all the data we need to set
+ # the TUI stdout fd to blocking again.
+ # Otherwise the fd will not be usable when we try to read from
+ # it again for unclear reasons.
+ os.set_blocking(self._tui_stdout_fd, True)
+
+ lines.append("\n") # seems to get lost somewhere on the way
+
+ # Write all the lines IS wrote to stdout to all consoles
+ # that we consider usable for the IS TUI.
+ for console_fo in self._console_write_fos.values():
+ for one_line in lines:
+ try:
+ console_fo.write(one_line)
+ except OSError:
+ log.exception("failed to write %s to console %s", one_line, console_fo)
+
+ # Don't go processing the events on other file descriptors until
+ # we're done with everything that's supposed to be on stdout
+ continue
+ elif self._tui_active_out_fd in rlist:
+ # Essentially the same as above but for the acrive console only
+ os.set_blocking(self._tui_active_out_fd, False)
+ lines = self._tui_active_out.readlines()
+ os.set_blocking(self._tui_active_out_fd, True)
+ write_fo = self._active_console_out
+ try:
+ for one_line in lines:
+ write_fo.write(one_line)
+ write_fo.flush()
+ except OSError:
+ log.exception("failed to write %s to active console", lines)
+ else:
+ for fd in rlist:
# Someone typed some input to a console and hit enter,
# forward the input to the IS TUI stdin.
read_fo = self._console_read_fos[fd]
write_fo = self._console_write_fos[fd]
# as the console is getting input we consider it to be
# the currently active console
- self._active_console = read_fo, write_fo
+ self._active_console_in = read_fo
+ self._active_console_out = write_fo
try:
data = read_fo.readline()
except TypeError:
@@ -148,7 +171,10 @@ class MultipleTTYHandler(object):
Always restores terminal settings before returning.
"""
- input_fo, output_fo = self._active_console
+
+ input_fo = self._active_console_in
+ output_fo = self._active_out
+
passwd = None
with contextlib.ExitStack() as stack:
input_fd = input_fo.fileno()
@@ -179,6 +205,7 @@ class MultipleTTYHandler(object):
passwd = self._fallback_getpass(prompt, output_fo, input_fo)
output_fo.write('\n')
+ output_fo.flush()
return passwd
def _fallback_getpass(self, prompt='Password: ', output_fo=None, input_fo=None):
@@ -239,6 +266,7 @@ class InitialSetupTextUserInterface(TextUserInterface):
# stdout
tui_stdout_fd, stdout_fd = os.pipe()
sys.stdout = os.fdopen(stdout_fd, "w")
+ sys.stdout.reconfigure(line_buffering=True)
# instantiate and start the multi TTY handler
self.multi_tty_handler = MultipleTTYHandler(tui_stdin_fd=tui_stdin_fd, tui_stdout_fd=tui_stdout_fd)
--
2.38.1.windows.1