1938 lines
73 KiB
Diff
1938 lines
73 KiB
Diff
From ad5b372db6d05698cf6c79f4b7fd98e1b03547f7 Mon Sep 17 00:00:00 2001
|
|
From: Joan Torres <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 @@
|
|
<method name="GetSeatId">
|
|
<arg name="filename" direction="out" type="s"/>
|
|
</method>
|
|
+ <method name="GetSessionId">
|
|
+ <arg name="session" direction="out" type="s"/>
|
|
+ </method>
|
|
<method name="IsInitial">
|
|
<arg name="initial" direction="out" type="b"/>
|
|
</method>
|
|
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 @@
|
|
<allow send_destination="org.gnome.DisplayManager"
|
|
send_interface="org.gnome.DisplayManager.Display"
|
|
send_member="GetSeatId"/>
|
|
+ <allow send_destination="org.gnome.DisplayManager"
|
|
+ send_interface="org.gnome.DisplayManager.Display"
|
|
+ send_member="GetSessionId"/>
|
|
<allow send_destination="org.gnome.DisplayManager"
|
|
send_interface="org.gnome.DisplayManager.Display"
|
|
send_member="GetX11DisplayName"/>
|
|
--
|
|
2.51.0
|
|
|
|
|
|
From 5dbd23f70423a37e4e50647c81bebae3ccb73592 Mon Sep 17 00:00:00 2001
|
|
From: Joan Torres <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 <joantolo@redhat.com>
|
|
Date: Thu, 15 May 2025 17:34:20 +0200
|
|
Subject: [PATCH 6/8] display-factories: Add <Create/Destroy>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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 <glib.h>
|
|
#include <glib/gi18n.h>
|
|
#include <glib-object.h>
|
|
+#include <polkit/polkit.h>
|
|
|
|
#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 @@
|
|
<method name="CreateTransientDisplay">
|
|
<arg name="id" direction="out" type="o"/>
|
|
</method>
|
|
+ <method name="CreateUserDisplay">
|
|
+ <arg name="user" direction="in" type="s"/>
|
|
+ </method>
|
|
+ <method name="DestroyUserDisplay">
|
|
+ <arg name="user" direction="in" type="s"/>
|
|
+ </method>
|
|
</interface>
|
|
</node>
|
|
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 @@
|
|
<method name="CreateRemoteDisplay">
|
|
<arg name="remote_id" direction="in" type="o"/>
|
|
</method>
|
|
+ <method name="CreateUserDisplay">
|
|
+ <arg name="user" direction="in" type="s"/>
|
|
+ </method>
|
|
+ <method name="DestroyUserDisplay">
|
|
+ <arg name="user" direction="in" type="s"/>
|
|
+ </method>
|
|
</interface>
|
|
</node>
|
|
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 @@
|
|
+<?xml version="1.0" encoding="UTF-8"?>
|
|
+<!DOCTYPE policyconfig PUBLIC
|
|
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
|
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
|
+<policyconfig>
|
|
+
|
|
+ <vendor>The GNOME Project</vendor>
|
|
+ <vendor_url>http://www.gnome.org/</vendor_url>
|
|
+
|
|
+ <action id="org.gnome.displaymanager.displayfactory.manage-user-displays">
|
|
+ <description>Allow managing user sessions</description>
|
|
+ <message>Authentication is required to start or stop a user session</message>
|
|
+ <defaults>
|
|
+ <allow_any>auth_admin</allow_any>
|
|
+ <allow_inactive>auth_admin</allow_inactive>
|
|
+ <allow_active>auth_admin</allow_active>
|
|
+ </defaults>
|
|
+ </action>
|
|
+
|
|
+</policyconfig>
|
|
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 <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 <joantolo@redhat.com>
|
|
+ * SPDX-License-Identifier: GPL-2.0-or-later
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+
|
|
+#include <gio/gio.h>
|
|
+#include <glib-unix.h>
|
|
+#include <sysexits.h>
|
|
+#include <systemd/sd-login.h>
|
|
+
|
|
+#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 <joantolo@redhat.com>
|
|
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: <https://gitlab.gnome.org/GNOME/gdm/-/merge_requests/288>
|
|
---
|
|
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 @@
|
|
<interface name="org.gnome.DisplayManager.RemoteDisplay">
|
|
<property name="RemoteId" type="o" access="read"/>
|
|
<property name="SessionId" type="s" access="read"/>
|
|
+ <method name="SetRemoteId">
|
|
+ <arg name="remote_id" direction="in" type="o"/>
|
|
+ </method>
|
|
</interface>
|
|
</node>
|
|
--
|
|
2.51.0
|
|
|