diff --git a/anaconda.py b/anaconda.py index 7a61f9f66f2..d3a6b1e93c1 100755 --- a/anaconda.py +++ b/anaconda.py @@ -23,6 +23,23 @@ # ...still messy (2013-07-12) # A lot less messy now. :) (2016-10-13) +print("AAA MINIMAL GTK EXAMPLE") + +import time +import os +import shlex +import subprocess + + +print("AAA set ENV early") +# FIXME: check if we need to define these env vars here or not +os.environ["XDG_RUNTIME_DIR"] = "/run/user/0" +os.environ["GDK_BACKEND"] = "wayland" +os.environ["XDG_SESSION_TYPE"] = "wayland" + +print("AAA: ENVIRON") +print(os.environ) + import os import atexit import sys @@ -172,6 +189,13 @@ def setup_environment(): print("anaconda must be run as root.") sys.exit(1) + # FIXME: try setting XDG_RUNTIME_DIR early + os.environ["XDG_RUNTIME_DIR"] = "/run/user/0" + # FIXME: enable GTK startup debugging + os.environ["G_MESSAGES_DEBUG"] = "all" + os.environ["GDK_DEBUG"] = "all" +# os.environ["GTK_DEBUG"] = "all" + print("Starting installer, one moment...") # Allow a file to be loaded as early as possible @@ -226,6 +250,8 @@ def setup_environment(): if startup_utils.prompt_for_ssh(opts): sys.exit(0) + log.debug("AAA: MAIN PID/PPID %d/%d", os.getpid(), os.getppid()) + log.info("%s %s", sys.argv[0], util.get_anaconda_version_string(build_time_version=True)) # Do not exceed default 8K limit on message length in rsyslog for log_line in util.get_image_packages_info(max_string_chars=8096-100): diff --git a/anaconda.spec.in b/anaconda.spec.in index 5eeface40b9..9627fd1dd25 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -258,6 +258,11 @@ Requires: nm-connection-editor Requires: librsvg2 Requires: gnome-kiosk Requires: brltty +# Wayland support dependecies +# FIXME: conditionalize this ? +Requires: python3-pam +Requires: sssd-client +Requires: xorg-x11-server-Xwayland # dependencies for rpm-ostree payload module Requires: rpm-ostree >= %{rpmostreever} Requires: ostree @@ -343,6 +348,18 @@ runtime on NFS/HTTP/FTP servers or local disks. %configure ANACONDA_RELEASE=%{release} %{!?with_glade:--disable-glade} %{__make} %{?_smp_mflags} +# FIXME: pam profile +cat > anaconda-session < boot option." \ @@ -245,6 +250,91 @@ def write_xdriver(driver, root=None): f.close() +# Wayland + +def start_wayland_compositor(): + """Start Wayland compositor for the Anaconda GUI. + + Add XDG_DATA_DIRS to the environment to pull in our overridden schema + files. + + FIXME: Will XDG_DATA_DIRS work with Wayland compositor ? + """ + # mark Wayland is in use + # FIXME: is this the correct place to set this flag ? + flags.wayland = True + + 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 wayland_preexec(): + # to set GUI subprocess SIGINT handler + # FIXME: does this even work with a Wayland compoitor ? + signal.signal(signal.SIGINT, signal.SIG_IGN) + +# childproc = util.startProgram(["gnome-kiosk", "--sm-disable", "--wayland"], +# env_add={ +# 'XDG_DATA_DIRS': xdg_data_dirs, +# 'XDG_RUNTIME_DIR': "/run/user/0"}, +# preexec_fn=wayland_preexec) + + +#"/run-in-new-session.py --user root --service anaconda-session --session-type wayland --session-class user /bin/gnome-kiosk --wayland --sm-disable" + + log.debug("AAA: DISPLAY PID/PPID %d/%d", os.getpid(), os.getppid()) + + # FIXME: try to force wayland + Gdk.set_allowed_backends("wayland") + + childproc = util.startProgram(["run-in-new-session.py", "--user", "root", + "--service", "anaconda-session", + "--vt", "6", + "--session-type", "wayland", "--session-class", "user", + "gnome-kiosk", "--sm-disable"], + env_add={ + 'XDG_DATA_DIRS': xdg_data_dirs, + 'XDG_RUNTIME_DIR': "/run/user/0"}, + preexec_fn=wayland_preexec) + + #WatchProcesses.watch_process(childproc, "gnome-kiosk") + + WatchProcesses.watch_process(childproc, "run-in-new-session.py") + + # FIXME: could WatchProcesses do this & what is it for actually ? + # Make sure gnome-kiosk is running, before continuing. Otherwise the GUI + # might try to connect to the Wayland compositor before its ready & + # will fail to to start. + log.debug("waiting for Wayland compositor (gnome-kiosk) to start") + while not os.path.exists('/run/user/0/wayland-0'): + time.sleep(0.1) + log.debug("Wayland compositor (gnome-kiosk) startup finished & ready to use") + +def set_wayland_resolution(runres): + """Set Wayland server screen resolution. + + FIXME: implement this using Mutter DBus API + FIXME: keep runres or use horizontal/vertical resolution instead ? + + :param str runres: a resolution specification string + """ + log.error("FIXME: set screen resolution - not yet implemented on Wayland") + + +def do_extra_wayland_actions(runres, gui_mode): + """Perform Wayland related 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_wayland_resolution(runres) + + start_user_systemd() + + # general display startup def setup_display(anaconda, options): """Setup the display for the installation environment. @@ -320,16 +410,35 @@ def setup_display(anaconda, options): 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 + # Xorg or Wayland? + want_x = False + want_wayland = False + + # try to run in local GUI mode + if anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc): + # is Xorg actually available? + xorg_server_available = os.access("/usr/bin/Xorg", os.X_OK) + + # is our Wayland compositor available ? + wayland_compositor_available = os.access("/usr/bin/gnome-kiosk", os.X_OK) + + # do we have at least one of the needed deps we need to run in graphical mode ? + if xorg_server_available: + # check for Wayland override + if "wayland" in kernel_arguments and wayland_compositor_available: + # this way we can test if insaller on Wayland works on a given system + want_wayland = True + else: + # no Wayland override or no Wayland, just use X + want_x = True + elif wayland_compositor_available: + want_wayland = True + else: + # neither X or Wayland is available + anaconda.display_mode = constants.DisplayModes.TUI + stdout_log.warning(_("Graphical installation is not available. " + "Starting text mode.")) + time.sleep(2) if anaconda.tui_mode and flags.vncquestion: # we prefer vnc over text mode, so ask about that @@ -347,7 +456,13 @@ def setup_display(anaconda, options): # check_memory may have changed the display mode want_x = want_x and (anaconda.gui_mode) - if want_x: + want_wayland = want_wayland and (anaconda.gui_mode) + + if want_wayland: + log.debug("Using Wayland compositor to provide locally running graphical interface.") + start_wayland_compositor() + elif want_x: + log.debug("Using Xorg server to provide locally running graphical interface.") try: start_x11(xtimeout) do_startup_x11_actions() diff --git a/pyanaconda/flags.py b/pyanaconda/flags.py index 000fd5df876..47ffbce0077 100644 --- a/pyanaconda/flags.py +++ b/pyanaconda/flags.py @@ -36,6 +36,7 @@ def __init__(self): self.usevnc = False self.vncquestion = True self.preexisting_x11 = False + self.wayland = False self.automatedInstall = False self.eject = True # ksprompt is whether or not to prompt for missing ksdata diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 520c9b942e9..1850dfb4847 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -283,6 +283,7 @@ def __init__(self, fullscreen=False, decorated=False): :param bool fullscreen: if True, fullscreen the window, if false maximize """ super().__init__() + # Keep the latest main window. self.__class__.__instance = self @@ -672,6 +673,7 @@ def _instantiateAction(self, actionClass): return obj def run(self): + log.debug("AAA: GTK PID/PPID %d/%d", os.getpid(), os.getppid()) (success, _args) = Gtk.init_check(None) if not success: raise RuntimeError("Failed to initialize Gtk") diff --git a/pyanaconda/ui/gui/kbd_wrapper.py b/pyanaconda/ui/gui/kbd_wrapper.py new file mode 100644 index 00000000000..977f7a1ebd7 --- /dev/null +++ b/pyanaconda/ui/gui/kbd_wrapper.py @@ -0,0 +1,208 @@ +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties 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, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# + +""" +This module provides an API compatible dummy +replacement for the XklWrapper, for temporary +use when Anaconda runs on a Wayland compositor. + +Eventually the class should be "filled in" and use +a real modern API that works on Wayland compositors +and possible even on X if we still support it +at that point. +""" + +import threading + +from pyanaconda.core.async_utils import async_action_wait + +from pyanaconda.anaconda_loggers import get_module_logger +log = get_module_logger(__name__) + +class KbdWrapper(object): + """ + Class wrapping a keyboard access API. + + Right now this is just a dummy class + that can be used in place of libXklavier + when running on a Wayland compositor. + + But eventually this class should wrap + a modern keyboard handling API that + works on Wayland (and ideally X if we + still support it at that point). + """ + + _instance = None + _instance_lock = threading.Lock() + + @staticmethod + def get_instance(): + with KbdWrapper._instance_lock: + if not KbdWrapper._instance: + KbdWrapper._instance = KbdWrapper() + + return KbdWrapper._instance + + def __init__(self): + self._layout_infos = dict() + self._layout_infos_lock = threading.RLock() + self._switch_opt_infos = dict() + self._switch_opt_infos_lock = threading.RLock() + log.debug("KbdWrapper: initialized") + + def get_current_layout(self): + """ + Get current activated X layout and variant + + :return: current activated X layout and variant (e.g. "cz (qwerty)") + + """ + + log.warning("KbdWrapper: get_current_layout() - returning dummy data") + return "cz (qwerty)" + + def get_available_layouts(self): + """A list of layouts""" + + log.warning("KbdWrapper: get_available_layouts() - returning dummy data") + with self._layout_infos_lock: + return list(self._layout_infos.keys()) + + def get_common_layouts(self): + """A list of common layouts""" + + log.warning("KbdWrapper: get_common_layouts() - returning dummy data") + return [] + + def get_switching_options(self): + """Method returning list of available layout switching options""" + + log.warning("KbdWrapper: get_switching_options() - returning dummy data") + with self._switch_opt_infos_lock: + return list(self._switch_opt_infos.keys()) + + def get_layout_variant_description(self, layout_variant, with_lang=True, xlated=True): + """ + Get description of the given layout-variant. + + :param layout_variant: layout-variant specification (e.g. 'cz (qwerty)') + :type layout_variant: str + :param with_lang: whether to include language of the layout-variant (if defined) + in the description or not + :type with_lang: bool + :param xlated: whethe to return translated or english version of the description + :type xlated: bool + :return: description of the layout-variant specification (e.g. 'Czech (qwerty)') + :rtype: str + + """ + log.warning("KbdWrapper: get_layout_variant_description() - returning dummy data") + return "cz (qwerty)" + + + def get_switch_opt_description(self, switch_opt): + """ + Get description of the given layout switching option. + + :param switch_opt: switching option name/ID (e.g. 'grp:alt_shift_toggle') + :type switch_opt: str + :return: description of the layout switching option (e.g. 'Alt + Shift') + :rtype: str + + """ + + log.warning("KbdWrapper: get_switch_opt_description() - returning dummy data") + return "" + + @async_action_wait + def activate_default_layout(self): + """ + Activates default layout (the first one in the list of configured + layouts). + + """ + + log.warning("KbdWrapper: activate_default_layout() - not implemented") + + def is_valid_layout(self, layout): + """Return if given layout is valid layout or not""" + + log.warning("KbdWrapper: is_valid_layout() - not implemented (always False)") + return False + + @async_action_wait + def add_layout(self, layout): + """ + Method that tries to add a given layout to the current X configuration. + + The X layouts configuration is handled by two lists. A list of layouts + and a list of variants. Index-matching items in these lists (as if they + were zipped) are used for the construction of real layouts (e.g. + 'cz (qwerty)'). + + :param layout: either 'layout' or 'layout (variant)' + :raise XklWrapperError: if the given layout is invalid or cannot be added + + """ + + log.warning("KbdWrapper: add_layout() - not implemented") + + @async_action_wait + def remove_layout(self, layout): + """ + Method that tries to remove a given layout from the current X + configuration. + + See also the documentation for the add_layout method. + + :param layout: either 'layout' or 'layout (variant)' + :raise XklWrapperError: if the given layout cannot be removed + + """ + + log.warning("KbdWrapper: remove_layout() - not implemented") + + @async_action_wait + def replace_layouts(self, layouts_list): + """ + Method that replaces the layouts defined in the current X configuration + with the new ones given. + + :param layouts_list: list of layouts defined as either 'layout' or + 'layout (variant)' + :raise XklWrapperError: if layouts cannot be replaced with the new ones + + """ + + log.warning("KbdWrapper: replace_layouts() - not implemented") + + @async_action_wait + def set_switching_options(self, options): + """ + Method that sets options for layout switching. It replaces the old + options with the new ones. + + :param options: layout switching options to be set + :type options: list or generator + :raise XklWrapperError: if the old options cannot be replaced with the + new ones + + """ + + log.warning("KbdWrapper: set_switching_options() - not implemented") diff --git a/pyanaconda/ui/gui/spokes/keyboard.py b/pyanaconda/ui/gui/spokes/keyboard.py index d2e8981dafb..719b2abe145 100644 --- a/pyanaconda/ui/gui/spokes/keyboard.py +++ b/pyanaconda/ui/gui/spokes/keyboard.py @@ -18,10 +18,11 @@ # import gi -gi.require_version("Gkbd", "3.0") +#gi.require_version("Gkbd", "3.0") gi.require_version("Gtk", "3.0") -from gi.repository import Gkbd, Gtk +#from gi.repository import Gkbd, Gtk +from gi.repository import Gtk import locale as locale_mod from pyanaconda.ui.gui import GUIObject @@ -29,7 +30,9 @@ from pyanaconda.ui.categories.localization import LocalizationCategory from pyanaconda.ui.gui.utils import gtk_call_once, escape_markup, gtk_batch_map, timed_action from pyanaconda.ui.gui.utils import override_cell_property +# FIXME: can we even import this ? from pyanaconda.ui.gui.xkl_wrapper import XklWrapper, XklWrapperError +from pyanaconda.ui.gui.kbd_wrapper import KbdWrapper from pyanaconda import keyboard from pyanaconda import flags from pyanaconda.core.i18n import _, N_, CN_ @@ -67,6 +70,15 @@ def _show_description(column, renderer, model, itr, wrapper): return value +def _get_keyboard_handling_wrapper(): + if flags.flags.wayland: + log.debug("keyboard: running with Wayland compositor, using KbdWrapper") + return KbdWrapper.get_instance() + else: + log.debug("keyboard - layout: running with Xorg, using XklWrapper") + return XklWrapper.get_instance() + + class AddLayoutDialog(GUIObject): builderObjects = ["addLayoutDialog", "newLayoutStore", "newLayoutStoreFilter"] @@ -75,7 +87,8 @@ class AddLayoutDialog(GUIObject): def __init__(self, data): super().__init__(data) - self._xkl_wrapper = XklWrapper.get_instance() + # FIXME: rename the variable ? + self._xkl_wrapper = _get_keyboard_handling_wrapper() self._chosen_layouts = [] def matches_entry(self, model, itr, user_data=None): @@ -214,7 +227,8 @@ class ConfigureSwitchingDialog(GUIObject): def __init__(self, data, l12_module): super().__init__(data) - self._xkl_wrapper = XklWrapper.get_instance() + # FIXME: rename the variable ? + self._xkl_wrapper = _get_keyboard_handling_wrapper() self._switchingOptsStore = self.builder.get_object("switchingOptsStore") @@ -311,6 +325,8 @@ def get_screen_id(): @classmethod def should_run(cls, environment, data): """Should the spoke run?""" + # FIXME + return False if not is_module_available(LOCALIZATION): return False @@ -320,7 +336,8 @@ def __init__(self, *args): super().__init__(*args) self._remove_last_attempt = False self._confirmed = False - self._xkl_wrapper = XklWrapper.get_instance() + # FIXME: rename the variable ? + self._xkl_wrapper = _get_keyboard_handling_wrapper() self._add_dialog = None self._ready = False diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 84a15a74458..b40a245ff06 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -21,7 +21,7 @@ dist_scripts_SCRIPTS = run-anaconda anaconda-pre-log-gen log-capture start-modul dist_noinst_SCRIPTS = makeupdates makebumpver dist_bin_SCRIPTS = anaconda-cleanup anaconda-disable-nm-ibft-plugin \ - anaconda-nm-disable-autocons + anaconda-nm-disable-autocons run-in-new-session.py miscdir = $(datadir)/$(PACKAGE_NAME) diff --git a/scripts/run-in-new-session.py b/scripts/run-in-new-session.py new file mode 100755 index 00000000000..7edb22988b3 --- /dev/null +++ b/scripts/run-in-new-session.py @@ -0,0 +1,197 @@ +#!/usr/bin/python3 +import argparse +import fcntl +import pam +import pwd +import os +import stat +import struct +import subprocess +import sys + +VT_GETSTATE = 0x5603 +VT_ACTIVATE = 0x5606 +VT_OPENQRY = 0x5600 +VT_WAITACTIVE = 0x5607 +TIOCSCTTY = 0x540E + +def is_running_in_logind_session(): + try: + with open('/proc/self/loginuid', 'r') as f: + loginuid = int(f.read().strip()) + return loginuid != 0xFFFFFFFF + except Exception as e: + raise Exception(f"Error reading /proc/self/loginuid: {e}") + +def find_free_vt(): + with open('/dev/tty0', 'w') as console: + result = fcntl.ioctl(console, VT_OPENQRY, struct.pack('i', 0)) + vt = struct.unpack('i', result)[0] + return vt + +def run_program_in_new_session(arguments, pam_environment, user, service, tty_input, tty_output, vt): + pam_handle = pam.pam() + + for key, value in pam_environment.items(): + pam_handle.putenv(f'{key}={value}') + + old_tty_input = os.fdopen(os.dup(0), 'r') + os.dup2(os.dup(tty_input.fileno()), 0) + + if not pam_handle.authenticate(user, '', service=service, call_end=False): + raise Exception("Authentication failed") + + for key, value in pam_environment.items(): + pam_handle.putenv(f'{key}={value}') + + if pam_handle.open_session() != pam.PAM_SUCCESS: + raise Exception("Failed to open PAM session") + + session_environment = os.environ.copy() + session_environment.update(pam_handle.getenvlist()) + + os.dup2(old_tty_input.fileno(), 0) + + user_info = pwd.getpwnam(user) + uid = user_info.pw_uid + gid = user_info.pw_gid + + old_tty_output = os.fdopen(os.dup(2), 'w') + + console = open("/dev/tty0", 'w') + + try: + old_vt = 0 + if vt: + vt_state = fcntl.ioctl(console, VT_GETSTATE, struct.pack('HHH', 0, 0, 0)) + old_vt, _, _ = struct.unpack('HHH', vt_state) + except OSError as e: + print(f"Could not read current VT: {e}", file=old_tty_output) + + pid = os.fork() + if pid == 0: + try: + os.setsid() + except OSError as e: + print(f"Could not create new pid session: {e}", file=old_tty_output) + + try: + fcntl.ioctl(tty_output, TIOCSCTTY, 1) + except OSError as e: + print(f"Could not take control of tty: {e}", file=old_tty_output) + + try: + fcntl.ioctl(console, VT_ACTIVATE, vt) + except OSError as e: + print(f"Could not change to VT {vt}: {e}", file=old_tty_output) + + try: + fcntl.ioctl(console, VT_WAITACTIVE, vt) + except OSError as e: + print(f"Could not wait for VT {vt} to change: {e}", file=old_tty_output) + + try: + os.dup2(tty_input.fileno(), 0) + os.dup2(tty_output.fileno(), 1) + os.dup2(tty_output.fileno(), 2) + except OSError as e: + print(f"Could not set up standard i/o: {e}", file=old_tty_output) + + try: + os.initgroups(user, gid) + os.setgid(gid) + os.setuid(uid); + except OSError as e: + print(f"Could not become user {user} (uid={uid}): {e}", file=old_tty_output) + + try: + os.execvpe(arguments[0], arguments, session_environment) + except OSError as e: + print(f"Could not run program \"{' '.join(arguments)}\": {e}", file=old_tty_output) + os._exit(1) + + try: + (_, exit_code) = os.waitpid(pid, 0); + except KeyboardInterrupt: + os.kill(pid, os.SIGINT) + except OSError as e: + print(f"Could not wait for program to finish: {e}", file=old_tty_output) + + try: + if old_vt: + fcntl.ioctl(console, VT_ACTIVATE, old_vt) + fcntl.ioctl(console, VT_WAITACTIVE, old_vt) + except OSError as e: + print(f"Could not change VTs back: {e}", file=old_tty_output) + + if os.WIFEXITED(exit_code): + exit_code = os.WEXITSTATUS(exit_code) + else: + os.kill(os.getpid(), os.WTERMSIG(exit_code)) + old_tty_output.close() + console.close() + + if pam_handle.close_session() != pam.PAM_SUCCESS: + raise Exception("Failed to close PAM session") + + pam_handle.end() + + return exit_code + +def main(): + parser = argparse.ArgumentParser(description='Run a program in a PAM session with specific environment variables as a specified user.') + parser.add_argument('--user', default='root', help='Username for which to run the program') + parser.add_argument('--service', default='su-l', help='PAM service to use') + parser.add_argument('--session-type', default='x11', help='e.g., x11, wayland, or tty') + parser.add_argument('--session-class', default='user', help='e.g., greeter or user') + parser.add_argument('--session-desktop', help='desktop file id associated with session, e.g. gnome, gnome-classic, gnome-wayland') + parser.add_argument('--vt', help='VT to run on') + + args, remaining_args = parser.parse_known_args() + + if not remaining_args: + remaining_args = [ "bash", "-l" ] + + if not args.vt: + vt = find_free_vt() + print(f'Using VT {vt}') + else: + vt = int(args.vt) + + if is_running_in_logind_session(): + program = ['systemd-run', + f'--unit=run-in-new-session-{os.getpid()}.service', + '--pipe', + '--wait', + '-d'] + + program += [sys.executable] + program += sys.argv + subprocess.run(program) + return + + try: + tty_path = f'/dev/tty{vt}' + tty_input = open(tty_path, 'r') + tty_output = open(tty_path, 'w') + + pam_environment = {} + pam_environment['XDG_SEAT'] = "seat0" + pam_environment['XDG_SESSION_TYPE'] = args.session_type + pam_environment['XDG_SESSION_CLASS'] = args.session_class + pam_environment['XDG_SESSION_DESKTOP'] = args.session_desktop + pam_environment['XDG_VTNR'] = vt + + try: + result = run_program_in_new_session(remaining_args, pam_environment, args.user, args.service, tty_input, tty_output, vt) + except OSError as e: + raise Exception(f"Error running program \"{' '.join(remaining_args)}\": {e}") + tty_input.close() + tty_output.close() + sys.exit(result) + except OSError as e: + raise Exception(f"Error opening tty associated with VT {vt}: {e}") + +if __name__ == '__main__': + main() + diff --git a/widgets/src/LayoutIndicator.c b/widgets/src/LayoutIndicator.c index 9fcd983e60a..15d0b6c3c9b 100644 --- a/widgets/src/LayoutIndicator.c +++ b/widgets/src/LayoutIndicator.c @@ -24,7 +24,7 @@ #include #include #include -#include +//#include #include "LayoutIndicator.h" #include "intl.h" @@ -77,7 +77,7 @@ struct _AnacondaLayoutIndicatorPrivate { GtkWidget *icon; GtkLabel *layout_label; GdkCursor *cursor; - XklConfigRec *config_rec; + //XklConfigRec *config_rec; gulong state_changed_handler_id; gboolean state_changed_handler_id_set; gulong config_changed_handler_id; @@ -98,11 +98,14 @@ static void anaconda_layout_indicator_refresh_layout(AnacondaLayoutIndicator *in static void anaconda_layout_indicator_refresh_tooltip(AnacondaLayoutIndicator *indicator); /* helper functions */ -static gchar* get_current_layout(XklEngine *engine, XklConfigRec *conf_rec); -static void x_state_changed(XklEngine *engine, XklEngineStateChange type, - gint arg2, gboolean arg3, gpointer indicator); -static void x_config_changed(XklEngine *engine, gpointer indicator); -static GdkFilterReturn handle_xevent(GdkXEvent *xev, GdkEvent *event, gpointer engine); +//static gchar* get_current_layout(XklEngine *engine, XklConfigRec *conf_rec); +static gchar* get_current_layout(); +//static void x_state_changed(XklEngine *engine, XklEngineStateChange type, +// gint arg2, gboolean arg3, gpointer indicator); +static void x_state_changed(gint arg2, gboolean arg3, gpointer indicator); +//static void x_config_changed(XklEngine *engine, gpointer indicator); +static void x_config_changed(gpointer indicator); +//static GdkFilterReturn handle_xevent(GdkXEvent *xev, GdkEvent *event, gpointer engine); static void anaconda_layout_indicator_class_init(AnacondaLayoutIndicatorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); @@ -165,24 +168,24 @@ static void anaconda_layout_indicator_init(AnacondaLayoutIndicator *self) { GdkDisplay *display; AnacondaLayoutIndicatorClass *klass = ANACONDA_LAYOUT_INDICATOR_GET_CLASS(self); - if (!klass->engine) { +// if (!klass->engine) { /* This code cannot go to class_init because that way it would be called when GObject type system is initialized and Gdk won't give us the display. Thus the first instance being created has to populate this class-wide stuff */ /* initialize XklEngine instance that will be used by all LayoutIndicator instances */ - display = gdk_display_get_default(); - klass->engine = xkl_engine_get_instance(GDK_DISPLAY_XDISPLAY(display)); +// display = gdk_display_get_default(); + //klass->engine = xkl_engine_get_instance(GDK_DISPLAY_XDISPLAY(display)); /* make XklEngine listening */ - xkl_engine_start_listen(klass->engine, XKLL_TRACK_KEYBOARD_STATE); + //xkl_engine_start_listen(klass->engine, XKLL_TRACK_KEYBOARD_STATE); /* hook up X events with XklEngine * (passing NULL as the first argument means we want X events from all windows) */ - gdk_window_add_filter(NULL, (GdkFilterFunc) handle_xevent, klass->engine); - } + //gdk_window_add_filter(NULL, (GdkFilterFunc) handle_xevent, klass->engine); +// } self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, ANACONDA_TYPE_LAYOUT_INDICATOR, @@ -209,21 +212,23 @@ static void anaconda_layout_indicator_init(AnacondaLayoutIndicator *self) { NULL); /* initialize XklConfigRec instance providing data */ - self->priv->config_rec = xkl_config_rec_new(); - xkl_config_rec_get_from_server(self->priv->config_rec, klass->engine); + //self->priv->config_rec = xkl_config_rec_new(); + //xkl_config_rec_get_from_server(self->priv->config_rec, klass->engine); /* hook up handler for "X-state-changed" and "X-config-changed" signals */ - self->priv->state_changed_handler_id = g_signal_connect(klass->engine, "X-state-changed", - G_CALLBACK(x_state_changed), - g_object_ref(self)); +// self->priv->state_changed_handler_id = g_signal_connect(klass->engine, "X-state-changed", +// G_CALLBACK(x_state_changed), +// g_object_ref(self)); + self->priv->state_changed_handler_id_set = TRUE; - self->priv->config_changed_handler_id = g_signal_connect(klass->engine, "X-config-changed", - G_CALLBACK(x_config_changed), - g_object_ref(self)); +// self->priv->config_changed_handler_id = g_signal_connect(klass->engine, "X-config-changed", +// G_CALLBACK(x_config_changed), +// g_object_ref(self)); self->priv->config_changed_handler_id_set = TRUE; /* init layout attribute with the current layout */ - self->priv->layout = get_current_layout(klass->engine, self->priv->config_rec); + //self->priv->layout = get_current_layout(klass->engine, self->priv->config_rec); + self->priv->layout = get_current_layout(); /* create layout label and set desired properties */ self->priv->layout_label = GTK_LABEL(gtk_label_new(NULL)); @@ -271,13 +276,13 @@ static void anaconda_layout_indicator_dispose(GObject *object) { /* disconnect signals (XklEngine will outlive us) */ if (self->priv->state_changed_handler_id_set) { - g_signal_handler_disconnect(klass->engine, self->priv->state_changed_handler_id); +// g_signal_handler_disconnect(klass->engine, self->priv->state_changed_handler_id); self->priv->state_changed_handler_id_set = FALSE; } if (self->priv->config_changed_handler_id_set) { - g_signal_handler_disconnect(klass->engine, self->priv->config_changed_handler_id); +// g_signal_handler_disconnect(klass->engine, self->priv->config_changed_handler_id); self->priv->config_changed_handler_id_set = FALSE; } @@ -290,10 +295,10 @@ static void anaconda_layout_indicator_dispose(GObject *object) { g_object_unref(self->priv->cursor); self->priv->cursor = NULL; } - if (self->priv->config_rec) { - g_object_unref(self->priv->config_rec); - self->priv->config_rec = NULL; - } +// if (self->priv->config_rec) { +// g_object_unref(self->priv->config_rec); +// self->priv->config_rec = NULL; +// } if (self->priv->layout) { g_free(self->priv->layout); self->priv->layout = NULL; @@ -343,14 +348,14 @@ static void anaconda_layout_indicator_clicked(GtkWidget *widget, GdkEvent *event if (event->type != GDK_BUTTON_RELEASE) return; - XklState *state = xkl_engine_get_current_state(klass->engine); - guint n_groups = xkl_engine_get_num_groups(klass->engine); + //XklState *state = xkl_engine_get_current_state(klass->engine); + //guint n_groups = xkl_engine_get_num_groups(klass->engine); /* cycle over groups */ - guint next_group = (state->group + 1) % n_groups; + //guint next_group = (state->group + 1) % n_groups; /* activate next group */ - xkl_engine_lock_group(klass->engine, next_group); + //xkl_engine_lock_group(klass->engine, next_group); } static void anaconda_layout_indicator_refresh_ui_elements(AnacondaLayoutIndicator *self) { @@ -364,7 +369,8 @@ static void anaconda_layout_indicator_refresh_layout(AnacondaLayoutIndicator *se AnacondaLayoutIndicatorClass *klass = ANACONDA_LAYOUT_INDICATOR_GET_CLASS(self); g_free(self->priv->layout); - self->priv->layout = get_current_layout(klass->engine, self->priv->config_rec); + //self->priv->layout = get_current_layout(klass->engine, self->priv->config_rec); + self->priv->layout = get_current_layout(); atk = gtk_widget_get_accessible(GTK_WIDGET(self)); atk_object_set_description(atk, self->priv->layout); @@ -374,16 +380,16 @@ static void anaconda_layout_indicator_refresh_layout(AnacondaLayoutIndicator *se static void anaconda_layout_indicator_refresh_tooltip(AnacondaLayoutIndicator *self) { AnacondaLayoutIndicatorClass *klass = ANACONDA_LAYOUT_INDICATOR_GET_CLASS(self); - guint n_groups = xkl_engine_get_num_groups(klass->engine); - gchar *tooltip; + //guint n_groups = xkl_engine_get_num_groups(klass->engine); + //gchar *tooltip; - if (n_groups > 1) - tooltip = g_strdup_printf(MULTIPLE_LAYOUTS_TIP, self->priv->layout); - else - tooltip = g_strdup_printf(SINGLE_LAYOUT_TIP, self->priv->layout); + //if (n_groups > 1) + // tooltip = g_strdup_printf(MULTIPLE_LAYOUTS_TIP, self->priv->layout); + //else + // tooltip = g_strdup_printf(SINGLE_LAYOUT_TIP, self->priv->layout); - gtk_widget_set_tooltip_text(GTK_WIDGET(self), tooltip); - g_free(tooltip); + //gtk_widget_set_tooltip_text(GTK_WIDGET(self), tooltip); + //g_free(tooltip); } /** @@ -392,61 +398,63 @@ static void anaconda_layout_indicator_refresh_tooltip(AnacondaLayoutIndicator *s * Returns: newly allocated string with the currently activated layout as * 'layout (variant)' */ -static gchar* get_current_layout(XklEngine *engine, XklConfigRec *conf_rec) { +static gchar* get_current_layout() { /* engine has to be listening with XKLL_TRACK_KEYBOARD_STATE mask */ gchar *layout = NULL; gchar *variant = NULL; gint32 cur_group; /* returns statically allocated buffer, shouldn't be freed */ - XklState *state = xkl_engine_get_current_state(engine); - cur_group = state->group; + //XklState *state = xkl_engine_get_current_state(engine); + //cur_group = state->group; - guint n_groups = xkl_engine_get_num_groups(engine); + //guint n_groups = xkl_engine_get_num_groups(engine); /* BUG?: if the last layout in the list is activated and removed, state->group may be equal to n_groups that would result in layout being NULL */ - if (cur_group >= n_groups) - cur_group = n_groups - 1; + //if (cur_group >= n_groups) + // cur_group = n_groups - 1; - layout = conf_rec->layouts[cur_group]; + //layout = conf_rec->layouts[cur_group]; /* variant defined for the current layout */ - variant = conf_rec->variants[cur_group]; + //variant = conf_rec->variants[cur_group]; /* variant may be NULL or "" if not defined */ - if (variant && g_strcmp0("", variant)) - return g_strdup_printf("%s (%s)", layout, variant); - else - return g_strdup(layout); + //if (variant && g_strcmp0("", variant)) + // return g_strdup_printf("%s (%s)", layout, variant); + //else + // return g_strdup(layout); } static GdkFilterReturn handle_xevent(GdkXEvent *xev, GdkEvent *event, gpointer data) { - XklEngine *engine = XKL_ENGINE(data); - XEvent *xevent = (XEvent *) xev; - - xkl_engine_filter_events(engine, xevent); +// XklEngine *engine = XKL_ENGINE(data); +// XEvent *xevent = (XEvent *) xev; +// +// xkl_engine_filter_events(engine, xevent); return GDK_FILTER_CONTINUE; } -static void x_state_changed(XklEngine *engine, XklEngineStateChange type, - gint arg2, gboolean arg3, gpointer data) { +//static void x_state_changed(XklEngine *engine, XklEngineStateChange type, +// gint arg2, gboolean arg3, gpointer data) { +static void x_state_changed(gint arg2, gboolean arg3, gpointer data) { g_return_if_fail(data); AnacondaLayoutIndicator *indicator = ANACONDA_LAYOUT_INDICATOR(data); anaconda_layout_indicator_refresh_layout(indicator); } -static void x_config_changed(XklEngine *engine, gpointer data) { +//static void x_config_changed(XklEngine *engine, gpointer data) { +static void x_config_changed(gpointer data) { g_return_if_fail(data); AnacondaLayoutIndicator *indicator = ANACONDA_LAYOUT_INDICATOR(data); AnacondaLayoutIndicatorClass *klass = ANACONDA_LAYOUT_INDICATOR_GET_CLASS(indicator); /* load current configuration from the X server */ - xkl_config_rec_get_from_server(indicator->priv->config_rec, klass->engine); + //xkl_config_rec_get_from_server(indicator->priv->config_rec, klass->engine); anaconda_layout_indicator_refresh_layout(indicator); } diff --git a/widgets/src/LayoutIndicator.h b/widgets/src/LayoutIndicator.h index 443d01b2b2a..21722b2a24c 100644 --- a/widgets/src/LayoutIndicator.h +++ b/widgets/src/LayoutIndicator.h @@ -19,7 +19,7 @@ #define _LAYOUT_INDICATOR_H #include -#include +//#include G_BEGIN_DECLS @@ -60,7 +60,7 @@ struct _AnacondaLayoutIndicatorClass { /* this has to be a class attribute, because XklEngine is a singleton that should be used by all instances */ - XklEngine *engine; +// XklEngine *engine; }; GType anaconda_layout_indicator_get_type (void);