From d50cc4fe3adff1a8f87dbcea891924c1bfade0fe Mon Sep 17 00:00:00 2001 From: AlmaLinux RelEng Bot Date: Tue, 19 May 2026 15:07:41 -0400 Subject: [PATCH] import CS gdm-47.0-19.el10 --- 0001-Add-headless-session-files.patch | 219 -- 0001-Introduce-gdm-new-session-tool.patch | 1937 +++++++++++++++++ ...Revert-hack-that-quits-plymouth-late.patch | 365 ++++ ...d-support-for-unified-authentication.patch | 165 ++ ...ess-session-Fix-autostarting-on-boot.patch | 29 - ...ctory-look-for-boot_display-sysfs-at.patch | 53 + ...manager-Keep-register-display-method.patch | 134 ++ ...mouth-when-no-local-display-is-avail.patch | 107 + 0001-meson-Define-missing-HAVE_LIBAUDIT.patch | 1 - ...port-service-unavailable-error-as-fa.patch | 50 + ...emory-leak-on-new-outside-connection.patch | 1 - ...-record-Rework-wtmp-utmp-btmp-fields.patch | 349 +++ 0002-data-Drop-X11-fallback-rules.patch | 93 + gdm.spec | 95 +- sources | 2 +- 15 files changed, 3336 insertions(+), 264 deletions(-) delete mode 100644 0001-Add-headless-session-files.patch create mode 100644 0001-Introduce-gdm-new-session-tool.patch create mode 100644 0001-Revert-hack-that-quits-plymouth-late.patch create mode 100644 0001-data-Add-support-for-unified-authentication.patch delete mode 100644 0001-headless-session-Fix-autostarting-on-boot.patch create mode 100644 0001-local-display-factory-look-for-boot_display-sysfs-at.patch create mode 100644 0001-manager-Keep-register-display-method.patch create mode 100644 0001-manager-Quit-plymouth-when-no-local-display-is-avail.patch create mode 100644 0001-session-Don-t-report-service-unavailable-error-as-fa.patch create mode 100644 0001-session-record-Rework-wtmp-utmp-btmp-fields.patch create mode 100644 0002-data-Drop-X11-fallback-rules.patch diff --git a/0001-Add-headless-session-files.patch b/0001-Add-headless-session-files.patch deleted file mode 100644 index 9ea57eb..0000000 --- a/0001-Add-headless-session-files.patch +++ /dev/null @@ -1,219 +0,0 @@ -From a8a0d952293337544da4681f0c896052eafd9d0f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20=C3=85dahl?= -Date: Fri, 5 Apr 2024 16:44:07 +0200 -Subject: [PATCH] Add headless session files - -It consists of a python script for running the session, and a systemd -system service template. ---- - data/gnome-headless-session@.service | 6 + - data/meson.build | 4 + - utils/gdm-headless-login-session | 157 +++++++++++++++++++++++++++ - utils/meson.build | 5 + - 4 files changed, 172 insertions(+) - create mode 100644 data/gnome-headless-session@.service - create mode 100644 utils/gdm-headless-login-session - -diff --git a/data/gnome-headless-session@.service b/data/gnome-headless-session@.service -new file mode 100644 -index 000000000..269d16288 ---- /dev/null -+++ b/data/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/data/meson.build b/data/meson.build -index 2211e98b5..2df07cd32 100644 ---- a/data/meson.build -+++ b/data/meson.build -@@ -221,3 +221,7 @@ if get_option('gdm-xsession') - install_dir: gdmconfdir, - ) - endif -+ -+headless_session_service = install_data('gnome-headless-session@.service', -+ install_dir: systemd_systemunitdir, -+ ) -diff --git a/utils/gdm-headless-login-session b/utils/gdm-headless-login-session -new file mode 100644 -index 000000000..e108be523 ---- /dev/null -+++ b/utils/gdm-headless-login-session -@@ -0,0 +1,157 @@ -+#!/usr/bin/env 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/utils/meson.build b/utils/meson.build -index e4141fb13..57dd6519f 100644 ---- a/utils/meson.build -+++ b/utils/meson.build -@@ -65,3 +65,8 @@ if distro != 'none' - install_dir: get_option('libexecdir'), - ) - endif -+ -+gdm_headless_login_session = install_data('gdm-headless-login-session', -+ install_mode: 'rwxr-xr-x', -+ install_dir: get_option('libexecdir'), -+ ) --- -2.44.0 - diff --git a/0001-Introduce-gdm-new-session-tool.patch b/0001-Introduce-gdm-new-session-tool.patch new file mode 100644 index 0000000..496593f --- /dev/null +++ b/0001-Introduce-gdm-new-session-tool.patch @@ -0,0 +1,1937 @@ +From ad5b372db6d05698cf6c79f4b7fd98e1b03547f7 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 14:54:34 +0200 +Subject: [PATCH 1/8] common: Add find graphical sessions for username + +This will be used in the next commits to check if starting a new user +session is allowed or there's already one started. + +Part-of: +--- + common/gdm-common.c | 91 ++++++++++++++++++++++++++++++++------------- + common/gdm-common.h | 4 ++ + 2 files changed, 69 insertions(+), 26 deletions(-) + +diff --git a/common/gdm-common.c b/common/gdm-common.c +index 4e538a5e2..7263e6d39 100644 +--- a/common/gdm-common.c ++++ b/common/gdm-common.c +@@ -960,6 +960,64 @@ _systemd_session_is_active (const char *session_id) + return TRUE; + } + ++static gboolean ++find_graphical_sessions (const uid_t uid, ++ char ***out_sessions) ++{ ++ g_auto (GStrv) sessions = NULL; ++ g_autoptr (GStrvBuilder) builder = NULL; ++ int n_sessions; ++ ++ g_debug ("Finding a graphical session for user %d", uid); ++ ++ n_sessions = sd_uid_get_sessions (uid, ++ GDM_SYSTEMD_SESSION_REQUIRE_ONLINE, ++ &sessions); ++ if (n_sessions < 0) ++ return FALSE; ++ ++ builder = g_strv_builder_new (); ++ ++ for (int i = 0; i < n_sessions; ++i) { ++ g_debug ("Considering session '%s'", sessions[i]); ++ ++ if (!_systemd_session_is_graphical (sessions[i])) ++ continue; ++ ++ if (!_systemd_session_is_active (sessions[i])) ++ continue; ++ ++ g_strv_builder_add (builder, g_strdup (sessions[i])); ++ } ++ ++ *out_sessions = g_strv_builder_end (builder); ++ ++ return TRUE; ++} ++ ++gboolean ++gdm_find_graphical_sessions_for_username (const char *username, ++ char ***sessions, ++ GError **error) ++{ ++ struct passwd *pwent; ++ ++ gdm_get_pwent_for_name (username, &pwent); ++ if (pwent == NULL) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Couldn't get pw entry for username %s", username); ++ return FALSE; ++ } ++ ++ if (!find_graphical_sessions (pwent->pw_uid, sessions)) { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Couldn't find sessions for username %s", username); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + gboolean + gdm_find_display_session (GPid pid, + const uid_t uid, +@@ -968,7 +1026,6 @@ gdm_find_display_session (GPid pid, + { + char *local_session_id = NULL; + g_auto(GStrv) sessions = NULL; +- int n_sessions; + int res; + + g_return_val_if_fail (out_session_id != NULL, FALSE); +@@ -993,13 +1050,7 @@ gdm_find_display_session (GPid pid, + pid, strerror (-res)); + } + +- g_debug ("Finding a graphical session for user %d", uid); +- +- n_sessions = sd_uid_get_sessions (uid, +- GDM_SYSTEMD_SESSION_REQUIRE_ONLINE, +- &sessions); +- +- if (n_sessions < 0) { ++ if (!find_graphical_sessions (uid, &sessions)) { + g_set_error (error, + GDM_COMMON_ERROR, + 0, +@@ -1008,23 +1059,7 @@ gdm_find_display_session (GPid pid, + return FALSE; + } + +- for (int i = 0; i < n_sessions; ++i) { +- g_debug ("Considering session '%s'", sessions[i]); +- +- if (!_systemd_session_is_graphical (sessions[i])) +- continue; +- +- if (!_systemd_session_is_active (sessions[i])) +- continue; +- +- /* +- * We get the sessions from newest to oldest, so take the last +- * one we find that's good +- */ +- local_session_id = sessions[i]; +- } +- +- if (local_session_id == NULL) { ++ if (g_strv_length (sessions) == 0) { + g_set_error (error, + GDM_COMMON_ERROR, + 0, +@@ -1033,7 +1068,11 @@ gdm_find_display_session (GPid pid, + return FALSE; + } + +- *out_session_id = g_strdup (local_session_id); ++ /* ++ * We get the sessions from newest to oldest, so take the last ++ * one we find that's good ++ */ ++ *out_session_id = g_strdup (sessions[0]); + + return TRUE; + } +diff --git a/common/gdm-common.h b/common/gdm-common.h +index 3e210b87b..641c76f2b 100644 +--- a/common/gdm-common.h ++++ b/common/gdm-common.h +@@ -81,6 +81,10 @@ gboolean gdm_get_login_window_session_id (const char *seat_id, + gboolean gdm_goto_login_session (GCancellable *cancellable, + GError **error); + ++gboolean gdm_find_graphical_sessions_for_username (const char *username, ++ char ***sessions, ++ GError **error); ++ + GPtrArray *gdm_get_script_environment (const char *username, + const char *display_name, + const char *display_hostname, +-- +2.51.0 + + +From a3630f535c72fabc1ff0ed5ba72cd2109baed4df Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 16:54:57 +0200 +Subject: [PATCH 2/8] display: Add GetSessionId method on its dbus iface + +This will be used when creating a new user session to track when it gets +destroyed. + +Part-of: +--- + daemon/gdm-display.c | 17 +++++++++++++++++ + daemon/gdm-display.xml | 3 +++ + data/gdm.conf.in | 3 +++ + 3 files changed, 23 insertions(+) + +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index 54e054808..41260cb8a 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -1132,6 +1132,21 @@ handle_get_seat_id (GdmDBusDisplay *skeleton, + return TRUE; + } + ++static gboolean ++handle_get_session_id (GdmDBusDisplay *skeleton, ++ GDBusMethodInvocation *invocation, ++ GdmDisplay *self) ++{ ++ GdmDisplayPrivate *priv = gdm_display_get_instance_private (self); ++ ++ gdm_dbus_display_complete_get_session_id (skeleton, ++ invocation, ++ priv->session_id ? ++ priv->session_id : ""); ++ ++ return TRUE; ++} ++ + static gboolean + handle_get_x11_display_name (GdmDBusDisplay *skeleton, + GDBusMethodInvocation *invocation, +@@ -1197,6 +1212,8 @@ register_display (GdmDisplay *self) + G_CALLBACK (handle_get_remote_hostname), self, 0); + g_signal_connect_object (priv->display_skeleton, "handle-get-seat-id", + G_CALLBACK (handle_get_seat_id), self, 0); ++ g_signal_connect_object (priv->display_skeleton, "handle-get-session-id", ++ G_CALLBACK (handle_get_session_id), self, 0); + g_signal_connect_object (priv->display_skeleton, "handle-get-x11-display-name", + G_CALLBACK (handle_get_x11_display_name), self, 0); + g_signal_connect_object (priv->display_skeleton, "handle-is-local", +diff --git a/daemon/gdm-display.xml b/daemon/gdm-display.xml +index 3e9b5d86f..18bd2a8b3 100644 +--- a/daemon/gdm-display.xml ++++ b/daemon/gdm-display.xml +@@ -10,6 +10,9 @@ + + + ++ ++ ++ + + + +diff --git a/data/gdm.conf.in b/data/gdm.conf.in +index eda131e02..425143d52 100644 +--- a/data/gdm.conf.in ++++ b/data/gdm.conf.in +@@ -56,6 +56,9 @@ + ++ + +-- +2.51.0 + + +From 5dbd23f70423a37e4e50647c81bebae3ccb73592 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 17:03:36 +0200 +Subject: [PATCH 3/8] manager: Check all autologin details in the same function + +In the next commits autologin will be possible in a different way, +having all checks in the same function is needed for that. + +Part-of: +--- + daemon/gdm-manager.c | 84 +++++++++++++++----------------------------- + 1 file changed, 29 insertions(+), 55 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index e048917..99e451c 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1324,45 +1324,38 @@ get_timed_login_details (GdmManager *manager, + } + + static gboolean +-get_automatic_login_details (GdmManager *manager, +- char **usernamep) ++get_automatic_login_details (GdmManager *manager, ++ GdmDisplay *display, ++ char **out_username) + { +- gboolean res; + gboolean enabled; +- char *username = NULL; ++ g_autofree char *seat_id = NULL; ++ g_autofree char *username = NULL; + +- enabled = FALSE; +- username = NULL; ++ g_object_get (G_OBJECT (display), ++ "seat-id", &seat_id, ++ NULL); + +- res = gdm_settings_direct_get_boolean (GDM_KEY_AUTO_LOGIN_ENABLE, &enabled); +- if (res && enabled) { +- res = gdm_settings_direct_get_string (GDM_KEY_AUTO_LOGIN_USER, &username); +- } ++ if (seat_id == NULL || !g_str_equal (seat_id, "seat0")) ++ return FALSE; + +- if (enabled && res && username != NULL && username[0] != '\0') { +- goto out; +- } ++ if (manager->did_automatic_login || manager->automatic_login_display != NULL) ++ return FALSE; + +- g_free (username); +- username = NULL; +- enabled = FALSE; ++ if (!gdm_settings_direct_get_boolean (GDM_KEY_AUTO_LOGIN_ENABLE, &enabled)) ++ return FALSE; + +- out: +- if (enabled) { +- g_debug ("GdmDisplay: Got automatic login details for display: %d %s", +- enabled, +- username); +- } else { +- g_debug ("GdmDisplay: Got automatic login details for display: 0"); +- } ++ if (!gdm_settings_direct_get_string (GDM_KEY_AUTO_LOGIN_USER, &username)) ++ return FALSE; + +- if (usernamep != NULL) { +- *usernamep = username; +- } else { +- g_free (username); +- } ++ if (!enabled || username == NULL || username[0] == '\0') ++ return FALSE; + +- return enabled; ++ g_debug ("GdmDisplay: Got automatic login details for user: %s", username); ++ ++ *out_username = g_steal_pointer (&username); ++ ++ return TRUE; + } + + static const char * +@@ -1496,25 +1489,12 @@ set_up_session (GdmManager *manager, + ActUserManager *user_manager; + ActUser *user; + gboolean loaded; +- gboolean seat_can_autologin = FALSE, seat_did_autologin = FALSE; +- gboolean autologin_enabled = FALSE; +- g_autofree char *seat_id = NULL; +- char *username = NULL; +- +- g_object_get (G_OBJECT (display), "seat-id", &seat_id, NULL); ++ gboolean autologin_enabled; ++ g_autofree char *username = NULL; + +- if (g_strcmp0 (seat_id, "seat0") == 0) +- seat_can_autologin = TRUE; +- +- if (manager->did_automatic_login || manager->automatic_login_display != NULL) +- seat_did_autologin = TRUE; +- +- if (seat_can_autologin && !seat_did_autologin) +- autologin_enabled = get_automatic_login_details (manager, &username); ++ autologin_enabled = get_automatic_login_details (manager, display, &username); + + if (!autologin_enabled) { +- g_free (username); +- + #ifdef HAVE_LIBXDMCP + if (GDM_IS_XDMCP_CHOOSER_DISPLAY (display)) { + set_up_chooser_session (manager, display); +@@ -1539,7 +1519,7 @@ set_up_session (GdmManager *manager, + operation = g_new (UsernameLookupOperation, 1); + operation->manager = g_object_ref (manager); + operation->display = g_object_ref (display); +- operation->username = username; ++ operation->username = g_steal_pointer (&username); + + g_signal_connect (user, + "notify::is-loaded", +@@ -2367,7 +2347,7 @@ on_session_conversation_started (GdmSession *session, + { + GdmDisplay *display; + gboolean enabled; +- char *username; ++ g_autofree char *username = NULL; + + g_debug ("GdmManager: session conversation started for service %s on session", service_name); + +@@ -2383,11 +2363,7 @@ on_session_conversation_started (GdmSession *session, + return; + } + +- if (!display_is_on_seat0 (display)) { +- return; +- } +- +- enabled = get_automatic_login_details (manager, &username); ++ enabled = get_automatic_login_details (manager, display, &username); + + if (! enabled) { + return; +@@ -2398,8 +2374,6 @@ on_session_conversation_started (GdmSession *session, + /* service_name will be "gdm-autologin" + */ + gdm_session_setup_for_user (session, service_name, username); +- +- g_free (username); + } + + static void +-- +2.51.0 + +From a61080680aa4206b172de0a0233aced0b41872f0 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 17:06:30 +0200 +Subject: [PATCH 4/8] display: Add "autologin-user" property + +This will be used to make it start a user session doing autologin instead of +a starting a greeter session. + +Part-of: +--- + daemon/gdm-display.c | 28 ++++++++++++++++++++++++++++ + daemon/gdm-manager.c | 5 +++++ + 2 files changed, 33 insertions(+) + +diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c +index 41260cb8a..acf9b4076 100644 +--- a/daemon/gdm-display.c ++++ b/daemon/gdm-display.c +@@ -70,6 +70,8 @@ typedef struct _GdmDisplayPrivate + int status; + time_t creation_time; + ++ char *autologin_user; ++ + char *x11_cookie; + gsize x11_cookie_size; + GdmDisplayAccessFile *access_file; +@@ -119,6 +121,7 @@ enum { + PROP_IS_LOCAL, + PROP_LAUNCH_ENVIRONMENT, + PROP_IS_INITIAL, ++ PROP_AUTOLOGIN_USER, + PROP_ALLOW_TIMED_LOGIN, + PROP_HAVE_EXISTING_USER_ACCOUNTS, + PROP_DOING_INITIAL_SETUP, +@@ -872,6 +875,17 @@ _gdm_display_set_is_local (GdmDisplay *self, + priv->is_local = is_local; + } + ++static void ++_gdm_display_set_autologin_user (GdmDisplay *self, ++ const char *user) ++{ ++ GdmDisplayPrivate *priv; ++ ++ priv = gdm_display_get_instance_private (self); ++ g_debug ("GdmDisplay: autologin user: %s", user); ++ g_set_str (&priv->autologin_user, user); ++} ++ + static void + _gdm_display_set_session_registered (GdmDisplay *self, + gboolean registered) +@@ -979,6 +993,9 @@ gdm_display_set_property (GObject *object, + case PROP_IS_LOCAL: + _gdm_display_set_is_local (self, g_value_get_boolean (value)); + break; ++ case PROP_AUTOLOGIN_USER: ++ _gdm_display_set_autologin_user (self, g_value_get_string (value)); ++ break; + case PROP_ALLOW_TIMED_LOGIN: + _gdm_display_set_allow_timed_login (self, g_value_get_boolean (value)); + break; +@@ -1051,6 +1068,9 @@ gdm_display_get_property (GObject *object, + case PROP_IS_LOCAL: + g_value_set_boolean (value, priv->is_local); + break; ++ case PROP_AUTOLOGIN_USER: ++ g_value_set_string (value, priv->autologin_user); ++ break; + case PROP_IS_CONNECTED: + #ifdef ENABLE_X11_SUPPORT + g_value_set_boolean (value, priv->xcb_connection != NULL); +@@ -1272,6 +1292,7 @@ gdm_display_dispose (GObject *object) + g_clear_handle_id (&priv->finish_idle_id, g_source_remove); + g_clear_object (&priv->launch_environment); + g_clear_pointer (&priv->supported_session_types, g_strfreev); ++ g_clear_pointer (&priv->autologin_user, g_free); + + g_warn_if_fail (priv->status != GDM_DISPLAY_MANAGED); + g_warn_if_fail (priv->user_access_file == NULL); +@@ -1358,6 +1379,13 @@ gdm_display_class_init (GdmDisplayClass *klass) + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); ++ g_object_class_install_property (object_class, ++ PROP_AUTOLOGIN_USER, ++ g_param_spec_string ("autologin-user", ++ NULL, ++ NULL, ++ NULL, ++ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_ALLOW_TIMED_LOGIN, + g_param_spec_boolean ("allow-timed-login", +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index f1d966152..e455dad36 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -1314,8 +1314,12 @@ get_automatic_login_details (GdmManager *manager, + + g_object_get (G_OBJECT (display), + "seat-id", &seat_id, ++ "autologin-user", &username, + NULL); + ++ if (username != NULL) ++ goto out; ++ + if (seat_id == NULL || !g_str_equal (seat_id, "seat0")) + return FALSE; + +@@ -1331,6 +1335,7 @@ get_automatic_login_details (GdmManager *manager, + if (!enabled || username == NULL || username[0] == '\0') + return FALSE; + ++out: + g_debug ("GdmDisplay: Got automatic login details for user: %s", username); + + *out_username = g_steal_pointer (&username); +-- +2.51.0 + + +From 3dfde2fb65cb4fec6aad9cd624608992f21c7080 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 17:27:07 +0200 +Subject: [PATCH 5/8] display-factories: Some small cleanups + +- Make the naming style of creator funcs the same. +- Make private local_display_factory_create_display func. +- Remove on remote factory unused display status changed callback. +- Return dbus method invocation enum on dbus handlers. + +Part-of: +--- + daemon/gdm-local-display-factory.c | 52 +++++++++++++---------------- + daemon/gdm-local-display-factory.h | 3 -- + daemon/gdm-remote-display-factory.c | 27 +++------------ + 3 files changed, 29 insertions(+), 53 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 23bc8a585..48c08cfa1 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -395,21 +395,16 @@ store_display (GdmLocalDisplayFactory *factory, + /org/gnome/DisplayManager/Manager \ + org.gnome.DisplayManager.Manager.GetDisplays + */ +-gboolean +-gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory, +- char **id, +- GError **error) ++static gboolean ++gdm_local_display_factory_create_display (GdmLocalDisplayFactory *factory, ++ char **id, ++ GError **error) + { +- gboolean ret; +- GdmDisplay *display = NULL; +- gboolean is_initial = FALSE; ++ gboolean is_initial = FALSE; ++ g_autoptr (GdmDisplay) display = NULL; + g_autofree gchar *preferred_display_server = NULL; + +- g_return_val_if_fail (GDM_IS_LOCAL_DISPLAY_FACTORY (factory), FALSE); +- +- ret = FALSE; +- +- g_debug ("GdmLocalDisplayFactory: Creating transient display"); ++ g_debug ("GdmLocalDisplayFactory: Creating local display"); + + preferred_display_server = get_preferred_display_server (factory); + +@@ -460,22 +455,23 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact + + store_display (factory, display); + +- if (! gdm_display_manage (display)) { +- display = NULL; +- goto out; ++ if (!gdm_display_manage (display)) { ++ g_set_error_literal (error, ++ GDM_DISPLAY_ERROR, ++ GDM_DISPLAY_ERROR_GENERAL, ++ "Failed managing display"); ++ return FALSE; + } + +- if (! gdm_display_get_id (display, id, NULL)) { +- display = NULL; +- goto out; ++ if (!gdm_display_get_id (display, id, NULL)) { ++ g_set_error_literal (error, ++ GDM_DISPLAY_ERROR, ++ GDM_DISPLAY_ERROR_GENERAL, ++ "Failed getting display id"); ++ return FALSE; + } + +- ret = TRUE; +- out: +- /* ref either held by store or not at all */ +- g_object_unref (display); +- +- return ret; ++ return TRUE; + } + + static void +@@ -1590,16 +1586,16 @@ handle_create_transient_display (GdmDBusLocalDisplayFactory *skeleton, + g_autofree char *id = NULL; + gboolean created; + +- created = gdm_local_display_factory_create_transient_display (factory, +- &id, +- &error); ++ created = gdm_local_display_factory_create_display (factory, ++ &id, ++ &error); + if (!created) { + g_dbus_method_invocation_return_gerror (invocation, error); + } else { + gdm_dbus_local_display_factory_complete_create_transient_display (skeleton, invocation, id); + } + +- return TRUE; ++ return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + static gboolean +diff --git a/daemon/gdm-local-display-factory.h b/daemon/gdm-local-display-factory.h +index 49c7140f9..63da3c69c 100644 +--- a/daemon/gdm-local-display-factory.h ++++ b/daemon/gdm-local-display-factory.h +@@ -43,9 +43,6 @@ GQuark gdm_local_display_factory_error_quark (v + + GdmLocalDisplayFactory * gdm_local_display_factory_new (GdmDisplayStore *display_store); + +-gboolean gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *factory, +- char **id, +- GError **error); + G_END_DECLS + + #endif /* __GDM_LOCAL_DISPLAY_FACTORY_H */ +diff --git a/daemon/gdm-remote-display-factory.c b/daemon/gdm-remote-display-factory.c +index deb6d402b..d3e2afe89 100644 +--- a/daemon/gdm-remote-display-factory.c ++++ b/daemon/gdm-remote-display-factory.c +@@ -47,20 +47,11 @@ static gpointer remote_display_factory_object = NULL; + + G_DEFINE_TYPE (GdmRemoteDisplayFactory, gdm_remote_display_factory, GDM_TYPE_DISPLAY_FACTORY) + +-static void +-on_display_status_changed (GdmDisplay *display, +- GParamSpec *arg1, +- GdmRemoteDisplayFactory *factory) +-{ +- g_debug ("GdmRemoteDisplayFactory: remote display status changed: %d", +- gdm_display_get_status (display)); +-} +- + static gboolean +-gdm_remote_display_factory_create_remote_display (GdmRemoteDisplayFactory *factory, +- const char *remote_id) ++gdm_remote_display_factory_create_display (GdmRemoteDisplayFactory *factory, ++ const char *remote_id) + { +- GdmDisplay *display = NULL; ++ g_autoptr (GdmDisplay) display = NULL; + GdmDisplayStore *store; + + g_debug ("GdmRemoteDisplayFactory: Creating remote display"); +@@ -72,17 +63,9 @@ gdm_remote_display_factory_create_remote_display (GdmRemoteDisplayFactory *facto + + if (!gdm_display_prepare (display)) { + gdm_display_unmanage (display); +- g_object_unref (display); + return FALSE; + } + +- g_signal_connect_after (display, +- "notify::status", +- G_CALLBACK (on_display_status_changed), +- factory); +- +- g_object_unref (display); +- + return TRUE; + } + +@@ -92,7 +75,7 @@ handle_create_remote_display (GdmDBusRemoteDisplayFactory *skeleton, + const char *remote_id, + GdmRemoteDisplayFactory *factory) + { +- if (!gdm_remote_display_factory_create_remote_display (factory, remote_id)) ++ if (!gdm_remote_display_factory_create_display (factory, remote_id)) + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, +@@ -101,7 +84,7 @@ handle_create_remote_display (GdmDBusRemoteDisplayFactory *skeleton, + gdm_dbus_remote_display_factory_complete_create_remote_display (factory->skeleton, + invocation); + +- return TRUE; ++ return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + static gboolean +-- +2.51.0 + + +From 1cfc7a686510b9fe655586ca891ab389cbeaeffc Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 17:34:20 +0200 +Subject: [PATCH 6/8] display-factories: Add UserDisplay on its + dbus ifaces + +This allows creating or destroying a graphical user session either locally or +headlessly. + +It uses a polkit policy to allow only processes in GDM group to call +these methods. + +In next commits the policy rule will be enhanced to only authorize processes +in GDM group running from specific systemd system units. + +Part-of: +--- + daemon/gdm-display-factory.c | 171 ++++++++++++++++++++++++++ + daemon/gdm-display-factory.h | 9 ++ + daemon/gdm-local-display-factory.c | 84 +++++++++++++ + daemon/gdm-local-display-factory.xml | 6 + + daemon/gdm-remote-display-factory.c | 90 +++++++++++++- + daemon/gdm-remote-display-factory.xml | 6 + + daemon/meson.build | 1 + + data/meson.build | 14 +++ + data/org.gnome.displaymanager.policy | 20 +++ + data/polkit-gdm.rules.in | 8 ++ + meson.build | 1 + + 11 files changed, 409 insertions(+), 1 deletion(-) + create mode 100644 data/org.gnome.displaymanager.policy + create mode 100644 data/polkit-gdm.rules.in + +diff --git a/daemon/gdm-display-factory.c b/daemon/gdm-display-factory.c +index 8ff40ce..640db4a 100644 +--- a/daemon/gdm-display-factory.c ++++ b/daemon/gdm-display-factory.c +@@ -26,14 +26,22 @@ + #include + #include + #include ++#include + + #include "gdm-display-factory.h" ++#include "gdm-common.h" + #include "gdm-display-store.h" + ++#define GDM_DISPLAY_FACTORY_MANAGE_DISPLAYS_POLKIT_ACTION "org.gnome.displaymanager.displayfactory.manage-user-displays" ++ + typedef struct _GdmDisplayFactoryPrivate + { + GdmDisplayStore *display_store; + guint purge_displays_id; ++ ++ GHashTable *display_creation_users; ++ ++ PolkitAuthority *authority; + } GdmDisplayFactoryPrivate; + + enum { +@@ -142,6 +150,157 @@ gdm_display_factory_stop (GdmDisplayFactory *factory) + return ret; + } + ++static gboolean ++ensure_polkit_authority (GdmDisplayFactory *factory, ++ GError **error) ++{ ++ GdmDisplayFactoryPrivate *priv; ++ ++ priv = gdm_display_factory_get_instance_private (factory); ++ ++ if (priv->authority) ++ return TRUE; ++ ++ priv->authority = polkit_authority_get_sync (NULL, error); ++ ++ return priv->authority != NULL; ++} ++ ++gboolean ++gdm_display_factory_authorize_manage_user_displays (GdmDisplayFactory *factory, ++ GDBusMethodInvocation *invocation, ++ GError **error) ++{ ++ g_autoptr (PolkitAuthorizationResult) result = NULL; ++ g_autoptr (PolkitSubject) subject = NULL; ++ g_autoptr (GError) local_error = NULL; ++ const char *action; ++ const char *method; ++ const char *sender; ++ PolkitCheckAuthorizationFlags flags; ++ GdmDisplayFactoryPrivate *priv; ++ ++ g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE); ++ ++ method = g_dbus_method_invocation_get_method_name (invocation); ++ if (g_strcmp0 (method, "CreateUserDisplay") != 0 && ++ g_strcmp0 (method, "DestroyUserDisplay") != 0) ++ return TRUE; ++ ++ if (!ensure_polkit_authority (factory, &local_error)) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "Error getting polkit authority: %s", ++ local_error->message); ++ return FALSE; ++ } ++ ++ priv = gdm_display_factory_get_instance_private (factory); ++ ++ sender = g_dbus_method_invocation_get_sender (invocation); ++ subject = polkit_system_bus_name_new (sender); ++ action = GDM_DISPLAY_FACTORY_MANAGE_DISPLAYS_POLKIT_ACTION; ++ flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; ++ result = polkit_authority_check_authorization_sync (priv->authority, ++ subject, action, ++ NULL, flags, NULL, ++ &local_error); ++ if (!result) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "Failed to check authorization: %s", ++ local_error->message); ++ return FALSE; ++ } ++ ++ if (!polkit_authorization_result_get_is_authorized (result)) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "Not authorized for action %s", action); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++gdm_display_factory_on_user_display_creation (GdmDisplayFactory *factory, ++ const char *user, ++ GError **error) ++{ ++ g_auto (GStrv) sessions = NULL; ++ GdmDisplayFactoryPrivate *priv; ++ ++ g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE); ++ ++ priv = gdm_display_factory_get_instance_private (factory); ++ ++ if (g_hash_table_lookup (priv->display_creation_users, user) != NULL) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "A display for user %s has already been created", user); ++ return FALSE; ++ } ++ ++ if (!gdm_find_graphical_sessions_for_username (user, &sessions, error)) ++ return FALSE; ++ ++ if (g_strv_length (sessions) > 0) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "There's already an opened session for user %s", user); ++ return FALSE; ++ } ++ ++ g_hash_table_insert (priv->display_creation_users, g_strdup (user), NULL); ++ ++ return TRUE; ++} ++ ++gboolean ++gdm_display_factory_on_user_display_destruction (GdmDisplayFactory *factory, ++ const char *user, ++ GError **error) ++{ ++ g_auto (GStrv) session_ids = NULL; ++ g_autoptr (GError) local_error = NULL; ++ g_autoptr (GDBusConnection) connection = NULL; ++ GdmDisplayFactoryPrivate *priv; ++ int i; ++ ++ g_return_val_if_fail (GDM_IS_DISPLAY_FACTORY (factory), FALSE); ++ ++ priv = gdm_display_factory_get_instance_private (factory); ++ ++ if (!g_hash_table_remove (priv->display_creation_users, user)) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "A display for user %s wasn't created", user); ++ return FALSE; ++ } ++ ++ if (!gdm_find_graphical_sessions_for_username (user, &session_ids, error)) ++ return FALSE; ++ ++ if (g_strv_length (session_ids) == 0) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "There's no display for user %s", user); ++ return FALSE; ++ } ++ ++ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error); ++ if (connection == NULL) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "Failed getting system bus %s", local_error->message); ++ return FALSE; ++ } ++ ++ for (i = 0; session_ids[i] != NULL; i++) { ++ if (!gdm_terminate_session_by_id (connection, NULL, session_ids[i])) { ++ g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, ++ "Failed to terminate session %s", ++ session_ids[i]); ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ + static void + gdm_display_factory_set_display_store (GdmDisplayFactory *factory, + GdmDisplayStore *display_store) +@@ -219,6 +378,14 @@ gdm_display_factory_class_init (GdmDisplayFactoryClass *klass) + static void + gdm_display_factory_init (GdmDisplayFactory *factory) + { ++ GdmDisplayFactoryPrivate *priv; ++ ++ priv = gdm_display_factory_get_instance_private (factory); ++ ++ priv->display_creation_users = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ (GDestroyNotify) g_free, ++ NULL); + } + + static void +@@ -235,7 +402,11 @@ gdm_display_factory_finalize (GObject *object) + + g_return_if_fail (priv != NULL); + ++ g_clear_pointer (&priv->display_creation_users, g_hash_table_destroy); ++ + g_clear_handle_id (&priv->purge_displays_id, g_source_remove); + ++ g_clear_object (&priv->authority); ++ + G_OBJECT_CLASS (gdm_display_factory_parent_class)->finalize (object); + } +diff --git a/daemon/gdm-display-factory.h b/daemon/gdm-display-factory.h +index b17cb1c..afb2b0a 100644 +--- a/daemon/gdm-display-factory.h ++++ b/daemon/gdm-display-factory.h +@@ -53,6 +53,15 @@ gboolean gdm_display_factory_start (GdmDispl + gboolean gdm_display_factory_stop (GdmDisplayFactory *manager); + GdmDisplayStore * gdm_display_factory_get_display_store (GdmDisplayFactory *manager); + void gdm_display_factory_queue_purge_displays (GdmDisplayFactory *manager); ++gboolean gdm_display_factory_authorize_manage_user_displays (GdmDisplayFactory *factory, ++ GDBusMethodInvocation *invocation, ++ GError **error); ++gboolean gdm_display_factory_on_user_display_creation (GdmDisplayFactory *factory, ++ const char *user, ++ GError **error); ++gboolean gdm_display_factory_on_user_display_destruction (GdmDisplayFactory *factory, ++ const char *user, ++ GError **error); + + G_END_DECLS + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 7fd3db5..2d4f2c0 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -398,6 +398,7 @@ store_display (GdmLocalDisplayFactory *factory, + */ + static gboolean + gdm_local_display_factory_create_display (GdmLocalDisplayFactory *factory, ++ const char *autologin_user, + char **id, + GError **error) + { +@@ -454,6 +455,7 @@ gdm_local_display_factory_create_display (GdmLocalDisplayFactory *factory, + "seat-id", "seat0", + "allow-timed-login", FALSE, + "is-initial", is_initial, ++ "autologin-user", autologin_user, + NULL); + + store_display (factory, display); +@@ -1603,6 +1605,7 @@ handle_create_transient_display (GdmDBusLocalDisplayFactory *skeleton, + gboolean created; + + created = gdm_local_display_factory_create_display (factory, ++ NULL, + &id, + &error); + if (!created) { +@@ -1614,6 +1617,73 @@ handle_create_transient_display (GdmDBusLocalDisplayFactory *skeleton, + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + ++static gboolean ++handle_create_user_display (GdmDBusLocalDisplayFactory *skeleton, ++ GDBusMethodInvocation *invocation, ++ const char *user, ++ GdmLocalDisplayFactory *factory) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ if (!gdm_display_factory_on_user_display_creation (GDM_DISPLAY_FACTORY (factory), ++ user, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ if (!gdm_local_display_factory_create_display (factory, ++ user, ++ NULL, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ gdm_dbus_local_display_factory_complete_create_user_display (skeleton, ++ invocation); ++ ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++} ++ ++static gboolean ++handle_destroy_user_display (GdmDBusLocalDisplayFactory *skeleton, ++ GDBusMethodInvocation *invocation, ++ const char *user, ++ GdmLocalDisplayFactory *factory) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ if (!gdm_display_factory_on_user_display_destruction (GDM_DISPLAY_FACTORY (factory), ++ user, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ gdm_dbus_local_display_factory_complete_destroy_user_display (skeleton, ++ invocation); ++ ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++} ++ ++static gboolean ++on_authorize_method (GdmDBusLocalDisplayFactory *skeleton, ++ GDBusMethodInvocation *invocation, ++ GdmLocalDisplayFactory *factory) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ if (!gdm_display_factory_authorize_manage_user_displays (GDM_DISPLAY_FACTORY (factory), ++ invocation, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + register_factory (GdmLocalDisplayFactory *factory) + { +@@ -1634,6 +1704,20 @@ register_factory (GdmLocalDisplayFactory *factory) + G_CALLBACK (handle_create_transient_display), + factory); + ++ g_signal_connect (factory->skeleton, ++ "handle-create-user-display", ++ G_CALLBACK (handle_create_user_display), ++ factory); ++ ++ g_signal_connect (factory->skeleton, ++ "handle-destroy-user-display", ++ G_CALLBACK (handle_destroy_user_display), ++ factory); ++ ++ g_signal_connect (factory->skeleton, "g-authorize-method", ++ G_CALLBACK (on_authorize_method), ++ factory); ++ + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (factory->skeleton), + factory->connection, + GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH, +diff --git a/daemon/gdm-local-display-factory.xml b/daemon/gdm-local-display-factory.xml +index 515d991..2241bc4 100644 +--- a/daemon/gdm-local-display-factory.xml ++++ b/daemon/gdm-local-display-factory.xml +@@ -4,5 +4,11 @@ + + + ++ ++ ++ ++ ++ ++ + + +diff --git a/daemon/gdm-remote-display-factory.c b/daemon/gdm-remote-display-factory.c +index d3e2afe..7fb50ef 100644 +--- a/daemon/gdm-remote-display-factory.c ++++ b/daemon/gdm-remote-display-factory.c +@@ -20,6 +20,7 @@ + + #include "config.h" + ++#include "gdm-common.h" + #include "gdm-remote-display.h" + #include "gdm-remote-display-factory.h" + #include "gdm-remote-display-factory-glue.h" +@@ -49,6 +50,7 @@ G_DEFINE_TYPE (GdmRemoteDisplayFactory, gdm_remote_display_factory, GDM_TYPE_DIS + + static gboolean + gdm_remote_display_factory_create_display (GdmRemoteDisplayFactory *factory, ++ const char *autologin_user, + const char *remote_id) + { + g_autoptr (GdmDisplay) display = NULL; +@@ -58,6 +60,10 @@ gdm_remote_display_factory_create_display (GdmRemoteDisplayFactory *factory, + + display = gdm_remote_display_new (remote_id); + ++ g_object_set (display, ++ "autologin-user", autologin_user, ++ NULL); ++ + store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory)); + gdm_display_store_add (store, display); + +@@ -75,7 +81,7 @@ handle_create_remote_display (GdmDBusRemoteDisplayFactory *skeleton, + const char *remote_id, + GdmRemoteDisplayFactory *factory) + { +- if (!gdm_remote_display_factory_create_display (factory, remote_id)) ++ if (!gdm_remote_display_factory_create_display (factory, NULL, remote_id)) + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, +@@ -87,6 +93,74 @@ handle_create_remote_display (GdmDBusRemoteDisplayFactory *skeleton, + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + ++static gboolean ++handle_create_user_display (GdmDBusRemoteDisplayFactory *skeleton, ++ GDBusMethodInvocation *invocation, ++ const char *user, ++ GdmRemoteDisplayFactory *factory) ++{ ++ g_auto (GStrv) sessions = NULL; ++ g_autoptr (GError) error = NULL; ++ ++ if (!gdm_display_factory_on_user_display_creation (GDM_DISPLAY_FACTORY (factory), ++ user, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ if (!gdm_remote_display_factory_create_display (factory, user, NULL)) { ++ g_dbus_method_invocation_return_error (invocation, ++ G_DBUS_ERROR, ++ G_DBUS_ERROR_FAILED, ++ "Error creating remote display"); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ gdm_dbus_remote_display_factory_complete_create_user_display (factory->skeleton, ++ invocation); ++ ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++} ++ ++static gboolean ++handle_destroy_user_display (GdmDBusRemoteDisplayFactory *skeleton, ++ GDBusMethodInvocation *invocation, ++ const char *user, ++ GdmRemoteDisplayFactory *factory) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ if (!gdm_display_factory_on_user_display_destruction (GDM_DISPLAY_FACTORY (factory), ++ user, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++ } ++ ++ gdm_dbus_remote_display_factory_complete_destroy_user_display (skeleton, ++ invocation); ++ ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++} ++ ++static gboolean ++on_authorize_method (GdmDBusRemoteDisplayFactory *skeleton, ++ GDBusMethodInvocation *invocation, ++ GdmRemoteDisplayFactory *factory) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ if (!gdm_display_factory_authorize_manage_user_displays (GDM_DISPLAY_FACTORY (factory), ++ invocation, ++ &error)) { ++ g_dbus_method_invocation_return_gerror (invocation, error); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + register_factory (GdmRemoteDisplayFactory *factory) + { +@@ -106,6 +180,20 @@ register_factory (GdmRemoteDisplayFactory *factory) + G_CALLBACK (handle_create_remote_display), + factory); + ++ g_signal_connect (factory->skeleton, ++ "handle-create-user-display", ++ G_CALLBACK (handle_create_user_display), ++ factory); ++ ++ g_signal_connect (factory->skeleton, ++ "handle-destroy-user-display", ++ G_CALLBACK (handle_destroy_user_display), ++ factory); ++ ++ g_signal_connect (factory->skeleton, "g-authorize-method", ++ G_CALLBACK (on_authorize_method), ++ factory); ++ + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (factory->skeleton), + factory->connection, + GDM_REMOTE_DISPLAY_FACTORY_DBUS_PATH, +diff --git a/daemon/gdm-remote-display-factory.xml b/daemon/gdm-remote-display-factory.xml +index 84ce477..27cb9d1 100644 +--- a/daemon/gdm-remote-display-factory.xml ++++ b/daemon/gdm-remote-display-factory.xml +@@ -4,5 +4,11 @@ + + + ++ ++ ++ ++ ++ ++ + + +diff --git a/daemon/meson.build b/daemon/meson.build +index 31f9baa..39e93ad 100644 +--- a/daemon/meson.build ++++ b/daemon/meson.build +@@ -74,6 +74,7 @@ gdm_daemon_deps = [ + gio_unix_dep, + json_glib, + libpam_dep, ++ polkit_dep, + ] + if have_x11_support + gdm_daemon_deps += [ +diff --git a/data/meson.build b/data/meson.build +index 0653029..06b63e7 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -223,3 +223,17 @@ if get_option('gdm-xsession') and have_x11_support + install_dir: gdmconfdir, + ) + endif ++ ++# Polkit ++configure_file( ++ input: 'polkit-gdm.rules.in', ++ output: '20-gdm.rules', ++ configuration: { ++ 'GDM_USERNAME': get_option('user'), ++ }, ++ install_dir: get_option('datadir') / 'polkit-1' / 'rules.d', ++) ++install_data( ++ 'org.gnome.displaymanager.policy', ++ install_dir: get_option('datadir') / 'polkit-1' / 'actions', ++) +diff --git a/data/org.gnome.displaymanager.policy b/data/org.gnome.displaymanager.policy +new file mode 100644 +index 0000000..e65da39 +--- /dev/null ++++ b/data/org.gnome.displaymanager.policy +@@ -0,0 +1,20 @@ ++ ++ ++ ++ ++ The GNOME Project ++ http://www.gnome.org/ ++ ++ ++ Allow managing user sessions ++ Authentication is required to start or stop a user session ++ ++ auth_admin ++ auth_admin ++ auth_admin ++ ++ ++ ++ +diff --git a/data/polkit-gdm.rules.in b/data/polkit-gdm.rules.in +new file mode 100644 +index 0000000..c9ef490 +--- /dev/null ++++ b/data/polkit-gdm.rules.in +@@ -0,0 +1,8 @@ ++polkit.addRule(function(action, subject) { ++ if (subject.user !== "@GDM_USERNAME@") ++ return undefined; ++ ++ if (action.id == "org.gnome.displaymanager.displayfactory.manage-user-displays") { ++ return polkit.Result.YES; ++ } ++}); +diff --git a/meson.build b/meson.build +index 623ec15..f3896df 100644 +--- a/meson.build ++++ b/meson.build +@@ -69,6 +69,7 @@ json_glib = dependency('json-glib-1.0', version: '>= 1.2.0', + accountsservice_dep = dependency('accountsservice', version: '>= 0.6.35') + keyutils_dep = dependency('libkeyutils', required: false) + libselinux_dep = dependency('libselinux', required: get_option('selinux')) ++polkit_dep = dependency('polkit-gobject-1') + + # udev + if udev_dir == '' +-- +2.51.0 + +From 812416db531899b85c69626ad9664d849246a47b Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Thu, 15 May 2025 21:12:20 +0200 +Subject: [PATCH 7/8] Introduce gdm-new-session + +This daemon/tool allows starting a user session either locally or headlessly. + +It communicates with the GDM daemon to start/stop the sessions using +the dbus ifaces RemoteDisplayFactory and LocalDisplayFactory. + +It watches the GDM Displays dbus object manager to: + 1. Check when a new session is created to ensure it has + successfully started the session for the user. + 2. Check when the session is finalized to terminate itself. + +When it finds out it has successfully started the session, it stores the +session_id. This allows terminating the session in the case this is +terminated. + +Install also a service that allows starting a headless user session: +gnome-headless-session@.service + +This provides the same functionality than TigerVNC vncserver@.service: + vncserver@.service allows to start a headless Xvnc session for a user. + There's a conf file that maps a display number and how that display + session should be, its username... + It is started as a system service. e.g. vncserver@:1.service + +In the case of gnome-headless-session@.service, there's no conf file. +It is a system service that starts a headless gnome desktop session +for the user passed as the argument. e.g. gnome-headless-session@joan.service + +In order to get remote access, it is needed to: + 1. Configure gnome-remote-desktop for the user with grdctl --headless ... + 2. Enable gnome-remote-desktop-headless.service. + +Now the polkit rule to use the dbus methods for create/destroy user +displays is updated and only authorizes processes run from the unit +service gnome-headless-session@.service and for the user passed as the. +argument. + +Part-of: +--- + daemon/gdm-display-factory.c | 10 +- + data/gnome-headless-session@.service.in | 34 +++ + data/meson.build | 10 + + data/polkit-gdm.rules.in | 9 +- + utils/gdm-new-session.c | 293 ++++++++++++++++++++++++ + utils/meson.build | 15 ++ + 6 files changed, 369 insertions(+), 2 deletions(-) + create mode 100644 data/gnome-headless-session@.service.in + create mode 100644 utils/gdm-new-session.c + +diff --git a/daemon/gdm-display-factory.c b/daemon/gdm-display-factory.c +index 640db4a..ca05f96 100644 +--- a/daemon/gdm-display-factory.c ++++ b/daemon/gdm-display-factory.c +@@ -173,10 +173,13 @@ gdm_display_factory_authorize_manage_user_displays (GdmDisplayFactory *fact + { + g_autoptr (PolkitAuthorizationResult) result = NULL; + g_autoptr (PolkitSubject) subject = NULL; ++ g_autoptr (PolkitDetails) details = NULL; + g_autoptr (GError) local_error = NULL; ++ const char *user = NULL; + const char *action; + const char *method; + const char *sender; ++ GVariant *parameters; + PolkitCheckAuthorizationFlags flags; + GdmDisplayFactoryPrivate *priv; + +@@ -196,13 +199,18 @@ gdm_display_factory_authorize_manage_user_displays (GdmDisplayFactory *fact + + priv = gdm_display_factory_get_instance_private (factory); + ++ parameters = g_dbus_method_invocation_get_parameters (invocation); ++ g_variant_get (parameters, "(s)", &user); ++ details = polkit_details_new (); ++ polkit_details_insert (details, "user", user); ++ + sender = g_dbus_method_invocation_get_sender (invocation); + subject = polkit_system_bus_name_new (sender); + action = GDM_DISPLAY_FACTORY_MANAGE_DISPLAYS_POLKIT_ACTION; + flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION; + result = polkit_authority_check_authorization_sync (priv->authority, + subject, action, +- NULL, flags, NULL, ++ details, flags, NULL, + &local_error); + if (!result) { + g_set_error (error, GDM_DISPLAY_ERROR, GDM_DISPLAY_ERROR_GENERAL, +diff --git a/data/gnome-headless-session@.service.in b/data/gnome-headless-session@.service.in +new file mode 100644 +index 0000000..77d7f6f +--- /dev/null ++++ b/data/gnome-headless-session@.service.in +@@ -0,0 +1,34 @@ ++[Unit] ++Description=GNOME Headless Desktop Session ++Requires=gdm.service ++After=gdm.service ++ ++[Service] ++User=@GDM_USERNAME@ ++ExecStart=@libexecdir@/gdm-new-session %i --headless ++Restart=on-failure ++ ++LockPersonality=yes ++MemoryDenyWriteExecute=yes ++RestrictNamespaces=yes ++RestrictAddressFamilies=AF_UNIX ++RestrictRealtime=yes ++RestrictSUIDSGID=yes ++SystemCallArchitectures=native ++CapabilityBoundingSet= ++ProtectKernelModules=yes ++ProtectKernelLogs=yes ++ProtectKernelTunables=yes ++ProtectClock=yes ++ProtectHostname=yes ++ProtectControlGroups=yes ++ProtectHostname=yes ++ProtectProc=invisible ++PrivateDevices=yes ++PrivateNetwork=yes ++NoNewPrivileges=yes ++SystemCallArchitectures=native ++SystemCallFilter=@system-service ++ ++[Install] ++WantedBy=graphical.target +diff --git a/data/meson.build b/data/meson.build +index 06b63e7..af8bcf7 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -192,6 +192,16 @@ if systemdsystemunitdir != 'no' + install_dir: systemd_systemunitdir, + format: 'cmake' + ) ++ ++ configure_file( ++ input: 'gnome-headless-session@.service.in', ++ output: '@BASENAME@', ++ configuration: { ++ 'GDM_USERNAME': get_option('user'), ++ 'libexecdir': gdm_prefix / get_option('libexecdir'), ++ }, ++ install_dir: systemd_systemunitdir, ++ ) + endif + + gdm_gnome_session_wanted_targets = [] +diff --git a/data/polkit-gdm.rules.in b/data/polkit-gdm.rules.in +index c9ef490..0e2327d 100644 +--- a/data/polkit-gdm.rules.in ++++ b/data/polkit-gdm.rules.in +@@ -3,6 +3,13 @@ polkit.addRule(function(action, subject) { + return undefined; + + if (action.id == "org.gnome.displaymanager.displayfactory.manage-user-displays") { +- return polkit.Result.YES; ++ const user = action.lookup("user"); ++ const system_unit = subject.system_unit; ++ ++ if (user && system_unit) { ++ const match = system_unit.match(/^gnome-headless-session@(.+?)\.service$/); ++ if (match && user == match[1]) ++ return polkit.Result.YES; ++ } + } + }); +diff --git a/utils/gdm-new-session.c b/utils/gdm-new-session.c +new file mode 100644 +index 0000000..898ca51 +--- /dev/null ++++ b/utils/gdm-new-session.c +@@ -0,0 +1,293 @@ ++/* ++ * SPDX-FileCopyrightText: 2025 Joan Torres Lopez ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++ ++#define GDM_DBUS_NAME "org.gnome.DisplayManager" ++#define GDM_LOCAL_DISPLAY_FACTORY_PATH "/org/gnome/DisplayManager/LocalDisplayFactory" ++#define GDM_REMOTE_DISPLAY_FACTORY_PATH "/org/gnome/DisplayManager/RemoteDisplayFactory" ++#define GDM_DISPLAYS_PATH "/org/gnome/DisplayManager/Displays" ++#define GDM_LOCAL_DISPLAY_FACTORY_INTERFACE "org.gnome.DisplayManager.LocalDisplayFactory" ++#define GDM_REMOTE_DISPLAY_FACTORY_INTERFACE "org.gnome.DisplayManager.RemoteDisplayFactory" ++#define GDM_DISPLAY_INTERFACE "org.gnome.DisplayManager.Display" ++ ++typedef struct _State { ++ GMainLoop *event_loop; ++ ++ GDBusConnection *connection; ++ GDBusObjectManager *displays; ++ ++ char *username; ++ gboolean headless; ++ ++ char *display_path; ++ ++ gboolean terminate_requested; ++} State; ++ ++static gboolean terminate_session (State *state, ++ GError **error); ++ ++static void ++state_free (State *state) ++{ ++ g_clear_pointer (&state->event_loop, g_main_loop_unref); ++ g_clear_object (&state->connection); ++ g_clear_object (&state->displays); ++ g_clear_pointer (&state->username, g_free); ++ g_clear_pointer (&state->display_path, g_free); ++ g_free (state); ++} ++ ++G_DEFINE_AUTOPTR_CLEANUP_FUNC (State, state_free); ++ ++static void ++on_display_added (GDBusObjectManager *displays, ++ GDBusObject *display_object, ++ State *state) ++{ ++ g_autoptr (GDBusInterface) display_proxy = NULL; ++ g_autoptr (GVariant) reply = NULL; ++ g_autoptr (GError) error = NULL; ++ g_autofree char *session_id = NULL; ++ g_autofree char *username = NULL; ++ ++ display_proxy = g_dbus_object_get_interface (display_object, GDM_DISPLAY_INTERFACE); ++ if (display_proxy == NULL) { ++ g_warning ("GDM exported a non Display object on dbus"); ++ return; ++ } ++ ++ reply = g_dbus_proxy_call_sync (G_DBUS_PROXY (display_proxy), ++ "GetSessionId", ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, ++ NULL, ++ &error); ++ if (reply == NULL) { ++ g_warning ("Failed getting SessionId: %s", error->message); ++ return; ++ } ++ ++ g_variant_get (reply, "(s)", &session_id); ++ ++ if (sd_session_get_username (session_id, &username) < 0) ++ return; ++ ++ if (g_strcmp0 (state->username, username) == 0) { ++ g_print ("Session started\n"); ++ ++ g_set_str (&state->display_path, g_dbus_object_get_object_path (display_object)); ++ ++ g_signal_handlers_disconnect_by_func (displays, ++ G_CALLBACK (on_display_added), ++ state); ++ ++ if (state->terminate_requested) { ++ if (!terminate_session (state, &error)) ++ g_warning ("Terminating session failed: %s", error->message); ++ } ++ } ++} ++static void ++on_display_removed (GDBusObjectManager *displays, ++ GDBusObject *display_object, ++ State *state) ++{ ++ const char *display_path = g_dbus_object_get_object_path (display_object); ++ ++ if (g_strcmp0 (state->display_path, display_path) == 0) { ++ g_print ("Session finished\n"); ++ ++ g_set_str (&state->display_path, NULL); ++ g_main_loop_quit (state->event_loop); ++ } ++} ++ ++static gboolean ++watch_displays (State *state, ++ GError **error) ++{ ++ g_autoptr (GDBusObjectManager) displays = NULL; ++ ++ displays = g_dbus_object_manager_client_new_sync (state->connection, ++ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, ++ GDM_DBUS_NAME, ++ GDM_DISPLAYS_PATH, ++ NULL, ++ NULL, ++ NULL, ++ NULL, ++ error); ++ if (displays == NULL) { ++ g_dbus_error_strip_remote_error (*error); ++ return FALSE; ++ } ++ ++ g_signal_connect (displays, ++ "object-added", ++ G_CALLBACK (on_display_added), ++ state); ++ ++ g_signal_connect (displays, ++ "object-removed", ++ G_CALLBACK (on_display_removed), ++ state); ++ ++ state->displays = g_steal_pointer (&displays); ++ ++ return TRUE; ++} ++ ++static gboolean ++setup (State *state, ++ GError **error) ++{ ++ g_autoptr (GDBusConnection) connection = NULL; ++ ++ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); ++ if (connection == NULL) { ++ g_dbus_error_strip_remote_error (*error); ++ return FALSE; ++ } ++ ++ state->connection = g_steal_pointer (&connection); ++ ++ if (!watch_displays (state, error)) ++ return FALSE; ++ ++ return TRUE; ++} ++ ++static gboolean ++call_gdm_display_factory_method (State *state, ++ const char *method, ++ GError **error) ++{ ++ GDBusConnection *connection = state->connection; ++ gboolean headless = state->headless; ++ g_autoptr (GVariant) reply = NULL; ++ const char *dbus_path; ++ const char *dbus_iface; ++ ++ if (headless) { ++ dbus_path = GDM_REMOTE_DISPLAY_FACTORY_PATH; ++ dbus_iface = GDM_REMOTE_DISPLAY_FACTORY_INTERFACE; ++ } else { ++ dbus_path = GDM_LOCAL_DISPLAY_FACTORY_PATH; ++ dbus_iface = GDM_LOCAL_DISPLAY_FACTORY_INTERFACE; ++ } ++ ++ reply = g_dbus_connection_call_sync (connection, ++ GDM_DBUS_NAME, ++ dbus_path, ++ dbus_iface, ++ method, ++ g_variant_new ("(s)", state->username), ++ NULL, ++ G_DBUS_CALL_FLAGS_NONE, ++ -1, NULL, error); ++ if (reply == NULL) { ++ g_dbus_error_strip_remote_error (*error); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static gboolean ++create_session (State *state, ++ GError **error) ++{ ++ return call_gdm_display_factory_method (state, ++ "CreateUserDisplay", ++ error); ++} ++ ++static gboolean ++terminate_session (State *state, ++ GError **error) ++{ ++ return call_gdm_display_factory_method (state, ++ "DestroyUserDisplay", ++ error); ++} ++ ++static gboolean ++on_sigterm (State *state) ++{ ++ g_autoptr (GError) error = NULL; ++ ++ // Got SIGTERM before the session is created, ++ // wait for it to be created and then terminate it ++ if (state->display_path == NULL) { ++ state->terminate_requested = TRUE; ++ return G_SOURCE_REMOVE; ++ } ++ ++ if (!terminate_session (state, &error)) ++ g_warning ("Terminating session failed: %s", error->message); ++ ++ return G_SOURCE_REMOVE; ++} ++ ++int ++main (int argc, ++ char *argv[]) ++{ ++ g_autoptr (GOptionContext) context = NULL; ++ g_autoptr (State) state = NULL; ++ g_autoptr (GError) error = NULL; ++ g_autofree char *username = NULL; ++ gboolean headless = FALSE; ++ ++ GOptionEntry entries[] = ++ { ++ { "headless", 'h', 0, G_OPTION_ARG_NONE, &headless, "Run headless session", NULL }, ++ { NULL } ++ }; ++ ++ context = g_option_context_new ("USER - Run a graphical session as a specified user"); ++ g_option_context_add_main_entries (context, entries, NULL); ++ ++ if (!g_option_context_parse (context, &argc, &argv, &error)) { ++ g_printerr ("Option parsing failed: %s\n", error->message); ++ return EX_USAGE; ++ } ++ ++ if (argc < 2) { ++ g_printerr ("Username is required\n"); ++ return EX_USAGE; ++ } ++ ++ username = g_strdup (argv[1]); ++ ++ state = g_new0 (State, 1); ++ state->username = g_steal_pointer (&username); ++ state->event_loop = g_main_loop_new (NULL, FALSE); ++ state->headless = headless; ++ ++ if (!setup (state, &error)) { ++ g_printerr ("Failed: %s\n", error->message); ++ return EX_SOFTWARE; ++ } ++ ++ if (!create_session (state, &error)) { ++ g_printerr ("Starting session failed: %s\n", error->message); ++ return EX_SOFTWARE; ++ } ++ ++ g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state); ++ ++ g_main_loop_run (state->event_loop); ++ ++ return EX_OK; ++} +diff --git a/utils/meson.build b/utils/meson.build +index 2cfbd14..a873b9f 100644 +--- a/utils/meson.build ++++ b/utils/meson.build +@@ -35,6 +35,21 @@ gdm_auth_config = executable('gdm-config', + install: true, + ) + ++# gdm-new-session ++gdm_new_session_deps = [ ++ glib_dep, ++ gio_dep, ++ logind_dep, ++] ++ ++gdm_new_session = executable('gdm-new-session', ++ 'gdm-new-session.c', ++ dependencies: gdm_new_session_deps, ++ include_directories: config_h_dir, ++ install: true, ++ install_dir: get_option('libexecdir'), ++) ++ + auth_config_distro_hooks = [ + 'redhat', + 'generic', +-- +2.51.0 + +From d383da48f4ea9a228f21e4da84a50f1be6349153 Mon Sep 17 00:00:00 2001 +From: Joan Torres +Date: Sun, 18 May 2025 20:52:35 +0200 +Subject: [PATCH 8/8] remote-display: Add SetRemoteId method + +Now accessing these created headless user sessions has two different ways: + 1. Directly connecting to the gnome-remote-desktop daemon headless + which is connected to that user session, as explained in the + previous commit. + 2. Through the remote login. i.e. connecting to the gnome-remote-desktop + system daemon that provides a headless login screen and after + authenticating, get that headless user session. + +To make the option 2 work, the system daemon needs to know the remote_id +of the headless display to be able to do the handover from the login +headless display to the user headless display. + +This allows the system daemon generate a valid remote_id and assign it to +this new headless display. + +Part-of: +--- + daemon/gdm-remote-display.c | 18 ++++++++++++++++++ + daemon/gdm-remote-display.xml | 3 +++ + 2 files changed, 21 insertions(+) + +diff --git a/daemon/gdm-remote-display.c b/daemon/gdm-remote-display.c +index 83954620f..0d243ff20 100644 +--- a/daemon/gdm-remote-display.c ++++ b/daemon/gdm-remote-display.c +@@ -60,6 +60,19 @@ gdm_remote_display_set_remote_id (GdmRemoteDisplay *display, + g_object_set (G_OBJECT (display->skeleton), "remote-id", remote_id, NULL); + } + ++static gboolean ++handle_set_remote_id (GdmDBusRemoteDisplay *skeleton, ++ GDBusMethodInvocation *invocation, ++ const char *remote_id, ++ GdmRemoteDisplay *display) ++{ ++ g_object_set (G_OBJECT (display->skeleton), "remote-id", remote_id, NULL); ++ ++ gdm_dbus_remote_display_complete_set_remote_id (skeleton, invocation); ++ ++ return G_DBUS_METHOD_INVOCATION_HANDLED; ++} ++ + static GObject * + gdm_remote_display_constructor (GType type, + guint n_construct_properties, +@@ -78,6 +91,11 @@ gdm_remote_display_constructor (GType type, + + g_object_bind_property (display, "session-id", display->skeleton, "session-id", G_BINDING_SYNC_CREATE); + ++ g_signal_connect (display->skeleton, ++ "handle-set-remote-id", ++ G_CALLBACK (handle_set_remote_id), ++ display); ++ + return G_OBJECT (display); + } + +diff --git a/daemon/gdm-remote-display.xml b/daemon/gdm-remote-display.xml +index 2fdf9ad7f..4d9542f24 100644 +--- a/daemon/gdm-remote-display.xml ++++ b/daemon/gdm-remote-display.xml +@@ -3,5 +3,8 @@ + + + ++ ++ ++ + + +-- +2.51.0 + diff --git a/0001-Revert-hack-that-quits-plymouth-late.patch b/0001-Revert-hack-that-quits-plymouth-late.patch new file mode 100644 index 0000000..a10b981 --- /dev/null +++ b/0001-Revert-hack-that-quits-plymouth-late.patch @@ -0,0 +1,365 @@ +From 8a91856f4020657adcbba67482daa6db373e8ed6 Mon Sep 17 00:00:00 2001 +From: Adrian Vovk +Date: Thu, 1 May 2025 15:42:49 -0400 +Subject: [PATCH 1/2] Revert hack that quits plymouth late + +Reverts 2cbd7ad1f66d0a757c1d2217705436aa1beca76a + +Fixes #375 + +Part-of: +--- + daemon/gdm-manager.c | 27 ++++++++------------------- + 1 file changed, 8 insertions(+), 19 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index e455dad36..0913f7a50 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -175,7 +175,7 @@ plymouth_prepare_for_transition (void) + } + } + +-static gboolean ++static void + plymouth_quit_with_transition (void) + { + gboolean res; +@@ -187,8 +187,6 @@ plymouth_quit_with_transition (void) + g_warning ("Could not quit plymouth: %s", error->message); + g_error_free (error); + } +- +- return G_SOURCE_REMOVE; + } + + static void +@@ -1506,6 +1504,13 @@ on_display_status_changed (GdmDisplay *display, + if (g_strcmp0 (session_class, "greeter") == 0) + set_up_session (manager, display); + } ++ ++#ifdef WITH_PLYMOUTH ++ if (status == GDM_DISPLAY_MANAGED && quit_plymouth) { ++ plymouth_quit_with_transition (); ++ manager->plymouth_is_running = FALSE; ++ } ++#endif + break; + case GDM_DISPLAY_FAILED: + case GDM_DISPLAY_UNMANAGED: +@@ -1892,15 +1897,6 @@ on_user_session_started (GdmSession *session, + { + g_debug ("GdmManager: session started %d", pid); + add_session_record (manager, session, pid, SESSION_RECORD_LOGIN); +- +-#ifdef WITH_PLYMOUTH +- if (g_strcmp0 (service_name, "gdm-autologin") == 0) { +- if (manager->plymouth_is_running) { +- g_timeout_add_seconds (20, (GSourceFunc) plymouth_quit_with_transition, NULL); +- manager->plymouth_is_running = FALSE; +- } +- } +-#endif + } + + static void +@@ -2124,13 +2120,6 @@ on_session_client_connected (GdmSession *session, + return; + } + +-#ifdef WITH_PLYMOUTH +- if (manager->plymouth_is_running) { +- plymouth_quit_with_transition (); +- manager->plymouth_is_running = FALSE; +- } +-#endif +- + g_object_get (G_OBJECT (display), "allow-timed-login", &allow_timed_login, NULL); + + if (!allow_timed_login) { +-- +2.51.0 + + +From deeb4b8aba46e37a1f6dcb85252ed713183cb170 Mon Sep 17 00:00:00 2001 +From: Adrian Vovk +Date: Thu, 1 May 2025 16:13:53 -0400 +Subject: [PATCH 2/2] manager: Combine register display with register session + +Before we'd always register the display as soon as the session was +started. However, this is too early for Wayland! The compositor might +not have completely initialized its Wayland connection yet, so the +display isn't really open. + +The "register display" should happen when the compositor is up and +running. This occurs when it calls RegisterSession, so it makes sense +to combine both. + +This helps on terminating plymouthd when the new session has been +started, and also kill background greeters. + +Part-of: +--- + daemon/gdm-local-display-factory.c | 30 ++++--------------- + daemon/gdm-manager.c | 47 ++++++------------------------ + daemon/gdm-manager.xml | 3 -- + daemon/gdm-wayland-session.c | 31 -------------------- + daemon/gdm-x-session.c | 32 -------------------- + 5 files changed, 15 insertions(+), 128 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 2d4f2c0..63117e1 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -515,25 +515,6 @@ on_finish_waiting_for_seat0_displays_timeout (GdmLocalDisplayFactory *factory) + return G_SOURCE_REMOVE; + } + +-static void +-on_session_registered_cb (GObject *gobject, +- GParamSpec *pspec, +- gpointer user_data) +-{ +- GdmDisplay *display = GDM_DISPLAY (gobject); +- GdmLocalDisplayFactory *factory = GDM_LOCAL_DISPLAY_FACTORY (user_data); +- gboolean registered; +- +- g_object_get (display, "session-registered", ®istered, NULL); +- +- if (!registered) +- return; +- +- g_debug ("GdmLocalDisplayFactory: session registered on display, looking for any background displays to kill"); +- +- finish_waiting_displays_on_seat (factory, "seat0"); +-} +- + static void + on_display_status_changed (GdmDisplay *display, + GParamSpec *arg1, +@@ -548,6 +529,7 @@ on_display_status_changed (GdmDisplay *display, + char *session_id = NULL; + gboolean is_initial = TRUE; + gboolean is_local = TRUE; ++ gboolean registered = FALSE; + + + if (!factory->is_started) +@@ -618,11 +600,11 @@ on_display_status_changed (GdmDisplay *display, + break; + case GDM_DISPLAY_MANAGED: + #if defined(ENABLE_USER_DISPLAY_SERVER) +- g_signal_connect_object (display, +- "notify::session-registered", +- G_CALLBACK (on_session_registered_cb), +- factory, +- 0); ++ g_object_get (display, "session-registered", ®istered, NULL); ++ if (registered) { ++ g_debug ("GdmLocalDisplayFactory: session registered on display, looking for any background displays to kill"); ++ finish_waiting_displays_on_seat (factory, "seat0"); ++ } + #endif + break; + case GDM_DISPLAY_WAITING_TO_FINISH: +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index a99e6b5..ff04203 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -732,7 +732,7 @@ find_user_session_for_display (GdmManager *self, + } + + static gboolean +-gdm_manager_handle_register_display (GdmDBusManager *manager, ++gdm_manager_handle_register_session (GdmDBusManager *manager, + GDBusMethodInvocation *invocation, + GVariant *details) + { +@@ -744,15 +744,15 @@ gdm_manager_handle_register_display (GdmDBusManager *manager, + GVariantIter iter; + char *key = NULL; + char *value = NULL; +- char *x11_display_name = NULL; +- char *tty = NULL; +- +- g_debug ("GdmManager: trying to register new display"); ++ g_autofree char *x11_display_name = NULL; ++ g_autofree char *tty = NULL; + + sender = g_dbus_method_invocation_get_sender (invocation); + connection = g_dbus_method_invocation_get_connection (invocation); + get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, &tty, NULL, NULL, NULL, NULL); + ++ g_debug ("GdmManager: trying to register new session on display %p", display); ++ + if (display == NULL) { + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, +@@ -792,38 +792,10 @@ gdm_manager_handle_register_display (GdmDBusManager *manager, + } + } + +- g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_MANAGED, NULL); +- +- gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager), +- invocation); +- +- g_clear_pointer (&x11_display_name, g_free); +- g_clear_pointer (&tty, g_free); +- return TRUE; +-} +- +-static gboolean +-gdm_manager_handle_register_session (GdmDBusManager *manager, +- GDBusMethodInvocation *invocation, +- GVariant *details) +-{ +- GdmManager *self = GDM_MANAGER (manager); +- GdmDisplay *display = NULL; +- const char *sender; +- GDBusConnection *connection; +- +- sender = g_dbus_method_invocation_get_sender (invocation); +- connection = g_dbus_method_invocation_get_connection (invocation); +- +- get_display_and_details_for_bus_sender (self, connection, sender, &display, +- NULL, NULL, NULL, NULL, NULL, NULL, NULL); +- +- g_debug ("GdmManager: trying to register new session on display %p", display); +- +- if (display != NULL) +- g_object_set (G_OBJECT (display), "session-registered", TRUE, NULL); +- else +- g_debug ("GdmManager: No display, not registering"); ++ g_object_set (G_OBJECT (display), ++ "status", GDM_DISPLAY_MANAGED, ++ "session-registered", TRUE, ++ NULL); + + gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager), + invocation); +@@ -1242,7 +1214,6 @@ gdm_manager_handle_open_reauthentication_channel (GdmDBusManager *manager + static void + manager_interface_init (GdmDBusManagerIface *interface) + { +- interface->handle_register_display = gdm_manager_handle_register_display; + interface->handle_register_session = gdm_manager_handle_register_session; + interface->handle_open_session = gdm_manager_handle_open_session; + interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel; +diff --git a/daemon/gdm-manager.xml b/daemon/gdm-manager.xml +index 92ef1d0..aba079a 100644 +--- a/daemon/gdm-manager.xml ++++ b/daemon/gdm-manager.xml +@@ -1,9 +1,6 @@ + + + +- +- +- + + + +diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c +index d0404d2..d4d1edd 100644 +--- a/daemon/gdm-wayland-session.c ++++ b/daemon/gdm-wayland-session.c +@@ -404,29 +404,6 @@ wait_on_subprocesses (State *state) + } + } + +-static gboolean +-register_display (State *state, +- GCancellable *cancellable) +-{ +- GError *error = NULL; +- gboolean registered = FALSE; +- GVariantBuilder details; +- +- g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); +- g_variant_builder_add (&details, "{ss}", "session-type", "wayland"); +- +- registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy, +- g_variant_builder_end (&details), +- cancellable, +- &error); +- if (error != NULL) { +- g_debug ("Could not register display: %s", error->message); +- g_error_free (error); +- } +- +- return registered; +-} +- + static void + init_state (State **state) + { +@@ -584,14 +561,6 @@ main (int argc, + if (!connect_to_display_manager (state)) + goto out; + +- ret = register_display (state, state->cancellable); +- +- if (!ret) { +- g_printerr ("Unable to register display with display manager\n"); +- exit_status = EX_SOFTWARE; +- goto out; +- } +- + if (register_session) { + g_debug ("gdm-wayland-session: Will register session in %d seconds", REGISTER_SESSION_TIMEOUT); + state->register_session_id = g_timeout_add_seconds (REGISTER_SESSION_TIMEOUT, +diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c +index 0b07ab5..36b3975 100644 +--- a/daemon/gdm-x-session.c ++++ b/daemon/gdm-x-session.c +@@ -757,30 +757,6 @@ wait_on_subprocesses (State *state) + } + } + +-static gboolean +-register_display (State *state, +- GCancellable *cancellable) +-{ +- GError *error = NULL; +- gboolean registered = FALSE; +- GVariantBuilder details; +- +- g_variant_builder_init (&details, G_VARIANT_TYPE ("a{ss}")); +- g_variant_builder_add (&details, "{ss}", "session-type", "x11"); +- g_variant_builder_add (&details, "{ss}", "x11-display-name", state->display_name); +- +- registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy, +- g_variant_builder_end (&details), +- cancellable, +- &error); +- if (error != NULL) { +- g_debug ("Could not register display: %s", error->message); +- g_error_free (error); +- } +- +- return registered; +-} +- + static void + init_state (State **state) + { +@@ -953,14 +929,6 @@ main (int argc, + if (!connect_to_display_manager (state)) + goto out; + +- ret = register_display (state, state->cancellable); +- +- if (!ret) { +- g_printerr ("Unable to register display with display manager\n"); +- exit_status = EX_SOFTWARE; +- goto out; +- } +- + ret = spawn_session (state, run_script, state->cancellable); + + if (!ret) { +-- +2.51.0 + diff --git a/0001-data-Add-support-for-unified-authentication.patch b/0001-data-Add-support-for-unified-authentication.patch new file mode 100644 index 0000000..e9d1c4e --- /dev/null +++ b/0001-data-Add-support-for-unified-authentication.patch @@ -0,0 +1,165 @@ +From 68976aadfb6c311196012439d97094c8244cdc49 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 18 Sep 2025 16:42:37 +0200 +Subject: [PATCH 1/3] session: Log JSON request when GDM_DEBUG_JSON_REQUESTS is + set + +This is only useful for debugging and testing. +--- + daemon/gdm-session.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c +index 388b0d037..9affbc438 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -846,8 +846,9 @@ gdm_session_handle_custom_json_request (GdmDBusWorkerManager *worker_manager_in + if (conversation != NULL) { + set_pending_query (conversation, invocation); + +- g_debug ("GdmSession: emitting custom JSON request '%s' v%u", +- protocol, version); ++ if (g_getenv ("GDM_DEBUG_JSON_REQUESTS") != NULL) ++ g_message ("GdmSession: emitting custom JSON request '%s' v%u: %s", ++ protocol, version, request); + gdm_dbus_user_verifier_custom_json_emit_request (custom_json_interface, + service_name, + protocol, +-- +2.51.1 + + +From bb975dec28884e371a5a54ae524315b8b7a7ea13 Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Mon, 22 Jan 2024 09:40:39 -0500 +Subject: [PATCH 2/3] data: Add support for unified authentication + +At the moment, every authentication mechanism gets its own +separate PAM conversation. + +Some PAM modules, like pam_sss, support more than one way +to authenticate the user. + +Rather than starting several conversations, one for each +mechanism, this commit adds a new "unified" authentication +setting. +--- + data/meson.build | 1 + + data/org.gnome.login-screen.gschema.xml | 30 +++++++++++++++++++++++++ + data/pam-redhat/gdm-switchable-auth.pam | 18 +++++++++++++++ + 3 files changed, 49 insertions(+) + create mode 100644 data/pam-redhat/gdm-switchable-auth.pam + +diff --git a/data/meson.build b/data/meson.build +index e82ce7ac1..2cbbf83e4 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -95,6 +95,7 @@ pam_data_files_map = { + 'gdm-fingerprint', + 'gdm-smartcard', + 'gdm-password', ++ 'gdm-switchable-auth', + ], + 'openembedded': [ + 'gdm-autologin', +diff --git a/data/org.gnome.login-screen.gschema.xml b/data/org.gnome.login-screen.gschema.xml +index 5a547e9b8..cf6b03820 100644 +--- a/data/org.gnome.login-screen.gschema.xml ++++ b/data/org.gnome.login-screen.gschema.xml +@@ -6,6 +6,36 @@ + + + ++ ++ true ++ ++ Whether or not to allow switchable authentication for login ++ ++ ++ The login screen can optionally allow a single PAM service to provide ++ multiple authentication mechanisms via a GDM PAM. ++ ++ ++ ++ true ++ ++ Whether or not to allow authentication via external web site ++ ++ ++ The login screen can optionally allow users to authenticate via ++ web login. ++ ++ ++ ++ true ++ ++ Whether or not to allow authentication using a passkey ++ ++ ++ The login screen can optionally allow users who have passkeys to log ++ in using those passkeys. ++ ++ + + true + +diff --git a/data/pam-redhat/gdm-switchable-auth.pam b/data/pam-redhat/gdm-switchable-auth.pam +new file mode 100644 +index 000000000..6648c3cec +--- /dev/null ++++ b/data/pam-redhat/gdm-switchable-auth.pam +@@ -0,0 +1,18 @@ ++auth substack switchable-auth ++auth optional pam_gnome_keyring.so ++auth include postlogin ++ ++account required pam_nologin.so ++account include switchable-auth ++ ++password substack switchable-auth ++-password optional pam_gnome_keyring.so use_authtok ++ ++session required pam_selinux.so close ++session required pam_loginuid.so ++session required pam_selinux.so open ++session optional pam_keyinit.so force revoke ++session required pam_namespace.so ++session include switchable-auth ++session optional pam_gnome_keyring.so auto_start ++session include postlogin +-- +2.51.1 + +From c18c8201c4d2af24b7aaf2168a3428fea542c733 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Thu, 12 Feb 2026 18:12:01 +0100 +Subject: [PATCH 3/3] session: Use g_once for GDM_DEBUG_JSON_REQUESTS environment + variable check + +Part-of: +--- + daemon/gdm-session.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c +index 5f103eddb..5c04ad1be 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -832,7 +832,13 @@ gdm_session_handle_custom_json_request (GdmDBusWorkerManager *worker_manager_in + if (conversation != NULL) { + set_pending_query (conversation, invocation); + +- if (g_getenv ("GDM_DEBUG_JSON_REQUESTS") != NULL) ++ static gsize debug_json_requests; ++ ++ if (g_once_init_enter (&debug_json_requests)) ++ g_once_init_leave (&debug_json_requests, ++ g_getenv ("GDM_DEBUG_JSON_REQUESTS") != NULL ? 1 : 2); ++ ++ if (debug_json_requests == 1) + g_message ("GdmSession: emitting custom JSON request '%s' v%u: %s", + protocol, version, request); + gdm_dbus_user_verifier_custom_json_emit_request (custom_json_interface, +-- +2.52.0 + diff --git a/0001-headless-session-Fix-autostarting-on-boot.patch b/0001-headless-session-Fix-autostarting-on-boot.patch deleted file mode 100644 index 115994a..0000000 --- a/0001-headless-session-Fix-autostarting-on-boot.patch +++ /dev/null @@ -1,29 +0,0 @@ -From e938a72b8ee65b7db2ad76f63dc3f77713871a82 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Jonas=20=C3=85dahl?= -Date: Thu, 3 Jul 2025 12:09:40 +0200 -Subject: [PATCH] headless-session: Fix autostarting on boot - -Make it wanted by graphical.target, and make sure it launches after gdm. ---- - data/gnome-headless-session@.service | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/data/gnome-headless-session@.service b/data/gnome-headless-session@.service -index 269d16288..cba7526f8 100644 ---- a/data/gnome-headless-session@.service -+++ b/data/gnome-headless-session@.service -@@ -1,6 +1,10 @@ - [Unit] - Description=Headless desktop session -+After=multi-user.target rescue.service rescue.target display-manager.service - - [Service] - ExecStart=/usr/libexec/gdm-headless-login-session --user=%i - Restart=on-failure -+ -+[Install] -+WantedBy=graphical.target --- -2.49.0 - - diff --git a/0001-local-display-factory-look-for-boot_display-sysfs-at.patch b/0001-local-display-factory-look-for-boot_display-sysfs-at.patch new file mode 100644 index 0000000..1dec403 --- /dev/null +++ b/0001-local-display-factory-look-for-boot_display-sysfs-at.patch @@ -0,0 +1,53 @@ +From 85982d61790dbbf940c12ad6e747bb1ada0b1355 Mon Sep 17 00:00:00 2001 +From: ceroma <165659-ceroma@users.noreply.gitlab.gnome.org> +Date: Thu, 18 Dec 2025 06:00:26 +0000 +Subject: [PATCH] local-display-factory: look for 'boot_display' sysfs attr + +Part-of: +--- + daemon/gdm-local-display-factory.c | 23 +++++++++++++++++++++-- + 1 file changed, 21 insertions(+), 2 deletions(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 63117e1..23344fa 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -716,6 +716,7 @@ udev_is_settled (GdmLocalDisplayFactory *factory) + const gchar *id_path = g_udev_device_get_property (device, "ID_PATH"); + g_autoptr (GUdevDevice) platform_device = NULL; + g_autoptr (GUdevDevice) pci_device = NULL; ++ g_autoptr (GUdevDevice) drm_device = NULL; + + if (g_str_has_prefix (id_path, "platform-simple-framebuffer")) { + node = next_node; +@@ -743,10 +744,28 @@ udev_is_settled (GdmLocalDisplayFactory *factory) + factory->seat0_has_boot_up_graphics = TRUE; + is_settled = TRUE; + break; +- } else { +- g_debug ("GdmLocalDisplayFactory: Found secondary PCI graphics adapter, not proceeding yet."); + } + } ++ ++ drm_device = g_udev_device_get_parent_with_subsystem (device, "drm", NULL); ++ ++ if (drm_device != NULL) { ++ gboolean boot_display; ++ ++ boot_display = g_udev_device_get_sysfs_attr_as_int (drm_device, "boot_display"); ++ ++ if (boot_display == 1) { ++ g_debug ("GdmLocalDisplayFactory: Found primary PCI graphics adapter, proceeding."); ++ factory->seat0_has_boot_up_graphics = TRUE; ++ is_settled = TRUE; ++ break; ++ } ++ } ++ ++ if (pci_device != NULL || drm_device != NULL) { ++ g_debug ("GdmLocalDisplayFactory: Found secondary PCI graphics adapter, not proceeding yet."); ++ } ++ + node = next_node; + } + diff --git a/0001-manager-Keep-register-display-method.patch b/0001-manager-Keep-register-display-method.patch new file mode 100644 index 0000000..735a7d0 --- /dev/null +++ b/0001-manager-Keep-register-display-method.patch @@ -0,0 +1,134 @@ +From 361a0a7d069e7c2bb83f9df8a57fb888ec7826ea Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Thu, 8 Jan 2026 01:00:00 +0100 +Subject: [PATCH] manager: Keep register display method + +The use of register session combines the old register display and register +session methods. This is the new behaviour. + +To keep ABI compatibility, keep the register display method, which +internally calls register session. + +--- + daemon/gdm-manager.c | 51 +++++++++++++++++++++++++++++++++++++----- + daemon/gdm-manager.xml | 3 +++ + 2 files changed, 48 insertions(+), 6 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index ff04203..be6daea 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -731,6 +731,16 @@ find_user_session_for_display (GdmManager *self, + return NULL; + } + ++static gboolean ++display_is_managed_and_registered (GdmDisplay *display) ++{ ++ gboolean session_registered = FALSE; ++ ++ g_object_get (display, "session-registered", &session_registered, NULL); ++ ++ return gdm_display_get_status (display) == GDM_DISPLAY_MANAGED && session_registered; ++} ++ + static gboolean + gdm_manager_handle_register_session (GdmDBusManager *manager, + GDBusMethodInvocation *invocation, +@@ -738,6 +748,7 @@ gdm_manager_handle_register_session (GdmDBusManager *manager, + { + GdmManager *self = GDM_MANAGER (manager); + const char *sender; ++ const char *method_name; + GDBusConnection *connection; + GdmDisplay *display = NULL; + GdmSession *session; +@@ -746,9 +757,13 @@ gdm_manager_handle_register_session (GdmDBusManager *manager, + char *value = NULL; + g_autofree char *x11_display_name = NULL; + g_autofree char *tty = NULL; ++ gboolean is_register_display; + + sender = g_dbus_method_invocation_get_sender (invocation); + connection = g_dbus_method_invocation_get_connection (invocation); ++ method_name = g_dbus_method_invocation_get_method_name (invocation); ++ is_register_display = g_strcmp0 (method_name, "RegisterDisplay") == 0; ++ + get_display_and_details_for_bus_sender (self, connection, sender, &display, NULL, NULL, &tty, NULL, NULL, NULL, NULL); + + g_debug ("GdmManager: trying to register new session on display %p", display); +@@ -762,11 +777,32 @@ gdm_manager_handle_register_session (GdmDBusManager *manager, + return TRUE; + } + ++ if (display_is_managed_and_registered (display)) { ++ g_debug ("GdmManager: display already managed and session-registered"); ++ if (is_register_display) ++ gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager), invocation); ++ else ++ gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager), invocation); ++ ++ return TRUE; ++ } ++ + g_variant_iter_init (&iter, details); +- while (g_variant_iter_loop (&iter, "{&s&s}", &key, &value)) { +- if (g_strcmp0 (key, "x11-display-name") == 0) { +- x11_display_name = g_strdup (value); +- break; ++ if (is_register_display) { ++ while (g_variant_iter_loop (&iter, "{&s&s}", &key, &value)) { ++ if (g_strcmp0 (key, "x11-display-name") == 0) { ++ x11_display_name = g_strdup (value); ++ break; ++ } ++ } ++ } else { ++ GVariant *variant_value = NULL; ++ while (g_variant_iter_loop (&iter, "{&sv}", &key, &variant_value)) { ++ if (g_strcmp0 (key, "x11-display-name") == 0 && ++ g_variant_is_of_type (variant_value, G_VARIANT_TYPE_STRING)) { ++ x11_display_name = g_variant_dup_string (variant_value, NULL); ++ break; ++ } + } + } + +@@ -797,8 +833,10 @@ gdm_manager_handle_register_session (GdmDBusManager *manager, + "session-registered", TRUE, + NULL); + +- gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager), +- invocation); ++ if (is_register_display) ++ gdm_dbus_manager_complete_register_display (GDM_DBUS_MANAGER (manager), invocation); ++ else ++ gdm_dbus_manager_complete_register_session (GDM_DBUS_MANAGER (manager), invocation); + + return TRUE; + } +@@ -1214,6 +1252,7 @@ gdm_manager_handle_open_reauthentication_channel (GdmDBusManager *manager + static void + manager_interface_init (GdmDBusManagerIface *interface) + { ++ interface->handle_register_display = gdm_manager_handle_register_session; + interface->handle_register_session = gdm_manager_handle_register_session; + interface->handle_open_session = gdm_manager_handle_open_session; + interface->handle_open_reauthentication_channel = gdm_manager_handle_open_reauthentication_channel; +diff --git a/daemon/gdm-manager.xml b/daemon/gdm-manager.xml +index aba079a..92ef1d0 100644 +--- a/daemon/gdm-manager.xml ++++ b/daemon/gdm-manager.xml +@@ -1,6 +1,9 @@ + + + ++ ++ ++ + + + +-- +2.51.1 + diff --git a/0001-manager-Quit-plymouth-when-no-local-display-is-avail.patch b/0001-manager-Quit-plymouth-when-no-local-display-is-avail.patch new file mode 100644 index 0000000..75f2a4d --- /dev/null +++ b/0001-manager-Quit-plymouth-when-no-local-display-is-avail.patch @@ -0,0 +1,107 @@ +From d2c0213ba03004c42cb474a543ca91042de570e0 Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Tue, 15 Jul 2025 16:19:01 +0200 +Subject: [PATCH] manager: Quit plymouth when no local display is available + +In this case plymouth is not needed and it needs to quit. This is +necessary to allow reaching graphical.target so that the user can +log in at least at serial console. +--- + daemon/gdm-local-display-factory.c | 23 ++++++++++++++++++++++- + daemon/gdm-manager.c | 17 +++++++++++++++++ + 2 files changed, 39 insertions(+), 1 deletion(-) + +diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c +index 3e77bf85b..e22f95fb8 100644 +--- a/daemon/gdm-local-display-factory.c ++++ b/daemon/gdm-local-display-factory.c +@@ -96,6 +96,13 @@ enum { + PROP_0, + }; + ++enum { ++ GRAPHICS_UNSUPPORTED, ++ LAST_SIGNAL, ++}; ++ ++static guint signals [LAST_SIGNAL] = { 0 }; ++ + static void gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass); + static void gdm_local_display_factory_init (GdmLocalDisplayFactory *factory); + static void gdm_local_display_factory_finalize (GObject *object); +@@ -937,8 +944,11 @@ ensure_display_for_seat (GdmLocalDisplayFactory *factory, + } + } + +- if (!seat_supports_graphics) ++ if (!seat_supports_graphics) { ++ if (is_seat0) ++ g_signal_emit (factory, signals[GRAPHICS_UNSUPPORTED], 0); + return; ++ } + + g_assert (session_types != NULL); + +@@ -1676,6 +1686,17 @@ gdm_local_display_factory_class_init (GdmLocalDisplayFactoryClass *klass) + + factory_class->start = gdm_local_display_factory_start; + factory_class->stop = gdm_local_display_factory_stop; ++ ++ signals [GRAPHICS_UNSUPPORTED] = ++ g_signal_new ("graphics-unsupported", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_FIRST, ++ 0, ++ NULL, ++ NULL, ++ NULL, ++ G_TYPE_NONE, ++ 0); + } + + static void +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index f73bccf61..0df57939b 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -2619,6 +2619,16 @@ gdm_manager_get_displays (GdmManager *manager, + return TRUE; + } + ++static void ++on_graphics_unsupported (GdmLocalDisplayFactory *factory, ++ GdmManager *manager) ++{ ++ if (manager->plymouth_is_running) { ++ plymouth_quit_without_transition (); ++ manager->plymouth_is_running = FALSE; ++ } ++} ++ + void + gdm_manager_stop (GdmManager *manager) + { +@@ -2628,6 +2638,9 @@ gdm_manager_stop (GdmManager *manager) + + if (manager->local_factory != NULL) { + gdm_display_factory_stop (GDM_DISPLAY_FACTORY (manager->local_factory)); ++ g_signal_handlers_disconnect_by_func (manager->local_factory, ++ G_CALLBACK (on_graphics_unsupported), ++ manager); + } + + #ifdef HAVE_LIBXDMCP +@@ -2655,6 +2668,10 @@ gdm_manager_start (GdmManager *manager) + #endif + if (!manager->xdmcp_enabled || manager->show_local_greeter) { + gdm_display_factory_start (GDM_DISPLAY_FACTORY (manager->local_factory)); ++ g_signal_connect (manager->local_factory, ++ "graphics-unsupported", ++ G_CALLBACK (on_graphics_unsupported), ++ manager); + } + + /* Accept remote connections */ +-- +2.50.1 + diff --git a/0001-meson-Define-missing-HAVE_LIBAUDIT.patch b/0001-meson-Define-missing-HAVE_LIBAUDIT.patch index 893ceb6..ee2ce28 100644 --- a/0001-meson-Define-missing-HAVE_LIBAUDIT.patch +++ b/0001-meson-Define-missing-HAVE_LIBAUDIT.patch @@ -23,4 +23,3 @@ index 8da5ae878..eaa93a652 100644 -- 2.49.0 - diff --git a/0001-session-Don-t-report-service-unavailable-error-as-fa.patch b/0001-session-Don-t-report-service-unavailable-error-as-fa.patch new file mode 100644 index 0000000..7037385 --- /dev/null +++ b/0001-session-Don-t-report-service-unavailable-error-as-fa.patch @@ -0,0 +1,50 @@ +From f04dc0418694b20f789479bfd1d88404fd0c5fde Mon Sep 17 00:00:00 2001 +From: Joan Torres Lopez +Date: Fri, 12 Sep 2025 17:24:15 +0200 +Subject: [PATCH] session: Don't report service unavailable error as failed + authentication + +Service unavailable error happens when authentication is performed +using gdm-fingerprint and fprintd service is terminated +(either manually or from a timeout). + +In those cases, failed authentications were being reported, making the login +greeter display something like: +"There were failed login attempts since the last successful login.". + +This fixes it. + +Part-of: +--- + daemon/gdm-session.c | 15 ++++++++++----- + 1 file changed, 10 insertions(+), 5 deletions(-) + +diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c +index ae6840188..46bd1da9d 100644 +--- a/daemon/gdm-session.c ++++ b/daemon/gdm-session.c +@@ -259,11 +259,16 @@ on_authenticate_cb (GdmDBusWorker *proxy, + if (worked) { + gdm_session_authorize (self, service_name); + } else { +- g_signal_emit (self, +- signals[AUTHENTICATION_FAILED], +- 0, +- service_name, +- conversation->worker_pid); ++ if (!g_error_matches (error, ++ GDM_SESSION_WORKER_ERROR, ++ GDM_SESSION_WORKER_ERROR_SERVICE_UNAVAILABLE)) { ++ g_signal_emit (self, ++ signals[AUTHENTICATION_FAILED], ++ 0, ++ service_name, ++ conversation->worker_pid); ++ } ++ + report_and_stop_conversation (self, service_name, error); + } + } +-- +2.52.0 + diff --git a/0001-session-Fix-memory-leak-on-new-outside-connection.patch b/0001-session-Fix-memory-leak-on-new-outside-connection.patch index eaf072f..f62151f 100644 --- a/0001-session-Fix-memory-leak-on-new-outside-connection.patch +++ b/0001-session-Fix-memory-leak-on-new-outside-connection.patch @@ -94,4 +94,3 @@ index 33dee7606..08ba39cf6 100644 -- 2.49.0 - diff --git a/0001-session-record-Rework-wtmp-utmp-btmp-fields.patch b/0001-session-record-Rework-wtmp-utmp-btmp-fields.patch new file mode 100644 index 0000000..03885d2 --- /dev/null +++ b/0001-session-record-Rework-wtmp-utmp-btmp-fields.patch @@ -0,0 +1,349 @@ +From 161ce350f8acd18ad49ca880667c1f90f2b55d93 Mon Sep 17 00:00:00 2001 +From: Adrian Vovk +Date: Wed, 12 Nov 2025 14:59:03 -0500 +Subject: [PATCH] session-record: Rework wtmp/utmp/btmp fields + +This reworks the logic we use when setting the various fields in the +wtmp/utmp/btmp records. + +Previously, we'd set the hostname to "[]:" in +the X11 case and to "login screen" on Wayland. We'd also set the "line" +(which is supposed to contain the TTY) to the TTY on X11 but the seat ID +on Wayland. And finally we simply wouldn't write these records for +remote Wayland sessions. + +Now: the hostname is always just a remote IP address for remote +sessions, and the string "local" for local sessions. The "line" is also +consistently set to the TTY, or the seat ID if no TTY is available, or +the string "headless" if no seat is set and the session is headless. +Also Wayland remote sessions are supported properly now. + +This also happens to get rid of this code's dependency on X11: we no +longer consider the X11 display name + +Co-authored-by: Joan Torres Lopez +--- + daemon/gdm-manager.c | 47 ++++---------- + daemon/gdm-session-record.c | 122 ++++++++++++++---------------------- + daemon/gdm-session-record.h | 13 ++-- + 3 files changed, 64 insertions(+), 118 deletions(-) + +diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c +index d1654dd33..67f5a7919 100644 +--- a/daemon/gdm-manager.c ++++ b/daemon/gdm-manager.c +@@ -642,68 +642,46 @@ add_session_record (GdmManager *manager, + SessionRecord record) + { + const char *username; +- char *display_name, *hostname, *display_device, *display_seat_id; +- gboolean recorded = FALSE; + +- display_name = NULL; +- username = NULL; +- hostname = NULL; +- display_device = NULL; +- display_seat_id = NULL; ++ g_autofree char *hostname = NULL; ++ g_autofree char *display_device = NULL; ++ g_autofree char *display_seat_id = NULL; + + username = gdm_session_get_username (session); +- +- if (username == NULL) { +- goto out; +- } ++ if (username == NULL) ++ return FALSE; + + g_object_get (G_OBJECT (session), +- "display-name", &display_name, + "display-hostname", &hostname, + "display-device", &display_device, + "display-seat-id", &display_seat_id, + NULL); + +- if (display_name == NULL && display_device == NULL) { +- if (display_seat_id == NULL) +- goto out; +- +- display_name = g_strdup ("login screen"); +- display_device = g_strdup (display_seat_id); +- } +- + switch (record) { + case SESSION_RECORD_LOGIN: + gdm_session_record_login (pid, + username, + hostname, +- display_name, +- display_device); ++ display_device, ++ display_seat_id); + break; + case SESSION_RECORD_LOGOUT: + gdm_session_record_logout (pid, + username, + hostname, +- display_name, +- display_device); ++ display_device, ++ display_seat_id); + break; + case SESSION_RECORD_FAILED: + gdm_session_record_failed (pid, + username, + hostname, +- display_name, +- display_device); ++ display_device, ++ display_seat_id); + break; + } + +- recorded = TRUE; +-out: +- g_free (display_name); +- g_free (hostname); +- g_free (display_device); +- g_free (display_seat_id); +- +- return recorded; ++ return FALSE; + } + + static GdmSession * +@@ -1867,7 +1845,6 @@ on_user_session_started (GdmSession *session, + GdmManager *manager) + { + g_debug ("GdmManager: session started %d", pid); +- add_session_record (manager, session, pid, SESSION_RECORD_LOGIN); + } + + static void +diff --git a/daemon/gdm-session-record.c b/daemon/gdm-session-record.c +index f913dee37..6f9e91bfa 100644 +--- a/daemon/gdm-session-record.c ++++ b/daemon/gdm-session-record.c +@@ -111,78 +111,52 @@ record_set_pid (UTMP *u, + + static void + record_set_host (UTMP *u, +- const char *x11_display_name, +- const char *host_name) ++ const char *remote_host) + { +- g_autofree char *hostname = NULL; +- ++ const char *hostname; + #if defined(HAVE_UT_UT_HOST) +- /* +- * Set ut_host to hostname:$DISPLAY if remote, otherwise set +- * to $DISPLAY +- */ +- if (host_name != NULL +- && x11_display_name != NULL +- && g_str_has_prefix (x11_display_name, ":")) { +- hostname = g_strdup_printf ("%s%s", host_name, x11_display_name); +- } else { +- hostname = g_strdup (x11_display_name); +- } +- +- if (hostname != NULL) { +- memccpy (u->ut_host, hostname, '\0', sizeof (u->ut_host)); +- g_debug ("using ut_host %.*s", (int) sizeof (u->ut_host), u->ut_host); ++ if (remote_host != NULL) ++ hostname = remote_host; ++ else ++ hostname = "local"; ++ memccpy (u->ut_host, hostname, '\0', sizeof (u->ut_host)); ++ g_debug ("using ut_host %.*s", (int) sizeof (u->ut_host), u->ut_host); + #ifdef HAVE_UT_UT_SYSLEN +- u->ut_syslen = MIN (strlen (hostname), sizeof (u->ut_host)); ++ u->ut_syslen = MIN (strlen (hostname), sizeof (u->ut_host)); + #endif +- } + #endif + } + + static void + record_set_line (UTMP *u, +- const char *display_device, +- const char *x11_display_name) ++ const char *tty, ++ const char *seat_id) + { +- /* +- * Set ut_line to the device name associated with this display +- * but remove the "/dev/" prefix if there is one. Otherwise, if it +- * seems like the display device is a seat id, just use it wholesale. +- * If there's no device at all, but $DISPLAY is set, just fall back to +- * using that. +- */ +- if (display_device != NULL && g_str_has_prefix (display_device, "/dev/")) { +- memccpy (u->ut_line, +- display_device + strlen ("/dev/"), +- '\0', +- sizeof (u->ut_line)); +- } else if (display_device != NULL && g_str_has_prefix (display_device, "seat")) { +- memccpy (u->ut_line, +- display_device, +- '\0', +- sizeof (u->ut_line)); +- } else if (x11_display_name != NULL) { +- memccpy (u->ut_line, +- x11_display_name, +- '\0', +- sizeof (u->ut_line)); +- } +- ++ const char *line; ++ ++ if (tty != NULL) { ++ if (g_str_has_prefix (tty, "/dev/")) ++ line = tty + strlen("/dev/"); ++ else ++ line = tty; ++ } else if (seat_id != NULL) ++ line = seat_id; ++ else ++ line = "headless"; ++ ++ memccpy (u->ut_line, line, '\0', sizeof (u->ut_line)); + g_debug ("using ut_line %.*s", (int) sizeof (u->ut_line), u->ut_line); + } + + void +-gdm_session_record_login (GPid session_pid, +- const char *user_name, +- const char *host_name, +- const char *x11_display_name, +- const char *display_device) ++gdm_session_record_login (GPid session_pid, ++ const char *user_name, ++ const char *host_name, ++ const char *tty, ++ const char *seat_id) + { + UTMP session_record = { 0 }; + +- if (x11_display_name == NULL) +- x11_display_name = display_device; +- + record_set_username (&session_record, user_name); + + g_debug ("Writing login record"); +@@ -194,8 +168,8 @@ gdm_session_record_login (GPid session_pid, + + record_set_timestamp (&session_record); + record_set_pid (&session_record, session_pid); +- record_set_host (&session_record, x11_display_name, host_name); +- record_set_line (&session_record, display_device, x11_display_name); ++ record_set_host (&session_record, host_name); ++ record_set_line (&session_record, tty, seat_id); + + /* Handle wtmp */ + g_debug ("Writing wtmp session record to " GDM_NEW_SESSION_RECORDS_FILE); +@@ -223,16 +197,15 @@ gdm_session_record_login (GPid session_pid, + } + + void +-gdm_session_record_logout (GPid session_pid, +- const char *user_name, +- const char *host_name, +- const char *x11_display_name, +- const char *display_device) ++gdm_session_record_logout (GPid session_pid, ++ const char *user_name, ++ const char *host_name, ++ const char *tty, ++ const char *seat_id) + { + UTMP session_record = { 0 }; + +- if (x11_display_name == NULL) +- x11_display_name = display_device; ++ record_set_username (&session_record, user_name); + + g_debug ("Writing logout record"); + +@@ -243,8 +216,8 @@ gdm_session_record_logout (GPid session_pid, + + record_set_timestamp (&session_record); + record_set_pid (&session_record, session_pid); +- record_set_host (&session_record, x11_display_name, host_name); +- record_set_line (&session_record, display_device, x11_display_name); ++ record_set_host (&session_record, host_name); ++ record_set_line (&session_record, tty, seat_id); + + /* Handle wtmp */ + g_debug ("Writing wtmp logout record to " GDM_NEW_SESSION_RECORDS_FILE); +@@ -268,17 +241,14 @@ gdm_session_record_logout (GPid session_pid, + } + + void +-gdm_session_record_failed (GPid session_pid, +- const char *user_name, +- const char *host_name, +- const char *x11_display_name, +- const char *display_device) ++gdm_session_record_failed (GPid session_pid, ++ const char *user_name, ++ const char *host_name, ++ const char *tty, ++ const char *seat_id) + { + UTMP session_record = { 0 }; + +- if (x11_display_name == NULL) +- x11_display_name = display_device; +- + record_set_username (&session_record, user_name); + + g_debug ("Writing failed session attempt record"); +@@ -290,8 +260,8 @@ gdm_session_record_failed (GPid session_pid, + + record_set_timestamp (&session_record); + record_set_pid (&session_record, session_pid); +- record_set_host (&session_record, x11_display_name, host_name); +- record_set_line (&session_record, display_device, x11_display_name); ++ record_set_host (&session_record, host_name); ++ record_set_line (&session_record, tty, seat_id); + + #if defined(HAVE_UPDWTMPX) || defined(HAVE_UPDWTMP) + /* Handle btmp */ +diff --git a/daemon/gdm-session-record.h b/daemon/gdm-session-record.h +index 3c53268fa..bbe323aa0 100644 +--- a/daemon/gdm-session-record.h ++++ b/daemon/gdm-session-record.h +@@ -29,21 +29,20 @@ void + gdm_session_record_login (GPid session_pid, + const char *user_name, + const char *host_name, +- const char *x11_display_name, +- const char *display_device); ++ const char *tty, ++ const char *seat_id); + void + gdm_session_record_logout (GPid session_pid, + const char *user_name, + const char *host_name, +- const char *x11_display_name, +- const char *display_device); ++ const char *tty, ++ const char *seat_id); + void + gdm_session_record_failed (GPid session_pid, + const char *user_name, + const char *host_name, +- const char *x11_display_name, +- const char *display_device); +- ++ const char *tty, ++ const char *seat_id); + + G_END_DECLS + +-- +2.51.0 + diff --git a/0002-data-Drop-X11-fallback-rules.patch b/0002-data-Drop-X11-fallback-rules.patch new file mode 100644 index 0000000..01d3c4a --- /dev/null +++ b/0002-data-Drop-X11-fallback-rules.patch @@ -0,0 +1,93 @@ +From 91608626f360638c0dbffa1dbad58c426c8fc733 Mon Sep 17 00:00:00 2001 +From: Neal Gompa +Date: Mon, 8 Jul 2024 20:35:28 -0400 +Subject: [PATCH 2/2] data: Drop X11 fallback rules + +We expect Wayland to work on all conceivable configurations. +--- + data/61-gdm.rules.in | 60 -------------------------------------------- + 1 file changed, 60 deletions(-) + +diff --git a/data/61-gdm.rules.in b/data/61-gdm.rules.in +index 354277bfe..eb9eedbf5 100644 +--- a/data/61-gdm.rules.in ++++ b/data/61-gdm.rules.in +@@ -10,22 +10,8 @@ ATTR{vendor}=="0x1b36", ATTR{device}=="0x0100", RUN+="/usr/bin/touch /run/udev/g + # vga + ATTR{vendor}=="0x1234", ATTR{device}=="0x1111", RUN+="/usr/bin/touch /run/udev/gdm-machine-has-virtual-gpu", ENV{GDM_MACHINE_HAS_VIRTUAL_GPU}="1", GOTO="gdm_pci_device_end" + +-# disable Wayland on Hi1710 chipsets +-ATTR{vendor}=="0x19e5", ATTR{device}=="0x1711", GOTO="gdm_disable_wayland" +- + LABEL="gdm_pci_device_end" + +-# disable Wayland if modesetting is disabled +-KERNEL!="card[0-9]*", GOTO="gdm_nomodeset_end" +-KERNEL=="card[0-9]-*", GOTO="gdm_nomodeset_end" +-SUBSYSTEM!="drm", GOTO="gdm_nomodeset_end" +-# but keep it enabled for simple framebuffer drivers +-DRIVERS=="simple-framebuffer", GOTO="gdm_nomodeset_end" +-IMPORT{parent}="GDM_MACHINE_HAS_VIRTUAL_GPU" +-ENV{GDM_MACHINE_HAS_VIRTUAL_GPU}!="1", RUN+="/usr/bin/touch /run/udev/gdm-machine-has-hardware-gpu" +-IMPORT{cmdline}="nomodeset", GOTO="gdm_disable_wayland" +-LABEL="gdm_nomodeset_end" +- + # The vendor nvidia driver has multiple modules that need to be loaded before GDM can make an + # informed choice on which way to proceed, so force GDM to wait until NVidia's modules are + # loaded before starting up. +@@ -41,17 +27,6 @@ IMPORT{program}="/bin/sh -c \"sed -e 's/: /=/g' -e 's/\([^[:upper:]]\)\([[:upper + # If it is, there's no need to check for the suspend/resume services + ENV{NVIDIA_ENABLE_S0IX_POWER_MANAGEMENT}=="1", GOTO="gdm_nvidia_suspend_end" + +-# Check if suspend/resume services necessary for working wayland support is available +-TEST{0711}!="/usr/bin/nvidia-sleep.sh", GOTO="gdm_disable_wayland" +-TEST{0711}!="/usr/lib/systemd/system-sleep/nvidia", GOTO="gdm_disable_wayland" +- +-ENV{NVIDIA_PRESERVE_VIDEO_MEMORY_ALLOCATIONS}!="1", GOTO="gdm_disable_wayland" +-IMPORT{program}="/bin/sh -c 'echo NVIDIA_HIBERNATE=`systemctl is-enabled nvidia-hibernate`'" +-ENV{NVIDIA_HIBERNATE}!="enabled", GOTO="gdm_disable_wayland" +-IMPORT{program}="/bin/sh -c 'echo NVIDIA_RESUME=`systemctl is-enabled nvidia-resume`'" +-ENV{NVIDIA_RESUME}!="enabled", GOTO="gdm_disable_wayland" +-IMPORT{program}="/bin/sh -c 'echo NVIDIA_SUSPEND=`systemctl is-enabled nvidia-suspend`'" +-ENV{NVIDIA_SUSPEND}!="enabled", GOTO="gdm_disable_wayland" + LABEL="gdm_nvidia_suspend_end" + LABEL="gdm_nvidia_end" + +@@ -64,35 +39,3 @@ IMPORT{program}="/bin/sh -c \"echo GDM_NUMBER_OF_GRAPHICS_CARDS=`ls -1d /sys/cla + ENV{GDM_NUMBER_OF_GRAPHICS_CARDS}=="1", RUN+="/usr/bin/rm -f /run/udev/gdm-machine-has-hybrid-graphics" + ENV{GDM_NUMBER_OF_GRAPHICS_CARDS}!="1", RUN+="/usr/bin/touch /run/udev/gdm-machine-has-hybrid-graphics" + LABEL="gdm_hybrid_graphics_check_end" +- +-# Disable wayland in situation where we're in a guest with a virtual gpu and host passthrough gpu +-#LABEL="gdm_virt_passthrough_check" +-TEST!="/run/udev/gdm-machine-has-hybrid-graphics", GOTO="gdm_virt_passthrough_check_end" +-TEST!="/run/udev/gdm-machine-has-virtual-gpu", GOTO="gdm_virt_passthrough_check_end" +-TEST!="/run/udev/gdm-machine-has-hardware-gpu", GOTO="gdm_virt_passthrough_check_end" +-GOTO="gdm_disable_wayland" +-LABEL="gdm_virt_passthrough_check_end" +- +-# Disable wayland when there are multiple virtual gpus +-#LABEL="gdm_virt_multi_gpu_check" +-TEST!="/run/udev/gdm-machine-has-hybrid-graphics", GOTO="gdm_virt_multi_gpu_check_end" +-TEST!="/run/udev/gdm-machine-has-virtual-gpu", GOTO="gdm_virt_multi_gpu_check_end" +-TEST=="/run/udev/gdm-machine-has-hardware-gpu", GOTO="gdm_virt_multi_gpu_check_end" +-LABEL="gdm_virt_multi_gpu_check_end" +- +-# Disable wayland when nvidia modeset is disabled +-KERNEL!="nvidia_drm", GOTO="gdm_nvidia_drm_end" +-SUBSYSTEM!="module", GOTO="gdm_nvidia_drm_end" +-ACTION!="add", GOTO="gdm_nvidia_drm_end" +-# disable wayland if nvidia-drm modeset is not enabled +-ATTR{parameters/modeset}!="Y", GOTO="gdm_disable_wayland" +-LABEL="gdm_nvidia_drm_end" +- +-GOTO="gdm_end" +- +-LABEL="gdm_disable_wayland" +-RUN+="@libexecdir@/gdm-runtime-config set daemon WaylandEnable false" +-GOTO="gdm_end" +- +-LABEL="gdm_end" +- +--- +2.45.2 diff --git a/gdm.spec b/gdm.spec index 3016022..8969004 100644 --- a/gdm.spec +++ b/gdm.spec @@ -2,7 +2,7 @@ ## (rpmautospec version 0.6.5) ## RPMAUTOSPEC: autorelease, autochangelog %define autorelease(e:s:pb:n) %{?-p:0.}%{lua: - release_number = 7; + release_number = 19; base_release_number = tonumber(rpm.expand("%{?-b*}%{!?-b:1}")); print(release_number + base_release_number - 1); }%{?-e:.%{-e*}}%{?-s:.%{-s*}}%{!?-n:%{?dist}} @@ -33,19 +33,49 @@ Source6: gdm.sysusers # Downstream patches Patch: 0001-Honor-initial-setup-being-disabled-by-distro-install.patch Patch: 0001-data-add-system-dconf-databases-to-gdm-profile.patch -Patch: 0001-Add-headless-session-files.patch -Patch: 0001-schema-Add-banner-message-path-and-source-settings.patch +Patch: 0002-data-Drop-X11-fallback-rules.patch -# RHEL-5703 -# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/298 -Patch: 0001-session-Fix-memory-leak-on-new-outside-connection.patch +# RHEL-4104 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/272 +Patch: 0001-schema-Add-banner-message-path-and-source-settings.patch # RHEL-1846 # https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/284 Patch: 0001-meson-Define-missing-HAVE_LIBAUDIT.patch -# RHEL-69319 -Patch: 0001-headless-session-Fix-autostarting-on-boot.patch +# RHEL-5703 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/298 +Patch: 0001-session-Fix-memory-leak-on-new-outside-connection.patch + +# RHEL-62663 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/303 +Patch: 0001-manager-Quit-plymouth-when-no-local-display-is-avail.patch + +# RHEL-126603 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288 +Patch: 0001-Introduce-gdm-new-session-tool.patch + +# RHEL-86140 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/285 +Patch: 0001-Revert-hack-that-quits-plymouth-late.patch +Patch: 0001-manager-Keep-register-display-method.patch + +# RHEL-4147 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/335 +Patch: 0001-session-record-Rework-wtmp-utmp-btmp-fields.patch + +# RHEL-14524 +# Passwordless work +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/330 +Patch: 0001-data-Add-support-for-unified-authentication.patch + +# RHEL-86485 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/317 +Patch: 0001-session-Don-t-report-service-unavailable-error-as-fa.patch + +# RHEL-168869 +# https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/343 +Patch: 0001-local-display-factory-look-for-boot_display-sysfs-at.patch BuildRequires: dconf BuildRequires: desktop-file-utils @@ -258,7 +288,7 @@ fi %{_libexecdir}/gdm-runtime-config %{_libexecdir}/gdm-session-worker %{_libexecdir}/gdm-wayland-session -%{_libexecdir}/gdm-headless-login-session +%{_libexecdir}/gdm-new-session %{_sbindir}/gdm %{_bindir}/gdmflexiserver %{_bindir}/gdm-config @@ -274,6 +304,8 @@ fi %{_datadir}/gdm/locale.alias %{_datadir}/gdm/gdb-cmd %{_datadir}/gnome-session/sessions/gnome-login.session +%{_datadir}/polkit-1/rules.d/20-gdm.rules +%{_datadir}/polkit-1/actions/org.gnome.displaymanager.policy %{_libdir}/girepository-1.0/Gdm-1.0.typelib %{_libdir}/security/pam_gdm.so %{_libdir}/libgdm*.so* @@ -285,6 +317,7 @@ fi %attr(0600, gdm, gdm) %{_localstatedir}/lib/gdm/.config/pulse/default.pa %attr(0711, root, gdm) %dir /run/gdm %config %{_sysconfdir}/pam.d/gdm-smartcard +%config %{_sysconfdir}/pam.d/gdm-switchable-auth %config %{_sysconfdir}/pam.d/gdm-fingerprint %{_sysconfdir}/pam.d/gdm-launch-environment %{_unitdir}/gdm.service @@ -313,14 +346,50 @@ fi %changelog ## START: Generated by rpmautospec -* Wed Jul 16 2025 Jonas Ådahl - 1:47.0-7 +* Thu Apr 23 2026 Joan Torres Lopez - 1:47.0-19 +- Check for "boot_vga" when creating the display + +* Tue Feb 17 2026 Joan Torres Lopez - 1:47.0-18 +- Fix reporting false failed authentication + +* Tue Feb 17 2026 Joan Torres Lopez - 1:47.0-17 +- Update debug_json_requests with extra optimization + +* Mon Jan 19 2026 Joan Torres Lopez - 1:47.0-16 +- Backport GDM passwordless feature + +* Mon Jan 19 2026 Joan Torres Lopez - 1:47.0-15 +- Keep register display method + +* Mon Dec 22 2025 Joan Torres Lopez - 1:47.0-14 +- Fix recording wtmp/utmp/btmp + +* Mon Dec 22 2025 Joan Torres Lopez - 1:47.0-13 +- Improve plymouth handling + +* Wed Nov 05 2025 Joan Torres Lopez - 1:47.0-12 +- Add starting headless sessions with C tool + +* Thu Aug 21 2025 Joan Torres Lopez - 1:47.0-11 +- Quit plymouth when no local display is available + +* Wed Jul 16 2025 Jonas Ådahl - 1:47.0-10 - Fix launching headless service on boot -* Tue May 06 2025 Joan Torres - 1:47.0-6 +* Thu Jun 19 2025 Joan Torres López - 1:47.0-9 +- Fix leak on new connections + +* Tue May 06 2025 Joan Torres - 1:47.0-8 - meson: Define missing HAVE_LIBAUDIT -* Thu Jun 19 2025 Joan Torres López - 1:47.0-5 -- Fix leak on new connections +* Tue May 06 2025 Joan Torres - 1:47.0-7 +- Specify downstream and upstream patches + +* Tue May 06 2025 Joan Torres - 1:47.0-6 +- Remove unused patches + +* Tue May 06 2025 Joan Torres - 1:47.0-5 +- Add and update removed by mistake patch * Wed Feb 05 2025 Florian Müllner - 1:47.0-4 - schema: Add banner-message-path and -source settings diff --git a/sources b/sources index 57007f9..f9d0dfe 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -5578b2839ec78ef0b155a6ef6b82384495ef0f3ceb49f4bc7c656bf9dafa95b260148c7fc9b8295b71d4a2d33f6fe6c4a47485cd9eba5c0929f0492ba06e0893 gdm-47.0.tar.xz +SHA512 (gdm-47.0.tar.xz) = 5578b2839ec78ef0b155a6ef6b82384495ef0f3ceb49f4bc7c656bf9dafa95b260148c7fc9b8295b71d4a2d33f6fe6c4a47485cd9eba5c0929f0492ba06e0893