summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIain Lane <iainl@gnome.org>2019-05-07 16:00:14 +0100
committerIain Lane <iain@orangesquash.org.uk>2019-05-29 15:53:35 +0000
commit1c061b84ffc3e874da825982d18d970556ff74bb (patch)
tree64f56c787fdfa6e2a205ea726d589fad546da300
parent9496f639715b1b8e31b2b0a5bd37a8b6761249b3 (diff)
downloadgdm-1c061b84ffc3e874da825982d18d970556ff74bb.tar.gz
Allow sessions to register with GDM
Recording when sessions start, for Wayland → Xorg fallback or transitioning to the user session, is currently done with timeouts. This isn't ideal, because on some very slow machines the timeout can be hit before the session has had a chance to fail: if gnome-session takes more than 3 seconds to fail then the session will be considered to have exited rather than failed, and so we don't do Xorg fallback. We can do this more reliably if we allow sessions to optionally register themselves with GDM. Then we will know when they've started, so can shut down the greeter or fall back to Xorg as appropriate. The mechanism is that they specify X-GDM-SessionRegisters=true in their file, and then call RegsterSession on the DisplayManager interface on the bus (added in the previous commit) to say that they've started up. If X-GDM-SessionRegisters is missing or false, GDM will call the same method for them after 10 seconds. Closes: #483
-rw-r--r--common/gdm-common.h2
-rw-r--r--daemon/gdm-display.c16
-rw-r--r--daemon/gdm-local-display-factory.c50
-rw-r--r--daemon/gdm-session.c47
-rw-r--r--daemon/gdm-session.h1
-rw-r--r--daemon/gdm-wayland-session.c81
-rw-r--r--daemon/gdm-x-session.c81
7 files changed, 201 insertions, 77 deletions
diff --git a/common/gdm-common.h b/common/gdm-common.h
index 07812b10..58814aa2 100644
--- a/common/gdm-common.h
+++ b/common/gdm-common.h
@@ -27,6 +27,8 @@
#include <pwd.h>
#include <errno.h>
+#define REGISTER_SESSION_TIMEOUT 10
+
#define VE_IGNORE_EINTR(expr) \
do { \
errno = 0; \
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
index 843a3641..8c106222 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -66,7 +66,6 @@ typedef struct _GdmDisplayPrivate
char *x11_display_name;
int status;
time_t creation_time;
- GTimer *server_timer;
char *x11_cookie;
gsize x11_cookie_size;
@@ -600,8 +599,6 @@ gdm_display_manage (GdmDisplay *self)
}
}
- g_timer_start (priv->server_timer);
-
if (g_strcmp0 (priv->session_class, "greeter") == 0) {
if (GDM_DISPLAY_GET_CLASS (self)->manage != NULL) {
GDM_DISPLAY_GET_CLASS (self)->manage (self);
@@ -673,7 +670,6 @@ gboolean
gdm_display_unmanage (GdmDisplay *self)
{
GdmDisplayPrivate *priv;
- gdouble elapsed;
g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
@@ -683,8 +679,6 @@ gdm_display_unmanage (GdmDisplay *self)
gdm_display_disconnect (self);
- g_timer_stop (priv->server_timer);
-
if (priv->user_access_file != NULL) {
gdm_display_access_file_close (priv->user_access_file);
g_object_unref (priv->user_access_file);
@@ -697,9 +691,8 @@ gdm_display_unmanage (GdmDisplay *self)
priv->access_file = NULL;
}
- elapsed = g_timer_elapsed (priv->server_timer, NULL);
- if (elapsed < 3) {
- g_warning ("GdmDisplay: display lasted %lf seconds", elapsed);
+ if (!priv->session_registered) {
+ g_warning ("GdmDisplay: Session never registered, failing");
_gdm_display_set_status (self, GDM_DISPLAY_FAILED);
} else {
_gdm_display_set_status (self, GDM_DISPLAY_UNMANAGED);
@@ -1408,7 +1401,6 @@ gdm_display_init (GdmDisplay *self)
priv = gdm_display_get_instance_private (self);
priv->creation_time = time (NULL);
- priv->server_timer = g_timer_new ();
}
static void
@@ -1446,10 +1438,6 @@ gdm_display_finalize (GObject *object)
g_object_unref (priv->user_access_file);
}
- if (priv->server_timer != NULL) {
- g_timer_destroy (priv->server_timer);
- }
-
G_OBJECT_CLASS (gdm_display_parent_class)->finalize (object);
}
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index 7dd74a62..ee41308b 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -66,7 +66,6 @@ struct _GdmLocalDisplayFactory
#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
unsigned int active_vt;
guint active_vt_watch_id;
- guint wait_to_finish_timeout_id;
#endif
};
@@ -299,6 +298,25 @@ finish_waiting_displays_on_seat (GdmLocalDisplayFactory *factory,
}
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", &registered, 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,
GdmLocalDisplayFactory *factory)
@@ -376,6 +394,13 @@ on_display_status_changed (GdmDisplay *display,
case GDM_DISPLAY_PREPARED:
break;
case GDM_DISPLAY_MANAGED:
+#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
+ g_signal_connect_object (display,
+ "notify::session-registered",
+ G_CALLBACK (on_session_registered_cb),
+ factory,
+ 0);
+#endif
break;
case GDM_DISPLAY_WAITING_TO_FINISH:
break;
@@ -604,14 +629,6 @@ lookup_by_session_id (const char *id,
}
#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
-static gboolean
-wait_to_finish_timeout (GdmLocalDisplayFactory *factory)
-{
- finish_waiting_displays_on_seat (factory, "seat0");
- factory->wait_to_finish_timeout_id = 0;
- return G_SOURCE_REMOVE;
-}
-
static void
maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory,
GdmDisplay *display)
@@ -643,16 +660,8 @@ maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory,
}
g_debug ("GdmLocalDisplayFactory: killing login window once its unused");
- g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL);
-
- /* We stop the greeter after a timeout to avoid flicker */
- if (factory->wait_to_finish_timeout_id != 0)
- g_source_remove (factory->wait_to_finish_timeout_id);
- factory->wait_to_finish_timeout_id =
- g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT,
- (GSourceFunc)wait_to_finish_timeout,
- factory);
+ g_object_set (G_OBJECT (display), "status", GDM_DISPLAY_WAITING_TO_FINISH, NULL);
}
static gboolean
@@ -748,7 +757,6 @@ on_vt_changed (GIOChannel *source,
display = gdm_display_store_find (store,
lookup_by_session_id,
(gpointer) login_session_id);
-
if (display != NULL)
maybe_stop_greeter_in_background (factory, display);
} else {
@@ -831,10 +839,6 @@ gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
factory->seat_removed_id = 0;
}
#if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
- if (factory->wait_to_finish_timeout_id != 0) {
- g_source_remove (factory->wait_to_finish_timeout_id);
- factory->wait_to_finish_timeout_id = 0;
- }
if (factory->active_vt_watch_id) {
g_source_remove (factory->active_vt_watch_id);
factory->active_vt_watch_id = 0;
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 6a116a85..d0e09b53 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -2837,6 +2837,7 @@ gdm_session_start_session (GdmSession *self,
gboolean allow_remote_connections = FALSE;
char *command;
char *program;
+ gboolean register_session;
g_return_if_fail (GDM_IS_SESSION (self));
g_return_if_fail (self->session_conversation == NULL);
@@ -2862,6 +2863,8 @@ gdm_session_start_session (GdmSession *self,
run_launcher = TRUE;
}
+ register_session = !gdm_session_session_registers (self);
+
if (self->selected_program == NULL) {
gboolean run_xsession_script;
@@ -2879,12 +2882,14 @@ gdm_session_start_session (GdmSession *self,
if (run_launcher) {
if (is_x11) {
- program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s %s\"%s\"",
+ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s%s %s\"%s\"",
+ register_session ? "--register-session " : "",
run_xsession_script? "--run-script " : "",
allow_remote_connections? "--allow-remote-connections " : "",
command);
} else {
- program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session \"%s\"",
+ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"%s\"",
+ register_session ? "--register-session " : "",
command);
}
} else if (run_xsession_script) {
@@ -2897,10 +2902,12 @@ gdm_session_start_session (GdmSession *self,
} else {
if (run_launcher) {
if (is_x11) {
- program = g_strdup_printf (LIBEXECDIR "/gdm-x-session \"%s\"",
+ program = g_strdup_printf (LIBEXECDIR "/gdm-x-session %s\"%s\"",
+ register_session ? "--register-session " : "",
self->selected_program);
} else {
- program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session \"%s\"",
+ program = g_strdup_printf (LIBEXECDIR "/gdm-wayland-session %s\"%s\"",
+ register_session ? "--register-session " : "",
self->selected_program);
}
} else {
@@ -3178,6 +3185,38 @@ update_session_type (GdmSession *self)
}
gboolean
+gdm_session_session_registers (GdmSession *self)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GKeyFile) key_file = NULL;
+ gboolean session_registers = FALSE;
+ g_autofree char *filename = NULL;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
+
+ filename = get_session_filename (self);
+
+ key_file = load_key_file_for_file (self, filename, NULL);
+
+ session_registers = g_key_file_get_boolean (key_file,
+ G_KEY_FILE_DESKTOP_GROUP,
+ "X-GDM-SessionRegisters",
+ &error);
+ if (!session_registers &&
+ error != NULL &&
+ !g_error_matches (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_KEY_NOT_FOUND)) {
+ g_warning ("GdmSession: Couldn't read session file '%s'", filename);
+ return FALSE;
+ }
+
+ g_debug ("GdmSession: '%s' %s self", filename,
+ session_registers ? "registers" : "does not register");
+
+ return session_registers;
+}
+
+gboolean
gdm_session_bypasses_xsession (GdmSession *self)
{
GError *error;
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index 98acb4f6..3b64ecd2 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -76,6 +76,7 @@ const char *gdm_session_get_display_device (GdmSession *sessi
const char *gdm_session_get_display_seat_id (GdmSession *session);
const char *gdm_session_get_session_id (GdmSession *session);
gboolean gdm_session_bypasses_xsession (GdmSession *session);
+gboolean gdm_session_session_registers (GdmSession *session);
GdmSessionDisplayMode gdm_session_get_display_mode (GdmSession *session);
#ifdef ENABLE_WAYLAND_SUPPORT
diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c
index 94f49e19..35679b19 100644
--- a/daemon/gdm-wayland-session.c
+++ b/daemon/gdm-wayland-session.c
@@ -45,6 +45,7 @@ typedef struct
GSubprocess *bus_subprocess;
GDBusConnection *bus_connection;
+ GdmDBusManager *display_manager_proxy;
char *bus_address;
char **environment;
@@ -53,6 +54,8 @@ typedef struct
char *session_command;
int session_exit_status;
+ guint register_session_id;
+
GMainLoop *main_loop;
guint32 debug_enabled : 1;
@@ -385,29 +388,14 @@ static gboolean
register_display (State *state,
GCancellable *cancellable)
{
- GdmDBusManager *manager = NULL;
GError *error = NULL;
gboolean registered = FALSE;
GVariantBuilder details;
- manager = gdm_dbus_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
- G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
- "org.gnome.DisplayManager",
- "/org/gnome/DisplayManager/Manager",
- cancellable,
- &error);
-
- if (!manager) {
- g_debug ("could not contact display manager: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
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 (manager,
+ registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy,
g_variant_builder_end (&details),
cancellable,
&error);
@@ -416,8 +404,6 @@ register_display (State *state,
g_error_free (error);
}
-out:
- g_clear_object (&manager);
return registered;
}
@@ -439,6 +425,7 @@ clear_state (State **out_state)
g_clear_object (&state->session_subprocess);
g_clear_pointer (&state->environment, g_strfreev);
g_clear_pointer (&state->main_loop, g_main_loop_unref);
+ g_clear_handle_id (&state->register_session_id, g_source_remove);
*out_state = NULL;
}
@@ -454,6 +441,49 @@ on_sigterm (State *state)
return G_SOURCE_CONTINUE;
}
+static gboolean
+register_session_timeout_cb (gpointer user_data)
+{
+ State *state;
+ GError *error = NULL;
+
+ state = (State *) user_data;
+
+ gdm_dbus_manager_call_register_session_sync (state->display_manager_proxy,
+ g_variant_new ("a{sv}", NULL),
+ state->cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_warning ("Could not register session: %s", error->message);
+ g_error_free (error);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+connect_to_display_manager (State *state)
+{
+ g_autoptr (GError) error = NULL;
+
+ state->display_manager_proxy = gdm_dbus_manager_proxy_new_for_bus_sync (
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ "org.gnome.DisplayManager",
+ "/org/gnome/DisplayManager/Manager",
+ state->cancellable,
+ &error);
+
+ if (state->display_manager_proxy == NULL) {
+ g_printerr ("gdm-wayland-session: could not contact display manager: %s\n",
+ error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
int
main (int argc,
char **argv)
@@ -464,7 +494,10 @@ main (int argc,
gboolean debug = FALSE;
gboolean ret;
int exit_status = EX_OK;
+ static gboolean register_session = FALSE;
+
static GOptionEntry entries [] = {
+ { "register-session", 0, 0, G_OPTION_ARG_NONE, &register_session, "Register session after a delay", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" },
{ NULL }
};
@@ -528,6 +561,9 @@ main (int argc,
goto out;
}
+ if (!connect_to_display_manager (state))
+ goto out;
+
ret = register_display (state, state->cancellable);
if (!ret) {
@@ -536,6 +572,15 @@ main (int argc,
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,
+ register_session_timeout_cb,
+ state);
+ } else {
+ g_debug ("gdm-wayland-session: Session will register itself");
+ }
+
g_main_loop_run (state->main_loop);
/* Only use exit status of session if we're here because it exit */
diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c
index 3b2fcef4..58865a6c 100644
--- a/daemon/gdm-x-session.c
+++ b/daemon/gdm-x-session.c
@@ -51,6 +51,7 @@ typedef struct
GSubprocess *bus_subprocess;
GDBusConnection *bus_connection;
+ GdmDBusManager *display_manager_proxy;
char *bus_address;
char **environment;
@@ -59,6 +60,8 @@ typedef struct
char *session_command;
int session_exit_status;
+ guint register_session_id;
+
GMainLoop *main_loop;
guint32 debug_enabled : 1;
@@ -737,30 +740,15 @@ static gboolean
register_display (State *state,
GCancellable *cancellable)
{
- GdmDBusManager *manager = NULL;
GError *error = NULL;
gboolean registered = FALSE;
GVariantBuilder details;
- manager = gdm_dbus_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
- G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
- G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
- "org.gnome.DisplayManager",
- "/org/gnome/DisplayManager/Manager",
- cancellable,
- &error);
-
- if (!manager) {
- g_debug ("could not contact display manager: %s", error->message);
- g_error_free (error);
- goto out;
- }
-
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 (manager,
+ registered = gdm_dbus_manager_call_register_display_sync (state->display_manager_proxy,
g_variant_builder_end (&details),
cancellable,
&error);
@@ -769,8 +757,6 @@ register_display (State *state,
g_error_free (error);
}
-out:
- g_clear_object (&manager);
return registered;
}
@@ -795,6 +781,7 @@ clear_state (State **out_state)
g_clear_pointer (&state->auth_file, g_free);
g_clear_pointer (&state->display_name, g_free);
g_clear_pointer (&state->main_loop, g_main_loop_unref);
+ g_clear_handle_id (&state->register_session_id, g_source_remove);
*out_state = NULL;
}
@@ -810,6 +797,49 @@ on_sigterm (State *state)
return G_SOURCE_CONTINUE;
}
+static gboolean
+register_session_timeout_cb (gpointer user_data)
+{
+ State *state;
+ GError *error = NULL;
+
+ state = (State *) user_data;
+
+ gdm_dbus_manager_call_register_session_sync (state->display_manager_proxy,
+ g_variant_new ("a{sv}", NULL),
+ state->cancellable,
+ &error);
+
+ if (error != NULL) {
+ g_warning ("Could not register session: %s", error->message);
+ g_error_free (error);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+connect_to_display_manager (State *state)
+{
+ g_autoptr (GError) error = NULL;
+
+ state->display_manager_proxy = gdm_dbus_manager_proxy_new_for_bus_sync (
+ G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
+ "org.gnome.DisplayManager",
+ "/org/gnome/DisplayManager/Manager",
+ state->cancellable,
+ &error);
+
+ if (state->display_manager_proxy == NULL) {
+ g_printerr ("gdm-x-session: could not contact display manager: %s\n",
+ error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
int
main (int argc,
char **argv)
@@ -822,9 +852,12 @@ main (int argc,
gboolean debug = FALSE;
gboolean ret;
int exit_status = EX_OK;
+ static gboolean register_session = FALSE;
+
static GOptionEntry entries [] = {
{ "run-script", 'r', 0, G_OPTION_ARG_NONE, &run_script, N_("Run program through /etc/gdm/Xsession wrapper script"), NULL },
{ "allow-remote-connections", 'a', 0, G_OPTION_ARG_NONE, &allow_remote_connections, N_("Listen on TCP socket"), NULL },
+ { "register-session", 0, 0, G_OPTION_ARG_NONE, &register_session, "Register session after a delay", NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, "", "" },
{ NULL }
};
@@ -896,6 +929,9 @@ main (int argc,
goto out;
}
+ if (!connect_to_display_manager (state))
+ goto out;
+
ret = register_display (state, state->cancellable);
if (!ret) {
@@ -912,6 +948,15 @@ main (int argc,
goto out;
}
+ if (register_session) {
+ g_debug ("gdm-x-session: Will register session in %d seconds", REGISTER_SESSION_TIMEOUT);
+ state->register_session_id = g_timeout_add_seconds (REGISTER_SESSION_TIMEOUT,
+ register_session_timeout_cb,
+ state);
+ } else {
+ g_debug ("gdm-x-session: Session will register itself");
+ }
+
g_main_loop_run (state->main_loop);
/* Only use exit status of session if we're here because it exit */