]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blobdiff - src/seat.c
Merge from trunk
[sojka/lightdm.git] / src / seat.c
index 0ed76a425a4ecd004a8c5787f8b43f9853059764..b183f745ec38ea147b9784a69d3217213c5a0994 100644 (file)
 #include <sys/wait.h>
 
 #include "seat.h"
+#include "configuration.h"
 #include "guest-account.h"
+#include "greeter.h"
+#include "session-config.h"
 
 enum {
-    DISPLAY_ADDED,
-    DISPLAY_REMOVED,
+    SESSION_ADDED,
+    RUNNING_USER_SESSION,
+    SESSION_REMOVED,
     STOPPED,
     LAST_SIGNAL
 };
@@ -35,11 +39,20 @@ struct SeatPrivate
     /* TRUE if display server can be shared for sessions */
     gboolean share_display_server;
 
-    /* Name of guest account */
-    gchar *guest_username;
+    /* The display servers on this seat */
+    GList *display_servers;
 
-    /* The displays for this seat */
-    GList *displays;
+    /* The sessions on this seat */
+    GList *sessions;
+
+    /* The last session set to active */
+    Session *active_session;
+
+    /* The session to set active when it starts */
+    Session *session_to_activate;
+  
+    /* TRUE once we have started */
+    gboolean started;
 
     /* TRUE if stopping this seat (waiting for displays to stop) */
     gboolean stopping;
@@ -48,6 +61,11 @@ struct SeatPrivate
     gboolean stopped;
 };
 
+/* PAM services to use */
+#define GREETER_SERVICE   "lightdm-greeter"
+#define USER_SERVICE      "lightdm"
+#define AUTOLOGIN_SERVICE "lightdm-autologin"
+
 G_DEFINE_TYPE (Seat, seat, G_TYPE_OBJECT);
 
 typedef struct
@@ -57,7 +75,10 @@ typedef struct
 } SeatModule;
 static GHashTable *seat_modules = NULL;
 
-static Display *create_display (Seat *seat);
+// FIXME: Make a get_display_server() that re-uses display servers if supported
+static DisplayServer *create_display_server (Seat *seat, const gchar *session_type);
+static Greeter *create_greeter_session (Seat *seat);
+static void start_session (Seat *seat, Session *session);
 
 void
 seat_register_module (const gchar *name, GType type)
@@ -100,13 +121,6 @@ seat_set_property (Seat *seat, const gchar *name, const gchar *value)
     g_hash_table_insert (seat->priv->properties, g_strdup (name), g_strdup (value));
 }
 
-gboolean
-seat_has_property (Seat *seat, const gchar *name)
-{
-    g_return_val_if_fail (seat != NULL, FALSE);
-    return g_hash_table_lookup (seat->priv->properties, name) != NULL;
-}
-
 const gchar *
 seat_get_string_property (Seat *seat, const gchar *name)
 {
@@ -155,105 +169,73 @@ seat_start (Seat *seat)
 }
 
 GList *
-seat_get_displays (Seat *seat)
+seat_get_sessions (Seat *seat)
 {
     g_return_val_if_fail (seat != NULL, NULL);
-    return seat->priv->displays;
+    return seat->priv->sessions;
 }
 
 void
-seat_set_active_display (Seat *seat, Display *display)
-{
-    g_return_if_fail (seat != NULL);
-    SEAT_GET_CLASS (seat)->set_active_display (seat, display);
-}
-
-Display *
-seat_get_active_display (Seat *seat)
+seat_set_active_session (Seat *seat, Session *session)
 {
-    g_return_val_if_fail (seat != NULL, NULL);
-    return SEAT_GET_CLASS (seat)->get_active_display (seat);
-}
+    GList *link;
 
-gboolean
-seat_get_can_switch (Seat *seat)
-{
-    g_return_val_if_fail (seat != NULL, FALSE);
-    return seat->priv->can_switch;
-}
+    g_return_if_fail (seat != NULL);
 
-gboolean
-seat_get_allow_guest (Seat *seat)
-{
-    g_return_val_if_fail (seat != NULL, FALSE);  
-    return seat_get_boolean_property (seat, "allow-guest") && guest_account_is_installed ();
-}
+    /* Unlock this session */
+    if (session != seat->priv->active_session && !IS_GREETER (session))
+        session_unlock (session);
 
-gboolean
-seat_get_greeter_allow_guest (Seat *seat)
-{
-    g_return_val_if_fail (seat != NULL, FALSE);  
-    return seat_get_allow_guest (seat) && seat_get_boolean_property (seat, "greeter-allow-guest");
-}
+    SEAT_GET_CLASS (seat)->set_active_session (seat, session);
 
-static gboolean
-switch_to_user (Seat *seat, const gchar *username, gboolean unlock)
-{
-    GList *link;
-
-    /* Switch to active display if it exists */
-    for (link = seat->priv->displays; link; link = link->next)
+    /* Stop any greeters */
+    for (link = seat->priv->sessions; link; link = link->next)
     {
-        Display *display = link->data;
+        Session *s = link->data;
 
-        if (display_get_is_stopped (display))
+        if (s == session || session_get_is_stopping (s))
             continue;
 
-        /* If already logged in, then switch to that display */
-        if (g_strcmp0 (display_get_username (display), username) == 0)        
+        if (IS_GREETER (s))
         {
-            if (username)
-                g_debug ("Switching to existing session for user %s", username);
-            else
-                g_debug ("Switching to existing greeter");
-            if (unlock)
-                display_unlock (display);
-            seat_set_active_display (seat, display);
-            return TRUE;
+            g_debug ("Stopping greeter");
+            session_stop (s);
         }
     }
 
-    return FALSE;
+    /* 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);
+    }
+    seat->priv->active_session = g_object_ref (session);
 }
 
-static gboolean
-display_switch_to_user_cb (Display *display, User *user, Seat *seat)
+Session *
+seat_get_active_session (Seat *seat)
 {
-    return switch_to_user (seat, user_get_name (user), TRUE);
+    g_return_val_if_fail (seat != NULL, NULL);
+    return SEAT_GET_CLASS (seat)->get_active_session (seat);
 }
 
-static gboolean
-display_switch_to_guest_cb (Display *display, Seat *seat)
+gboolean
+seat_get_can_switch (Seat *seat)
 {
-    /* No guest account */
-    if (!seat->priv->guest_username)
-        return FALSE;
-
-    return switch_to_user (seat, seat->priv->guest_username, TRUE);
+    g_return_val_if_fail (seat != NULL, FALSE);
+    return seat->priv->can_switch;
 }
 
-static const gchar *
-display_get_guest_username_cb (Display *display, Seat *seat)
+gboolean
+seat_get_allow_guest (Seat *seat)
 {
-    if (seat->priv->guest_username)
-        return seat->priv->guest_username;
-
-    seat->priv->guest_username = guest_account_setup ();
-    return g_strdup (seat->priv->guest_username);
+    g_return_val_if_fail (seat != NULL, FALSE);
+    return seat_get_boolean_property (seat, "allow-guest") && guest_account_is_installed ();
 }
 
 static gboolean
-run_script (Seat *seat, Display *display, const gchar *script_name, User *user)
+run_script (Seat *seat, DisplayServer *display_server, const gchar *script_name, User *user)
 {
     Process *script;
     gboolean result = FALSE;
@@ -286,7 +268,7 @@ run_script (Seat *seat, Display *display, const gchar *script_name, User *user)
     else
         process_set_env (script, "HOME", "/");
 
-    SEAT_GET_CLASS (seat)->run_script (seat, display, script);
+    SEAT_GET_CLASS (seat)->run_script (seat, display_server, script);
 
     if (process_start (script, TRUE))
     {
@@ -306,7 +288,7 @@ run_script (Seat *seat, Display *display, const gchar *script_name, User *user)
 }
 
 static void
-seat_real_run_script (Seat *seat, Display *display, Process *process)
+seat_real_run_script (Seat *seat, DisplayServer *display_server, Process *process)
 {  
 }
 
@@ -324,271 +306,884 @@ emit_upstart_signal (const gchar *signal)
     g_free (cmd);
 }
 
+static void
+check_stopped (Seat *seat)
+{
+    if (seat->priv->stopping &&
+        !seat->priv->stopped &&
+        g_list_length (seat->priv->display_servers) == 0 &&
+        g_list_length (seat->priv->sessions) == 0)
+    {
+        seat->priv->stopped = TRUE;
+        g_debug ("Seat stopped");
+        g_signal_emit (seat, signals[STOPPED], 0);
+    }
+}
+
 static gboolean
-display_display_server_ready_cb (Display *display, Seat *seat)
+get_start_local_sessions (Seat *seat)
 {
-    const gchar *script;
+    return SEAT_GET_CLASS (seat)->get_start_local_sessions (seat);
+}
 
-    /* Run setup script */
-    script = seat_get_string_property (seat, "display-setup-script");
-    if (script && !run_script (seat, display, script, NULL))
-        return FALSE;
+static void
+display_server_stopped_cb (DisplayServer *display_server, Seat *seat)
+{
+    GList *list, *link;
+    Session *active_session;
 
-    emit_upstart_signal ("login-session-start");
+    g_debug ("Display server stopped");
 
-    return TRUE;
-}
+    g_signal_handlers_disconnect_matched (display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
+    seat->priv->display_servers = g_list_remove (seat->priv->display_servers, display_server);
 
-static Session *
-display_create_session_cb (Display *display, const gchar *username, Seat *seat)
-{
-    GList *link;
-    Display *user_display = NULL;
+    if (seat->priv->stopping || !seat->priv->started)
+    {
+        check_stopped (seat);
+        g_object_unref (display_server);
+        return;
+    }
 
-    /* Find display belonging to username, if any */
-    for (link = seat->priv->displays; link; link = link->next)
+    /* Stop all sessions on this display server */
+    list = g_list_copy (seat->priv->sessions);
+    for (link = list; link; link = link->next)
+        g_object_ref (link->data);
+    for (link = list; link; link = link->next)
     {
-        Display *display = link->data;
+        Session *session = link->data;
 
-        if (!display_get_is_stopped (display) && g_strcmp0 (display_get_username (display), username) == 0)
-            user_display = display;
+        if (session_get_display_server (session) != display_server || session_get_is_stopping (session))
+            continue;
+
+        /* Stop seat if this is the only display server and it failed to start a greeter */
+        if (IS_GREETER (session) &&
+            !session_get_is_started (session) &&
+            g_list_length (seat->priv->display_servers) == 0)
+        {
+            g_debug ("Stopping seat, greeter display server failed to start");
+            seat_stop (seat);
+        }
+
+        g_debug ("Stopping session");
+        session_stop (session);
     }
+    g_list_free_full (list, g_object_unref);
 
-    return SEAT_GET_CLASS (seat)->create_session (seat, display, user_display);
+    if (!seat->priv->stopping && get_start_local_sessions (seat))
+    {
+        /* If we were the active session, switch to a greeter */
+        active_session = seat_get_active_session (seat);
+        if (!active_session || session_get_display_server (active_session) == display_server)
+        {
+            g_debug ("Active display server stopped, starting greeter");
+            seat_switch_to_greeter (seat);
+        }
+    }
+
+    g_object_unref (display_server);
 }
 
 static gboolean
-display_start_greeter_cb (Display *display, Seat *seat)
+can_share_display_server (Seat *seat, DisplayServer *display_server)
 {
-    Session *session;
-    const gchar *script;
+    return seat->priv->share_display_server && display_server_get_can_share (display_server);
+}
+
+static void
+switch_to_greeter_from_failed_session (Seat *seat, Session *session)
+{
+    Greeter *greeter_session;
 
-    session = display_get_session (display);
+    greeter_session = create_greeter_session (seat);
+    if (session_get_is_guest (session))
+        greeter_set_hint (greeter_session, "select-guest", "true");
+    else
+        greeter_set_hint (greeter_session, "select-user", session_get_username (session));
+    if (seat->priv->session_to_activate)
+        g_object_unref (seat->priv->session_to_activate);
+    seat->priv->session_to_activate = g_object_ref (greeter_session);
 
-    script = seat_get_string_property (seat, "greeter-setup-script");
-    if (script)
-        return !run_script (seat, display, script, session_get_user (session));
+    if (can_share_display_server (seat, session_get_display_server (session)))
+        session_set_display_server (SESSION (greeter_session), session_get_display_server (session));
     else
-        return FALSE;
+    {
+        DisplayServer *display_server;
+
+        display_server = create_display_server (seat, session_get_session_type (session));
+        if (!display_server_start (display_server))
+        {
+            g_debug ("Failed to start display server for greeter");
+            seat_stop (seat);
+        }
+
+        session_set_display_server (session, display_server);
+    }
+
+    start_session (seat, SESSION (greeter_session));
+
+    /* Stop failed session */
+    session_stop (session);
 }
 
-static gboolean
-display_start_session_cb (Display *display, Seat *seat)
+static void
+start_session (Seat *seat, Session *session)
+{
+    /* Use system location for greeter log file */
+    if (IS_GREETER (session))
+    {
+        gchar *log_dir, *filename, *log_filename;
+
+        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)));
+        log_filename = g_build_filename (log_dir, filename, NULL);
+        g_free (log_dir);
+        g_free (filename);
+        session_set_log_file (session, log_filename);
+        g_free (log_filename);
+    }
+
+    if (session_start (session))
+        return;
+
+    if (IS_GREETER (session))
+    {
+        g_debug ("Failed to start greeter");
+        display_server_stop (session_get_display_server (session));
+        return;
+    }
+
+    g_debug ("Failed to start session, starting greeter");
+    switch_to_greeter_from_failed_session (seat, session);
+}
+
+static void
+run_session (Seat *seat, Session *session)
 {
-    Session *session;
     const gchar *script;
 
-    session = display_get_session (display);
+    if (IS_GREETER (session))
+        script = seat_get_string_property (seat, "greeter-setup-script");
+    else
+        script = seat_get_string_property (seat, "session-setup-script");
+    if (script && !run_script (seat, session_get_display_server (session), script, NULL))
+    {
+        g_debug ("Switching to greeter due to failed setup script");
+        switch_to_greeter_from_failed_session (seat, session);
+        return;
+    }
+
+    if (!IS_GREETER (session))
+    {
+        g_signal_emit (seat, signals[RUNNING_USER_SESSION], 0, session);
+        emit_upstart_signal ("desktop-session-start");
+    }
+
+    session_run (session);
+
+    // FIXME: Wait until the session is ready
+
+    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;
+    }
+}
 
-    script = seat_get_string_property (seat, "session-setup-script");
-    if (script)
-        return !run_script (seat, display, script, session_get_user (session));
+static void
+session_authentication_complete_cb (Session *session, Seat *seat)
+{
+    if (session_get_is_authenticated (session))
+    {
+        g_debug ("Session authenticated, running command");
+        run_session (seat, session);
+    }
+    else if (!IS_GREETER (session))
+    {
+        g_debug ("Switching to greeter due to failed authentication");
+        switch_to_greeter_from_failed_session (seat, session);
+    }
     else
-        return FALSE;
+    {
+        g_debug ("Stopping session that failed authentication");
+        session_stop (session);
+    }
 }
 
 static void
 session_stopped_cb (Session *session, Seat *seat)
 {
-    Display *display = NULL;
+    DisplayServer *display_server;
+
+    g_debug ("Session stopped");
+
+    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;
+    }
+    if (session == seat->priv->session_to_activate)
+    {
+        g_object_unref (seat->priv->session_to_activate);
+        seat->priv->session_to_activate = NULL;
+    }
+
+    display_server = session_get_display_server (session);
+    if (!display_server)
+    {
+        g_object_unref (session);
+        return;
+    }
+
+    /* Cleanup */
+    if (!IS_GREETER (session))
+    {
+        const gchar *script;
+        script = seat_get_string_property (seat, "session-cleanup-script");
+        if (script)
+            run_script (seat, display_server, script, session_get_user (session));
+    }
+
+    /* 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;
+    }
+
+    if (seat->priv->stopping)
+    {
+        check_stopped (seat);
+        g_object_unref (session);
+        return;
+    }
+
+    /* If this is the greeter session then re-use this display server */
+    if (IS_GREETER (session) &&
+        can_share_display_server (seat, display_server) &&
+        greeter_get_start_session (GREETER (session)))
+    {
+        GList *link;
+
+        for (link = seat->priv->sessions; link; link = link->next)
+        {
+            Session *s = link->data;
+
+            /* Skip this session and sessions on other display servers */
+            if (s == session || session_get_display_server (s) != display_server || session_get_is_stopping (s))
+                continue;
+
+            if (session_get_is_authenticated (s))
+            {
+                g_debug ("Greeter stopped, running session");
+                run_session (seat, s);
+            }
+            else
+            {
+                g_debug ("Greeter stopped, starting session authentication");
+                start_session (seat, s);
+            }
+            break;
+        }
+    }
+    /* 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)) &&
+        g_list_length (seat->priv->display_servers) == 1 &&
+        g_list_nth_data (seat->priv->display_servers, 0) == display_server)
+    {
+        g_debug ("Stopping seat, failed to start a greeter");
+        seat_stop (seat);
+    }
+    /* If we were the active session, switch to a greeter */
+    else if (!IS_GREETER (session) && session == seat_get_active_session (seat))
+    {
+        g_debug ("Active session stopped, starting greeter");
+        seat_switch_to_greeter (seat);
+    }
+
+    /* Stop the display server if no-longer required */
+    if (display_server && !display_server_get_is_stopping (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)
+        {
+            g_debug ("Stopping display server, no sessions require it");
+            display_server_stop (display_server);
+        }
+    }
+
+    g_signal_emit (seat, signals[SESSION_REMOVED], 0, session);
+    g_object_unref (session);
+}
+
+static void
+set_session_env (Session *session)
+{
+    /* Connect using the session bus */
+    if (getuid () != 0)
+    {
+        if (g_getenv ("DBUS_SESSION_BUS_ADDRESS"))
+            session_set_env (session, "DBUS_SESSION_BUS_ADDRESS", g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
+        session_set_env (session, "LDM_BUS", "SESSION");
+        if (g_getenv ("LD_PRELOAD"))
+            session_set_env (session, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
+        if (g_getenv ("LD_LIBRARY_PATH"))
+            session_set_env (session, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
+        if (g_getenv ("PATH"))
+            session_set_env (session, "PATH", g_getenv ("PATH"));
+    }
+
+    /* Variables required for regression tests */
+    if (g_getenv ("LIGHTDM_TEST_ROOT"))
+    {
+        session_set_env (session, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
+        session_set_env (session, "DBUS_SYSTEM_BUS_ADDRESS", g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
+        session_set_env (session, "DBUS_SESSION_BUS_ADDRESS", g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
+        session_set_env (session, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
+        session_set_env (session, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
+        session_set_env (session, "GI_TYPELIB_PATH", g_getenv ("GI_TYPELIB_PATH"));
+    }
+}
+
+static Session *
+find_user_session (Seat *seat, const gchar *username)
+{
     GList *link;
-    const gchar *script;
+
+    if (!username)
+        return NULL;
+
+    for (link = seat->priv->sessions; link; link = link->next)
+    {
+        Session *session = link->data;
+
+        if (!session_get_is_stopping (session) && strcmp (session_get_username (session), username) == 0)
+            return session;
+    }
+
+    return NULL;
+}
+
+static Session *
+create_session (Seat *seat, gboolean autostart, const gchar *username)
+{
+    Session *session;
+    Session *user_session;
+
+    user_session = find_user_session (seat, username);
+    session = SEAT_GET_CLASS (seat)->create_session (seat, user_session);
+    seat->priv->sessions = g_list_append (seat->priv->sessions, session);
+    if (autostart)
+        g_signal_connect (session, "authentication-complete", G_CALLBACK (session_authentication_complete_cb), seat);
+    g_signal_connect (session, "stopped", G_CALLBACK (session_stopped_cb), seat);
+
+    set_session_env (session);
+
+    g_signal_emit (seat, signals[SESSION_ADDED], 0, session);
+
+    return session;
+}
+
+static gchar **
+get_session_argv (SessionConfig *session_config, const gchar *session_wrapper)
+{
+    gboolean result;
+    int argc;
+    gchar **argv, *path;
+    GError *error = NULL;
+
+    /* If configured, run sessions through a wrapper */
+    if (session_wrapper)
+    {
+        argv = g_malloc (sizeof (gchar *) * 3);
+        path = g_find_program_in_path (session_wrapper);
+        argv[0] = path ? path : g_strdup (session_wrapper);
+        argv[1] = g_strdup (session_config_get_command (session_config));
+        argv[2] = NULL;
+        return argv;
+    }
+
+    /* Split command into an array listing and make command absolute */
+    result = g_shell_parse_argv (session_config_get_command (session_config), &argc, &argv, &error);
+    if (error)
+        g_debug ("Invalid session command '%s': %s", session_config_get_command (session_config), error->message);
+    g_clear_error (&error);
+    if (!result)
+        return NULL;
+    path = g_find_program_in_path (argv[0]);
+    if (path)
+    {
+        g_free (argv[0]);
+        argv[0] = path;
+    }
   
-    /* Work out what display this session is on, it's a bit hacky because we really should know already... */
-    for (link = seat->priv->displays; link; link = link->next)
+    return argv;
+}
+
+static SessionConfig *
+find_session_config (const gchar *sessions_dir, const gchar *session_name)
+{
+    gchar **dirs;
+    SessionConfig *session_config = NULL;
+    int i;
+    GError *error = NULL;
+
+    g_return_val_if_fail (sessions_dir != NULL, NULL);
+    g_return_val_if_fail (session_name != NULL, NULL);
+
+    dirs = g_strsplit (sessions_dir, ":", -1);
+    for (i = 0; dirs[i]; i++)
     {
-        Display *d = link->data;
-        if (display_get_session (d) == session)
-        {
-            display = d;
+        gchar *filename, *path;
+
+        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);
+        g_free (path);
+        if (session_config)
             break;
+
+        if (dirs[i+1] == NULL)
+            g_debug ("Failed to find session configuration %s", session_name);
+        g_clear_error (&error);
+    }
+    g_strfreev (dirs);
+
+    return session_config;
+}
+
+static Session *
+create_user_session (Seat *seat, const gchar *username)
+{
+    User *user;
+    gchar *sessions_dir;
+    const gchar *session_name, *language;
+    SessionConfig *session_config;
+    Session *session = NULL;
+
+    g_debug ("Creating user session");
+
+    /* Load user preferences */
+    user = accounts_get_user_by_name (username);
+    if (!user)
+    {
+        g_debug ("Can't login unknown user '%s'", username);
+        return NULL;
+    }
+    session_name = user_get_xsession (user);
+    language = user_get_language (user);
+
+    if (!session_name)
+        session_name = seat_get_string_property (seat, "user-session");
+    sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
+    session_config = find_session_config (sessions_dir, session_name);
+    g_free (sessions_dir);
+    if (session_config)
+    {
+        gchar **argv;
+
+        session = create_session (seat, TRUE, username);
+        session_set_session_type (session, session_config_get_session_type (session_config));
+        session_set_env (session, "DESKTOP_SESSION", session_name);
+        session_set_env (session, "GDMSESSION", session_name);
+        if (language && language[0] != '\0')
+        {
+            session_set_env (session, "LANG", language);
+            session_set_env (session, "GDM_LANG", language);
         }
+        session_set_pam_service (session, AUTOLOGIN_SERVICE);
+        session_set_username (session, username);
+        session_set_do_authenticate (session, TRUE);
+        argv = get_session_argv (session_config, seat_get_string_property (seat, "session-wrapper"));
+        session_set_argv (session, argv);
+        g_strfreev (argv);
+
+        g_object_unref (session_config);
     }
-    g_return_if_fail (display != NULL);
+    else
+        g_debug ("Can't find session '%s'", seat_get_string_property (seat, "user-session"));
 
-    /* Cleanup */
-    script = seat_get_string_property (seat, "session-cleanup-script");
-    if (script)
-        run_script (seat, display, script, session_get_user (session));
 
-    if (seat->priv->guest_username && strcmp (session_get_username (session), seat->priv->guest_username) == 0)
+    g_object_unref (user);
+
+    return session;
+}
+
+static Session *
+create_guest_session (Seat *seat)
+{
+    gchar *sessions_dir, **argv;
+    SessionConfig *session_config;
+    Session *session;
+
+    sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
+    session_config = find_session_config (sessions_dir, seat_get_string_property (seat, "user-session"));
+    g_free (sessions_dir);
+    if (!session_config)
     {
-        g_free (seat->priv->guest_username);
-        seat->priv->guest_username = NULL;
+        g_debug ("Can't find session '%s'", seat_get_string_property (seat, "user-session"));
+        return NULL;
     }
+
+    session = create_session (seat, TRUE, NULL);
+    session_set_session_type (session, session_config_get_session_type (session_config));
+    session_set_do_authenticate (session, TRUE);
+    session_set_is_guest (session, TRUE);
+    argv = get_session_argv (session_config, seat_get_string_property (seat, "session-wrapper"));
+    g_object_unref (session_config);
+    session_set_argv (session, argv);
+    g_strfreev (argv);
+  
+    return session;
 }
 
-static gboolean
-display_session_started_cb (Display *display, Seat *seat)
+static Session *
+greeter_create_session_cb (Greeter *greeter, Seat *seat, const gchar *username)
 {
-    g_signal_connect (display_get_session (display), "stopped", G_CALLBACK (session_stopped_cb), seat);
-    emit_upstart_signal ("desktop-session-start");
-    return FALSE;
+    Session *session;
+
+    session = create_session (seat, FALSE, username);
+    session_set_session_type (session, session_get_session_type (SESSION (greeter)));
+    session_set_display_server (session, session_get_display_server (SESSION (greeter)));
+
+    return g_object_ref (session);
 }
 
 static void
-display_ready_cb (Display *display, Seat *seat)
+prepend_argv (gchar ***argv, const gchar *value)
 {
-    /* Switch to this new display */
-    g_debug ("New display ready, switching to it");
-    SEAT_GET_CLASS (seat)->set_active_display (seat, display);
+    gchar **old_argv, **new_argv;
+    gint i;
+
+    old_argv = *argv;
+    new_argv = g_malloc (sizeof (gchar *) * (g_strv_length (*argv) + 2));
+    new_argv[0] = g_strdup (value);
+    for (i = 0; old_argv[i]; i++)
+        new_argv[i + 1] = old_argv[i];
+    new_argv[i + 1] = NULL;
+
+    g_free (*argv);
+    *argv = new_argv;
 }
 
-static Display *
-display_create_display_cb (Display *display, Session *session, Seat *seat)
+static gboolean
+greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *session_name, Seat *seat)
 {
-    Display *d;
+    Session *session, *existing_session;
+    const gchar *username, *language = NULL;
+    SessionConfig *session_config;
+    User *user;
+    gchar *sessions_dir = NULL;
+    gchar **argv;
+    DisplayServer *display_server;
+
+    /* Get the session to use */
+    if (greeter_get_guest_authenticated (greeter))
+    {
+        session = create_guest_session (seat);
+        if (!session)
+            return FALSE;
+        session_set_pam_service (session, AUTOLOGIN_SERVICE);
+    }
+    else
+        session = greeter_get_authentication_session (greeter);
+
+    /* 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);
+
+    /* Return to existing session if it is open */
+    username = session_get_username (session);
+    existing_session = find_user_session (seat, username);
+    if (existing_session && session != existing_session)
+    {
+        g_debug ("Returning to existing user session %s", username);
+        session_stop (session);
+        seat_set_active_session (seat, existing_session);
+        return TRUE;
+    }
+
+    /* Get session command to run */
+    switch (type)
+    {
+    case SESSION_TYPE_LOCAL:
+        sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
+        break;
+    case SESSION_TYPE_REMOTE:
+        sessions_dir = config_get_string (config_get_instance (), "LightDM", "remote-sessions-directory");
+        break;
+    }
+
+    /* Load user preferences */
+    user = session_get_user (session);
+    if (user)
+    {
+        if (!session_name)
+            session_name = user_get_xsession (user);
+        language = user_get_language (user);
+    }
+
+    if (!session_name)
+        session_name = seat_get_string_property (seat, "user-session");
+    if (user)
+        user_set_xsession (session_get_user (session), session_name);
+
+    session_config = find_session_config (sessions_dir, session_name);
+    g_free (sessions_dir);
+    if (!session_config)
+    {
+        g_debug ("Can't find session '%s'", seat_get_string_property (seat, "user-session"));
+        return FALSE;
+    }
+
+    session_set_session_type (session, session_config_get_session_type (session_config));
+    argv = get_session_argv (session_config, seat_get_string_property (seat, "session-wrapper"));
+    session_set_argv (session, argv);
+    g_strfreev (argv);
+    session_set_env (session, "DESKTOP_SESSION", session_name);
+    session_set_env (session, "GDMSESSION", session_name);
+    if (language && language[0] != '\0')
+    {
+        session_set_env (session, "LANG", language);
+        session_set_env (session, "GDM_LANG", language);
+    }
+
+    g_object_unref (session_config);
+
+    /* If can re-use the display server, stop the greeter first */
+    display_server = session_get_display_server (SESSION (greeter));
+    if (can_share_display_server (seat, display_server) &&
+        strcmp (display_server_get_session_type (display_server), session_get_session_type (session)) == 0)
+    {
+        g_debug ("Stopping greeter; display server will be re-used for user session");
 
-    d = create_display (seat);
-    g_signal_connect (d, "ready", G_CALLBACK (display_ready_cb), seat);
-    g_signal_emit (seat, signals[DISPLAY_ADDED], 0, d);
+        /* Run on the same display server after the greeter has stopped */
+        session_set_display_server (session, display_server);
 
-    display_start_with_session (d, session);
+        /* Stop the greeter */
+        session_stop (SESSION (greeter));
+
+        return TRUE;
+    }
+    /* Otherwise start a new display server for this session */
+    else
+    {
+        display_server = create_display_server (seat, session_get_session_type (session));
+        session_set_display_server (session, display_server);
+        if (!display_server_start (display_server))
+        {
+            g_debug ("Failed to start display server for new session");
+            return FALSE;
+        }
 
-    return g_object_ref (d);
+        return TRUE;
+    }
 }
 
-static void
-check_stopped (Seat *seat)
+static Greeter *
+create_greeter_session (Seat *seat)
 {
-    if (seat->priv->stopping &&
-        !seat->priv->stopped &&
-        g_list_length (seat->priv->displays) == 0)
+    gchar *sessions_dir, **argv;
+    SessionConfig *session_config;
+    Greeter *greeter_session;
+    gchar *greeter_user;
+    const gchar *greeter_wrapper;
+
+    g_debug ("Creating greeter session");
+
+    sessions_dir = config_get_string (config_get_instance (), "LightDM", "greeters-directory");
+    session_config = find_session_config (sessions_dir, seat_get_string_property (seat, "greeter-session"));
+    g_free (sessions_dir);
+    if (!session_config)
+        return NULL;
+
+    argv = get_session_argv (session_config, NULL);
+    greeter_wrapper = seat_get_string_property (seat, "greeter-wrapper");
+    if (greeter_wrapper)
     {
-        seat->priv->stopped = TRUE;
-        g_debug ("Seat stopped");
-        g_signal_emit (seat, signals[STOPPED], 0);
+        gchar *path;
+        path = g_find_program_in_path (greeter_wrapper);
+        prepend_argv (&argv, path ? path : greeter_wrapper);
+        g_free (path);
     }
+
+    greeter_session = SEAT_GET_CLASS (seat)->create_greeter_session (seat);
+    session_set_session_type (SESSION (greeter_session), session_config_get_session_type (session_config));
+    seat->priv->sessions = g_list_append (seat->priv->sessions, SESSION (greeter_session));
+    g_signal_connect (greeter_session, "authentication-complete", G_CALLBACK (session_authentication_complete_cb), seat);
+    g_signal_connect (greeter_session, "stopped", G_CALLBACK (session_stopped_cb), seat);
+  
+    set_session_env (SESSION (greeter_session));
+
+    session_set_pam_service (SESSION (greeter_session), GREETER_SERVICE);
+    greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
+    session_set_username (SESSION (greeter_session), greeter_user);
+    g_free (greeter_user);
+    session_set_argv (SESSION (greeter_session), argv);
+    g_strfreev (argv);
+
+    greeter_set_pam_services (greeter_session, USER_SERVICE, AUTOLOGIN_SERVICE);
+    g_signal_connect (greeter_session, "create-session", G_CALLBACK (greeter_create_session_cb), seat);
+    g_signal_connect (greeter_session, "start-session", G_CALLBACK (greeter_start_session_cb), seat);
+
+    /* Set hints to greeter */
+    greeter_set_hint (greeter_session, "default-session", seat_get_string_property (seat, "user-session"));
+    greeter_set_allow_guest (greeter_session, seat_get_allow_guest (seat));
+    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");
+
+    g_object_unref (session_config);
+
+    return greeter_session;
+}
+
+static Session *
+find_session_for_display_server (Seat *seat, DisplayServer *display_server)
+{
+    GList *link;
+
+    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))
+            return session;
+    }
+
+    return NULL;
 }
 
 static void
-display_stopped_cb (Display *display, Seat *seat)
+display_server_ready_cb (DisplayServer *display_server, Seat *seat)
 {
-    gboolean is_active;
+    const gchar *script;
+    Session *session;
 
-    is_active = display == seat_get_active_display (seat);
+    /* Run setup script */
+    script = seat_get_string_property (seat, "display-setup-script");
+    if (script && !run_script (seat, display_server, script, NULL))
+    {
+        g_debug ("Stopping display server due to failed setup script");
+        display_server_stop (display_server);
+        return;
+    }
 
-    g_signal_handlers_disconnect_matched (display, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
-    seat->priv->displays = g_list_remove (seat->priv->displays, display);
+    /* Stop if don't need to run a session */
+    if (!get_start_local_sessions (seat))
+        return;
 
-    g_signal_emit (seat, signals[DISPLAY_REMOVED], 0, display);
+    emit_upstart_signal ("login-session-start");
 
-    /* If no more displays running either start a greeter or stop the seat */
-    if (!seat->priv->stopping)
+    /* Start the session waiting for this display server */
+    session = find_session_for_display_server (seat, display_server);
+    if (session)
     {
-        if (g_list_length (seat->priv->displays) == 0)
+        if (session_get_is_authenticated (session))
         {
-            /* If failed to start then stop this seat */
-            if (!display_get_is_ready (display))
-            {
-                g_debug ("Stopping seat, failed to start a display");
-                seat_stop (seat);
-            }
-            /* Attempt to start a greeter */
-            else if (!seat->priv->can_switch)
-            {
-                g_debug ("Stopping seat, display stopped");
-                seat_stop (seat);             
-            }
-            
-            else if (!seat_switch_to_greeter (seat))
-            {
-                g_debug ("Stopping seat, unable to start greeter");
-                seat_stop (seat);
-            }        
+            g_debug ("Display server ready, running session");
+            run_session (seat, session);
         }
-        else if (is_active)
+        else
         {
-            g_debug ("Active display stopped, switching to greeter");
-            if (!seat_switch_to_greeter (seat))
-            {
-                g_debug ("Stopping seat, unable to start greeter");
-                seat_stop (seat);              
-            }
+            g_debug ("Display server ready, starting session authentication");
+            start_session (seat, session);
         }
     }
-
-    g_object_unref (display);
-
-    check_stopped (seat);
+    else
+    {
+        g_debug ("Stopping not required display server");
+        display_server_stop (display_server);
+    }
 }
 
-static Display *
-create_display (Seat *seat)
+static DisplayServer *
+create_display_server (Seat *seat, const gchar *session_type)
 {
-    Display *display;
     DisplayServer *display_server;
 
-    display_server = SEAT_GET_CLASS (seat)->create_display_server (seat);
-    display = display_new (display_server);
-    g_object_unref (display_server);
+    g_debug ("Creating display server of type %s", session_type);
+
+    display_server = SEAT_GET_CLASS (seat)->create_display_server (seat, session_type);
+    if (!display_server)
+        return NULL;
+
+    seat->priv->display_servers = g_list_append (seat->priv->display_servers, display_server);
+    g_signal_connect (display_server, "ready", G_CALLBACK (display_server_ready_cb), seat);
+    g_signal_connect (display_server, "stopped", G_CALLBACK (display_server_stopped_cb), seat);
 
-    g_signal_connect (display, "display-server-ready", G_CALLBACK (display_display_server_ready_cb), seat);  
-    g_signal_connect (display, "switch-to-user", G_CALLBACK (display_switch_to_user_cb), seat);
-    g_signal_connect (display, "switch-to-guest", G_CALLBACK (display_switch_to_guest_cb), seat);
-    g_signal_connect (display, "get-guest-username", G_CALLBACK (display_get_guest_username_cb), seat);
-    g_signal_connect (display, "create-session", G_CALLBACK (display_create_session_cb), seat);
-    g_signal_connect (display, "start-greeter", G_CALLBACK (display_start_greeter_cb), seat);
-    g_signal_connect (display, "start-session", G_CALLBACK (display_start_session_cb), seat);
-    g_signal_connect_after (display, "start-session", G_CALLBACK (display_session_started_cb), seat);
-    g_signal_connect (display, "create-display", G_CALLBACK (display_create_display_cb), seat);
-    g_signal_connect (display, "stopped", G_CALLBACK (display_stopped_cb), seat);
-    display_set_greeter_session (display, seat_get_string_property (seat, "greeter-session"));
-    display_set_session_wrapper (display, seat_get_string_property (seat, "session-wrapper"));
-    display_set_greeter_wrapper (display, seat_get_string_property (seat, "greeter-wrapper"));
-    display_set_hide_users_hint (display, seat_get_boolean_property (seat, "greeter-hide-users"));
-    display_set_show_manual_login_hint (display, seat_get_boolean_property (seat, "greeter-show-manual-login"));
-    display_set_show_remote_login_hint (display, seat_get_boolean_property (seat, "greeter-show-remote-login"));
-    display_set_allow_guest (display, seat_get_allow_guest (seat));
-    display_set_greeter_allow_guest (display, seat_get_greeter_allow_guest (seat));
-    display_set_user_session (display, SESSION_TYPE_LOCAL, seat_get_string_property (seat, "user-session"));
-    display_set_share_display_server (display, seat->priv->share_display_server);
-
-    seat->priv->displays = g_list_append (seat->priv->displays, display);
-
-    return display;
+    return display_server;
 }
 
-static gboolean
-start_display (Seat *seat, Display *display)
+static Greeter *
+find_greeter_session (Seat *seat)
 {
-    g_signal_emit (seat, signals[DISPLAY_ADDED], 0, display);
+    GList *link;
 
-    /* Switch to this display if currently not looking at anything */
-    if (seat_get_active_display (seat) == NULL)
-        seat_set_active_display (seat, display);
+    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);
+    }
 
-    return display_start (display);
+    return NULL;
 }
 
 gboolean
 seat_switch_to_greeter (Seat *seat)
 {
-    Display *display;
+    Greeter *greeter_session;
+    DisplayServer *display_server;
 
     g_return_val_if_fail (seat != NULL, FALSE);
 
     if (!seat->priv->can_switch)
         return FALSE;
 
-    g_debug ("Switching to greeter");
-
     /* Switch to greeter if one open (shouldn't be though) */
-    if (switch_to_user (seat, NULL, FALSE))
+    greeter_session = find_greeter_session (seat);
+    if (greeter_session)
+    {
+        g_debug ("Switching to existing greeter");
+        seat_set_active_session (seat, SESSION (greeter_session));
         return TRUE;
+    }
 
-    g_debug ("Starting new display for greeter");
+    greeter_session = create_greeter_session (seat);
+    if (seat->priv->session_to_activate)
+        g_object_unref (seat->priv->session_to_activate);
+    seat->priv->session_to_activate = g_object_ref (greeter_session);
 
-    display = create_display (seat);
-    g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
+    display_server = create_display_server (seat, session_get_session_type (SESSION (greeter_session)));
+    session_set_display_server (SESSION (greeter_session), display_server);
+    if (!display_server_start (display_server))
+        return FALSE;
 
-    return start_display (seat, display);
+    return TRUE;
 }
 
 gboolean
 seat_switch_to_user (Seat *seat, const gchar *username, const gchar *session_name)
 {
-    Display *display;
+    Session *session;
+    DisplayServer *display_server;
 
     g_return_val_if_fail (seat != NULL, FALSE);
     g_return_val_if_fail (username != NULL, FALSE);
@@ -598,28 +1193,50 @@ seat_switch_to_user (Seat *seat, const gchar *username, const gchar *session_nam
 
     g_debug ("Switching to user %s", username);
 
-    /* Switch to session if one open */
-    if (switch_to_user (seat, username, FALSE))
+    session = find_user_session (seat, username);
+    if (session)
+    {
+        g_debug ("Switching to existing user session %s", username);
+        seat_set_active_session (seat, session);
         return TRUE;
+    }
 
-    if (username)
-        g_debug ("Starting new display for greeter with user %s selected", username);
-    else
-        g_debug ("Starting new display for greeter");
+    session = create_user_session (seat, username);
+    if (!session)
+        return FALSE;
+    if (seat->priv->session_to_activate)
+        g_object_unref (seat->priv->session_to_activate);
+    seat->priv->session_to_activate = g_object_ref (session);
+    session_set_pam_service (session, USER_SERVICE);
+
+    display_server = create_display_server (seat, session_get_session_type (session));
+    session_set_display_server (session, display_server);
+    if (!display_server_start (display_server))
+        return FALSE;
 
-    display = create_display (seat);
-    g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
-    display_set_select_user_hint (display, username, FALSE, TRUE);
-    if (session_name != NULL)
-        display_set_user_session (display, SESSION_TYPE_LOCAL, session_name);
+    return FALSE;
+}
 
-    return start_display (seat, display);
+static Session *
+find_guest_session (Seat *seat)
+{
+    GList *link;
+
+    for (link = seat->priv->sessions; link; link = link->next)
+    {
+        Session *session = link->data;
+        if (!session_get_is_stopping (session) && session_get_is_guest (session))
+            return session;
+    }
+
+    return NULL;
 }
 
 gboolean
 seat_switch_to_guest (Seat *seat, const gchar *session_name)
 {
-    Display *display;
+    Session *session;
+    DisplayServer *display_server;
 
     g_return_val_if_fail (seat != NULL, FALSE);
 
@@ -627,27 +1244,36 @@ seat_switch_to_guest (Seat *seat, const gchar *session_name)
         return FALSE;
 
     /* Switch to session if one open */
-    if (seat->priv->guest_username)
+    session = find_guest_session (seat);
+    if (session)
     {
-        g_debug ("Switching to existing guest account %s", seat->priv->guest_username);
-        return switch_to_user (seat, seat->priv->guest_username, FALSE);
+        g_debug ("Switching to existing guest account %s", session_get_username (session));
+        seat_set_active_session (seat, session);
+        return TRUE;
     }
 
-    g_debug ("Starting new display for automatic guest login");
+    session = create_guest_session (seat);
+    if (!session)
+        return FALSE;
+
+    display_server = create_display_server (seat, session_get_session_type (session));
+    if (!display_server_start (display_server))
+        return FALSE;
 
-    display = create_display (seat);
-    g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
-    display_set_autologin_user (display, NULL, TRUE, 0);
-    if (session_name != NULL)
-        display_set_user_session (display, SESSION_TYPE_LOCAL, session_name);
+    if (seat->priv->session_to_activate)
+        g_object_unref (seat->priv->session_to_activate);
+    seat->priv->session_to_activate = g_object_ref (session);
+    session_set_pam_service (session, AUTOLOGIN_SERVICE);
+    session_set_display_server (session, display_server);
 
-    return start_display (seat, display);
+    return TRUE;
 }
 
 gboolean
 seat_lock (Seat *seat, const gchar *username)
 {
-    Display *display;
+    Greeter *greeter_session;
+    DisplayServer *display_server;
 
     g_return_val_if_fail (seat != NULL, FALSE);
 
@@ -657,17 +1283,31 @@ seat_lock (Seat *seat, const gchar *username)
     g_debug ("Locking seat");
 
     /* Switch to greeter if one open (shouldn't be though) */
-    if (switch_to_user (seat, NULL, FALSE))
+    greeter_session = find_greeter_session (seat);
+    if (greeter_session)
+    {
+        g_debug ("Switching to existing greeter");
+        seat_set_active_session (seat, SESSION (greeter_session));
         return TRUE;
+    }
 
-    g_debug ("Starting new display for greeter (lock screen)");
+    greeter_session = create_greeter_session (seat);
+    if (!greeter_session)
+        return FALSE;
 
-    display = create_display (seat);
-    g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
-    display_set_lock_hint (display, TRUE);
-    display_set_select_user_hint (display, username, FALSE, FALSE);
+    display_server = create_display_server (seat, session_get_session_type (SESSION (greeter_session)));
+    if (!display_server_start (display_server))
+        return FALSE;
 
-    return start_display (seat, display);
+    if (seat->priv->session_to_activate)
+        g_object_unref (seat->priv->session_to_activate);
+    seat->priv->session_to_activate = g_object_ref (greeter_session);
+    greeter_set_hint (greeter_session, "lock-screen", "true");
+    if (username)
+        greeter_set_hint (greeter_session, "select-user", username);
+    session_set_display_server (SESSION (greeter_session), display_server);
+
+    return TRUE;
 }
 
 void
@@ -690,35 +1330,15 @@ seat_get_is_stopping (Seat *seat)
     return seat->priv->stopping;
 }
 
-static void
-seat_real_setup (Seat *seat)
+static gboolean
+seat_real_get_start_local_sessions (Seat *seat)
 {
+    return TRUE;
 }
 
 static void
-autologin_greeter_ready_cb (Display *display, Seat *seat)
+seat_real_setup (Seat *seat)
 {
-    const gchar *autologin_username;
-    gboolean autologin_guest;
-    Display *autologin_display;
-
-    g_debug ("Greeter ready, starting autologin session");
-
-    /* Get autologin settings */
-    autologin_username = seat_get_string_property (seat, "autologin-user");
-    if (g_strcmp0 (autologin_username, "") == 0)
-        autologin_username = NULL;
-    autologin_guest = seat_get_boolean_property (seat, "autologin-guest");
-
-    if (autologin_guest)
-        g_debug ("Starting new display for automatic guest login");
-    else if (autologin_username)
-        g_debug ("Starting new display for automatic login as user %s", autologin_username);
-
-    autologin_display = create_display (seat);
-    display_set_autologin_user (autologin_display, autologin_username, autologin_guest, 0);
-
-    start_display (seat, autologin_display);
 }
 
 static gboolean
@@ -727,12 +1347,19 @@ seat_real_start (Seat *seat)
     const gchar *autologin_username;
     int autologin_timeout;
     gboolean autologin_guest;
-    gboolean do_autologin;
     gboolean autologin_in_background;
-    Display *display;
+    Session *session = NULL, *background_session = NULL;
 
     g_debug ("Starting seat");
 
+    /* If this display server doesn't have a session running on it, just start it */
+    if (!get_start_local_sessions (seat))
+    {
+        DisplayServer *display_server;
+        display_server = create_display_server (seat, "x"); // FIXME: Not necessarily an X seat, but not sure what to put here
+        return display_server_start (display_server);
+    }
+
     /* Get autologin settings */
     autologin_username = seat_get_string_property (seat, "autologin-user");
     if (g_strcmp0 (autologin_username, "") == 0)
@@ -740,58 +1367,130 @@ seat_real_start (Seat *seat)
     autologin_timeout = seat_get_integer_property (seat, "autologin-user-timeout");
     autologin_guest = seat_get_boolean_property (seat, "autologin-guest");
     autologin_in_background = seat_get_boolean_property (seat, "autologin-in-background");
-    do_autologin = autologin_username != NULL || autologin_guest;
 
-    /* Background automatic logins need the greeter showing first */
-    if (do_autologin && autologin_in_background && !switch_to_user (seat, NULL, FALSE)) 
+    /* Autologin if configured */
+    if (autologin_timeout == 0 || autologin_in_background)
     {
-        g_debug ("Autologin in background, opening greeter first");
-        display = create_display (seat);
-        g_signal_connect (display, "ready", G_CALLBACK (autologin_greeter_ready_cb), seat);
-        if (autologin_timeout > 0)
-            display_set_autologin_user (display, autologin_username, autologin_guest, autologin_timeout);
-        return start_display (seat, display);
-    }
+        if (autologin_guest)
+            session = create_guest_session (seat);
+        else if (autologin_username != NULL)
+            session = create_user_session (seat, autologin_username);
 
-    if (autologin_guest)
-        g_debug ("Starting new display for automatic guest login");
-    else if (autologin_username)
-        g_debug ("Starting new display for automatic login as user %s", autologin_username);
-    else
-        g_debug ("Starting new display for greeter");
+        if (session)
+            session_set_pam_service (session, AUTOLOGIN_SERVICE);
 
-    display = create_display (seat);
-    g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
-    display_set_autologin_user (display, autologin_username, autologin_guest, autologin_timeout);
+        /* Load in background if required */
+        if (autologin_in_background && session)
+        {
+            background_session = session;
+            session = NULL;
+        }
+      
+        if (session)
+        {
+            DisplayServer *display_server;
 
-    return start_display (seat, display);
-}
+            if (seat->priv->session_to_activate)
+                g_object_unref (seat->priv->session_to_activate);
+            seat->priv->session_to_activate = g_object_ref (session);
 
-static void
-seat_real_set_active_display (Seat *seat, Display *display)
-{
-    GList *link;
+            display_server = create_display_server (seat, session_get_session_type (session));
+            session_set_display_server (session, display_server);
+            if (!display_server || !display_server_start (display_server))
+            {
+                g_debug ("Can't create display server for automatic login");
+                session_stop (session);
+                if (display_server)
+                    display_server_stop (display_server);
+                session = NULL;
+            }
+        }
+    }
 
-    for (link = seat->priv->displays; link; link = link->next)
+    /* Fallback to a greeter */
+    if (!session)
     {
-        Display *d = link->data;
+        Greeter *greeter_session;
+        DisplayServer *display_server;
 
-        if (d == display)
-            continue;
+        greeter_session = create_greeter_session (seat);
+        if (!greeter_session)
+        {
+            g_debug ("Failed to create greeter session");
+            return FALSE;
+        }
+
+        if (seat->priv->session_to_activate)
+            g_object_unref (seat->priv->session_to_activate);
+        seat->priv->session_to_activate = g_object_ref (greeter_session);
+        session = SESSION (greeter_session);
 
-        /* Stop any greeters and lock any other sessions */
-        if (!display_get_username (d))
+        if (autologin_timeout)
         {
-            g_debug ("Stopping background greeter");
-            display_stop (d);
+            gchar *value;
+
+            value = g_strdup_printf ("%d", autologin_timeout);
+            greeter_set_hint (greeter_session, "autologin-timeout", value);
+            g_free (value);
+            if (autologin_username)
+                greeter_set_hint (greeter_session, "autologin-user", autologin_username);
+            if (autologin_guest)
+                greeter_set_hint (greeter_session, "autologin-guest", "true");
         }
-        else
-            display_lock (d);
-    }  
+
+        display_server = create_display_server (seat, session_get_session_type (session));
+        session_set_display_server (session, display_server);
+        if (!display_server || !display_server_start (display_server))
+        {
+            g_debug ("Can't create display server for greeter");
+            session_stop (session);
+            if (display_server)
+                display_server_stop (display_server);
+            session = NULL;
+        }
+    }
+
+    /* Fail if can't start a session */
+    if (!session)
+    {
+        seat_stop (seat);
+        return FALSE;
+    }
+
+    /* Start background session */
+    if (background_session)
+    {
+        DisplayServer *background_display_server;
+
+        background_display_server = create_display_server (seat, session_get_session_type (background_session));
+        session_set_display_server (background_session, background_display_server);
+        if (!display_server_start (background_display_server))
+            g_warning ("Failed to start display server for background session");
+    }
+
+    seat->priv->started = TRUE;
+    return TRUE;
+}
+
+static Greeter *
+seat_real_create_greeter_session (Seat *seat)
+{
+    return greeter_new ();
+}
+
+static Session *
+seat_real_create_session (Seat *seat, Session *user_session)
+{
+    return session_new ();
+}
+
+static void
+seat_real_set_active_session (Seat *seat, Session *session)
+{
 }
 
-static Display *
-seat_real_get_active_display (Seat *seat)
+static Session *
+seat_real_get_active_session (Seat *seat)
 {
     return NULL;
 }
@@ -799,20 +1498,40 @@ seat_real_get_active_display (Seat *seat)
 static void
 seat_real_stop (Seat *seat)
 {
-    GList *displays, *link;
+    GList *list, *link;
 
     check_stopped (seat);
     if (seat->priv->stopped)
         return;
 
-    /* Stop all the displays on the seat. Copy the list as it might be modified if a display stops during this loop */
-    displays = g_list_copy (seat->priv->displays);
-    for (link = displays; link; link = link->next)
+    /* Stop all the display servers and sessions on the seat. Copy the list as
+     * it might be modified if a display server / session stops during this loop */
+    list = g_list_copy (seat->priv->display_servers);
+    for (link = list; link; link = link->next)
+        g_object_ref (link->data);
+    for (link = list; link; link = link->next)
     {
-        Display *display = link->data;
-        display_stop (display);
+        DisplayServer *display_server = link->data;
+        if (!display_server_get_is_stopping (display_server))
+        {
+            g_debug ("Stopping display server");
+            display_server_stop (display_server);
+        }
     }
-    g_list_free (displays);
+    g_list_free_full (list, g_object_unref);
+    list = g_list_copy (seat->priv->sessions);
+    for (link = list; link; link = link->next)
+        g_object_ref (link->data);
+    for (link = list; link; link = link->next)
+    {
+        Session *session = link->data;
+        if (!session_get_is_stopping (session))
+        {
+            g_debug ("Stopping session");
+            session_stop (session);
+        }
+    }
+    g_list_free_full (list, g_object_unref);
 }
 
 static void
@@ -832,13 +1551,22 @@ seat_finalize (GObject *object)
     self = SEAT (object);
 
     g_hash_table_unref (self->priv->properties);
-    g_free (self->priv->guest_username);
-    for (link = self->priv->displays; link; link = link->next)
+    for (link = self->priv->display_servers; link; link = link->next)
     {
-        Display *display = link->data;
-        g_signal_handlers_disconnect_matched (display, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
+        DisplayServer *display_server = link->data;
+        g_signal_handlers_disconnect_matched (display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
     }  
-    g_list_free_full (self->priv->displays, g_object_unref);
+    g_list_free_full (self->priv->display_servers, g_object_unref);
+    for (link = self->priv->sessions; link; link = link->next)
+    {
+        Session *session = link->data;
+        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->session_to_activate)
+        g_object_unref (self->priv->session_to_activate);
 
     G_OBJECT_CLASS (seat_parent_class)->finalize (object);
 }
@@ -848,10 +1576,13 @@ seat_class_init (SeatClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
+    klass->get_start_local_sessions = seat_real_get_start_local_sessions;
     klass->setup = seat_real_setup;
     klass->start = seat_real_start;
-    klass->set_active_display = seat_real_set_active_display;
-    klass->get_active_display = seat_real_get_active_display;
+    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;
+    klass->get_active_session = seat_real_get_active_session;
     klass->run_script = seat_real_run_script;
     klass->stop = seat_real_stop;
 
@@ -859,28 +1590,36 @@ seat_class_init (SeatClass *klass)
 
     g_type_class_add_private (klass, sizeof (SeatPrivate));
 
-    signals[DISPLAY_ADDED] =
-        g_signal_new ("display-added",
+    signals[SESSION_ADDED] =
+        g_signal_new ("session-added",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (SeatClass, session_added),
+                      NULL, NULL,
+                      NULL,
+                      G_TYPE_NONE, 1, SESSION_TYPE);
+    signals[RUNNING_USER_SESSION] =
+        g_signal_new ("running-user-session",
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (SeatClass, display_added),
+                      G_STRUCT_OFFSET (SeatClass, running_user_session),
                       NULL, NULL,
-                      g_cclosure_marshal_VOID__OBJECT,
-                      G_TYPE_NONE, 1, DISPLAY_TYPE);
-    signals[DISPLAY_REMOVED] =
-        g_signal_new ("display-removed",
+                      NULL,
+                      G_TYPE_NONE, 1, SESSION_TYPE);
+    signals[SESSION_REMOVED] =
+        g_signal_new ("session-removed",
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
-                      G_STRUCT_OFFSET (SeatClass, display_removed),
+                      G_STRUCT_OFFSET (SeatClass, session_removed),
                       NULL, NULL,
-                      g_cclosure_marshal_VOID__OBJECT,
-                      G_TYPE_NONE, 1, DISPLAY_TYPE);
+                      NULL,
+                      G_TYPE_NONE, 1, SESSION_TYPE);
     signals[STOPPED] =
         g_signal_new ("stopped",
                       G_TYPE_FROM_CLASS (klass),
                       G_SIGNAL_RUN_LAST,
                       G_STRUCT_OFFSET (SeatClass, stopped),
                       NULL, NULL,
-                      g_cclosure_marshal_VOID__VOID,
+                      NULL,
                       G_TYPE_NONE, 0);
 }