Blob Blame History Raw
From d6aca6da3ab9c16e907c7f46cf6d7bc2abbc2890 Mon Sep 17 00:00:00 2001
From: Iain Lane <iainl@gnome.org>
Date: Tue, 7 May 2019 16:00:14 +0100
Subject: [PATCH 50/51] Allow sessions to register with GDM
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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
---
 common/gdm-common.h                |  2 +
 daemon/gdm-display.c               | 19 ++-----
 daemon/gdm-local-display-factory.c | 49 +++++++++---------
 daemon/gdm-session.c               | 47 +++++++++++++++--
 daemon/gdm-session.h               |  1 +
 daemon/gdm-wayland-session.c       | 81 +++++++++++++++++++++++-------
 daemon/gdm-x-session.c             | 81 +++++++++++++++++++++++-------
 7 files changed, 201 insertions(+), 79 deletions(-)

diff --git a/common/gdm-common.h b/common/gdm-common.h
index 3fbf07653..7fd6458d7 100644
--- a/common/gdm-common.h
+++ b/common/gdm-common.h
@@ -1,59 +1,61 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
  * License as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Library General Public License for more details.
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */
 
 #ifndef _GDM_COMMON_H
 #define _GDM_COMMON_H
 
 #include <glib-unix.h>
 #include <gio/gio.h>
 
 #include <pwd.h>
 #include <errno.h>
 
+#define REGISTER_SESSION_TIMEOUT 10
+
 #define        VE_IGNORE_EINTR(expr) \
         do {                         \
                 errno = 0;           \
                 expr;                \
         } while G_UNLIKELY (errno == EINTR);
 
 GQuark gdm_common_error_quark (void);
 #define GDM_COMMON_ERROR gdm_common_error_quark()
 
 typedef char * (*GdmExpandVarFunc) (const char *var,
                                     gpointer user_data);
 
 G_BEGIN_DECLS
 
 int            gdm_wait_on_pid           (int pid);
 int            gdm_wait_on_and_disown_pid (int pid,
                                            int timeout);
 int            gdm_signal_pid            (int pid,
                                           int signal);
 gboolean       gdm_get_pwent_for_name    (const char     *name,
                                           struct passwd **pwentp);
 
 gboolean       gdm_clear_close_on_exec_flag (int fd);
 
 const char *   gdm_make_temp_dir         (char    *template);
 
 char          *gdm_generate_random_bytes (gsize          size,
                                           GError       **error);
 gboolean       gdm_get_login_window_session_id (const char  *seat_id,
                                                 char       **session_id);
diff --git a/daemon/gdm-display.c b/daemon/gdm-display.c
index 09cc2e116..ae20491cd 100644
--- a/daemon/gdm-display.c
+++ b/daemon/gdm-display.c
@@ -39,61 +39,60 @@
 
 #include "gdm-common.h"
 #include "gdm-display.h"
 #include "gdm-display-glue.h"
 #include "gdm-display-access-file.h"
 #include "gdm-launch-environment.h"
 
 #include "gdm-settings-direct.h"
 #include "gdm-settings-keys.h"
 
 #include "gdm-launch-environment.h"
 #include "gdm-dbus-util.h"
 
 #define GNOME_SESSION_SESSIONS_PATH DATADIR "/gnome-session/sessions"
 
 #define GDM_DISPLAY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_DISPLAY, GdmDisplayPrivate))
 
 struct GdmDisplayPrivate
 {
         char                 *id;
         char                 *seat_id;
         char                 *session_id;
         char                 *session_class;
         char                 *session_type;
 
         char                 *remote_hostname;
         int                   x11_display_number;
         char                 *x11_display_name;
         int                   status;
         time_t                creation_time;
-        GTimer               *server_timer;
 
         char                 *x11_cookie;
         gsize                 x11_cookie_size;
         GdmDisplayAccessFile *access_file;
 
         guint                 finish_idle_id;
 
         xcb_connection_t     *xcb_connection;
         int                   xcb_screen_number;
 
         GDBusConnection      *connection;
         GdmDisplayAccessFile *user_access_file;
 
         GdmDBusDisplay       *display_skeleton;
         GDBusObjectSkeleton  *object_skeleton;
 
         /* this spawns and controls the greeter session */
         GdmLaunchEnvironment *launch_environment;
 
         guint                 is_local : 1;
         guint                 is_initial : 1;
         guint                 allow_timed_login : 1;
         guint                 have_existing_user_accounts : 1;
         guint                 doing_initial_setup : 1;
         guint                 session_registered : 1;
 };
 
 enum {
         PROP_0,
         PROP_ID,
@@ -510,62 +509,60 @@ gdm_display_prepare (GdmDisplay *self)
          * asynchronously
          */
         look_for_existing_users_sync (self);
 
         self->priv->doing_initial_setup = wants_initial_setup (self);
 
         g_object_ref (self);
         ret = GDM_DISPLAY_GET_CLASS (self)->prepare (self);
         g_object_unref (self);
 
         return ret;
 }
 
 gboolean
 gdm_display_manage (GdmDisplay *self)
 {
         gboolean res;
 
         g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
 
         g_debug ("GdmDisplay: Managing display: %s", self->priv->id);
 
         /* If not explicitly prepared, do it now */
         if (self->priv->status == GDM_DISPLAY_UNMANAGED) {
                 res = gdm_display_prepare (self);
                 if (! res) {
                         return FALSE;
                 }
         }
 
-        g_timer_start (self->priv->server_timer);
-
         if (g_strcmp0 (self->priv->session_class, "greeter") == 0) {
                 if (GDM_DISPLAY_GET_CLASS (self)->manage != NULL) {
                         GDM_DISPLAY_GET_CLASS (self)->manage (self);
                 }
         }
 
         return TRUE;
 }
 
 gboolean
 gdm_display_finish (GdmDisplay *self)
 {
         g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
 
         if (self->priv->finish_idle_id != 0) {
                 g_source_remove (self->priv->finish_idle_id);
                 self->priv->finish_idle_id = 0;
         }
 
         _gdm_display_set_status (self, GDM_DISPLAY_FINISHED);
 
         g_debug ("GdmDisplay: finish display");
 
         return TRUE;
 }
 
 static void
 gdm_display_disconnect (GdmDisplay *self)
 {
         /* These 3 bits are reserved/unused by the X protocol */
@@ -576,85 +573,80 @@ gdm_display_disconnect (GdmDisplay *self)
 
         if (self->priv->xcb_connection == NULL) {
                 return;
         }
 
         setup = xcb_get_setup (self->priv->xcb_connection);
 
         /* resource_id_mask is the bits given to each client for
          * addressing resources */
         highest_client = (XID) ~unused_bits & ~setup->resource_id_mask;
         client_increment = setup->resource_id_mask + 1;
 
         /* Kill every client but ourselves, then close our own connection
          */
         for (client = 0;
              client <= highest_client;
              client += client_increment) {
 
                 if (client != setup->resource_id_base)
                         xcb_kill_client (self->priv->xcb_connection, client);
         }
 
         xcb_flush (self->priv->xcb_connection);
 
         g_clear_pointer (&self->priv->xcb_connection, xcb_disconnect);
 }
 
 gboolean
 gdm_display_unmanage (GdmDisplay *self)
 {
-        gdouble elapsed;
-
         g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
 
         g_debug ("GdmDisplay: unmanage display");
 
         gdm_display_disconnect (self);
 
-        g_timer_stop (self->priv->server_timer);
-
         if (self->priv->user_access_file != NULL) {
                 gdm_display_access_file_close (self->priv->user_access_file);
                 g_object_unref (self->priv->user_access_file);
                 self->priv->user_access_file = NULL;
         }
 
         if (self->priv->access_file != NULL) {
                 gdm_display_access_file_close (self->priv->access_file);
                 g_object_unref (self->priv->access_file);
                 self->priv->access_file = NULL;
         }
 
-        elapsed = g_timer_elapsed (self->priv->server_timer, NULL);
-        if (elapsed < 3) {
-                g_warning ("GdmDisplay: display lasted %lf seconds", elapsed);
+        if (!self->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);
         }
 
         return TRUE;
 }
 
 gboolean
 gdm_display_get_id (GdmDisplay         *self,
                     char              **id,
                     GError            **error)
 {
         g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
 
         if (id != NULL) {
                 *id = g_strdup (self->priv->id);
         }
 
         return TRUE;
 }
 
 gboolean
 gdm_display_get_x11_display_name (GdmDisplay   *self,
                                   char        **x11_display,
                                   GError      **error)
 {
         g_return_val_if_fail (GDM_IS_DISPLAY (self), FALSE);
 
         if (x11_display != NULL) {
@@ -898,61 +890,61 @@ gdm_display_get_property (GObject        *object,
         case PROP_X11_DISPLAY_NAME:
                 g_value_set_string (value, self->priv->x11_display_name);
                 break;
         case PROP_X11_COOKIE:
                 g_value_set_string (value, self->priv->x11_cookie);
                 break;
         case PROP_X11_AUTHORITY_FILE:
                 g_value_take_string (value,
                                      self->priv->access_file?
                                      gdm_display_access_file_get_path (self->priv->access_file) : NULL);
                 break;
         case PROP_IS_LOCAL:
                 g_value_set_boolean (value, self->priv->is_local);
                 break;
         case PROP_IS_CONNECTED:
                 g_value_set_boolean (value, self->priv->xcb_connection != NULL);
                 break;
         case PROP_LAUNCH_ENVIRONMENT:
                 g_value_set_object (value, self->priv->launch_environment);
                 break;
         case PROP_IS_INITIAL:
                 g_value_set_boolean (value, self->priv->is_initial);
                 break;
         case PROP_HAVE_EXISTING_USER_ACCOUNTS:
                 g_value_set_boolean (value, self->priv->have_existing_user_accounts);
                 break;
         case PROP_DOING_INITIAL_SETUP:
                 g_value_set_boolean (value, self->priv->doing_initial_setup);
                 break;
         case PROP_SESSION_REGISTERED:
-                g_value_set_boolean (value, priv->session_registered);
+                g_value_set_boolean (value, self->priv->session_registered);
                 break;
         case PROP_ALLOW_TIMED_LOGIN:
                 g_value_set_boolean (value, self->priv->allow_timed_login);
                 break;
         default:
                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static gboolean
 handle_get_id (GdmDBusDisplay        *skeleton,
                GDBusMethodInvocation *invocation,
                GdmDisplay            *self)
 {
         char *id;
 
         gdm_display_get_id (self, &id, NULL);
 
         gdm_dbus_display_complete_get_id (skeleton, invocation, id);
 
         g_free (id);
         return TRUE;
 }
 
 static gboolean
 handle_get_remote_hostname (GdmDBusDisplay        *skeleton,
                             GDBusMethodInvocation *invocation,
                             GdmDisplay            *self)
 {
@@ -1251,99 +1243,94 @@ gdm_display_class_init (GdmDisplayClass *klass)
                                                                FALSE,
                                                                G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
         g_object_class_install_property (object_class,
                                          PROP_LAUNCH_ENVIRONMENT,
                                          g_param_spec_object ("launch-environment",
                                                               NULL,
                                                               NULL,
                                                               GDM_TYPE_LAUNCH_ENVIRONMENT,
                                                               G_PARAM_READWRITE));
         g_object_class_install_property (object_class,
                                          PROP_STATUS,
                                          g_param_spec_int ("status",
                                                            "status",
                                                            "status",
                                                            -1,
                                                            G_MAXINT,
                                                            GDM_DISPLAY_UNMANAGED,
                                                            G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
         g_type_class_add_private (klass, sizeof (GdmDisplayPrivate));
 }
 
 static void
 gdm_display_init (GdmDisplay *self)
 {
 
         self->priv = GDM_DISPLAY_GET_PRIVATE (self);
 
         self->priv->creation_time = time (NULL);
-        self->priv->server_timer = g_timer_new ();
 }
 
 static void
 gdm_display_finalize (GObject *object)
 {
         GdmDisplay *self;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GDM_IS_DISPLAY (object));
 
         self = GDM_DISPLAY (object);
 
         g_return_if_fail (self->priv != NULL);
 
         g_debug ("GdmDisplay: Finalizing display: %s", self->priv->id);
         g_free (self->priv->id);
         g_free (self->priv->seat_id);
         g_free (self->priv->session_class);
         g_free (self->priv->remote_hostname);
         g_free (self->priv->x11_display_name);
         g_free (self->priv->x11_cookie);
 
         g_clear_object (&self->priv->display_skeleton);
         g_clear_object (&self->priv->object_skeleton);
         g_clear_object (&self->priv->connection);
 
         if (self->priv->access_file != NULL) {
                 g_object_unref (self->priv->access_file);
         }
 
         if (self->priv->user_access_file != NULL) {
                 g_object_unref (self->priv->user_access_file);
         }
 
-        if (self->priv->server_timer != NULL) {
-                g_timer_destroy (self->priv->server_timer);
-        }
-
         G_OBJECT_CLASS (gdm_display_parent_class)->finalize (object);
 }
 
 GDBusObjectSkeleton *
 gdm_display_get_object_skeleton (GdmDisplay *self)
 {
         return self->priv->object_skeleton;
 }
 
 static void
 on_launch_environment_session_opened (GdmLaunchEnvironment *launch_environment,
                                       GdmDisplay           *self)
 {
         char       *session_id;
 
         g_debug ("GdmDisplay: Greeter session opened");
         session_id = gdm_launch_environment_get_session_id (launch_environment);
         _gdm_display_set_session_id (self, session_id);
         g_free (session_id);
 }
 
 static void
 on_launch_environment_session_started (GdmLaunchEnvironment *launch_environment,
                                        GdmDisplay           *self)
 {
         g_debug ("GdmDisplay: Greeter started");
 }
 
 static void
 self_destruct (GdmDisplay *self)
diff --git a/daemon/gdm-local-display-factory.c b/daemon/gdm-local-display-factory.c
index aae226750..35880563d 100644
--- a/daemon/gdm-local-display-factory.c
+++ b/daemon/gdm-local-display-factory.c
@@ -39,61 +39,60 @@
 #include "gdm-settings-keys.h"
 #include "gdm-settings-direct.h"
 #include "gdm-display-store.h"
 #include "gdm-local-display.h"
 #include "gdm-legacy-display.h"
 
 #define GDM_LOCAL_DISPLAY_FACTORY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_LOCAL_DISPLAY_FACTORY, GdmLocalDisplayFactoryPrivate))
 
 #define GDM_DBUS_PATH                       "/org/gnome/DisplayManager"
 #define GDM_LOCAL_DISPLAY_FACTORY_DBUS_PATH GDM_DBUS_PATH "/LocalDisplayFactory"
 #define GDM_MANAGER_DBUS_NAME               "org.gnome.DisplayManager.LocalDisplayFactory"
 
 #define MAX_DISPLAY_FAILURES 5
 #define WAIT_TO_FINISH_TIMEOUT 10 /* seconds */
 
 struct GdmLocalDisplayFactoryPrivate
 {
         GdmDBusLocalDisplayFactory *skeleton;
         GDBusConnection *connection;
         GHashTable      *used_display_numbers;
 
         /* FIXME: this needs to be per seat? */
         guint            num_failures;
 
         guint            seat_new_id;
         guint            seat_removed_id;
 
 #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
 };
 
 enum {
         PROP_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);
 
 static GdmDisplay *create_display                       (GdmLocalDisplayFactory      *factory,
                                                          const char                  *seat_id,
                                                          const char                  *session_type,
                                                          gboolean                    initial_display);
 
 static void     on_display_status_changed               (GdmDisplay                  *display,
                                                          GParamSpec                  *arg1,
                                                          GdmLocalDisplayFactory      *factory);
 
 static gboolean gdm_local_display_factory_sync_seats    (GdmLocalDisplayFactory *factory);
 static gpointer local_display_factory_object = NULL;
 static gboolean lookup_by_session_id (const char *id,
                                       GdmDisplay *display,
                                       gpointer    user_data);
 
 G_DEFINE_TYPE (GdmLocalDisplayFactory, gdm_local_display_factory, GDM_TYPE_DISPLAY_FACTORY)
 
 GQuark
 gdm_local_display_factory_error_quark (void)
@@ -276,60 +275,79 @@ gdm_local_display_factory_create_transient_display (GdmLocalDisplayFactory *fact
 static gboolean
 finish_display_on_seat_if_waiting (GdmDisplayStore *display_store,
                                    GdmDisplay      *display,
                                    const char      *seat_id)
 {
         if (gdm_display_get_status (display) != GDM_DISPLAY_WAITING_TO_FINISH)
                 return FALSE;
 
         g_debug ("GdmLocalDisplayFactory: finish background display\n");
         gdm_display_stop_greeter_session (display);
         gdm_display_unmanage (display);
         gdm_display_finish (display);
 
         return FALSE;
 }
 
 static void
 finish_waiting_displays_on_seat (GdmLocalDisplayFactory *factory,
                                  const char             *seat_id)
 {
         GdmDisplayStore *store;
 
         store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
 
         gdm_display_store_foreach (store,
                                    (GdmDisplayStoreFunc) finish_display_on_seat_if_waiting,
                                    (gpointer)
                                    seat_id);
 }
 
+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)
 {
         int              status;
         int              num;
         char            *seat_id = NULL;
         char            *session_type = NULL;
         char            *session_class = NULL;
         gboolean         is_initial = TRUE;
         gboolean         is_local = TRUE;
 
         num = -1;
         gdm_display_get_x11_display_number (display, &num, NULL);
 
         g_object_get (display,
                       "seat-id", &seat_id,
                       "is-initial", &is_initial,
                       "is-local", &is_local,
                       "session-type", &session_type,
                       "session-class", &session_class,
                       NULL);
 
         status = gdm_display_get_status (display);
 
         g_debug ("GdmLocalDisplayFactory: display status changed: %d", status);
         switch (status) {
         case GDM_DISPLAY_FINISHED:
                 /* remove the display number from factory->priv->used_display_numbers
@@ -359,60 +377,67 @@ on_display_status_changed (GdmDisplay             *display,
                 /* Create a new equivalent display if it was static */
                 if (is_local) {
 
                         factory->priv->num_failures++;
 
                         if (factory->priv->num_failures > MAX_DISPLAY_FAILURES) {
                                 /* oh shit */
                                 g_warning ("GdmLocalDisplayFactory: maximum number of X display failures reached: check X server log for errors");
                         } else {
 #ifdef ENABLE_WAYLAND_SUPPORT
                                 if (g_strcmp0 (session_type, "wayland") == 0) {
                                         g_free (session_type);
                                         session_type = NULL;
 
                                         /* workaround logind race for now
                                          * bug 1643874
                                          */
                                         g_usleep (2 * G_USEC_PER_SEC);
                                 }
 
 #endif
                                 create_display (factory, seat_id, session_type, is_initial);
                         }
                 }
                 break;
         case GDM_DISPLAY_UNMANAGED:
                 break;
         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;
         default:
                 g_assert_not_reached ();
                 break;
         }
 
         g_free (seat_id);
         g_free (session_type);
         g_free (session_class);
 }
 
 static gboolean
 lookup_by_seat_id (const char *id,
                    GdmDisplay *display,
                    gpointer    user_data)
 {
         const char *looking_for = user_data;
         char *current;
         gboolean res;
 
         g_object_get (G_OBJECT (display), "seat-id", &current, NULL);
 
         res = g_strcmp0 (current, looking_for) == 0;
 
         g_free(current);
 
         return res;
 }
@@ -587,100 +612,83 @@ on_seat_new (GDBusConnection *connection,
 }
 
 static void
 on_seat_removed (GDBusConnection *connection,
                  const gchar     *sender_name,
                  const gchar     *object_path,
                  const gchar     *interface_name,
                  const gchar     *signal_name,
                  GVariant        *parameters,
                  gpointer         user_data)
 {
         const char *seat;
 
         g_variant_get (parameters, "(&s&o)", &seat, NULL);
         delete_display (GDM_LOCAL_DISPLAY_FACTORY (user_data), seat);
 }
 
 #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
 static gboolean
 lookup_by_session_id (const char *id,
                       GdmDisplay *display,
                       gpointer    user_data)
 {
         const char *looking_for = user_data;
         const char *current;
 
         current = gdm_display_get_session_id (display);
         return g_strcmp0 (current, looking_for) == 0;
 }
 
-static gboolean
-wait_to_finish_timeout (GdmLocalDisplayFactory *factory)
-{
-        finish_waiting_displays_on_seat (factory, "seat0");
-        factory->priv->wait_to_finish_timeout_id = 0;
-        return G_SOURCE_REMOVE;
-}
-
 static void
 maybe_stop_greeter_in_background (GdmLocalDisplayFactory *factory,
                                   GdmDisplay             *display)
 {
         gboolean doing_initial_setup = FALSE;
 
         if (gdm_display_get_status (display) != GDM_DISPLAY_MANAGED) {
                 g_debug ("GdmLocalDisplayFactory: login window not in managed state, so ignoring");
                 return;
         }
 
         g_object_get (G_OBJECT (display),
                       "doing-initial-setup", &doing_initial_setup,
                       NULL);
 
         /* we don't ever stop initial-setup implicitly */
         if (doing_initial_setup) {
                 g_debug ("GdmLocalDisplayFactory: login window is performing initial-setup, so ignoring");
                 return;
         }
 
         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->priv->wait_to_finish_timeout_id != 0)
-                g_source_remove (factory->priv->wait_to_finish_timeout_id);
-
-        factory->priv->wait_to_finish_timeout_id =
-                g_timeout_add_seconds (WAIT_TO_FINISH_TIMEOUT,
-                                       (GSourceFunc)wait_to_finish_timeout,
-                                       factory);
 }
 
 static gboolean
 on_vt_changed (GIOChannel    *source,
                GIOCondition   condition,
                GdmLocalDisplayFactory *factory)
 {
         GIOStatus status;
         g_autofree char *tty_of_active_vt = NULL;
         g_autofree char *login_session_id = NULL;
         g_autofree char *active_session_id = NULL;
         unsigned int previous_vt, new_vt;
         const char *session_type = NULL;
         int ret, n_returned;
 
         g_debug ("GdmLocalDisplayFactory: received VT change event");
         g_io_channel_seek_position (source, 0, G_SEEK_SET, NULL);
 
         if (condition & G_IO_PRI) {
                 g_autoptr (GError) error = NULL;
                 status = g_io_channel_read_line (source, &tty_of_active_vt, NULL, NULL, &error);
 
                 if (error != NULL) {
                         g_warning ("could not read active VT from kernel: %s", error->message);
                 }
                 switch (status) {
                         case G_IO_STATUS_ERROR:
                             return G_SOURCE_REMOVE;
                         case G_IO_STATUS_EOF:
                             return G_SOURCE_REMOVE;
@@ -722,61 +730,60 @@ on_vt_changed (GIOChannel    *source,
         factory->priv->active_vt = new_vt;
 
         /* don't do anything at start up */
         if (previous_vt == 0) {
                 g_debug ("GdmLocalDisplayFactory: VT is %u at startup",
                          factory->priv->active_vt);
                 return G_SOURCE_CONTINUE;
         }
 
         g_debug ("GdmLocalDisplayFactory: VT changed from %u to %u",
                  previous_vt, factory->priv->active_vt);
 
         /* if the old VT was running a wayland login screen kill it
          */
         if (gdm_get_login_window_session_id ("seat0", &login_session_id)) {
                 unsigned int login_window_vt;
 
                 ret = sd_session_get_vt (login_session_id, &login_window_vt);
                 if (ret == 0 && login_window_vt != 0) {
                         g_debug ("GdmLocalDisplayFactory: VT of login window is %u", login_window_vt);
                         if (login_window_vt == previous_vt) {
                                 GdmDisplayStore *store;
                                 GdmDisplay *display;
 
                                 g_debug ("GdmLocalDisplayFactory: VT switched from login window");
 
                                 store = gdm_display_factory_get_display_store (GDM_DISPLAY_FACTORY (factory));
                                 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 {
                                 g_debug ("GdmLocalDisplayFactory: VT not switched from login window");
                         }
                 }
         }
 
         /* if user jumped back to initial vt and it's empty put a login screen
          * on it (unless a login screen is already running elsewhere, then
          * jump to that login screen)
          */
         if (factory->priv->active_vt != GDM_INITIAL_VT) {
                 g_debug ("GdmLocalDisplayFactory: active VT is not initial VT, so ignoring");
                 return G_SOURCE_CONTINUE;
         }
 
         if (gdm_local_display_factory_use_wayland ())
                 session_type = "wayland";
 
         g_debug ("GdmLocalDisplayFactory: creating new display on seat0 because of VT change");
 
         create_display (factory, "seat0", session_type, TRUE);
 
         return G_SOURCE_CONTINUE;
 }
 #endif
 
 static void
 gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
@@ -805,64 +812,60 @@ gdm_local_display_factory_start_monitor (GdmLocalDisplayFactory *factory)
                                                                              g_object_unref);
 
 #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
         io_channel = g_io_channel_new_file ("/sys/class/tty/tty0/active", "r", NULL);
 
         if (io_channel != NULL) {
                 factory->priv->active_vt_watch_id =
                         g_io_add_watch (io_channel,
                                         G_IO_PRI,
                                         (GIOFunc)
                                         on_vt_changed,
                                         factory);
         }
 #endif
 }
 
 static void
 gdm_local_display_factory_stop_monitor (GdmLocalDisplayFactory *factory)
 {
         if (factory->priv->seat_new_id) {
                 g_dbus_connection_signal_unsubscribe (factory->priv->connection,
                                                       factory->priv->seat_new_id);
                 factory->priv->seat_new_id = 0;
         }
         if (factory->priv->seat_removed_id) {
                 g_dbus_connection_signal_unsubscribe (factory->priv->connection,
                                                       factory->priv->seat_removed_id);
                 factory->priv->seat_removed_id = 0;
         }
 #if defined(ENABLE_WAYLAND_SUPPORT) && defined(ENABLE_USER_DISPLAY_SERVER)
-        if (factory->priv->wait_to_finish_timeout_id != 0) {
-                g_source_remove (factory->priv->wait_to_finish_timeout_id);
-                factory->priv->wait_to_finish_timeout_id = 0;
-        }
         if (factory->priv->active_vt_watch_id) {
                 g_source_remove (factory->priv->active_vt_watch_id);
                 factory->priv->active_vt_watch_id = 0;
         }
 #endif
 }
 
 static void
 on_display_added (GdmDisplayStore        *display_store,
                   const char             *id,
                   GdmLocalDisplayFactory *factory)
 {
         GdmDisplay *display;
 
         display = gdm_display_store_lookup (display_store, id);
 
         if (display != NULL) {
                 g_signal_connect_object (display, "notify::status",
                                          G_CALLBACK (on_display_status_changed),
                                          factory,
                                          0);
 
                 g_object_weak_ref (G_OBJECT (display), (GWeakNotify)on_display_disposed, factory);
         }
 }
 
 static void
 on_display_removed (GdmDisplayStore        *display_store,
                     GdmDisplay             *display,
                     GdmLocalDisplayFactory *factory)
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 5b76ba129..540a2534d 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -2849,132 +2849,139 @@ on_start_program_cb (GdmDBusWorker *worker,
 
         self = conversation->session;
         service_name = conversation->service_name;
 
         if (worked) {
                 self->priv->session_pid = pid;
                 self->priv->session_conversation = conversation;
 
                 g_debug ("GdmSession: Emitting 'session-started' signal with pid '%d'", pid);
                 g_signal_emit (self, signals[SESSION_STARTED], 0, service_name, pid);
         } else {
                 gdm_session_stop_conversation (self, service_name);
 
                 g_debug ("GdmSession: Emitting 'session-start-failed' signal");
                 g_signal_emit (self, signals[SESSION_START_FAILED], 0, service_name, error->message);
         }
 }
 
 void
 gdm_session_start_session (GdmSession *self,
                            const char *service_name)
 {
         GdmSessionConversation *conversation;
         GdmSessionDisplayMode   display_mode;
         gboolean                is_x11 = TRUE;
         gboolean                run_launcher = FALSE;
         gboolean                allow_remote_connections = FALSE;
         gboolean                run_separate_bus = FALSE;
         char                   *command;
         char                   *program;
+        gboolean               register_session;
 
         g_return_if_fail (GDM_IS_SESSION (self));
         g_return_if_fail (self->priv->session_conversation == NULL);
 
         conversation = find_conversation_by_name (self, service_name);
 
         if (conversation == NULL) {
                 g_warning ("GdmSession: Tried to start session of "
                            "nonexistent conversation %s", service_name);
                 return;
         }
 
         stop_all_other_conversations (self, conversation, FALSE);
 
         display_mode = gdm_session_get_display_mode (self);
 
 #ifdef ENABLE_WAYLAND_SUPPORT
         is_x11 = g_strcmp0 (self->priv->session_type, "wayland") != 0;
 #endif
 
         if (display_mode == GDM_SESSION_DISPLAY_MODE_LOGIND_MANAGED ||
             display_mode == GDM_SESSION_DISPLAY_MODE_NEW_VT) {
                 run_launcher = TRUE;
         }
 
         if (g_strcmp0 (self->priv->display_seat_id, "seat0") != 0 && !run_launcher) {
                 run_separate_bus = TRUE;
         }
 
+        register_session = !gdm_session_session_registers (self);
+
         if (self->priv->selected_program == NULL) {
                 gboolean run_xsession_script;
 
                 command = get_session_command (self);
 
                 run_xsession_script = !gdm_session_bypasses_xsession (self);
 
                 if (self->priv->display_is_local) {
                         gboolean disallow_tcp = TRUE;
                         gdm_settings_direct_get_boolean (GDM_KEY_DISALLOW_TCP, &disallow_tcp);
                         allow_remote_connections = !disallow_tcp;
                 } else {
                         allow_remote_connections = TRUE;
                 }
 
                 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) {
                     if (run_separate_bus) {
                             program = g_strdup_printf ("dbus-run-session -- " GDMCONFDIR "/Xsession \"%s\"", command);
                     } else {
                             program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command);
                     }
                 } else {
                         program = g_strdup (command);
                 }
 
                 g_free (command);
         } 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->priv->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->priv->selected_program);
                         }
                 } else {
                         if (run_separate_bus) {
                                 program = g_strdup_printf ("dbus-run-session -- %s",
                                                            self->priv->selected_program);
                         } else {
                                 program = g_strdup (self->priv->selected_program);
                         }
                 }
         }
 
         set_up_session_environment (self);
         send_environment (self, conversation);
 
         gdm_dbus_worker_call_start_program (conversation->worker_proxy,
                                             program,
                                             conversation->worker_cancellable,
                                             (GAsyncReadyCallback) on_start_program_cb,
                                             conversation);
         g_free (program);
 }
 
 static void
 stop_all_conversations (GdmSession *self)
 {
         stop_all_other_conversations (self, NULL, TRUE);
 }
 
 static void
@@ -3199,60 +3206,92 @@ gdm_session_is_wayland_session (GdmSession *self)
                 }
 
                 if (full_path != NULL && strstr (full_path, "/wayland-sessions/") != NULL) {
                         is_wayland_session = TRUE;
                 }
         }
 
 out:
         g_debug ("GdmSession: checking if file '%s' is wayland session: %s", filename, is_wayland_session? "yes" : "no");
 
         return is_wayland_session;
 }
 #endif
 
 static void
 update_session_type (GdmSession *self)
 {
 #ifdef ENABLE_WAYLAND_SUPPORT
         gboolean is_wayland_session = FALSE;
 
         is_wayland_session = gdm_session_is_wayland_session (self);
 
         if (is_wayland_session) {
                 set_session_type (self, "wayland");
         } else {
                 set_session_type (self, NULL);
         }
 #endif
 }
 
+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, 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;
         GKeyFile   *key_file;
         gboolean    res;
         gboolean    bypasses_xsession = FALSE;
         char       *filename = NULL;
 
         g_return_val_if_fail (self != NULL, FALSE);
         g_return_val_if_fail (GDM_IS_SESSION (self), FALSE);
 
 #ifdef ENABLE_WAYLAND_SUPPORT
         if (gdm_session_is_wayland_session (self)) {
                 bypasses_xsession = TRUE;
                 goto out;
         }
 #endif
 
         filename = get_session_filename (self);
 
         key_file = load_key_file_for_file (self, filename, "x11",  NULL);
 
         error = NULL;
         res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", NULL);
         if (!res) {
                 goto out;
         } else {
                 bypasses_xsession = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GDM-BypassXsession", &error);
                 if (error) {
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index a22c09543..682d2c99f 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -108,60 +108,61 @@ typedef struct
         void (* conversation_started)        (GdmSession   *session,
                                               const char   *service_name);
         void (* conversation_stopped)        (GdmSession   *session,
                                               const char   *service_name);
         void (* setup_complete)              (GdmSession   *session,
                                               const char   *service_name);
 } GdmSessionClass;
 
 GType            gdm_session_get_type                 (void);
 
 GdmSession      *gdm_session_new                      (GdmSessionVerificationMode verification_mode,
                                                        uid_t         allowed_user,
                                                        const char   *display_name,
                                                        const char   *display_hostname,
                                                        const char   *display_device,
                                                        const char   *display_seat_id,
                                                        const char   *display_x11_authority_file,
                                                        gboolean      display_is_local,
                                                        const char * const *environment);
 uid_t             gdm_session_get_allowed_user       (GdmSession     *session);
 void              gdm_session_start_reauthentication (GdmSession *session,
                                                       GPid        pid_of_caller,
                                                       uid_t       uid_of_caller);
 
 const char       *gdm_session_get_server_address          (GdmSession     *session);
 const char       *gdm_session_get_username                (GdmSession     *session);
 const char       *gdm_session_get_display_device          (GdmSession     *session);
 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
 void              gdm_session_set_ignore_wayland          (GdmSession *session,
                                                            gboolean    ignore_wayland);
 #endif
 gboolean          gdm_session_start_conversation          (GdmSession *session,
                                                            const char *service_name);
 void              gdm_session_stop_conversation           (GdmSession *session,
                                                            const char *service_name);
 const char       *gdm_session_get_conversation_session_id (GdmSession *session,
                                                            const char *service_name);
 void              gdm_session_setup                       (GdmSession *session,
                                                            const char *service_name);
 void              gdm_session_setup_for_user              (GdmSession *session,
                                                            const char *service_name,
                                                            const char *username);
 void              gdm_session_setup_for_program           (GdmSession *session,
                                                            const char *service_name,
                                                            const char *username,
                                                            const char *log_file);
 void              gdm_session_set_environment_variable    (GdmSession *session,
                                                            const char *key,
                                                            const char *value);
 void              gdm_session_send_environment            (GdmSession *self,
                                                            const char *service_name);
 void              gdm_session_reset                       (GdmSession *session);
 void              gdm_session_cancel                      (GdmSession *session);
 void              gdm_session_authenticate                (GdmSession *session,
                                                            const char *service_name);
diff --git a/daemon/gdm-wayland-session.c b/daemon/gdm-wayland-session.c
index 94f49e19c..35679b194 100644
--- a/daemon/gdm-wayland-session.c
+++ b/daemon/gdm-wayland-session.c
@@ -18,68 +18,71 @@
  * 02110-1301, USA.
  */
 #include "config.h"
 
 #include <locale.h>
 #include <sysexits.h>
 
 #include "gdm-common.h"
 #include "gdm-settings-direct.h"
 #include "gdm-settings-keys.h"
 #include "gdm-log.h"
 
 #include "gdm-manager-glue.h"
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <glib-unix.h>
 #include <glib.h>
 
 #include <gio/gunixinputstream.h>
 
 #define BUS_ADDRESS_FILENO (STDERR_FILENO + 1)
 
 typedef struct
 {
         GdmSettings  *settings;
         GCancellable *cancellable;
 
         GSubprocess     *bus_subprocess;
         GDBusConnection *bus_connection;
+        GdmDBusManager  *display_manager_proxy;
         char            *bus_address;
 
         char           **environment;
 
         GSubprocess  *session_subprocess;
         char         *session_command;
         int           session_exit_status;
 
+        guint         register_session_id;
+
         GMainLoop    *main_loop;
 
         guint32       debug_enabled : 1;
 } State;
 
 static void
 on_bus_finished (GSubprocess  *subprocess,
                  GAsyncResult *result,
                  State        *state)
 {
         gboolean cancelled;
 
         cancelled = !g_subprocess_wait_finish (subprocess, result, NULL);
 
         if (cancelled) {
                 goto out;
         }
 
         if (g_subprocess_get_if_exited (subprocess)) {
                 int exit_status;
 
                 exit_status = g_subprocess_get_exit_status (subprocess);
 
                 g_debug ("message bus exited with status %d", exit_status);
         } else {
                 int signal_number;
 
                 signal_number = g_subprocess_get_term_sig (subprocess);
                 g_debug ("message bus was killed with status %d", signal_number);
         }
@@ -358,140 +361,170 @@ out:
 }
 
 static void
 signal_subprocesses (State *state)
 {
         if (state->session_subprocess != NULL) {
                 g_subprocess_send_signal (state->session_subprocess, SIGTERM);
         }
 
         if (state->bus_subprocess != NULL) {
                 g_subprocess_send_signal (state->bus_subprocess, SIGTERM);
         }
 }
 
 static void
 wait_on_subprocesses (State *state)
 {
         if (state->bus_subprocess != NULL) {
                 g_subprocess_wait (state->bus_subprocess, NULL, NULL);
         }
 
         if (state->session_subprocess != NULL) {
                 g_subprocess_wait (state->session_subprocess, NULL, NULL);
         }
 }
 
 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);
         if (error != NULL) {
                 g_debug ("Could not register display: %s", error->message);
                 g_error_free (error);
         }
 
-out:
-        g_clear_object (&manager);
         return registered;
 }
 
 static void
 init_state (State **state)
 {
         static State state_allocation;
 
         *state = &state_allocation;
 }
 
 static void
 clear_state (State **out_state)
 {
         State *state = *out_state;
 
         g_clear_object (&state->cancellable);
         g_clear_object (&state->bus_connection);
         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;
 }
 
 static gboolean
 on_sigterm (State *state)
 {
         g_cancellable_cancel (state->cancellable);
 
         if (g_main_loop_is_running (state->main_loop)) {
                 g_main_loop_quit (state->main_loop);
         }
 
         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)
 {
         State           *state = NULL;
         GOptionContext  *context = NULL;
         static char    **args = NULL;
         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 }
         };
 
         bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
         textdomain (GETTEXT_PACKAGE);
         setlocale (LC_ALL, "");
 
         gdm_log_init ();
 
         context = g_option_context_new (_("GNOME Display Manager Wayland Session Launcher"));
         g_option_context_add_main_entries (context, entries, NULL);
 
         g_option_context_parse (context, &argc, &argv, NULL);
         g_option_context_free (context);
 
         if (args == NULL || args[0] == NULL || args[1] != NULL) {
                 g_warning ("gdm-wayland-session takes one argument (the session)");
                 exit_status = EX_USAGE;
                 goto out;
         }
 
         init_state (&state);
 
         state->session_command = args[0];
 
         state->settings = gdm_settings_new ();
         ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/");
 
         if (!ret) {
@@ -501,55 +534,67 @@ main (int    argc,
         }
 
         gdm_settings_direct_get_boolean (GDM_KEY_DEBUG, &debug);
         state->debug_enabled = debug;
 
         gdm_log_set_debug (debug);
 
         state->main_loop = g_main_loop_new (NULL, FALSE);
         state->cancellable = g_cancellable_new ();
 
         g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state);
 
         ret = spawn_bus (state, state->cancellable);
 
         if (!ret) {
                 g_printerr ("Unable to run session message bus\n");
                 exit_status = EX_SOFTWARE;
                 goto out;
         }
 
         import_environment (state, state->cancellable);
 
         ret = spawn_session (state, state->cancellable);
 
         if (!ret) {
                 g_printerr ("Unable to run session\n");
                 exit_status = EX_SOFTWARE;
                 goto out;
         }
 
+        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,
+                                                                    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 */
 
         if (state->session_subprocess == NULL) {
                 exit_status = state->session_exit_status;
         }
 
 out:
         if (state != NULL) {
                 signal_subprocesses (state);
                 wait_on_subprocesses (state);
                 clear_state (&state);
         }
 
         return exit_status;
 }
diff --git a/daemon/gdm-x-session.c b/daemon/gdm-x-session.c
index d8e3c7d53..f0082fdc6 100644
--- a/daemon/gdm-x-session.c
+++ b/daemon/gdm-x-session.c
@@ -24,68 +24,71 @@
 
 #include "gdm-common.h"
 #include "gdm-settings-direct.h"
 #include "gdm-settings-keys.h"
 #include "gdm-log.h"
 
 #include "gdm-manager-glue.h"
 
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
 #include <glib-unix.h>
 #include <glib.h>
 #include <gio/gunixinputstream.h>
 #include <glib-unix.h>
 #include <X11/Xauth.h>
 
 #define DISPLAY_FILENO (STDERR_FILENO + 1)
 #define BUS_ADDRESS_FILENO (DISPLAY_FILENO + 1)
 
 typedef struct
 {
         GdmSettings  *settings;
         GCancellable *cancellable;
 
         GSubprocess  *x_subprocess;
         char         *auth_file;
         char         *display_name;
 
         GSubprocess     *bus_subprocess;
         GDBusConnection *bus_connection;
+        GdmDBusManager  *display_manager_proxy;
         char            *bus_address;
 
         char           **environment;
 
         GSubprocess  *session_subprocess;
         char         *session_command;
         int           session_exit_status;
 
+        guint         register_session_id;
+
         GMainLoop    *main_loop;
 
         guint32       debug_enabled : 1;
 } State;
 
 static FILE *
 create_auth_file (char **filename)
 {
         char *auth_dir = NULL;
         char *auth_file = NULL;
         int fd;
         FILE *fp = NULL;
 
         auth_dir = g_build_filename (g_get_user_runtime_dir (),
                                      "gdm",
                                      NULL);
 
         g_mkdir_with_parents (auth_dir, 0711);
         auth_file = g_build_filename (auth_dir, "Xauthority", NULL);
         g_clear_pointer (&auth_dir, g_free);
 
         fd = g_open (auth_file, O_RDWR | O_CREAT | O_TRUNC, 0700);
 
         if (fd < 0) {
                 g_debug ("could not open %s to store auth cookie: %m",
                          auth_file);
                 g_clear_pointer (&auth_file, g_free);
                 goto out;
         }
 
@@ -711,148 +714,178 @@ signal_subprocesses (State *state)
 
         if (state->bus_subprocess != NULL) {
                 g_subprocess_send_signal (state->bus_subprocess, SIGTERM);
         }
 
         if (state->x_subprocess != NULL) {
                 g_subprocess_send_signal (state->x_subprocess, SIGTERM);
         }
 }
 
 static void
 wait_on_subprocesses (State *state)
 {
         if (state->x_subprocess != NULL) {
                 g_subprocess_wait (state->x_subprocess, NULL, NULL);
         }
 
         if (state->bus_subprocess != NULL) {
                 g_subprocess_wait (state->bus_subprocess, NULL, NULL);
         }
 
         if (state->session_subprocess != NULL) {
                 g_subprocess_wait (state->session_subprocess, NULL, NULL);
         }
 }
 
 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);
         if (error != NULL) {
                 g_debug ("Could not register display: %s", error->message);
                 g_error_free (error);
         }
 
-out:
-        g_clear_object (&manager);
         return registered;
 }
 
 static void
 init_state (State **state)
 {
         static State state_allocation;
 
         *state = &state_allocation;
 }
 
 static void
 clear_state (State **out_state)
 {
         State *state = *out_state;
 
         g_clear_object (&state->cancellable);
         g_clear_object (&state->bus_connection);
         g_clear_object (&state->session_subprocess);
         g_clear_object (&state->x_subprocess);
         g_clear_pointer (&state->environment, g_strfreev);
         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;
 }
 
 static gboolean
 on_sigterm (State *state)
 {
         g_cancellable_cancel (state->cancellable);
 
         if (g_main_loop_is_running (state->main_loop)) {
                 g_main_loop_quit (state->main_loop);
         }
 
         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)
 {
         State           *state = NULL;
         GOptionContext  *context = NULL;
         static char    **args = NULL;
         static gboolean  run_script = FALSE;
         static gboolean  allow_remote_connections = FALSE;
         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 }
         };
 
         bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
         textdomain (GETTEXT_PACKAGE);
         setlocale (LC_ALL, "");
 
         gdm_log_init ();
 
         context = g_option_context_new (_("GNOME Display Manager X Session Launcher"));
         g_option_context_add_main_entries (context, entries, NULL);
 
         g_option_context_parse (context, &argc, &argv, NULL);
         g_option_context_free (context);
 
         if (args == NULL || args[0] == NULL || args[1] != NULL) {
                 g_warning ("gdm-x-session takes one argument (the session)");
                 exit_status = EX_USAGE;
                 goto out;
         }
 
         init_state (&state);
 
         state->session_command = args[0];
 
         state->settings = gdm_settings_new ();
         ret = gdm_settings_direct_init (state->settings, DATADIR "/gdm/gdm.schemas", "/");
 
         if (!ret) {
@@ -870,63 +903,75 @@ main (int    argc,
         state->cancellable = g_cancellable_new ();
 
         g_unix_signal_add (SIGTERM, (GSourceFunc) on_sigterm, state);
 
         ret = spawn_x_server (state, allow_remote_connections, state->cancellable);
 
         if (!ret) {
                 g_printerr ("Unable to run X server\n");
                 exit_status = EX_SOFTWARE;
                 goto out;
         }
 
         ret = spawn_bus (state, state->cancellable);
 
         if (!ret) {
                 g_printerr ("Unable to run session message bus\n");
                 exit_status = EX_SOFTWARE;
                 goto out;
         }
 
         import_environment (state, state->cancellable);
 
         ret = update_bus_environment (state, state->cancellable);
 
         if (!ret) {
                 g_printerr ("Unable to update bus environment\n");
                 exit_status = EX_SOFTWARE;
                 goto out;
         }
 
+        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) {
                 g_printerr ("Unable to run session\n");
                 exit_status = EX_SOFTWARE;
                 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 */
 
         if (state->session_subprocess == NULL) {
                 exit_status = state->session_exit_status;
         }
 
 out:
         if (state != NULL) {
                 signal_subprocesses (state);
                 wait_on_subprocesses (state);
                 clear_state (&state);
         }
 
         return exit_status;
 }
-- 
2.27.0