208 lines
8.8 KiB
Diff
208 lines
8.8 KiB
Diff
From 0b851a3f25d6e2ac7e6d06e342d0823c206ee25a Mon Sep 17 00:00:00 2001
|
|
From: Vladimir Slavik <vslavik@redhat.com>
|
|
Date: Wed, 21 Apr 2021 20:00:54 +0200
|
|
Subject: [PATCH] Another attempt at the X thing. This gives up the exception
|
|
handler test temporarily, and solves almost everything.
|
|
|
|
The main problem of other solutions is that once X starts,
|
|
it steals the screen by going to tty6. If the exception handler
|
|
test-invoking handler is not returned back immediately,
|
|
an "after-timeout" handler can be installed instead, which switches back to tty1.
|
|
|
|
With that in place, it's also safe to terminate Xorg once
|
|
it's clear it's not coming in time. The termination will happen later,
|
|
but that does not matter any more.
|
|
|
|
Finally, with the termination happening,
|
|
it is also safe to return the crash report text handler.
|
|
|
|
Resolves: rhbz#1918702
|
|
|
|
Previous work: #3107, #3132, #3141, #3295. Thanks to @bitcoffeeiux and @poncovka.
|
|
|
|
The one avenue left unexplored is using the -displayfd option.
|
|
---
|
|
pyanaconda/core/util.py | 62 ++++++++++++++++++++++++++++++++---------
|
|
pyanaconda/display.py | 29 +++++++++++++++++--
|
|
2 files changed, 75 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/pyanaconda/core/util.py b/pyanaconda/core/util.py
|
|
index b7a1731..3013cd8 100644
|
|
--- a/pyanaconda/core/util.py
|
|
+++ b/pyanaconda/core/util.py
|
|
@@ -47,7 +47,7 @@ from pyanaconda.core.constants import DRACUT_SHUTDOWN_EJECT, TRANSLATIONS_UPDATE
|
|
IPMI_ABORTED, X_TIMEOUT, TAINT_HARDWARE_UNSUPPORTED, TAINT_SUPPORT_REMOVED, \
|
|
WARNING_HARDWARE_UNSUPPORTED, WARNING_SUPPORT_REMOVED
|
|
from pyanaconda.core.constants import SCREENSHOTS_DIRECTORY, SCREENSHOTS_TARGET_DIRECTORY
|
|
-from pyanaconda.errors import RemovedModuleError, ExitError
|
|
+from pyanaconda.errors import RemovedModuleError
|
|
|
|
from pyanaconda.anaconda_logging import program_log_lock
|
|
from pyanaconda.anaconda_loggers import get_module_logger, get_program_logger
|
|
@@ -204,6 +204,19 @@ def startProgram(argv, root='/', stdin=None, stdout=subprocess.PIPE, stderr=subp
|
|
preexec_fn=preexec, cwd=root, env=env, **kwargs)
|
|
|
|
|
|
+class X11Status:
|
|
+ """Status of Xorg launch.
|
|
+
|
|
+ Values of an instance can be modified from the handler functions.
|
|
+ """
|
|
+ def __init__(self):
|
|
+ self.started = False
|
|
+ self.timed_out = False
|
|
+
|
|
+ def needs_waiting(self):
|
|
+ return not (self.started or self.timed_out)
|
|
+
|
|
+
|
|
def startX(argv, output_redirect=None, timeout=X_TIMEOUT):
|
|
""" Start X and return once X is ready to accept connections.
|
|
|
|
@@ -217,28 +230,36 @@ def startX(argv, output_redirect=None, timeout=X_TIMEOUT):
|
|
:param output_redirect: file or file descriptor to redirect stdout and stderr to
|
|
:param timeout: Number of seconds to timing out.
|
|
"""
|
|
- # Use a list so the value can be modified from the handler function
|
|
- x11_started = [False]
|
|
+ x11_status = X11Status()
|
|
|
|
- def sigusr1_handler(num, frame):
|
|
+ # Handle successful start before timeout
|
|
+ def sigusr1_success_handler(num, frame):
|
|
log.debug("X server has signalled a successful start.")
|
|
- x11_started[0] = True
|
|
+ x11_status.started = True
|
|
|
|
# Fail after, let's say a minute, in case something weird happens
|
|
# and we don't receive SIGUSR1
|
|
def sigalrm_handler(num, frame):
|
|
# Check that it didn't make it under the wire
|
|
- if x11_started[0]:
|
|
+ if x11_status.started:
|
|
return
|
|
+ x11_status.timed_out = True
|
|
log.error("Timeout trying to start %s", argv[0])
|
|
- raise ExitError("Timeout trying to start %s" % argv[0])
|
|
|
|
- # preexec_fn to add the SIGUSR1 handler in the child
|
|
+ # Handle delayed start after timeout
|
|
+ def sigusr1_too_late_handler(num, frame):
|
|
+ if x11_status.timed_out:
|
|
+ log.debug("SIGUSR1 received after X server timeout. Switching back to tty1. "
|
|
+ "SIGUSR1 now again initiates test of exception reporting.")
|
|
+ signal.signal(signal.SIGUSR1, old_sigusr1_handler)
|
|
+
|
|
+ # preexec_fn to add the SIGUSR1 handler in the child we are starting
|
|
+ # see man page XServer(1), section "signals"
|
|
def sigusr1_preexec():
|
|
signal.signal(signal.SIGUSR1, signal.SIG_IGN)
|
|
|
|
try:
|
|
- old_sigusr1_handler = signal.signal(signal.SIGUSR1, sigusr1_handler)
|
|
+ old_sigusr1_handler = signal.signal(signal.SIGUSR1, sigusr1_success_handler)
|
|
old_sigalrm_handler = signal.signal(signal.SIGALRM, sigalrm_handler)
|
|
|
|
# Start the timer
|
|
@@ -249,16 +270,31 @@ def startX(argv, output_redirect=None, timeout=X_TIMEOUT):
|
|
preexec_fn=sigusr1_preexec)
|
|
WatchProcesses.watch_process(childproc, argv[0])
|
|
|
|
- # Wait for SIGUSR1
|
|
- while not x11_started[0]:
|
|
+ # Wait for SIGUSR1 or SIGALRM
|
|
+ while x11_status.needs_waiting():
|
|
signal.pause()
|
|
|
|
finally:
|
|
- # Put everything back where it was
|
|
+ # Stop the timer
|
|
signal.alarm(0)
|
|
- signal.signal(signal.SIGUSR1, old_sigusr1_handler)
|
|
signal.signal(signal.SIGALRM, old_sigalrm_handler)
|
|
|
|
+ # Handle outcome of X start attempt
|
|
+ if x11_status.started:
|
|
+ signal.signal(signal.SIGUSR1, old_sigusr1_handler)
|
|
+ elif x11_status.timed_out:
|
|
+ signal.signal(signal.SIGUSR1, sigusr1_too_late_handler)
|
|
+ # Kill Xorg because from now on we will not use it. It will exit only after sending
|
|
+ # the signal, but at least we don't have to track that.
|
|
+ WatchProcesses.unwatch_process(childproc)
|
|
+ childproc.terminate()
|
|
+ log.debug("Exception handler test suspended to prevent accidental activation by "
|
|
+ "delayed Xorg start. Next SIGUSR1 will be handled as delayed Xorg start.")
|
|
+ # Raise an exception to notify the caller that things went wrong. This affects
|
|
+ # particularly pyanaconda.display.do_startup_x11_actions(), where the window manager
|
|
+ # is started immediately after this. The WM would just wait forever.
|
|
+ raise TimeoutError("Timeout trying to start %s" % argv[0])
|
|
+
|
|
|
|
def _run_program(argv, root='/', stdin=None, stdout=None, env_prune=None, log_output=True,
|
|
binary_output=False, filter_stderr=False):
|
|
diff --git a/pyanaconda/display.py b/pyanaconda/display.py
|
|
index 8379d9c..b577eb8 100644
|
|
--- a/pyanaconda/display.py
|
|
+++ b/pyanaconda/display.py
|
|
@@ -22,6 +22,7 @@
|
|
import os
|
|
import subprocess
|
|
import time
|
|
+import textwrap
|
|
import pkgutil
|
|
|
|
from pyanaconda.core.configuration.anaconda import conf
|
|
@@ -49,6 +50,14 @@ from pyanaconda.anaconda_loggers import get_module_logger, get_stdout_logger
|
|
log = get_module_logger(__name__)
|
|
stdout_log = get_stdout_logger()
|
|
|
|
+X_TIMEOUT_ADVICE = \
|
|
+ "Do not load the stage2 image over a slow network link.\n" \
|
|
+ "Wait longer for the X server startup with the inst.xtimeout=<SECONDS> boot option." \
|
|
+ "The default is 60 seconds.\n" \
|
|
+ "Load the stage2 image into memory with the rd.live.ram boot option to decrease access " \
|
|
+ "time.\n" \
|
|
+ "Enforce text mode when installing from remote media with the inst.text boot option."
|
|
+# on RHEL also: "Use the customer portal download URL in ilo/drac devices for greater speed."
|
|
|
|
# Spice
|
|
|
|
@@ -78,7 +87,7 @@ def ask_vnc_question(anaconda, vnc_server, message):
|
|
App.initialize()
|
|
loop = App.get_event_loop()
|
|
loop.set_quit_callback(tui_quit_callback)
|
|
- spoke = AskVNCSpoke(anaconda.ksdata, message)
|
|
+ spoke = AskVNCSpoke(anaconda.ksdata, message=message)
|
|
ScreenHandler.schedule_screen(spoke)
|
|
App.run()
|
|
|
|
@@ -314,9 +323,23 @@ def setup_display(anaconda, options):
|
|
try:
|
|
start_x11(xtimeout)
|
|
do_startup_x11_actions()
|
|
- except (OSError, RuntimeError) as e:
|
|
+ except TimeoutError as e:
|
|
log.warning("X startup failed: %s", e)
|
|
- stdout_log.warning("X startup failed, falling back to text mode")
|
|
+ print("\nX did not start in the expected time, falling back to text mode. There are "
|
|
+ "multiple ways to avoid this issue:")
|
|
+ wrapper = textwrap.TextWrapper(initial_indent=" * ", subsequent_indent=" ",
|
|
+ width=os.get_terminal_size().columns - 3)
|
|
+ for line in X_TIMEOUT_ADVICE.split("\n"):
|
|
+ print(wrapper.fill(line))
|
|
+ util.vtActivate(1)
|
|
+ anaconda.display_mode = constants.DisplayModes.TUI
|
|
+ anaconda.gui_startup_failed = True
|
|
+ time.sleep(2)
|
|
+
|
|
+ except (OSError, RuntimeError) as e:
|
|
+ log.warning("X or window manager startup failed: %s", e)
|
|
+ print("\nX or window manager startup failed, falling back to text mode.")
|
|
+ util.vtActivate(1)
|
|
anaconda.display_mode = constants.DisplayModes.TUI
|
|
anaconda.gui_startup_failed = True
|
|
time.sleep(2)
|
|
--
|
|
2.23.0
|
|
|