diff options
-rw-r--r-- | common/gdm-common.c | 117 | ||||
-rw-r--r-- | common/gdm-common.h | 7 | ||||
-rw-r--r-- | daemon/gdm-manager.c | 11 | ||||
-rw-r--r-- | libgdm/Makefile.am | 1 | ||||
-rw-r--r-- | libgdm/gdm-user-switching.c | 8 |
5 files changed, 130 insertions, 14 deletions
diff --git a/common/gdm-common.c b/common/gdm-common.c index 373d5b85..41bdb389 100644 --- a/common/gdm-common.c +++ b/common/gdm-common.c @@ -484,6 +484,7 @@ goto_login_session (GDBusConnection *connection, char *our_session; char *session_id; char *seat_id; + GError *local_error = NULL; ret = FALSE; session_id = NULL; @@ -496,10 +497,8 @@ goto_login_session (GDBusConnection *connection, * since the data allocated is from libsystemd-logind, which * does not use GLib's g_malloc (). */ - res = sd_pid_get_session (0, &our_session); - if (res < 0) { - g_debug ("failed to determine own session: %s", strerror (-res)); - g_set_error (error, GDM_COMMON_ERROR, 0, _("Could not identify the current session.")); + if (!gdm_find_display_session_for_uid (getuid (), &our_session, &local_error)) { + g_propagate_prefixed_error (error, local_error, _("Could not identify the current session: ")); return FALSE; } @@ -843,3 +842,113 @@ gdm_shell_expand (const char *str, } return g_string_free (s, FALSE); } + +static gboolean +_systemd_session_is_graphical (const char *session_id) +{ + const gchar * const graphical_session_types[] = { "wayland", "x11", "mir", NULL }; + int saved_errno; + g_autofree gchar *type = NULL; + + saved_errno = sd_session_get_type (session_id, &type); + if (saved_errno < 0) { + g_warning ("Couldn't get type for session '%s': %s", + session_id, + g_strerror (-saved_errno)); + return FALSE; + } + + if (!g_strv_contains (graphical_session_types, type)) { + g_debug ("Session '%s' is not a graphical session (type: '%s')", + session_id, + type); + return FALSE; + } + + return TRUE; +} + +static gboolean +_systemd_session_is_active (const char *session_id) +{ + const gchar * const active_states[] = { "active", "online", NULL }; + int saved_errno; + g_autofree gchar *state = NULL; + + /* + * display sessions can be 'closing' if they are logged out but some + * processes are lingering; we shouldn't consider these (this is + * checking for a race condition since we specified + * GDM_SYSTEMD_SESSION_REQUIRE_ONLINE) + */ + saved_errno = sd_session_get_state (session_id, &state); + if (saved_errno < 0) { + g_warning ("Couldn't get state for session '%s': %s", + session_id, + g_strerror (-saved_errno)); + return FALSE; + } + + if (!g_strv_contains (active_states, state)) { + g_debug ("Session '%s' is not active or online", session_id); + return FALSE; + } + + return TRUE; +} + +gboolean +gdm_find_display_session_for_uid (const uid_t uid, + char **out_session_id, + GError **error) +{ + char *local_session_id = NULL; + g_auto(GStrv) sessions = NULL; + int n_sessions; + + g_return_val_if_fail (out_session_id != NULL, FALSE); + + 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) { + g_set_error (error, + GDM_COMMON_ERROR, + 0, + "Failed to get sessions for user %d", + uid); + 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) { + g_set_error (error, + GDM_COMMON_ERROR, + 0, + "Could not find a graphical session for user %d", + uid); + return FALSE; + } + + *out_session_id = g_strdup (local_session_id); + + return TRUE; +} diff --git a/common/gdm-common.h b/common/gdm-common.h index 58814aa2..5c3fe137 100644 --- a/common/gdm-common.h +++ b/common/gdm-common.h @@ -35,6 +35,8 @@ expr; \ } while G_UNLIKELY (errno == EINTR); +#define GDM_SYSTEMD_SESSION_REQUIRE_ONLINE 0 + GQuark gdm_common_error_quark (void); #define GDM_COMMON_ERROR gdm_common_error_quark() @@ -48,6 +50,11 @@ int gdm_wait_on_and_disown_pid (int pid, int timeout); int gdm_signal_pid (int pid, int signal); + +gboolean gdm_find_display_session_for_uid (const uid_t uid, + char **out_session_id, + GError **error); + gboolean gdm_get_pwent_for_name (const char *name, struct passwd **pwentp); diff --git a/daemon/gdm-manager.c b/daemon/gdm-manager.c index 23e3b85d..c9150bfd 100644 --- a/daemon/gdm-manager.c +++ b/daemon/gdm-manager.c @@ -494,21 +494,22 @@ get_display_and_details_for_bus_sender (GdmManager *self, ret = gdm_dbus_get_uid_for_name (sender, &caller_uid, &error); if (!ret) { - g_debug ("GdmManager: Error while retrieving uid for sender: %s", + g_debug ("GdmManager: Error while retrieving uid for sender: %d", error->message); g_error_free (error); goto out; } - session_id = get_session_id_for_pid (pid, &error); + ret = gdm_find_display_session_for_uid (caller_uid, &session_id, &error); - if (session_id == NULL) { - g_debug ("GdmManager: Error while retrieving session id for sender: %s", + if (!ret) { + g_debug ("GdmManager: Unable to find display session for uid %s: %s", + caller_uid, error->message); g_error_free (error); goto out; } - + if (out_session_id != NULL) { *out_session_id = g_strdup (session_id); } diff --git a/libgdm/Makefile.am b/libgdm/Makefile.am index 88534a54..fadffbec 100644 --- a/libgdm/Makefile.am +++ b/libgdm/Makefile.am @@ -66,6 +66,7 @@ libgdm_la_LDFLAGS = \ $(END_OF_LIST) libgdm_la_LIBADD = \ + $(top_builddir)/common/libgdmcommon.la \ $(LIBGDM_LIBS) \ $(SYSTEMD_LIBS) \ $(END_OF_LIST) diff --git a/libgdm/gdm-user-switching.c b/libgdm/gdm-user-switching.c index 3d4303e3..3a33fcbb 100644 --- a/libgdm/gdm-user-switching.c +++ b/libgdm/gdm-user-switching.c @@ -191,6 +191,7 @@ goto_login_session (GDBusConnection *connection, char *our_session; char *session_id; char *seat_id; + GError *local_error = NULL; ret = FALSE; session_id = NULL; @@ -202,11 +203,8 @@ goto_login_session (GDBusConnection *connection, /* Note that we mostly use free () here, instead of g_free () * since the data allocated is from libsystemd-logind, which * does not use GLib's g_malloc (). */ - - res = sd_pid_get_session (0, &our_session); - if (res < 0) { - g_debug ("failed to determine own session: %s", strerror (-res)); - g_set_error (error, GDM_CLIENT_ERROR, 0, _("Could not identify the current session.")); + if (!gdm_find_display_session_for_uid (getuid (), &our_session, &local_error)) { + g_propagate_prefixed_error (error, local_error, _("Could not identify the current session: ")); return FALSE; } |