]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - src/seat.c
Load all users only when really needed
[sojka/lightdm.git] / src / seat.c
index 3be153c8420fd5c4768e67fadc90a099c01d53a9..9cb7ec9ba8e047a462f62dd5516e1e850df54511 100644 (file)
@@ -16,7 +16,7 @@
 #include "seat.h"
 #include "configuration.h"
 #include "guest-account.h"
-#include "greeter.h"
+#include "greeter-session.h"
 #include "session-config.h"
 
 enum {
@@ -65,6 +65,9 @@ struct SeatPrivate
 
     /* TRUE if stopped */
     gboolean stopped;
+    
+    /* The greeter to be started to replace the current one */
+    GreeterSession *replacement_greeter;
 };
 
 static void seat_logger_iface_init (LoggerInterface *iface);
@@ -75,23 +78,32 @@ G_DEFINE_TYPE_WITH_CODE (Seat, seat, G_TYPE_OBJECT,
 
 typedef struct
 {
-    const gchar *name;
+    gchar *name;
     GType type;
 } SeatModule;
 static GHashTable *seat_modules = NULL;
 
 // FIXME: Make a get_display_server() that re-uses display servers if supported
 static DisplayServer *create_display_server (Seat *seat, Session *session);
-static Greeter *create_greeter_session (Seat *seat);
+static gboolean start_display_server (Seat *seat, DisplayServer *display_server);
+static GreeterSession *create_greeter_session (Seat *seat);
 static void start_session (Seat *seat, Session *session);
 
+static void
+free_seat_module (gpointer data)
+{
+    SeatModule *module = data;
+    g_free (module->name);
+    g_free (module);
+}
+
 void
 seat_register_module (const gchar *name, GType type)
 {
     SeatModule *module;
 
     if (!seat_modules)
-        seat_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+        seat_modules = g_hash_table_new_full (g_str_hash, g_str_equal, free_seat_module, NULL);
 
     g_debug ("Registered seat module %s", name);
 
@@ -144,7 +156,19 @@ seat_get_string_list_property (Seat *seat, const gchar *name)
 gboolean
 seat_get_boolean_property (Seat *seat, const gchar *name)
 {
-    return g_strcmp0 (seat_get_string_property (seat, name), "true") == 0;
+    const gchar *value;
+    gint i, length = 0;
+
+    value = seat_get_string_property (seat, name);
+    if (!value)
+        return FALSE;
+
+    /* Count the number of non-whitespace characters */
+    for (i = 0; value[i]; i++)
+        if (!g_ascii_isspace (value[i]))
+            length = i + 1;
+
+    return strncmp (value, "true", MAX (length, 4)) == 0;
 }
 
 gint
@@ -220,12 +244,12 @@ seat_set_active_session (Seat *seat, Session *session)
         if (s == session || session_get_is_stopping (s))
             continue;
 
-        if (IS_GREETER (s))
+        if (IS_GREETER_SESSION (s))
         {
-            Greeter *greeter = GREETER (s);
+            Greeter *greeter = greeter_session_get_greeter (GREETER_SESSION (s));
             if (greeter_get_resettable (greeter))
             {
-                if (seat->priv->active_session == SESSION (greeter))
+                if (seat->priv->active_session == s)
                 {
                     l_debug (seat, "Idling greeter");
                     /* Do this in an idle callback, because we might very well
@@ -244,14 +268,11 @@ seat_set_active_session (Seat *seat, Session *session)
     }
 
     /* Lock previous sessions */
-    if (seat->priv->active_session)
-    {
-        if (session != seat->priv->active_session && !IS_GREETER (seat->priv->active_session))
-            session_lock (seat->priv->active_session);
-        g_object_unref (seat->priv->active_session);
-    }
+    if (seat->priv->active_session && session != seat->priv->active_session && !IS_GREETER_SESSION (seat->priv->active_session))
+        session_lock (seat->priv->active_session);
 
     session_activate (session);
+    g_clear_object (&seat->priv->active_session);
     seat->priv->active_session = g_object_ref (session);
 }
 
@@ -269,6 +290,53 @@ seat_get_next_session (Seat *seat)
     return seat->priv->next_session;
 }
 
+/**
+ * Obtains the active session which lightdm expects to be active.
+ *
+ * This function is different from seat_get_active_session() in that the
+ * later (in the case of local seats) dynamically finds the session that is
+ * really active (based on the active VT), whereas this function returns the
+ * session that lightdm activated last by itself, which may not be the actual
+ * active session (i.e. VT changes).
+ */
+Session *
+seat_get_expected_active_session (Seat *seat)
+{
+    g_return_val_if_fail (seat != NULL, NULL);
+    return seat->priv->active_session;
+}
+
+/**
+ * Sets the active session which lightdm expects to be active.
+ *
+ * This function is different from seat_set_active_session() in that the
+ * later performs an actual session activation, whereas this function just
+ * updates the active session after the session has been activated by some
+ * means external to lightdm (i.e. VT changes).
+ */
+void
+seat_set_externally_activated_session (Seat *seat, Session *session)
+{
+    g_return_if_fail (seat != NULL);
+    g_clear_object (&seat->priv->active_session);
+    seat->priv->active_session = g_object_ref (session);
+}
+
+Session *
+seat_find_session_by_login1_id (Seat *seat, const gchar *login1_session_id)
+{
+    GList *session_link;
+
+    for (session_link = seat->priv->sessions; session_link; session_link = session_link->next)
+    {
+        Session *session = session_link->data;
+        if (g_strcmp0 (login1_session_id, session_get_login1_session_id (session)) == 0)
+            return session;
+    }
+
+    return NULL;
+}
+
 gboolean
 seat_get_can_switch (Seat *seat)
 {
@@ -346,13 +414,14 @@ emit_upstart_signal (const gchar *signal)
 {
     g_return_if_fail (signal != NULL);
     g_return_if_fail (signal[0] != 0);
+    GSubprocess *p;
 
     if (getuid () != 0)
         return;
 
-    gchar *cmd = g_strdup_printf ("initctl -q emit %s DISPLAY_MANAGER=lightdm", signal);
-    g_spawn_command_line_async (cmd, NULL); /* OK if it fails, probably not installed */
-    g_free (cmd);
+    /* OK if it fails, probably not installed or not running upstart */
+    p = g_subprocess_new (G_SUBPROCESS_FLAGS_STDERR_SILENCE, NULL, "initctl", "-q", "emit", signal, "DISPLAY_MANAGER=lightdm", NULL);
+    g_object_unref (p);
 }
 
 static void
@@ -405,7 +474,7 @@ display_server_stopped_cb (DisplayServer *display_server, Seat *seat)
             continue;
 
         /* Stop seat if this is the only display server and it failed to start a greeter */
-        if (IS_GREETER (session) &&
+        if (IS_GREETER_SESSION (session) &&
             !session_get_is_started (session) &&
             g_list_length (seat->priv->display_servers) == 0)
         {
@@ -442,7 +511,7 @@ can_share_display_server (Seat *seat, DisplayServer *display_server)
     return seat->priv->share_display_server && display_server_get_can_share (display_server);
 }
 
-static Greeter *
+static GreeterSession *
 find_greeter_session (Seat *seat)
 {
     GList *link;
@@ -450,14 +519,14 @@ find_greeter_session (Seat *seat)
     for (link = seat->priv->sessions; link; link = link->next)
     {
         Session *session = link->data;
-        if (!session_get_is_stopping (session) && IS_GREETER (session))
-            return GREETER (session);
+        if (!session_get_is_stopping (session) && IS_GREETER_SESSION (session))
+            return GREETER_SESSION (session);
     }
 
     return NULL;
 }
 
-static Greeter *
+static GreeterSession *
 find_resettable_greeter (Seat *seat)
 {
     GList *link;
@@ -465,29 +534,30 @@ find_resettable_greeter (Seat *seat)
     for (link = seat->priv->sessions; link; link = link->next)
     {
         Session *session = link->data;
-        if (!session_get_is_stopping (session) && IS_GREETER (session) &&
-            greeter_get_resettable (GREETER (session)))
-            return GREETER (session);
+        if (!session_get_is_stopping (session) && IS_GREETER_SESSION (session) &&
+            greeter_get_resettable (greeter_session_get_greeter (GREETER_SESSION (session))))
+            return GREETER_SESSION (session);
     }
 
     return NULL;
 }
 
 static void
-set_greeter_hints (Seat *seat, Greeter *greeter_session)
+set_greeter_hints (Seat *seat, Greeter *greeter)
 {
-    greeter_clear_hints (greeter_session);
-    greeter_set_hint (greeter_session, "default-session", seat_get_string_property (seat, "user-session"));
-    greeter_set_hint (greeter_session, "hide-users", seat_get_boolean_property (seat, "greeter-hide-users") ? "true" : "false");
-    greeter_set_hint (greeter_session, "show-manual-login", seat_get_boolean_property (seat, "greeter-show-manual-login") ? "true" : "false");
-    greeter_set_hint (greeter_session, "show-remote-login", seat_get_boolean_property (seat, "greeter-show-remote-login") ? "true" : "false");
-    greeter_set_hint (greeter_session, "has-guest-account", seat_get_allow_guest (seat) && seat_get_boolean_property (seat, "greeter-allow-guest") ? "true" : "false");
+    greeter_clear_hints (greeter);
+    greeter_set_hint (greeter, "default-session", seat_get_string_property (seat, "user-session"));
+    greeter_set_hint (greeter, "hide-users", seat_get_boolean_property (seat, "greeter-hide-users") ? "true" : "false");
+    greeter_set_hint (greeter, "show-manual-login", seat_get_boolean_property (seat, "greeter-show-manual-login") ? "true" : "false");
+    greeter_set_hint (greeter, "show-remote-login", seat_get_boolean_property (seat, "greeter-show-remote-login") ? "true" : "false");
+    greeter_set_hint (greeter, "has-guest-account", seat_get_allow_guest (seat) && seat_get_boolean_property (seat, "greeter-allow-guest") ? "true" : "false");
 }
 
 static void
 switch_to_greeter_from_failed_session (Seat *seat, Session *session)
 {
-    Greeter *greeter_session;
+    GreeterSession *greeter_session;
+    Greeter *greeter;  
     gboolean existing = FALSE;
 
     /* Switch to greeter if one open */
@@ -495,28 +565,28 @@ switch_to_greeter_from_failed_session (Seat *seat, Session *session)
     if (greeter_session)
     {
         l_debug (seat, "Switching to existing greeter");
-        set_greeter_hints (seat, greeter_session);
+        set_greeter_hints (seat, greeter_session_get_greeter (greeter_session));
         existing = TRUE;
     }
     else
     {
         greeter_session = create_greeter_session (seat);
     }
+    greeter = greeter_session_get_greeter (greeter_session);
 
     if (session_get_is_guest (session))
-        greeter_set_hint (greeter_session, "select-guest", "true");
+        greeter_set_hint (greeter, "select-guest", "true");
     else
-        greeter_set_hint (greeter_session, "select-user", session_get_username (session));
+        greeter_set_hint (greeter, "select-user", session_get_username (session));
 
     if (existing)
     {
-        greeter_reset (greeter_session);
+        greeter_reset (greeter);
         seat_set_active_session (seat, SESSION (greeter_session));
     }
     else
     {
-        if (seat->priv->session_to_activate)
-            g_object_unref (seat->priv->session_to_activate);
+        g_clear_object (&seat->priv->session_to_activate);
         seat->priv->session_to_activate = g_object_ref (greeter_session);
 
         if (can_share_display_server (seat, session_get_display_server (session)))
@@ -527,7 +597,7 @@ switch_to_greeter_from_failed_session (Seat *seat, Session *session)
 
             display_server = create_display_server (seat, session);
             session_set_display_server (session, display_server);
-            if (!display_server_start (display_server))
+            if (!start_display_server (seat, display_server))
             {
                 l_debug (seat, "Failed to start display server for greeter");
                 seat_stop (seat);
@@ -545,23 +615,25 @@ static void
 start_session (Seat *seat, Session *session)
 {
     /* Use system location for greeter log file */
-    if (IS_GREETER (session))
+    if (IS_GREETER_SESSION (session))
     {
         gchar *log_dir, *filename, *log_filename;
+        gboolean backup_logs;
 
         log_dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
-        filename = g_strdup_printf ("%s-greeter.log", display_server_get_name (session_get_display_server (session)));
+        filename = g_strdup_printf ("%s-greeter.log", seat->priv->name);
         log_filename = g_build_filename (log_dir, filename, NULL);
         g_free (log_dir);
         g_free (filename);
-        session_set_log_file (session, log_filename);
+        backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");
+        session_set_log_file (session, log_filename, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND);
         g_free (log_filename);
     }
 
     if (session_start (session))
         return;
 
-    if (IS_GREETER (session))
+    if (IS_GREETER_SESSION (session))
     {
         l_debug (seat, "Failed to start greeter");
         display_server_stop (session_get_display_server (session));
@@ -577,7 +649,7 @@ run_session (Seat *seat, Session *session)
 {
     const gchar *script;
 
-    if (IS_GREETER (session))
+    if (IS_GREETER_SESSION (session))
         script = seat_get_string_property (seat, "greeter-setup-script");
     else
         script = seat_get_string_property (seat, "session-setup-script");
@@ -588,7 +660,7 @@ run_session (Seat *seat, Session *session)
         return;
     }
 
-    if (!IS_GREETER (session))
+    if (!IS_GREETER_SESSION (session))
     {
         g_signal_emit (seat, signals[RUNNING_USER_SESSION], 0, session);
         emit_upstart_signal ("desktop-session-start");
@@ -601,8 +673,7 @@ run_session (Seat *seat, Session *session)
     if (session == seat->priv->session_to_activate)
     {
         seat_set_active_session (seat, session);
-        g_object_unref (seat->priv->session_to_activate);
-        seat->priv->session_to_activate = NULL;
+        g_clear_object (&seat->priv->session_to_activate);
     }
     else if (seat->priv->active_session)
     {
@@ -646,8 +717,7 @@ greeter_active_username_changed_cb (Greeter *greeter, GParamSpec *pspec, Seat *s
 
     session = find_user_session (seat, greeter_get_active_username (greeter), seat->priv->active_session);
 
-    if (seat->priv->next_session)
-        g_object_unref (seat->priv->next_session);
+    g_clear_object (&seat->priv->next_session);
     seat->priv->next_session = session ? g_object_ref (session) : NULL;
 
     SEAT_GET_CLASS (seat)->set_next_session (seat, session);
@@ -673,7 +743,7 @@ session_authentication_complete_cb (Session *session, Seat *seat)
             run_session (seat, session);
         }
     }
-    else if (!IS_GREETER (session))
+    else if (!IS_GREETER_SESSION (session))
     {
         l_debug (seat, "Switching to greeter due to failed authentication");
         switch_to_greeter_from_failed_session (seat, session);
@@ -695,20 +765,11 @@ session_stopped_cb (Session *session, Seat *seat)
     g_signal_handlers_disconnect_matched (session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
     seat->priv->sessions = g_list_remove (seat->priv->sessions, session);
     if (session == seat->priv->active_session)
-    {
-        g_object_unref (seat->priv->active_session);
-        seat->priv->active_session = NULL;
-    }
+        g_clear_object (&seat->priv->active_session);
     if (session == seat->priv->next_session)
-    {
-        g_object_unref (seat->priv->next_session);
-        seat->priv->next_session = NULL;
-    }
+        g_clear_object (&seat->priv->next_session);
     if (session == seat->priv->session_to_activate)
-    {
-        g_object_unref (seat->priv->session_to_activate);
-        seat->priv->session_to_activate = NULL;
-    }
+        g_clear_object (&seat->priv->session_to_activate);
 
     display_server = session_get_display_server (session);
     if (!display_server)
@@ -718,7 +779,7 @@ session_stopped_cb (Session *session, Seat *seat)
     }
 
     /* Cleanup */
-    if (!IS_GREETER (session))
+    if (!IS_GREETER_SESSION (session))
     {
         const gchar *script;
         script = seat_get_string_property (seat, "session-cleanup-script");
@@ -729,10 +790,7 @@ session_stopped_cb (Session *session, Seat *seat)
     /* We were waiting for this session, but it didn't start :( */
     // FIXME: Start a greeter on this?
     if (session == seat->priv->session_to_activate)
-    {
-        g_object_unref (seat->priv->session_to_activate);
-        seat->priv->session_to_activate = NULL;
-    }
+        g_clear_object (&seat->priv->session_to_activate);
 
     if (seat->priv->stopping)
     {
@@ -740,11 +798,30 @@ session_stopped_cb (Session *session, Seat *seat)
         g_object_unref (session);
         return;
     }
+    
+    /* If there is a pending replacement greeter, start it */
+    if (IS_GREETER_SESSION (session) && seat->priv->replacement_greeter)
+    {
+        GreeterSession *replacement_greeter = seat->priv->replacement_greeter;
+        seat->priv->replacement_greeter = NULL;
+        
+        if (session_get_is_authenticated (SESSION (replacement_greeter)))
+        {
+            l_debug (seat, "Greeter stopped, running session");
+            run_session (seat, SESSION (replacement_greeter));
+        }
+        else
+        {
+            l_debug (seat, "Greeter stopped, starting session authentication");
+            start_session (seat, SESSION (replacement_greeter));
+        }
 
+        g_object_unref (replacement_greeter);
+    }
     /* If this is the greeter session then re-use this display server */
-    if (IS_GREETER (session) &&
+    else if (IS_GREETER_SESSION (session) &&
         can_share_display_server (seat, display_server) &&
-        greeter_get_start_session (GREETER (session)))
+        greeter_get_start_session (greeter_session_get_greeter (GREETER_SESSION (session))))
     {
         GList *link;
 
@@ -770,8 +847,8 @@ session_stopped_cb (Session *session, Seat *seat)
         }
     }
     /* If this is the greeter and nothing else is running then stop the seat */
-    else if (IS_GREETER (session) &&
-        !greeter_get_start_session (GREETER (session)) &&
+    else if (IS_GREETER_SESSION (session) &&
+        !greeter_get_start_session (greeter_session_get_greeter (GREETER_SESSION (session))) &&
         g_list_length (seat->priv->display_servers) == 1 &&
         g_list_nth_data (seat->priv->display_servers, 0) == display_server)
     {
@@ -779,7 +856,7 @@ session_stopped_cb (Session *session, Seat *seat)
         seat_stop (seat);
     }
     /* If we were the active session, switch to a greeter */
-    else if (!IS_GREETER (session) && session == seat_get_active_session (seat))
+    else if (!IS_GREETER_SESSION (session) && session == seat_get_active_session (seat))
     {
         l_debug (seat, "Active session stopped, starting greeter");
         if (!seat_switch_to_greeter (seat))
@@ -790,24 +867,11 @@ session_stopped_cb (Session *session, Seat *seat)
     }
 
     /* Stop the display server if no-longer required */
-    if (display_server && !display_server_get_is_stopping (display_server))
+    if (display_server && !display_server_get_is_stopping (display_server) &&
+        !SEAT_GET_CLASS (seat)->display_server_is_used (seat, display_server))
     {
-        GList *link;
-        int n_sessions = 0;
-
-        for (link = seat->priv->sessions; link; link = link->next)
-        {
-            Session *s = link->data;
-            if (s == session)
-                continue;
-            if (session_get_display_server (s) == display_server)
-                n_sessions++;
-        }
-        if (n_sessions == 0)
-        {
-            l_debug (seat, "Stopping display server, no sessions require it");
-            display_server_stop (display_server);
-        }
+        l_debug (seat, "Stopping display server, no sessions require it");
+        display_server_stop (display_server);
     }
 
     g_signal_emit (seat, signals[SESSION_REMOVED], 0, session);
@@ -912,11 +976,15 @@ find_session_config (Seat *seat, const gchar *sessions_dir, const gchar *session
     for (i = 0; dirs[i]; i++)
     {
         gchar *filename, *path;
+        const gchar *default_session_type = "x";
+
+        if (strcmp (dirs[i], WAYLAND_SESSIONS_DIR) == 0)
+            default_session_type = "wayland";
 
         filename = g_strdup_printf ("%s.desktop", session_name);
         path = g_build_filename (dirs[i], filename, NULL);
         g_free (filename);
-        session_config = session_config_new_from_file (path, &error);
+        session_config = session_config_new_from_file (path, default_session_type, &error);
         g_free (path);
         if (session_config)
             break;
@@ -933,15 +1001,20 @@ find_session_config (Seat *seat, const gchar *sessions_dir, const gchar *session
 static void
 configure_session (Session *session, SessionConfig *config, const gchar *session_name, const gchar *language)
 {
-    const gchar *desktop_name;
+    gchar **desktop_names;
 
     session_set_config (session, config);
     session_set_env (session, "XDG_SESSION_DESKTOP", session_name);
     session_set_env (session, "DESKTOP_SESSION", session_name);
     session_set_env (session, "GDMSESSION", session_name);
-    desktop_name = session_config_get_desktop_name (config);
-    if (desktop_name)
-        session_set_env (session, "XDG_CURRENT_DESKTOP", desktop_name);
+    desktop_names = session_config_get_desktop_names (config);
+    if (desktop_names)
+    {
+        gchar *value;
+        value = g_strjoinv (":", desktop_names);
+        session_set_env (session, "XDG_CURRENT_DESKTOP", value);
+        g_free (value);
+    }
     if (language && language[0] != '\0')
     {
         session_set_env (session, "LANG", language);
@@ -970,6 +1043,14 @@ create_user_session (Seat *seat, const gchar *username, gboolean autostart)
     session_name = user_get_xsession (user);
     language = user_get_language (user);
 
+    /* Override session for autologin if configured */
+    if (autostart)
+    {
+        const gchar *autologin_session_name = seat_get_string_property (seat, "autologin-session");
+        if (autologin_session_name)
+            session_name = autologin_session_name;
+    }
+
     if (!session_name)
         session_name = seat_get_string_property (seat, "user-session");
     sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
@@ -1055,14 +1136,33 @@ create_guest_session (Seat *seat, const gchar *session_name)
     return session;
 }
 
+// FIXME: This is inefficient and we already know the greeter session when we set the callbacks...
+static Session *
+get_greeter_session (Seat *seat, Greeter *greeter)
+{
+    GList *link;
+
+    /* Stop any greeters */
+    for (link = seat->priv->sessions; link; link = link->next)
+    {
+        Session *session = link->data;
+
+        if (IS_GREETER_SESSION (session) && greeter_session_get_greeter (GREETER_SESSION (session)))
+            return session;
+    }
+
+    return NULL;
+}
+
 static Session *
 greeter_create_session_cb (Greeter *greeter, Seat *seat)
 {
-    Session *session;
+    Session *greeter_session, *session;
 
+    greeter_session = get_greeter_session (seat, greeter);
     session = create_session (seat, FALSE);
-    session_set_config (session, session_get_config (SESSION (greeter)));
-    session_set_display_server (session, session_get_display_server (SESSION (greeter)));
+    session_set_config (session, session_get_config (greeter_session));
+    session_set_display_server (session, session_get_display_server (greeter_session));
 
     return g_object_ref (session);
 }
@@ -1070,14 +1170,14 @@ greeter_create_session_cb (Greeter *greeter, Seat *seat)
 static gboolean
 greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *session_name, Seat *seat)
 {
-    Session *session, *existing_session;
+    Session *session, *existing_session, *greeter_session;
     const gchar *username;
     DisplayServer *display_server;
 
     /* Get the session to use */
     if (greeter_get_guest_authenticated (greeter))
     {
-        session = create_guest_session (seat, session_name);
+        session = g_object_ref (create_guest_session (seat, session_name));
         if (!session)
             return FALSE;
         session_set_pam_service (session, seat_get_string_property (seat, "pam-autologin-service"));
@@ -1090,7 +1190,7 @@ greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *sessi
         gchar *sessions_dir = NULL;
         gchar **argv;
 
-        session = greeter_get_authentication_session (greeter);
+        session = greeter_take_authentication_session (greeter);
 
         /* Get session command to run */
         switch (type)
@@ -1133,9 +1233,8 @@ greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *sessi
     }
 
     /* Switch to this session when it is ready */
-    if (seat->priv->session_to_activate)
-        g_object_unref (seat->priv->session_to_activate);
-    seat->priv->session_to_activate = g_object_ref (session);
+    g_clear_object (&seat->priv->session_to_activate);
+    seat->priv->session_to_activate = session;
 
     /* Return to existing session if it is open */
     username = session_get_username (session);
@@ -1150,42 +1249,46 @@ greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *sessi
     }
 
     /* If can re-use the display server, stop the greeter first */
-    display_server = session_get_display_server (SESSION (greeter));
-    if (!greeter_get_resettable (greeter) &&
-        can_share_display_server (seat, display_server) &&
-        strcmp (display_server_get_session_type (display_server), session_get_session_type (session)) == 0)
+    greeter_session = get_greeter_session (seat, greeter);
+    if (greeter_session)
     {
-        l_debug (seat, "Stopping greeter; display server will be re-used for user session");
+        display_server = session_get_display_server (greeter_session);
+        if (display_server &&
+            !greeter_get_resettable (greeter) &&
+            can_share_display_server (seat, display_server) &&
+            strcmp (display_server_get_session_type (display_server), session_get_session_type (session)) == 0)
+        {
+            l_debug (seat, "Stopping greeter; display server will be re-used for user session");
 
-        /* Run on the same display server after the greeter has stopped */
-        session_set_display_server (session, display_server);
+            /* Run on the same display server after the greeter has stopped */
+            session_set_display_server (session, display_server);
 
-        /* Stop the greeter */
-        session_stop (SESSION (greeter));
+            /* Stop the greeter */
+            session_stop (greeter_session);
 
-        return TRUE;
+            return TRUE;
+        }
     }
+
     /* Otherwise start a new display server for this session */
-    else
+    display_server = create_display_server (seat, session);
+    session_set_display_server (session, display_server);
+    if (!start_display_server (seat, display_server))
     {
-        display_server = create_display_server (seat, session);
-        session_set_display_server (session, display_server);
-        if (!display_server_start (display_server))
-        {
-            l_debug (seat, "Failed to start display server for new session");
-            return FALSE;
-        }
-
-        return TRUE;
+        l_debug (seat, "Failed to start display server for new session");
+        return FALSE;
     }
+
+    return TRUE;
 }
 
-static Greeter *
+static GreeterSession *
 create_greeter_session (Seat *seat)
 {
     gchar *sessions_dir, **argv;
     SessionConfig *session_config;
-    Greeter *greeter_session;
+    GreeterSession *greeter_session;
+    Greeter *greeter;  
     const gchar *greeter_wrapper;
     const gchar *autologin_username;
     int autologin_timeout;
@@ -1210,9 +1313,10 @@ create_greeter_session (Seat *seat)
     }
 
     greeter_session = SEAT_GET_CLASS (seat)->create_greeter_session (seat);
+    greeter = greeter_session_get_greeter (greeter_session);
     session_set_config (SESSION (greeter_session), session_config);
     seat->priv->sessions = g_list_append (seat->priv->sessions, SESSION (greeter_session));
-    g_signal_connect (greeter_session, GREETER_SIGNAL_ACTIVE_USERNAME_CHANGED, G_CALLBACK (greeter_active_username_changed_cb), seat);
+    g_signal_connect (greeter, GREETER_SIGNAL_ACTIVE_USERNAME_CHANGED, G_CALLBACK (greeter_active_username_changed_cb), seat);
     g_signal_connect (greeter_session, SESSION_SIGNAL_AUTHENTICATION_COMPLETE, G_CALLBACK (session_authentication_complete_cb), seat);
     g_signal_connect (greeter_session, SESSION_SIGNAL_STOPPED, G_CALLBACK (session_stopped_cb), seat);
 
@@ -1235,15 +1339,15 @@ create_greeter_session (Seat *seat)
     session_set_argv (SESSION (greeter_session), argv);
     g_strfreev (argv);
 
-    greeter_set_pam_services (greeter_session,
+    greeter_set_pam_services (greeter,
                               seat_get_string_property (seat, "pam-service"),
                               seat_get_string_property (seat, "pam-autologin-service"));
-    g_signal_connect (greeter_session, GREETER_SIGNAL_CREATE_SESSION, G_CALLBACK (greeter_create_session_cb), seat);
-    g_signal_connect (greeter_session, GREETER_SIGNAL_START_SESSION, G_CALLBACK (greeter_start_session_cb), seat);
+    g_signal_connect (greeter, GREETER_SIGNAL_CREATE_SESSION, G_CALLBACK (greeter_create_session_cb), seat);
+    g_signal_connect (greeter, GREETER_SIGNAL_START_SESSION, G_CALLBACK (greeter_start_session_cb), seat);
 
     /* Set hints to greeter */
-    greeter_set_allow_guest (greeter_session, seat_get_allow_guest (seat));
-    set_greeter_hints (seat, greeter_session);
+    greeter_set_allow_guest (greeter, seat_get_allow_guest (seat));
+    set_greeter_hints (seat, greeter);
 
     /* Configure for automatic login */
     autologin_username = seat_get_string_property (seat, "autologin-user");
@@ -1256,12 +1360,12 @@ create_greeter_session (Seat *seat)
         gchar *value;
 
         value = g_strdup_printf ("%d", autologin_timeout);
-        greeter_set_hint (greeter_session, "autologin-timeout", value);
+        greeter_set_hint (greeter, "autologin-timeout", value);
         g_free (value);
         if (autologin_username)
-            greeter_set_hint (greeter_session, "autologin-user", autologin_username);
+            greeter_set_hint (greeter, "autologin-user", autologin_username);
         if (autologin_guest)
-            greeter_set_hint (greeter_session, "autologin-guest", "true");
+            greeter_set_hint (greeter, "autologin-guest", "true");
     }
 
     g_object_unref (session_config);
@@ -1277,7 +1381,10 @@ find_session_for_display_server (Seat *seat, DisplayServer *display_server)
     for (link = seat->priv->sessions; link; link = link->next)
     {
         Session *session = link->data;
-        if (session_get_display_server (session) == display_server && !session_get_is_stopping (session))
+
+        if (session_get_display_server (session) == display_server &&
+            !session_get_is_stopping (session) &&
+            !session_get_is_run (session))
             return session;
     }
 
@@ -1334,17 +1441,33 @@ create_display_server (Seat *seat, Session *session)
     if (!display_server)
         return NULL;
 
-    seat->priv->display_servers = g_list_append (seat->priv->display_servers, display_server);
-    g_signal_connect (display_server, DISPLAY_SERVER_SIGNAL_READY, G_CALLBACK (display_server_ready_cb), seat);
-    g_signal_connect (display_server, DISPLAY_SERVER_SIGNAL_STOPPED, G_CALLBACK (display_server_stopped_cb), seat);
+    /* Remember this display server */
+    if (!g_list_find (seat->priv->display_servers, display_server)) 
+    {
+        seat->priv->display_servers = g_list_append (seat->priv->display_servers, display_server);
+        g_signal_connect (display_server, DISPLAY_SERVER_SIGNAL_READY, G_CALLBACK (display_server_ready_cb), seat);
+        g_signal_connect (display_server, DISPLAY_SERVER_SIGNAL_STOPPED, G_CALLBACK (display_server_stopped_cb), seat);
+    }
 
     return display_server;
 }
 
+static gboolean
+start_display_server (Seat *seat, DisplayServer *display_server)
+{
+    if (display_server_get_is_ready (display_server))
+    {
+        display_server_ready_cb (display_server, seat);
+        return TRUE;
+    }
+    else
+        return display_server_start (display_server);
+}
+
 gboolean
 seat_switch_to_greeter (Seat *seat)
 {
-    Greeter *greeter_session;
+    GreeterSession *greeter_session;
     DisplayServer *display_server;
 
     g_return_val_if_fail (seat != NULL, FALSE);
@@ -1365,20 +1488,20 @@ seat_switch_to_greeter (Seat *seat)
     if (!greeter_session)
         return FALSE;
 
-    if (seat->priv->session_to_activate)
-        g_object_unref (seat->priv->session_to_activate);
+    g_clear_object (&seat->priv->session_to_activate);
     seat->priv->session_to_activate = g_object_ref (greeter_session);
 
     display_server = create_display_server (seat, SESSION (greeter_session));
     session_set_display_server (SESSION (greeter_session), display_server);
 
-    return display_server_start (display_server);
+    return start_display_server (seat, display_server);
 }
 
 static void
 switch_authentication_complete_cb (Session *session, Seat *seat)
 {
-    Greeter *greeter_session;
+    GreeterSession *greeter_session;
+    Greeter *greeter;
     DisplayServer *display_server;
     gboolean existing = FALSE;
 
@@ -1398,12 +1521,11 @@ switch_authentication_complete_cb (Session *session, Seat *seat)
         else
         {
             l_debug (seat, "Session authenticated, starting display server");
-            if (seat->priv->session_to_activate)
-                g_object_unref (seat->priv->session_to_activate);
+            g_clear_object (&seat->priv->session_to_activate);
             seat->priv->session_to_activate = g_object_ref (session);
             display_server = create_display_server (seat, session);
             session_set_display_server (session, display_server);
-            display_server_start (display_server);
+            start_display_server (seat, display_server);
         }
 
         return;
@@ -1416,7 +1538,7 @@ switch_authentication_complete_cb (Session *session, Seat *seat)
     if (greeter_session)
     {
         l_debug (seat, "Switching to existing greeter to authenticate session");
-        set_greeter_hints (seat, greeter_session);
+        set_greeter_hints (seat, greeter_session_get_greeter (greeter_session));
         existing = TRUE;
     }
     else
@@ -1424,26 +1546,26 @@ switch_authentication_complete_cb (Session *session, Seat *seat)
         l_debug (seat, "Starting greeter to authenticate session");
         greeter_session = create_greeter_session (seat);
     }
+    greeter = greeter_session_get_greeter (greeter_session);
 
     if (session_get_is_guest (session))
-        greeter_set_hint (greeter_session, "select-guest", "true");
+        greeter_set_hint (greeter, "select-guest", "true");
     else
-        greeter_set_hint (greeter_session, "select-user", session_get_username (session));
+        greeter_set_hint (greeter, "select-user", session_get_username (session));
 
     if (existing)
     {
-        greeter_reset (greeter_session);
+        greeter_reset (greeter);
         seat_set_active_session (seat, SESSION (greeter_session));
     }
     else
     {
-        if (seat->priv->session_to_activate)
-            g_object_unref (seat->priv->session_to_activate);
+        g_clear_object (&seat->priv->session_to_activate);
         seat->priv->session_to_activate = g_object_ref (greeter_session);
 
         display_server = create_display_server (seat, SESSION (greeter_session));
         session_set_display_server (SESSION (greeter_session), display_server);
-        display_server_start (display_server);
+        start_display_server (seat, display_server);
     }
 }
 
@@ -1514,27 +1636,30 @@ seat_switch_to_guest (Seat *seat, const gchar *session_name)
 
     display_server = create_display_server (seat, session);
 
-    if (seat->priv->session_to_activate)
-        g_object_unref (seat->priv->session_to_activate);
+    g_clear_object (&seat->priv->session_to_activate);
     seat->priv->session_to_activate = g_object_ref (session);
     session_set_pam_service (session, seat_get_string_property (seat, "pam-autologin-service"));
     session_set_display_server (session, display_server);
 
-    return display_server_start (display_server);
+    return start_display_server (seat, display_server);
 }
 
 gboolean
 seat_lock (Seat *seat, const gchar *username)
 {
-    Greeter *greeter_session;
-    DisplayServer *display_server;
-    gboolean existing = FALSE;
+    GreeterSession *greeter_session;
+    Greeter *greeter;
+    DisplayServer *display_server = NULL;
+    gboolean reset_existing = FALSE;
+    gboolean reuse_xserver = FALSE;
 
     g_return_val_if_fail (seat != NULL, FALSE);
 
     if (!seat_get_can_switch (seat))
         return FALSE;
 
+    // FIXME: If already locked then don't bother...
+
     l_debug (seat, "Locking");
 
     /* Switch to greeter we can reuse */
@@ -1542,36 +1667,57 @@ seat_lock (Seat *seat, const gchar *username)
     if (greeter_session)
     {
         l_debug (seat, "Switching to existing greeter");
-        set_greeter_hints (seat, greeter_session);
-        existing = TRUE;
+        set_greeter_hints (seat, greeter_session_get_greeter (greeter_session));
+        reset_existing = TRUE;
     }
     else
     {
+        /* If the existing greeter can't be reused, stop it and reuse its display server */
+        greeter_session = find_greeter_session (seat);
+        if (greeter_session)
+        {
+            display_server = session_get_display_server (SESSION (greeter_session));
+            if (!session_get_is_stopping (SESSION (greeter_session)))
+            {
+                l_debug (seat, "Stopping session");
+                session_stop (SESSION (greeter_session));
+            }
+            reuse_xserver = TRUE;
+        }
+        
         greeter_session = create_greeter_session (seat);
         if (!greeter_session)
             return FALSE;
     }
+    greeter = greeter_session_get_greeter (greeter_session);
 
-    greeter_set_hint (greeter_session, "lock-screen", "true");
+    greeter_set_hint (greeter, "lock-screen", "true");
     if (username)
-        greeter_set_hint (greeter_session, "select-user", username);
+        greeter_set_hint (greeter, "select-user", username);
 
-    if (existing)
+    if (reset_existing)
     {
-        greeter_reset (greeter_session);
+        greeter_reset (greeter);
         seat_set_active_session (seat, SESSION (greeter_session));
         return TRUE;
     }
     else
     {
-        display_server = create_display_server (seat, SESSION (greeter_session));
+        if (!reuse_xserver)
+            display_server = create_display_server (seat, SESSION (greeter_session));
+        session_set_display_server (SESSION (greeter_session), display_server);
 
-        if (seat->priv->session_to_activate)
-            g_object_unref (seat->priv->session_to_activate);
+        g_clear_object (&seat->priv->session_to_activate);
         seat->priv->session_to_activate = g_object_ref (greeter_session);
-        session_set_display_server (SESSION (greeter_session), display_server);
 
-        return display_server_start (display_server);
+        if (reuse_xserver)
+        {
+            g_clear_object (&seat->priv->replacement_greeter);
+            seat->priv->replacement_greeter = g_object_ref (greeter_session);
+            return TRUE;
+        }
+        else
+            return start_display_server (seat, display_server);
     }
 }
 
@@ -1639,13 +1785,12 @@ seat_real_start (Seat *seat)
         {
             DisplayServer *display_server;
 
-            if (seat->priv->session_to_activate)
-                g_object_unref (seat->priv->session_to_activate);
+            g_clear_object (&seat->priv->session_to_activate);
             seat->priv->session_to_activate = g_object_ref (session);
 
             display_server = create_display_server (seat, session);
             session_set_display_server (session, display_server);
-            if (!display_server || !display_server_start (display_server))
+            if (!display_server || !start_display_server (seat, display_server))
             {
                 l_debug (seat, "Can't create display server for automatic login");
                 session_stop (session);
@@ -1659,7 +1804,7 @@ seat_real_start (Seat *seat)
     /* Fallback to a greeter */
     if (!session)
     {
-        Greeter *greeter_session;
+        GreeterSession *greeter_session;
         DisplayServer *display_server;
 
         greeter_session = create_greeter_session (seat);
@@ -1669,14 +1814,13 @@ seat_real_start (Seat *seat)
             return FALSE;
         }
 
-        if (seat->priv->session_to_activate)
-            g_object_unref (seat->priv->session_to_activate);
+        g_clear_object (&seat->priv->session_to_activate);
         seat->priv->session_to_activate = g_object_ref (greeter_session);
         session = SESSION (greeter_session);
 
         display_server = create_display_server (seat, session);
         session_set_display_server (session, display_server);
-        if (!display_server || !display_server_start (display_server))
+        if (!display_server || !start_display_server (seat, display_server))
         {
             l_debug (seat, "Can't create display server for greeter");
             session_stop (session);
@@ -1700,23 +1844,81 @@ seat_real_start (Seat *seat)
 
         background_display_server = create_display_server (seat, background_session);
         session_set_display_server (background_session, background_display_server);
-        if (!display_server_start (background_display_server))
+        if (!start_display_server (seat, background_display_server))
             l_warning (seat, "Failed to start display server for background session");
     }
 
     return TRUE;
 }
 
-static Greeter *
+static DisplayServer *
+seat_real_create_display_server (Seat *seat, Session *session)
+{
+    return NULL;
+}
+
+static gboolean
+seat_real_display_server_is_used (Seat *seat, DisplayServer *display_server)
+{
+    GList *link;
+
+    for (link = seat->priv->sessions; link; link = link->next)
+    {
+        Session *session = link->data;
+        DisplayServer *d;
+
+        d = session_get_display_server (session);
+        if (!d)
+            continue;
+
+        if (d == display_server || display_server_get_parent (d) == display_server)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static GreeterSession *
 seat_real_create_greeter_session (Seat *seat)
 {
-    return greeter_new ();
+    return greeter_session_new ();
+}
+
+static Session *
+create_session_cb (Greeter *greeter, Seat *seat)
+{
+    return g_object_ref (create_session (seat, FALSE));
+}
+
+static Greeter *
+create_greeter_cb (Session *session, Seat *seat)
+{
+    Greeter *greeter;
+
+    greeter = greeter_new ();
+
+    greeter_set_pam_services (greeter,
+                              seat_get_string_property (seat, "pam-service"),
+                              seat_get_string_property (seat, "pam-autologin-service"));
+    g_signal_connect (greeter, GREETER_SIGNAL_CREATE_SESSION, G_CALLBACK (create_session_cb), seat);
+    g_signal_connect (greeter, GREETER_SIGNAL_START_SESSION, G_CALLBACK (greeter_start_session_cb), seat);
+
+    /* Set hints to greeter */
+    greeter_set_allow_guest (greeter, seat_get_allow_guest (seat));
+    set_greeter_hints (seat, greeter);
+
+    return greeter;
 }
 
 static Session *
 seat_real_create_session (Seat *seat)
 {
-    return session_new ();
+    Session *session;
+
+    session = session_new ();
+    g_signal_connect (session, SESSION_SIGNAL_CREATE_GREETER, G_CALLBACK (create_greeter_cb), seat);
+
+    return session;
 }
 
 static void
@@ -1785,11 +1987,9 @@ seat_init (Seat *seat)
 static void
 seat_finalize (GObject *object)
 {
-    Seat *self;
+    Seat *self = SEAT (object);
     GList *link;
 
-    self = SEAT (object);
-
     g_free (self->priv->name);
     g_hash_table_unref (self->priv->properties);
     for (link = self->priv->display_servers; link; link = link->next)
@@ -1804,12 +2004,10 @@ seat_finalize (GObject *object)
         g_signal_handlers_disconnect_matched (session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
     }
     g_list_free_full (self->priv->sessions, g_object_unref);
-    if (self->priv->active_session)
-        g_object_unref (self->priv->active_session);
-    if (self->priv->next_session)
-        g_object_unref (self->priv->next_session);
-    if (self->priv->session_to_activate)
-        g_object_unref (self->priv->session_to_activate);
+    g_clear_object (&self->priv->active_session);
+    g_clear_object (&self->priv->next_session);
+    g_clear_object (&self->priv->session_to_activate);
+    g_clear_object (&self->priv->replacement_greeter);
 
     G_OBJECT_CLASS (seat_parent_class)->finalize (object);
 }
@@ -1821,6 +2019,8 @@ seat_class_init (SeatClass *klass)
 
     klass->setup = seat_real_setup;
     klass->start = seat_real_start;
+    klass->create_display_server = seat_real_create_display_server;
+    klass->display_server_is_used = seat_real_display_server_is_used;  
     klass->create_greeter_session = seat_real_create_greeter_session;
     klass->create_session = seat_real_create_session;
     klass->set_active_session = seat_real_set_active_session;