diff --git a/anaconda.spec.in b/anaconda.spec.in
index 3246bd59b66c..458bfd7ecb62 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -248,15 +248,20 @@ Requires: zram-generator
# needed for proper driver disk support - if RPMs must be installed, a repo is needed
Requires: createrepo_c
# Display stuff moved from lorax templates
+## For Wayland
+Requires: weston
+Requires: xorg-x11-server-Xwayland
+## For X11
Requires: xorg-x11-drivers
Requires: xorg-x11-server-Xorg
Requires: xrandr
+Requires: gnome-kiosk
+## Common stuff
Requires: xrdb
Requires: dbus-x11
Requires: gsettings-desktop-schemas
Requires: nm-connection-editor
Requires: librsvg2
-Requires: gnome-kiosk
Requires: brltty
# dependencies for rpm-ostree payload module
Requires: rpm-ostree >= %{rpmostreever}
@@ -394,6 +399,7 @@ rm -rf \
%{_sbindir}/anaconda
%{_sbindir}/handle-sshpw
%{_datadir}/anaconda
+%{_sysconfdir}/pam.d/anaconda
%{_prefix}/libexec/anaconda
%exclude %{_datadir}/anaconda/gnome
%exclude %{_datadir}/anaconda/pixmaps
diff --git a/data/Makefile.am b/data/Makefile.am
index df3974fb7604..5406e9d4f95d 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see .
-SUBDIRS = command-stubs liveinst systemd pixmaps window-manager dbus conf.d profile.d
+SUBDIRS = command-stubs liveinst systemd pixmaps window-manager dbus conf.d profile.d pam
CLEANFILES = *~
diff --git a/data/pam/Makefile.am b/data/pam/Makefile.am
new file mode 100644
index 000000000000..97e6657be152
--- /dev/null
+++ b/data/pam/Makefile.am
@@ -0,0 +1,21 @@
+# Copyright (C) 2024 Neal Gompa.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see .
+
+CLEANFILES = *~
+
+pamdir = $(sysconfdir)/pam.d
+dist_pam_DATA = anaconda
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/data/pam/anaconda b/data/pam/anaconda
new file mode 100644
index 000000000000..4e6d91adf2c5
--- /dev/null
+++ b/data/pam/anaconda
@@ -0,0 +1,8 @@
+#%PAM-1.0
+auth sufficient pam_permit.so
+account sufficient pam_permit.so
+password sufficient pam_permit.so
+session required pam_loginuid.so
+-session optional pam_keyinit.so revoke
+-session optional pam_limits.so
+session required pam_systemd.so
diff --git a/data/systemd/anaconda.service b/data/systemd/anaconda.service
index a80c6bb7075c..23918415c388 100644
--- a/data/systemd/anaconda.service
+++ b/data/systemd/anaconda.service
@@ -8,3 +8,4 @@ Type=forking
Environment=HOME=/root MALLOC_CHECK_=2 MALLOC_PERTURB_=204 PATH=/usr/bin:/bin:/sbin:/usr/sbin:/mnt/sysimage/bin:/mnt/sysimage/usr/bin:/mnt/sysimage/usr/sbin:/mnt/sysimage/sbin LANG=en_US.UTF-8 GDK_BACKEND=x11 XDG_RUNTIME_DIR=/tmp GIO_USE_VFS=local
WorkingDirectory=/root
ExecStart=/usr/bin/tmux -u -f /usr/share/anaconda/tmux.conf start
+PAMName=anaconda
diff --git a/pyanaconda/core/constants.py b/pyanaconda/core/constants.py
index 40e9adf171ba..17881ea28631 100644
--- a/pyanaconda/core/constants.py
+++ b/pyanaconda/core/constants.py
@@ -261,6 +261,9 @@ class SecretStatus(Enum):
IPMI_ABORTED = 0x9 # installation finished unsuccessfully, due to some non-exn error
IPMI_FAILED = 0xA # installation hit an exception
+# Wayland socket name to use
+WAYLAND_SOCKET_NAME = "wl-sysinstall-0"
+
# X display number to use
X_DISPLAY_NUMBER = 1
@@ -315,6 +318,14 @@ class DisplayModes(Enum):
False: "noninteractive"
}
+# Weston configuration
+WESTON_CONFIG = {
+ "core": {
+ "shell": "kiosk",
+ "xwayland": "true"
+ }
+}
+
# Loggers
LOGGER_ANACONDA_ROOT = "anaconda"
LOGGER_MAIN = "anaconda.main"
@@ -322,6 +333,9 @@ class DisplayModes(Enum):
LOGGER_PROGRAM = "program"
LOGGER_SIMPLELINE = "simpleline"
+# Timeout for starting Wayland
+WAYLAND_TIMEOUT = 60
+
# Timeout for starting X
X_TIMEOUT = 60
diff --git a/pyanaconda/core/util.py b/pyanaconda/core/util.py
index 8005a52eb3c4..446d837c0370 100644
--- a/pyanaconda/core/util.py
+++ b/pyanaconda/core/util.py
@@ -18,6 +18,7 @@
# along with this program. If not, see .
#
+import configparser
import os
import os.path
import subprocess
@@ -40,7 +41,8 @@
from pyanaconda.core.path import make_directories, open_with_perm, join_paths
from pyanaconda.core.process_watchers import WatchProcesses
from pyanaconda.core.constants import DRACUT_SHUTDOWN_EJECT, \
- IPMI_ABORTED, X_TIMEOUT, PACKAGES_LIST_FILE
+ IPMI_ABORTED, WAYLAND_SOCKET_NAME, WAYLAND_TIMEOUT, \
+ WESTON_CONFIG, X_TIMEOUT, PACKAGES_LIST_FILE
from pyanaconda.core.live_user import get_live_user
from pyanaconda.errors import RemovedModuleError
@@ -165,6 +167,68 @@ def preexec():
return partsubp(preexec_fn=preexec)
+class WaylandStatus:
+ """Status of Wayland 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 startWl(weston_config=WESTON_CONFIG, output_redirect=None, timeout=WAYLAND_TIMEOUT):
+ """ Start Weston for Wayland and return once Weston is ready to accept connections.
+
+ We can identify whether Weston is ready by testing if
+ the Wayland socket is open yet. Once it is, we can return success.
+
+ :param weston_config: The weston.ini(5) configuration to use, as a dictionary
+ :param output_redirect: file or file descriptor to redirect stdout and stderr to
+ :param timeout: Number of seconds to timing out.
+ """
+ wl_status = WaylandStatus()
+
+ # Create the config file for Weston
+ weston_config_file = tempfile.NamedTemporaryFile(suffix="-wl-weston-sysinstall-ini",
+ delete=False)
+ weston_config_ini = configparser.ConfigParser()
+ for section, options in weston_config.items():
+ weston_config_ini.add_section(section)
+ for key, value in options.items():
+ weston_config_ini.set(section, key, str(value))
+ weston_config_ini.write(weston_config_file)
+
+ log.debug("Starting Weston.")
+ argv = ["weston", f"--config={weston_config_file}", "--log=/tmp/weston.log",
+ f"--socket={WAYLAND_SOCKET_NAME}"]
+
+ childproc = startProgram(argv, stdout=output_redirect, stderr=output_redirect)
+ WatchProcesses.watch_process(childproc, argv[0])
+
+ for _ in range(0, timeout):
+ try:
+ xdg_runtime_dir = os.getenv("XDG_RUNTIME_DIR")
+ path = os.path.join(xdg_runtime_dir, WAYLAND_SOCKET_NAME)
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ sock.connect(path)
+ sock.close()
+ wl_status.started = True
+ return wl_status.started
+ except Exception:
+ if wl_status.needs_waiting():
+ sleep(1)
+ wl_status.timed_out = True
+ WatchProcesses.unwatch_process(childproc)
+ childproc.terminate()
+ log.debug("Exception handler test suspended to prevent accidental activation by "
+ "delayed Weston start.")
+ raise TimeoutError("Timeout trying to start %s" % argv[0])
+
+
class X11Status:
"""Status of Xorg launch.
diff --git a/pyanaconda/display.py b/pyanaconda/display.py
index 8e5527161e21..457056973a45 100644
--- a/pyanaconda/display.py
+++ b/pyanaconda/display.py
@@ -1,8 +1,7 @@
#
# display.py: graphical display setup for the Anaconda GUI
#
-# Copyright (C) 2016
-# Red Hat, Inc. All rights reserved.
+# Copyright (C) 2024 Neal Gompa. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -17,233 +16,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-# Author(s): Martin Kolman
+# Author(s): Neal Gompa
#
-import os
-import subprocess
-import time
-import textwrap
-import pkgutil
-import signal
-from pyanaconda.core.configuration.anaconda import conf
-from pyanaconda.core.process_watchers import WatchProcesses
-from pyanaconda import startup_utils
-from pyanaconda.core import util, constants, hw
-from pyanaconda import vnc
-from pyanaconda.core.i18n import _
-from pyanaconda.flags import flags
-from pyanaconda.modules.common.constants.services import NETWORK
-from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke
-from pyanaconda.ui.tui import tui_quit_callback
-# needed for checking if the pyanaconda.ui.gui modules are available
-import pyanaconda.ui
-
-import blivet
-
-from pykickstart.constants import DISPLAY_MODE_TEXT
-
-from simpleline import App
-from simpleline.render.screen_handler import ScreenHandler
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= 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."
-
-
-def start_user_systemd():
- """Start the user instance of systemd.
-
- The service org.a11y.Bus runs the dbus-broker-launch in
- the user scope that requires the user instance of systemd.
- """
- if not conf.system.can_start_user_systemd:
- log.debug("Don't start the user instance of systemd.")
- return
-
- childproc = util.startProgram(["/usr/lib/systemd/systemd", "--user"])
- WatchProcesses.watch_process(childproc, "systemd")
-
-
-# Spice
-
-def start_spice_vd_agent():
- """Start the spice vdagent.
-
- For certain features to work spice requires that the guest os
- is running the spice vdagent.
- """
- try:
- status = util.execWithRedirect("spice-vdagent", [])
- except OSError as e:
- log.warning("spice-vdagent failed: %s", e)
- return
-
- if status:
- log.info("spice-vdagent exited with status %d", status)
- else:
- log.info("Started spice-vdagent.")
-
-
-# VNC
-
-def ask_vnc_question(anaconda, vnc_server, message):
- """ Ask the user if TUI or GUI-over-VNC should be started.
-
- :param anaconda: instance of the Anaconda class
- :param vnc_server: instance of the VNC server object
- :param str message: a message to show to the user together
- with the question
- """
- App.initialize()
- loop = App.get_event_loop()
- loop.set_quit_callback(tui_quit_callback)
- spoke = AskVNCSpoke(anaconda.ksdata, message=message)
- ScreenHandler.schedule_screen(spoke)
- App.run()
-
- if anaconda.ksdata.vnc.enabled:
- if not anaconda.gui_mode:
- log.info("VNC requested via VNC question, switching Anaconda to GUI mode.")
- anaconda.display_mode = constants.DisplayModes.GUI
- flags.usevnc = True
- vnc_server.password = anaconda.ksdata.vnc.password
-
-
-def check_vnc_can_be_started(anaconda):
- """Check if we can start VNC in the current environment.
-
- :returns: if VNC can be started and list of possible reasons
- why VNC can't be started
- :rtype: (boot, list)
- """
-
- error_messages = []
- vnc_startup_possible = True
-
- # disable VNC over text question when not enough memory is available
- min_gui_ram = hw.minimal_memory_needed(with_gui=True)
- if blivet.util.total_memory() < min_gui_ram:
- error_messages.append("Not asking for VNC because current memory (%d) < MIN_GUI_RAM (%d)" %
- (blivet.util.total_memory(), min_gui_ram))
- vnc_startup_possible = False
-
- # disable VNC question if text mode is requested and this is a ks install
- if anaconda.tui_mode and flags.automatedInstall:
- error_messages.append("Not asking for VNC because of an automated install")
- vnc_startup_possible = False
-
- # disable VNC question if we were explicitly asked for text in kickstart
- if anaconda.ksdata.displaymode.displayMode == DISPLAY_MODE_TEXT:
- error_messages.append("Not asking for VNC because text mode was explicitly asked for in kickstart")
- vnc_startup_possible = False
-
- # disable VNC question if we don't have network
- network_proxy = NETWORK.get_proxy()
- if not network_proxy.IsConnecting() and not network_proxy.Connected:
- error_messages.append("Not asking for VNC because we don't have a network")
- vnc_startup_possible = False
-
- # disable VNC question if we don't have Xvnc
- if not os.access('/usr/bin/Xvnc', os.X_OK):
- error_messages.append("Not asking for VNC because we don't have Xvnc")
- vnc_startup_possible = False
-
- return vnc_startup_possible, error_messages
-
-
-# X11
-
-def start_x11(xtimeout):
- """Start the X server for the Anaconda GUI."""
-
- # Start Xorg and wait for it become ready
- util.startX(["Xorg", "-br", "-logfile", "/tmp/X.log",
- ":%s" % constants.X_DISPLAY_NUMBER, "vt6", "-s", "1440", "-ac",
- "-nolisten", "tcp", "-dpi", "96",
- "-noreset"],
- output_redirect=subprocess.DEVNULL, timeout=xtimeout)
-
-
-# function to handle X startup special issues for anaconda
-
-def do_startup_x11_actions():
- """Start the window manager.
-
- When window manager actually connects to the X server is unknowable, but
- fortunately it doesn't matter. Wm does not need to be the first
- connection to Xorg, and if anaconda starts up before wm, wm
- will just take over and maximize the window and make everything right,
- fingers crossed.
- Add XDG_DATA_DIRS to the environment to pull in our overridden schema
- files.
- """
- datadir = os.environ.get('ANACONDA_DATADIR', '/usr/share/anaconda')
- if 'XDG_DATA_DIRS' in os.environ:
- xdg_data_dirs = datadir + '/window-manager:' + os.environ['XDG_DATA_DIRS']
- else:
- xdg_data_dirs = datadir + '/window-manager:/usr/share'
-
- def x11_preexec():
- # to set GUI subprocess SIGINT handler
- signal.signal(signal.SIGINT, signal.SIG_IGN)
-
- childproc = util.startProgram(["gnome-kiosk", "--display", ":1", "--sm-disable", "--x11"],
- env_add={'XDG_DATA_DIRS': xdg_data_dirs},
- preexec_fn=x11_preexec)
- WatchProcesses.watch_process(childproc, "gnome-kiosk")
-
-
-def set_x_resolution(runres):
- """Set X server screen resolution.
-
- :param str runres: a resolution specification string
- """
- try:
- log.info("Setting the screen resolution to: %s.", runres)
- util.execWithRedirect("xrandr", ["-d", ":1", "-s", runres])
- except RuntimeError:
- log.error("The X resolution was not set")
- util.execWithRedirect("xrandr", ["-d", ":1", "-q"])
-
-
-def do_extra_x11_actions(runres, gui_mode):
- """Perform X11 actions not related to startup.
-
- :param str runres: a resolution specification string
- :param gui_mode: an Anaconda display mode
- """
- if runres and gui_mode and not flags.usevnc:
- set_x_resolution(runres)
-
- # Load the system-wide Xresources
- util.execWithRedirect("xrdb", ["-nocpp", "-merge", "/etc/X11/Xresources"])
-
- start_user_systemd()
- start_spice_vd_agent()
-
-
-def write_xdriver(driver, root=None):
- """Write the X driver."""
- if root is None:
- root = conf.target.system_root
-
- if not os.path.isdir("%s/etc/X11" % (root,)):
- os.makedirs("%s/etc/X11" % (root,), mode=0o755)
-
- f = open("%s/etc/X11/xorg.conf" % (root,), 'w')
- f.write('Section "Device"\n\tIdentifier "Videocard0"\n\tDriver "%s"\nEndSection\n' % driver)
- f.close()
-
# general display startup
def setup_display(anaconda, options):
@@ -253,138 +33,9 @@ def setup_display(anaconda, options):
:param options: command line/boot options
"""
- try:
- xtimeout = int(options.xtimeout)
- except ValueError:
- log.warning("invalid inst.xtimeout option value: %s", options.xtimeout)
- xtimeout = constants.X_TIMEOUT
-
- vnc_server = vnc.VncServer() # The vnc Server object.
- vnc_server.anaconda = anaconda
- vnc_server.timeout = xtimeout
-
- anaconda.display_mode = options.display_mode
- anaconda.interactive_mode = not options.noninteractive
-
- if options.vnc:
- flags.usevnc = True
- if not anaconda.gui_mode:
- log.info("VNC requested via boot/CLI option, switching Anaconda to GUI mode.")
- anaconda.display_mode = constants.DisplayModes.GUI
- vnc_server.password = options.vncpassword
-
- # Only consider vncconnect when vnc is a param
- if options.vncconnect:
- cargs = options.vncconnect.split(":")
- vnc_server.vncconnecthost = cargs[0]
- if len(cargs) > 1 and len(cargs[1]) > 0:
- if len(cargs[1]) > 0:
- vnc_server.vncconnectport = cargs[1]
-
- if options.xdriver:
- write_xdriver(options.xdriver, root="/")
-
- if flags.rescue_mode:
- return
-
- if anaconda.ksdata.vnc.enabled:
- flags.usevnc = True
- if not anaconda.gui_mode:
- log.info("VNC requested via kickstart, switching Anaconda to GUI mode.")
- anaconda.display_mode = constants.DisplayModes.GUI
-
- if vnc_server.password == "":
- vnc_server.password = anaconda.ksdata.vnc.password
-
- if vnc_server.vncconnecthost == "":
- vnc_server.vncconnecthost = anaconda.ksdata.vnc.host
-
- if vnc_server.vncconnectport == "":
- vnc_server.vncconnectport = anaconda.ksdata.vnc.port
-
- # check if GUI without WebUI
- if anaconda.gui_mode and not anaconda.is_webui_supported:
- mods = (tup[1] for tup in pkgutil.iter_modules(pyanaconda.ui.__path__, "pyanaconda.ui."))
- if "pyanaconda.ui.gui" not in mods:
- stdout_log.warning("Graphical user interface not available, falling back to text mode")
- anaconda.display_mode = constants.DisplayModes.TUI
- flags.usevnc = False
- flags.vncquestion = False
-
- # check if VNC can be started
- vnc_can_be_started, vnc_error_messages = check_vnc_can_be_started(anaconda)
- if not vnc_can_be_started:
- # VNC can't be started - disable the VNC question and log
- # all the errors that prevented VNC from being started
- flags.vncquestion = False
- for error_message in vnc_error_messages:
- stdout_log.warning(error_message)
-
- # Should we try to start Xorg?
- want_x = anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc)
-
- # Is Xorg is actually available?
- if want_x and not os.access("/usr/bin/Xorg", os.X_OK):
- stdout_log.warning(_("Graphical installation is not available. "
- "Starting text mode."))
- time.sleep(2)
- anaconda.display_mode = constants.DisplayModes.TUI
- want_x = False
-
- if anaconda.tui_mode and flags.vncquestion:
- # we prefer vnc over text mode, so ask about that
- message = _("Text mode provides a limited set of installation "
- "options. It does not offer custom partitioning for "
- "full control over the disk layout. Would you like "
- "to use VNC mode instead?")
- ask_vnc_question(anaconda, vnc_server, message)
- if not anaconda.ksdata.vnc.enabled:
- # user has explicitly specified text mode
- flags.vncquestion = False
-
- anaconda.log_display_mode()
- startup_utils.check_memory(anaconda, options)
-
- # check_memory may have changed the display mode
- want_x = want_x and (anaconda.gui_mode)
- if want_x:
- try:
- start_x11(xtimeout)
- do_startup_x11_actions()
- except TimeoutError as e:
- log.warning("X startup failed: %s", e)
- 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)
-
- if not anaconda.gui_startup_failed:
- do_extra_x11_actions(options.runres, gui_mode=anaconda.gui_mode)
-
- if anaconda.tui_mode and anaconda.gui_startup_failed and flags.vncquestion and not anaconda.ksdata.vnc.enabled:
- message = _("X was unable to start on your machine. Would you like to start VNC to connect to "
- "this computer from another computer and perform a graphical installation or continue "
- "with a text mode installation?")
- ask_vnc_question(anaconda, vnc_server, message)
-
- # if they want us to use VNC do that now
- if anaconda.gui_mode and flags.usevnc:
- vnc_server.startServer()
- do_startup_x11_actions()
-
- # with X running we can initialize the UI interface
- anaconda.initInterface()
+ if options.x11:
+ from pyanaconda import display_x11
+ display_x11.setup_display(anaconda, opts)
+ else:
+ from pyanaconda import display_wayland
+ display_wayland.setup_display(anaconda, opts)
diff --git a/pyanaconda/display_wayland.py b/pyanaconda/display_wayland.py
new file mode 100644
index 000000000000..ff4de9756ec9
--- /dev/null
+++ b/pyanaconda/display_wayland.py
@@ -0,0 +1,333 @@
+#
+# display_wayland.py: Wayland graphical display setup for the Anaconda GUI
+#
+# Copyright (C) 2024 Neal Gompa.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Neal Gompa
+#
+import glob
+import os
+import subprocess
+import time
+import textwrap
+import pkgutil
+import signal
+
+from pyanaconda.core.configuration.anaconda import conf
+from pyanaconda.core.process_watchers import WatchProcesses
+from pyanaconda import startup_utils
+from pyanaconda.core import util, constants, hw
+from pyanaconda.core.i18n import _
+from pyanaconda.flags import flags
+from pyanaconda.modules.common.constants.services import NETWORK
+from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke
+from pyanaconda.ui.tui import tui_quit_callback
+# needed for checking if the pyanaconda.ui.gui modules are available
+import pyanaconda.ui
+
+import blivet
+
+from pykickstart.constants import DISPLAY_MODE_TEXT
+
+from simpleline import App
+from simpleline.render.screen_handler import ScreenHandler
+
+from pyanaconda.anaconda_loggers import get_module_logger, get_stdout_logger
+log = get_module_logger(__name__)
+stdout_log = get_stdout_logger()
+
+WAYLAND_TIMEOUT_ADVICE = \
+ "Do not load the stage2 image over a slow network link.\n" \
+ "Wait longer for the compositor startup with the inst.xtimeout= 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."
+
+
+def start_user_systemd():
+ """Start the user instance of systemd.
+
+ The service org.a11y.Bus runs the dbus-broker-launch in
+ the user scope that requires the user instance of systemd.
+ """
+ if not conf.system.can_start_user_systemd:
+ log.debug("Don't start the user instance of systemd.")
+ return
+
+ childproc = util.startProgram(["/usr/lib/systemd/systemd", "--user"])
+ WatchProcesses.watch_process(childproc, "systemd")
+
+
+# Spice
+
+def start_spice_vd_agent():
+ """Start the spice vdagent.
+
+ For certain features to work spice requires that the guest os
+ is running the spice vdagent.
+ """
+ try:
+ status = util.execWithRedirect("spice-vdagent", [])
+ except OSError as e:
+ log.warning("spice-vdagent failed: %s", e)
+ return
+
+ if status:
+ log.info("spice-vdagent exited with status %d", status)
+ else:
+ log.info("Started spice-vdagent.")
+
+
+# VNC
+
+def ask_vnc_question(anaconda, message):
+ """ Ask the user if TUI or GUI-over-VNC should be started.
+
+ :param anaconda: instance of the Anaconda class
+ :param vnc_server: instance of the VNC server object
+ :param str message: a message to show to the user together
+ with the question
+ """
+ App.initialize()
+ loop = App.get_event_loop()
+ loop.set_quit_callback(tui_quit_callback)
+ spoke = AskVNCSpoke(anaconda.ksdata, message=message)
+ ScreenHandler.schedule_screen(spoke)
+ App.run()
+
+ if anaconda.ksdata.vnc.enabled:
+ if not anaconda.gui_mode:
+ log.info("VNC requested via VNC question, switching Anaconda to GUI mode.")
+ anaconda.display_mode = constants.DisplayModes.GUI
+ flags.usevnc = True
+
+
+def check_vnc_can_be_started(anaconda):
+ """Check if we can start VNC in the current environment.
+
+ :returns: if VNC can be started and list of possible reasons
+ why VNC can't be started
+ :rtype: (boot, list)
+ """
+
+ error_messages = []
+ vnc_startup_possible = True
+
+ # disable VNC over text question when not enough memory is available
+ min_gui_ram = hw.minimal_memory_needed(with_gui=True)
+ if blivet.util.total_memory() < min_gui_ram:
+ error_messages.append("Not asking for VNC because current memory (%d) < MIN_GUI_RAM (%d)" %
+ (blivet.util.total_memory(), min_gui_ram))
+ vnc_startup_possible = False
+
+ # disable VNC question if text mode is requested and this is a ks install
+ if anaconda.tui_mode and flags.automatedInstall:
+ error_messages.append("Not asking for VNC because of an automated install")
+ vnc_startup_possible = False
+
+ # disable VNC question if we were explicitly asked for text in kickstart
+ if anaconda.ksdata.displaymode.displayMode == DISPLAY_MODE_TEXT:
+ error_messages.append("Not asking for VNC because text mode was explicitly asked for in kickstart")
+ vnc_startup_possible = False
+
+ # disable VNC question if we don't have network
+ network_proxy = NETWORK.get_proxy()
+ if not network_proxy.IsConnecting() and not network_proxy.Connected:
+ error_messages.append("Not asking for VNC because we don't have a network")
+ vnc_startup_possible = False
+
+ # disable VNC question if we don't have Weston's VNC backend
+ if not glob.glob("/usr/lib*/libweston*/vnc-backend.so"):
+ error_messages.append("Not asking for VNC because we don't have weston-vnc")
+ vnc_startup_possible = False
+
+ return vnc_startup_possible, error_messages
+
+# Wayland
+
+def start_weston(wconfig, wltimeout):
+ """Start Weston for the Anaconda GUI"""
+
+ # Declare Weston configuration
+ # Start Weston and wait for it to become ready
+ util.vtActivate(6)
+ util.startWl(weston_config=wconfig,
+ output_redirect=subprocess.DEVNULL, timeout=wltimeout)
+
+# X11
+
+def start_x11(xtimeout):
+ """Start the X server for the Anaconda GUI."""
+
+ # Start Xorg and wait for it become ready
+ util.startX(["Xwayland", "-rootless", "-logfile", "/tmp/X.log",
+ ":%s" % constants.X_DISPLAY_NUMBER, "-s", "1440", "-ac",
+ "-nolisten", "tcp", "-dpi", "96",
+ "-noreset"],
+ output_redirect=subprocess.DEVNULL, timeout=xtimeout)
+
+
+def do_extra_x11_actions():
+ """Perform X11 actions not related to startup."""
+
+ # Load the system-wide Xresources
+ util.execWithRedirect("xrdb", ["-nocpp", "-merge", "/etc/X11/Xresources"])
+
+ start_user_systemd()
+ start_spice_vd_agent()
+
+
+# general display startup
+def setup_display(anaconda, options):
+ """Setup the display for the installation environment.
+
+ :param anaconda: instance of the Anaconda class
+ :param options: command line/boot options
+ """
+
+ try:
+ wltimeout = int(options.xtimeout)
+ except ValueError:
+ log.warning("invalid inst.xtimeout option value: %s", options.xtimeout)
+ wltimeout = constants.WAYLAND_TIMEOUT
+
+ weston_core_config = {
+ "core": {
+ "shell": "kiosk",
+ }
+ }
+
+ weston_vnc_config = {
+ "vnc": {
+ "port": "5900",
+ },
+ "output": {
+ "name": "vnc",
+ "resizeable": "true",
+ "mode": "800x600",
+ },
+ }
+
+ anaconda.display_mode = options.display_mode
+ anaconda.interactive_mode = not options.noninteractive
+
+ if options.vnc:
+ flags.usevnc = True
+ if not anaconda.gui_mode:
+ log.info("VNC requested via boot/CLI option, switching Anaconda to GUI mode.")
+ anaconda.display_mode = constants.DisplayModes.GUI
+
+ if flags.rescue_mode:
+ return
+
+ if anaconda.ksdata.vnc.enabled:
+ flags.usevnc = True
+ if not anaconda.gui_mode:
+ log.info("VNC requested via kickstart, switching Anaconda to GUI mode.")
+ anaconda.display_mode = constants.DisplayModes.GUI
+
+ # check if GUI without WebUI
+ if anaconda.gui_mode and not anaconda.is_webui_supported:
+ mods = (tup[1] for tup in pkgutil.iter_modules(pyanaconda.ui.__path__, "pyanaconda.ui."))
+ if "pyanaconda.ui.gui" not in mods:
+ stdout_log.warning("Graphical user interface not available, falling back to text mode")
+ anaconda.display_mode = constants.DisplayModes.TUI
+ flags.usevnc = False
+ flags.vncquestion = False
+
+ # check if VNC can be started
+ vnc_can_be_started, vnc_error_messages = check_vnc_can_be_started(anaconda)
+ if not vnc_can_be_started:
+ # VNC can't be started - disable the VNC question and log
+ # all the errors that prevented VNC from being started
+ flags.vncquestion = False
+ for error_message in vnc_error_messages:
+ stdout_log.warning(error_message)
+
+ # Are Weston and Xwayland actually available?
+ have_gui = os.access("/usr/bin/weston", os.X_OK) and os.access("/usr/bin/Xwayland", os.X_OK)
+ # Should we try to start the graphical environment?
+ want_gui = anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc)
+
+ if want_gui and not have_gui:
+ stdout_log.warning(_("Graphical installation is not available. "
+ "Starting text mode."))
+ time.sleep(2)
+ anaconda.display_mode = constants.DisplayModes.TUI
+ want_gui = False
+
+ if anaconda.tui_mode and have_gui and flags.vncquestion:
+ # we prefer vnc over text mode, so ask about that
+ message = _("Text mode provides a limited set of installation "
+ "options. It does not offer custom partitioning for "
+ "full control over the disk layout. Would you like "
+ "to use VNC mode instead?")
+ ask_vnc_question(anaconda, vnc_server, message)
+ if not anaconda.ksdata.vnc.enabled:
+ # user has explicitly specified text mode
+ flags.vncquestion = False
+
+ anaconda.log_display_mode()
+ startup_utils.check_memory(anaconda, options)
+
+ # check_memory may have changed the display mode
+ want_gui = want_gui and (anaconda.gui_mode)
+ if want_gui:
+ try:
+ if flags.usevnc:
+ start_weston(weston_core_config | weston_vnc_config, wltimeout)
+ else:
+ start_weston(weston_core_config, wltimeout)
+ start_x11(wltimeout)
+ except TimeoutError as e:
+ log.warning("Graphics startup failed: %s", e)
+ print("\nGraphics 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 WAYLAND_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("Xwayland startup failed: %s", e)
+ print("\nXwayland startup failed, falling back to text mode.")
+ util.vtActivate(1)
+ anaconda.display_mode = constants.DisplayModes.TUI
+ anaconda.gui_startup_failed = True
+ time.sleep(2)
+
+ if not anaconda.gui_startup_failed:
+ do_extra_x11_actions()
+
+ if anaconda.tui_mode and anaconda.gui_startup_failed and flags.vncquestion and not anaconda.ksdata.vnc.enabled:
+ message = _("Graphics was unable to start on your machine. Would you like to start VNC to connect to "
+ "this computer from another computer and perform a graphical installation or continue "
+ "with a text mode installation?")
+ ask_vnc_question(anaconda, message)
+
+ # if they want us to use VNC do that now
+ if anaconda.gui_mode and flags.usevnc:
+ start_weston(weston_core_config | weston_vnc_config, wltimeout)
+ start_x11(wltimeout)
+
+ # with X running we can initialize the UI interface
+ anaconda.initInterface()
diff --git a/pyanaconda/display_x11.py b/pyanaconda/display_x11.py
new file mode 100644
index 000000000000..d46b154915e4
--- /dev/null
+++ b/pyanaconda/display_x11.py
@@ -0,0 +1,390 @@
+#
+# display_x11.py: X11 graphical display setup for the Anaconda GUI
+#
+# Copyright (C) 2016
+# Red Hat, Inc. All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# Author(s): Martin Kolman
+#
+import os
+import subprocess
+import time
+import textwrap
+import pkgutil
+import signal
+
+from pyanaconda.core.configuration.anaconda import conf
+from pyanaconda.core.process_watchers import WatchProcesses
+from pyanaconda import startup_utils
+from pyanaconda.core import util, constants, hw
+from pyanaconda import vnc
+from pyanaconda.core.i18n import _
+from pyanaconda.flags import flags
+from pyanaconda.modules.common.constants.services import NETWORK
+from pyanaconda.ui.tui.spokes.askvnc import AskVNCSpoke
+from pyanaconda.ui.tui import tui_quit_callback
+# needed for checking if the pyanaconda.ui.gui modules are available
+import pyanaconda.ui
+
+import blivet
+
+from pykickstart.constants import DISPLAY_MODE_TEXT
+
+from simpleline import App
+from simpleline.render.screen_handler import ScreenHandler
+
+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= 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."
+
+
+def start_user_systemd():
+ """Start the user instance of systemd.
+
+ The service org.a11y.Bus runs the dbus-broker-launch in
+ the user scope that requires the user instance of systemd.
+ """
+ if not conf.system.can_start_user_systemd:
+ log.debug("Don't start the user instance of systemd.")
+ return
+
+ childproc = util.startProgram(["/usr/lib/systemd/systemd", "--user"])
+ WatchProcesses.watch_process(childproc, "systemd")
+
+
+# Spice
+
+def start_spice_vd_agent():
+ """Start the spice vdagent.
+
+ For certain features to work spice requires that the guest os
+ is running the spice vdagent.
+ """
+ try:
+ status = util.execWithRedirect("spice-vdagent", [])
+ except OSError as e:
+ log.warning("spice-vdagent failed: %s", e)
+ return
+
+ if status:
+ log.info("spice-vdagent exited with status %d", status)
+ else:
+ log.info("Started spice-vdagent.")
+
+
+# VNC
+
+def ask_vnc_question(anaconda, vnc_server, message):
+ """ Ask the user if TUI or GUI-over-VNC should be started.
+
+ :param anaconda: instance of the Anaconda class
+ :param vnc_server: instance of the VNC server object
+ :param str message: a message to show to the user together
+ with the question
+ """
+ App.initialize()
+ loop = App.get_event_loop()
+ loop.set_quit_callback(tui_quit_callback)
+ spoke = AskVNCSpoke(anaconda.ksdata, message=message)
+ ScreenHandler.schedule_screen(spoke)
+ App.run()
+
+ if anaconda.ksdata.vnc.enabled:
+ if not anaconda.gui_mode:
+ log.info("VNC requested via VNC question, switching Anaconda to GUI mode.")
+ anaconda.display_mode = constants.DisplayModes.GUI
+ flags.usevnc = True
+ vnc_server.password = anaconda.ksdata.vnc.password
+
+
+def check_vnc_can_be_started(anaconda):
+ """Check if we can start VNC in the current environment.
+
+ :returns: if VNC can be started and list of possible reasons
+ why VNC can't be started
+ :rtype: (boot, list)
+ """
+
+ error_messages = []
+ vnc_startup_possible = True
+
+ # disable VNC over text question when not enough memory is available
+ min_gui_ram = hw.minimal_memory_needed(with_gui=True)
+ if blivet.util.total_memory() < min_gui_ram:
+ error_messages.append("Not asking for VNC because current memory (%d) < MIN_GUI_RAM (%d)" %
+ (blivet.util.total_memory(), min_gui_ram))
+ vnc_startup_possible = False
+
+ # disable VNC question if text mode is requested and this is a ks install
+ if anaconda.tui_mode and flags.automatedInstall:
+ error_messages.append("Not asking for VNC because of an automated install")
+ vnc_startup_possible = False
+
+ # disable VNC question if we were explicitly asked for text in kickstart
+ if anaconda.ksdata.displaymode.displayMode == DISPLAY_MODE_TEXT:
+ error_messages.append("Not asking for VNC because text mode was explicitly asked for in kickstart")
+ vnc_startup_possible = False
+
+ # disable VNC question if we don't have network
+ network_proxy = NETWORK.get_proxy()
+ if not network_proxy.IsConnecting() and not network_proxy.Connected:
+ error_messages.append("Not asking for VNC because we don't have a network")
+ vnc_startup_possible = False
+
+ # disable VNC question if we don't have Xvnc
+ if not os.access('/usr/bin/Xvnc', os.X_OK):
+ error_messages.append("Not asking for VNC because we don't have Xvnc")
+ vnc_startup_possible = False
+
+ return vnc_startup_possible, error_messages
+
+
+# X11
+
+def start_x11(xtimeout):
+ """Start the X server for the Anaconda GUI."""
+
+ # Start Xorg and wait for it become ready
+ util.startX(["Xorg", "-br", "-logfile", "/tmp/X.log",
+ ":%s" % constants.X_DISPLAY_NUMBER, "vt6", "-s", "1440", "-ac",
+ "-nolisten", "tcp", "-dpi", "96",
+ "-noreset"],
+ output_redirect=subprocess.DEVNULL, timeout=xtimeout)
+
+
+# function to handle X startup special issues for anaconda
+
+def do_startup_x11_actions():
+ """Start the window manager.
+
+ When window manager actually connects to the X server is unknowable, but
+ fortunately it doesn't matter. Wm does not need to be the first
+ connection to Xorg, and if anaconda starts up before wm, wm
+ will just take over and maximize the window and make everything right,
+ fingers crossed.
+ Add XDG_DATA_DIRS to the environment to pull in our overridden schema
+ files.
+ """
+ datadir = os.environ.get('ANACONDA_DATADIR', '/usr/share/anaconda')
+ if 'XDG_DATA_DIRS' in os.environ:
+ xdg_data_dirs = datadir + '/window-manager:' + os.environ['XDG_DATA_DIRS']
+ else:
+ xdg_data_dirs = datadir + '/window-manager:/usr/share'
+
+ def x11_preexec():
+ # to set GUI subprocess SIGINT handler
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+ childproc = util.startProgram(["gnome-kiosk", "--display", ":1", "--sm-disable", "--x11"],
+ env_add={'XDG_DATA_DIRS': xdg_data_dirs},
+ preexec_fn=x11_preexec)
+ WatchProcesses.watch_process(childproc, "gnome-kiosk")
+
+
+def set_x_resolution(runres):
+ """Set X server screen resolution.
+
+ :param str runres: a resolution specification string
+ """
+ try:
+ log.info("Setting the screen resolution to: %s.", runres)
+ util.execWithRedirect("xrandr", ["-d", ":1", "-s", runres])
+ except RuntimeError:
+ log.error("The X resolution was not set")
+ util.execWithRedirect("xrandr", ["-d", ":1", "-q"])
+
+
+def do_extra_x11_actions(runres, gui_mode):
+ """Perform X11 actions not related to startup.
+
+ :param str runres: a resolution specification string
+ :param gui_mode: an Anaconda display mode
+ """
+ if runres and gui_mode and not flags.usevnc:
+ set_x_resolution(runres)
+
+ # Load the system-wide Xresources
+ util.execWithRedirect("xrdb", ["-nocpp", "-merge", "/etc/X11/Xresources"])
+
+ start_user_systemd()
+ start_spice_vd_agent()
+
+
+def write_xdriver(driver, root=None):
+ """Write the X driver."""
+ if root is None:
+ root = conf.target.system_root
+
+ if not os.path.isdir("%s/etc/X11" % (root,)):
+ os.makedirs("%s/etc/X11" % (root,), mode=0o755)
+
+ f = open("%s/etc/X11/xorg.conf" % (root,), 'w')
+ f.write('Section "Device"\n\tIdentifier "Videocard0"\n\tDriver "%s"\nEndSection\n' % driver)
+ f.close()
+
+
+# general display startup
+def setup_display(anaconda, options):
+ """Setup the display for the installation environment.
+
+ :param anaconda: instance of the Anaconda class
+ :param options: command line/boot options
+ """
+
+ try:
+ xtimeout = int(options.xtimeout)
+ except ValueError:
+ log.warning("invalid inst.xtimeout option value: %s", options.xtimeout)
+ xtimeout = constants.X_TIMEOUT
+
+ vnc_server = vnc.VncServer() # The vnc Server object.
+ vnc_server.anaconda = anaconda
+ vnc_server.timeout = xtimeout
+
+ anaconda.display_mode = options.display_mode
+ anaconda.interactive_mode = not options.noninteractive
+
+ if options.vnc:
+ flags.usevnc = True
+ if not anaconda.gui_mode:
+ log.info("VNC requested via boot/CLI option, switching Anaconda to GUI mode.")
+ anaconda.display_mode = constants.DisplayModes.GUI
+ vnc_server.password = options.vncpassword
+
+ # Only consider vncconnect when vnc is a param
+ if options.vncconnect:
+ cargs = options.vncconnect.split(":")
+ vnc_server.vncconnecthost = cargs[0]
+ if len(cargs) > 1 and len(cargs[1]) > 0:
+ if len(cargs[1]) > 0:
+ vnc_server.vncconnectport = cargs[1]
+
+ if options.xdriver:
+ write_xdriver(options.xdriver, root="/")
+
+ if flags.rescue_mode:
+ return
+
+ if anaconda.ksdata.vnc.enabled:
+ flags.usevnc = True
+ if not anaconda.gui_mode:
+ log.info("VNC requested via kickstart, switching Anaconda to GUI mode.")
+ anaconda.display_mode = constants.DisplayModes.GUI
+
+ if vnc_server.password == "":
+ vnc_server.password = anaconda.ksdata.vnc.password
+
+ if vnc_server.vncconnecthost == "":
+ vnc_server.vncconnecthost = anaconda.ksdata.vnc.host
+
+ if vnc_server.vncconnectport == "":
+ vnc_server.vncconnectport = anaconda.ksdata.vnc.port
+
+ # check if GUI without WebUI
+ if anaconda.gui_mode and not anaconda.is_webui_supported:
+ mods = (tup[1] for tup in pkgutil.iter_modules(pyanaconda.ui.__path__, "pyanaconda.ui."))
+ if "pyanaconda.ui.gui" not in mods:
+ stdout_log.warning("Graphical user interface not available, falling back to text mode")
+ anaconda.display_mode = constants.DisplayModes.TUI
+ flags.usevnc = False
+ flags.vncquestion = False
+
+ # check if VNC can be started
+ vnc_can_be_started, vnc_error_messages = check_vnc_can_be_started(anaconda)
+ if not vnc_can_be_started:
+ # VNC can't be started - disable the VNC question and log
+ # all the errors that prevented VNC from being started
+ flags.vncquestion = False
+ for error_message in vnc_error_messages:
+ stdout_log.warning(error_message)
+
+ # Should we try to start Xorg?
+ want_x = anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc)
+
+ # Is Xorg is actually available?
+ if want_x and not os.access("/usr/bin/Xorg", os.X_OK):
+ stdout_log.warning(_("Graphical installation is not available. "
+ "Starting text mode."))
+ time.sleep(2)
+ anaconda.display_mode = constants.DisplayModes.TUI
+ want_x = False
+
+ if anaconda.tui_mode and flags.vncquestion:
+ # we prefer vnc over text mode, so ask about that
+ message = _("Text mode provides a limited set of installation "
+ "options. It does not offer custom partitioning for "
+ "full control over the disk layout. Would you like "
+ "to use VNC mode instead?")
+ ask_vnc_question(anaconda, vnc_server, message)
+ if not anaconda.ksdata.vnc.enabled:
+ # user has explicitly specified text mode
+ flags.vncquestion = False
+
+ anaconda.log_display_mode()
+ startup_utils.check_memory(anaconda, options)
+
+ # check_memory may have changed the display mode
+ want_x = want_x and (anaconda.gui_mode)
+ if want_x:
+ try:
+ start_x11(xtimeout)
+ do_startup_x11_actions()
+ except TimeoutError as e:
+ log.warning("X startup failed: %s", e)
+ 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)
+
+ if not anaconda.gui_startup_failed:
+ do_extra_x11_actions(options.runres, gui_mode=anaconda.gui_mode)
+
+ if anaconda.tui_mode and anaconda.gui_startup_failed and flags.vncquestion and not anaconda.ksdata.vnc.enabled:
+ message = _("X was unable to start on your machine. Would you like to start VNC to connect to "
+ "this computer from another computer and perform a graphical installation or continue "
+ "with a text mode installation?")
+ ask_vnc_question(anaconda, vnc_server, message)
+
+ # if they want us to use VNC do that now
+ if anaconda.gui_mode and flags.usevnc:
+ vnc_server.startServer()
+ do_startup_x11_actions()
+
+ # with X running we can initialize the UI interface
+ anaconda.initInterface()
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index 520c9b942e9a..b84f6b9730dd 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -672,6 +672,9 @@ def _instantiateAction(self, actionClass):
return obj
def run(self):
+ # Ensure we launch as an "X11" application since we do not support anything else yet
+ util.setenv("GDK_BACKEND", "x11")
+
(success, _args) = Gtk.init_check(None)
if not success:
raise RuntimeError("Failed to initialize Gtk")