From 2232576cc05895a91927fa06c14aa5498f1b7b77 Mon Sep 17 00:00:00 2001 From: Takao Fujiwara Date: Tue, 17 Sep 2024 10:24:19 +0900 Subject: [PATCH] Update CI for RHEL10 - Add no-overview@fthx until gnome-shell-extension-no-overview is available - Add gnome-headless-session@.service until GDM will provide it Resolves: RHEL-58993 --- tests/ibus-desktop-testing/run/test.sh | 5 + .../tmp/gdm-headless-login-session | 157 ++++++++++++++++++ .../tmp/gnome-headless-session@.service | 6 + .../tmp/no-overview@fthx/extension.js | 26 +++ .../tmp/no-overview@fthx/metadata.json | 10 ++ 5 files changed, 204 insertions(+) create mode 100755 tests/ibus-desktop-testing/tmp/gdm-headless-login-session create mode 100644 tests/ibus-desktop-testing/tmp/gnome-headless-session@.service create mode 100644 tests/ibus-desktop-testing/tmp/no-overview@fthx/extension.js create mode 100644 tests/ibus-desktop-testing/tmp/no-overview@fthx/metadata.json diff --git a/tests/ibus-desktop-testing/run/test.sh b/tests/ibus-desktop-testing/run/test.sh index d53faf5..af411a6 100755 --- a/tests/ibus-desktop-testing/run/test.sh +++ b/tests/ibus-desktop-testing/run/test.sh @@ -4,6 +4,11 @@ rlJournalStart rlPhaseStartTest + rlRun -t "mkdir -p /usr/lib/systemd/system" + rlRun -t "cp ../tmp/gnome-headless-session@.service /usr/lib/systemd/system/." + rlRun -t "cp ../tmp/gdm-headless-login-session /usr/libexec/." + rlRun -t "chmod 755 /usr/libexec/gdm-headless-login-session" + rlRun -t "cp -R ../tmp/no-overview@fthx ." rlRun -t "/usr/bin/ibus-desktop-testing-runner --runner gnome --tests ibus-anthy --no-graphics --lang ja_JP.UTF-8" rlPhaseEnd rlPhaseStartCleanup diff --git a/tests/ibus-desktop-testing/tmp/gdm-headless-login-session b/tests/ibus-desktop-testing/tmp/gdm-headless-login-session new file mode 100755 index 0000000..68d9669 --- /dev/null +++ b/tests/ibus-desktop-testing/tmp/gdm-headless-login-session @@ -0,0 +1,157 @@ +#!/usr/bin/python3 + +import argparse +import pam +import pwd +import os +import signal +import sys + +import gi +gi.require_version('AccountsService', '1.0') +from gi.repository import AccountsService, GLib + +def run_desktop_in_new_session(pam_environment, user, session_desktop, tty_input, tty_output): + keyfile = GLib.KeyFile() + keyfile.load_from_data_dirs(f'wayland-sessions/{session_desktop}.desktop', + GLib.KeyFileFlags.NONE) + + try: + can_run_headless = keyfile.get_boolean(GLib.KEY_FILE_DESKTOP_GROUP, + 'X-GDM-CanRunHeadless') + except GLib.GError: + raise Exception(f"Session {session_desktop} can't run headlessly") + + if not can_run_headless: + raise Exception(f"Session {session_desktop} can't run headlessly") + + executable = keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, + GLib.KEY_FILE_DESKTOP_KEY_TRY_EXEC) + if GLib.find_program_in_path(executable) is None: + raise Exception(f"Invalid session {session_desktop}") + + command = keyfile.get_string(GLib.KEY_FILE_DESKTOP_GROUP, + GLib.KEY_FILE_DESKTOP_KEY_EXEC) + [success, args] = GLib.shell_parse_argv(command) + + pam_handle = pam.pam() + + for key, value in pam_environment.items(): + pam_handle.putenv(f'{key}={value}') + + if not pam_handle.authenticate(user, '', service='gdm-autologin', 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()) + + user_info = pwd.getpwnam(user) + uid = user_info.pw_uid + gid = user_info.pw_gid + + old_tty_output = os.fdopen(os.dup(2), 'w') + + 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: + 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(args[0], args, session_environment) + except OSError as e: + print(f"Could not run program \"{' '.join(arguments)}\": {e}", file=old_tty_output) + os._exit(1) + + + def signal_handler(sig, frame): + os.kill(pid, sig) + + signal.signal(signal.SIGTERM, signal_handler) + + try: + (_, exit_code) = os.waitpid(pid, 0); + except KeyboardInterrupt: + os.kill(pid, signal.SIGTERM) + except OSError as e: + print(f"Could not wait for program to finish: {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() + + if pam_handle.close_session() != pam.PAM_SUCCESS: + raise Exception("Failed to close PAM session") + + pam_handle.end() + + return exit_code + +def wait_for_user_data(user): + main_context = GLib.MainContext.default() + while not user.is_loaded(): + main_context.iteration(True) + +def main(): + parser = argparse.ArgumentParser(description='Run a desktop session in a PAM session as a specified user.') + parser.add_argument('--user', help='Username for which to run the session') + + args = parser.parse_args() + + if args.user is None: + parser.print_usage() + sys.exit(1) + + try: + tty_path = '/dev/null' + + tty_input = open(tty_path, 'r') + tty_output = open(tty_path, 'w') + except OSError as e: + raise Exception(f"Error opening /dev/null as tty associated with VT {vt}: {e}") + + user_manager = AccountsService.UserManager().get_default() + user = user_manager.get_user(args.user) + wait_for_user_data(user) + session_desktop = user.get_session() + if not session_desktop: + session_desktop = 'gnome' + + pam_environment = {} + pam_environment['XDG_SESSION_TYPE'] = 'wayland' + pam_environment['XDG_SESSION_CLASS'] = 'user' + pam_environment['XDG_SESSION_DESKTOP'] = session_desktop + + try: + result = run_desktop_in_new_session(pam_environment, args.user, session_desktop, tty_input, tty_output) + except Exception as e: + raise Exception(f"Error running desktop session \"{session_desktop}\": {e}") + tty_input.close() + tty_output.close() + sys.exit(result) + +if __name__ == '__main__': + main() diff --git a/tests/ibus-desktop-testing/tmp/gnome-headless-session@.service b/tests/ibus-desktop-testing/tmp/gnome-headless-session@.service new file mode 100644 index 0000000..269d162 --- /dev/null +++ b/tests/ibus-desktop-testing/tmp/gnome-headless-session@.service @@ -0,0 +1,6 @@ +[Unit] +Description=Headless desktop session + +[Service] +ExecStart=/usr/libexec/gdm-headless-login-session --user=%i +Restart=on-failure diff --git a/tests/ibus-desktop-testing/tmp/no-overview@fthx/extension.js b/tests/ibus-desktop-testing/tmp/no-overview@fthx/extension.js new file mode 100644 index 0000000..f0a6f28 --- /dev/null +++ b/tests/ibus-desktop-testing/tmp/no-overview@fthx/extension.js @@ -0,0 +1,26 @@ +/* + No overview at start-up + GNOME Shell 45+ extension + Contributors: @fthx + License: GPL v3 +*/ + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; + +export default class NoOverviewExtension { + enable() { + if (!Main.layoutManager._startingUp) { + return; + } + + Main.layoutManager.connectObject( + 'startup-complete', + () => Main.overview.hide(), + this + ); + } + + disable() { + Main.layoutManager.disconnectObject(this); + } +} diff --git a/tests/ibus-desktop-testing/tmp/no-overview@fthx/metadata.json b/tests/ibus-desktop-testing/tmp/no-overview@fthx/metadata.json new file mode 100644 index 0000000..695d317 --- /dev/null +++ b/tests/ibus-desktop-testing/tmp/no-overview@fthx/metadata.json @@ -0,0 +1,10 @@ +{ + "_generated": "Generated by SweetTooth, do not edit", + "description": "No overview at start-up. Nothing more.", + "name": "No overview at start-up", + "original-authors": ["fthx"], + "shell-version": ["46"], + "url": "https://github.com/fthx/no-overview", + "uuid": "no-overview@fthx", + "version": 999 +} \ No newline at end of file